Изменить размер изображения, чтобы оно поместилось в ограничительной рамке - PullRequest
38 голосов
/ 10 июля 2009

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

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

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

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

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

Ответы [ 9 ]

81 голосов
/ 10 июля 2009

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

Пояснение:

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

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

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

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

26 голосов
/ 10 июля 2009

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

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;
}

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

7 голосов
/ 20 марта 2011

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

Например,

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 ) );
}
6 голосов
/ 20 августа 2009

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

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

5 голосов
/ 29 июля 2012

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

    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))
            );
    }
3 голосов
/ 06 марта 2015

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

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)
2 голосов
/ 10 июля 2009

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)
1 голос
/ 25 января 2014

Основываясь на предыдущих ответах, вот функция 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
    };
}
0 голосов
/ 10 июля 2009

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

...