Подтвердить что ты не робот

Изменить размер изображения в соответствии с рамкой

Легкая проблема, но по какой-то причине я просто не могу понять это сегодня.

Мне нужно изменить размер изображения до максимально возможного размера, который будет помещаться в ограничительную рамку, сохраняя соотношение сторон.

В основном я ищу код для заполнения этой функции:

void CalcNewDimensions(ref int w, ref int h, int MaxWidth, int MaxHeight);

Где w и h - исходная высота и ширина (в), а новая высота и ширина (out), а MaxWidth и MaxHeight определяют ограничивающий прямоугольник, в который должно быть вложено изображение.

4b9b3361

Ответ 1

Найдите меньший размер: MaxWidth / w или MaxHeight / h Затем умножьте w и h на это число

Объяснение:

Вам нужно найти коэффициент масштабирования, который делает изображение подходящим.

Чтобы найти коэффициент масштабирования s, для ширины, тогда s должен быть таким, чтобы:   s * w = MaxWidth. Следовательно, коэффициент масштабирования MaxWidth / w.

Аналогично для высоты.

Тот, который требует наибольшего масштабирования (меньше s), является фактором, по которому вы должны масштабировать все изображение.

Ответ 2

Основываясь на предположении Эрика, я бы сделал что-то вроде этого:

private static Size ExpandToBound(Size image, Size boundingBox)
{       
    double widthScale = 0, heightScale = 0;
    if (image.Width != 0)
        widthScale = (double)boundingBox.Width / (double)image.Width;
    if (image.Height != 0)
        heightScale = (double)boundingBox.Height / (double)image.Height;                

    double scale = Math.Min(widthScale, heightScale);

    Size result = new Size((int)(image.Width * scale), 
                        (int)(image.Height * scale));
    return result;
}

Возможно, я немного переборщил с кастами, но я просто пытался сохранить точность вычислений.

Ответ 3

Пробовал код г-на Уоррена, но он не давал достоверных результатов.

Например,

ExpandToBound(new Size(640,480), new Size(66, 999)).Dump();
// {Width=66, Height=49}

ExpandToBound(new Size(640,480), new Size(999,50)).Dump();
// {Width=66, Height=50}

Вы можете видеть, высота = 49 и высота = 50 в другой.

Здесь моя (основанная версия кода г-на Уоррена) без несоответствия и небольшой рефакторинг:

// Passing null for either maxWidth or maxHeight maintains aspect ratio while
//        the other non-null parameter is guaranteed to be constrained to
//        its maximum value.
//
//  Example: maxHeight = 50, maxWidth = null
//    Constrain the height to a maximum value of 50, respecting the aspect
//    ratio, to any width.
//
//  Example: maxHeight = 100, maxWidth = 90
//    Constrain the height to a maximum of 100 and width to a maximum of 90
//    whichever comes first.
//
private static Size ScaleSize( Size from, int? maxWidth, int? maxHeight )
{
   if ( !maxWidth.HasValue && !maxHeight.HasValue ) throw new ArgumentException( "At least one scale factor (toWidth or toHeight) must not be null." );
   if ( from.Height == 0 || from.Width == 0 ) throw new ArgumentException( "Cannot scale size from zero." );

   double? widthScale = null;
   double? heightScale = null;

   if ( maxWidth.HasValue )
   {
       widthScale = maxWidth.Value / (double)from.Width;
   }
   if ( maxHeight.HasValue )
   {
       heightScale = maxHeight.Value / (double)from.Height;
   }

   double scale = Math.Min( (double)(widthScale ?? heightScale),
                            (double)(heightScale ?? widthScale) );

   return new Size( (int)Math.Floor( from.Width * scale ), (int)Math.Ceiling( from.Height * scale ) );
}

Ответ 4

Чтобы выполнить аспектное заполнение вместо соответствия аспекту, используйте большее соотношение. То есть, измените код Matt от Math.Min до Math.Max.

(Аспектное заполнение не оставляет ни одной ограничивающей рамки пустой, но может помещать часть изображения за пределы, в то время как аспект не оставляет ни одного изображения за пределами границ, но может оставить часть ограничивающей рамки пустой.)

Ответ 5

Следующий код дает более точные результаты:

    public static Size CalculateResizeToFit(Size imageSize, Size boxSize)
    {
        // TODO: Check for arguments (for null and <=0)
        var widthScale = boxSize.Width / (double)imageSize.Width;
        var heightScale = boxSize.Height / (double)imageSize.Height;
        var scale = Math.Min(widthScale, heightScale);
        return new Size(
            (int)Math.Round((imageSize.Width * scale)),
            (int)Math.Round((imageSize.Height * scale))
            );
    }

Ответ 6

Убер простой.:) Проблема заключается в том, чтобы найти фактор, по которому вам нужно умножить ширину и высоту. Решение состоит в том, чтобы попытаться использовать его, и если оно не подходит, используйте другое. Так что...

private float ScaleFactor(Rectangle outer, Rectangle inner)
{
    float factor = (float)outer.Height / (float)inner.Height;
    if ((float)inner.Width * factor > outer.Width) // Switch!
        factor = (float)outer.Width / (float)inner.Width;
    return factor;
}

Чтобы установить изображение (pctRect) в окно (wndRect), вызовите этот

float factor=ScaleFactor(wndRect, pctRect); // Outer, inner
RectangleF resultRect=new RectangleF(0,0,pctRect.Width*factor,pctRect.Height*Factor)

Ответ 7

Код Python, но, возможно, он укажет вам в правильном направлении:

def fit_within_box(box_width, box_height, width, height):
    """
    Returns a tuple (new_width, new_height) which has the property
    that it fits within box_width and box_height and has (close to)
    the same aspect ratio as the original size
    """
    new_width, new_height = width, height
    aspect_ratio = float(width) / float(height)

    if new_width > box_width:
        new_width = box_width
        new_height = int(new_width / aspect_ratio)

    if new_height > box_height:
        new_height = box_height
        new_width = int(new_height * aspect_ratio)

    return (new_width, new_height)

Ответ 8

Основываясь на предыдущих ответах, здесь функция Javascript:

/**
* fitInBox
* Constrains a box (width x height) to fit in a containing box (maxWidth x maxHeight), preserving the aspect ratio
* @param width      width of the box to be resized
* @param height     height of the box to be resized
* @param maxWidth   width of the containing box
* @param maxHeight  height of the containing box
* @param expandable (Bool) if output size is bigger than input size, output is left unchanged (false) or expanded (true)
* @return           {width, height} of the resized box
*/
function fitInBox(width, height, maxWidth, maxHeight, expandable) {
    "use strict";

    var aspect = width / height,
        initWidth = width,
        initHeight = height;

    if (width > maxWidth || height < maxHeight) {
        width = maxWidth;
        height = Math.floor(width / aspect);
    }

    if (height > maxHeight || width < maxWidth) {
        height = maxHeight;
        width = Math.floor(height * aspect);
    }

    if (!!expandable === false && (width >= initWidth || height >= initHeight)) {
        width = initWidth;
        height = initHeight;
    }

    return {
        width: width,
        height: height
    };
}

Ответ 9

У меня была бы аналогичная проблема, и я нашел это очень полезным: статья. Как я правильно понял, вам нужно изменить размер изображения?

Ответ 10

private static Size CalculateDimensions(Size originalSize, Size targetSize)
{
    Size newSize = new Size();
    int cooficientA = targetSize.Height * originalSize.Width;
    int cooficientB = targetSize.Width * originalSize.Height;

    if (cooficientA < cooficientB)
    {
        newSize.Width = (int)Math.Round((double)cooficientA / originalSize.Height);
        newSize.Height = targetSize.Height;
    }
    else
    {
        newSize.Width = targetSize.Width;
        newSize.Height = (int)Math.Round((double)cooficientB / originalSize.Width);
    }
    return newSize;
}

Size newSize = CalculateDimensions(originalSize, targetSize);
x = (targetSize.Width - newSize.Width) / 2;
y = (targetSize.Height - newSize.Height) / 2;