Изменение размера изображения в ограничивающем поле

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

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

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

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

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

9 ответов


найти который меньше: MaxWidth / w или MaxHeight / h Тогда умножьте w и h число

объяснение:

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

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

аналогично по высоте.

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


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

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

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


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

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


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

например,

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

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

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

код 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)

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

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)

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


основываясь на предыдущих ответах, вот функция 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
    };
}