8

For instance, if I need to fill a bounding box that is 100px wide by 50px tall, the following input images would have the following behavior:

  1. 200w x 200h gets scaled down 50% and 25% gets chopped off the top and bottom.

  2. 200w x 100h gets scaled down 50% with no cropping.

  3. 100w x 200h gets is not scaled, but 75px get chopped off top and bottom.

This seems like it'd be a common resizing function, but I haven't been able to track down an example of the algorithm.

Will accept answer in any language including pseudo code. A link to a page with the answer is great too!

Juha Syrjälä
  • 33,425
  • 31
  • 131
  • 183
Larsenal
  • 49,878
  • 43
  • 152
  • 220

3 Answers3

13

What you're asking for is pretty easy. Calculate the different scaling factors for the width and the height, then pick the larger one for your actual scale factor. Multiply your input size by the scale, and crop whichever one comes out too large.

scale = max(maxwidth/oldwidth, maxheight/oldheight)
scaledwidth = oldwidth * scale
scaledheight = oldheight * scale
if scaledheight > maxheight:
    croptop = (scaledheight - maxheight) / 2
    cropbottom = (scaledheight - maxheight) - croptop
if scaledwidth > maxwidth:
    cropleft = (scaledwidth - maxwidth) / 2
    cropright = (scaledwidth - maxwidth) - cropleft
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • Only the X (width) is under constraints of resizing. The other Y (height) is cropping. – Suroot Mar 06 '09 at 03:55
  • That constraint wasn't stated, nor was it evident from your examples. In that case, use scale=maxwidth/oldwidth instead and remove the cropleft/cropright calculations, the rest remains the same. – Mark Ransom Mar 06 '09 at 03:58
  • Should we round the output numbers as they are often fractions of a pixel? – Gringo Suave Oct 28 '16 at 16:36
  • @GringoSuave yes rounding of `scaledwidth` and `scaledheight` would be a good idea. Especially since truncation might leave you with an off-by-one error due to floating point inaccuracy. – Mark Ransom Oct 28 '16 at 17:42
1

Here we make sure that we only scale if X is greater than 100%; then after we've done that, we ensure that we are only 50 px on our Y. If we're greater than 50, then we take the difference and divide by 2 to get the amount removed from the top/bottom.

double percent_x = 1.0;

if(X > 100) {
 percent_x = (float)100/X;
 X *= percent_x;
 Y *= percent_x;
}

int diff_y;
int top_cut, bott_cut;
if( Y > 50 ) {
 diff_y = (Y - 50) / 2;
 top_cut = bott_cut = diff_y;
}
Suroot
  • 4,315
  • 1
  • 22
  • 28
0

Largely inspired by Mark Ransom's answer (thank you so much - you saved me). For anyone who would like to do this without cropping the image (just fit within the bounds), I've found that this works:

if (maxWidth > width && maxHeight > height) {
  return { width, height };
}

aspectRatio = width / height,
scale       = max(maxWidth / width, maxHeight / height);

scaledHeight = height * scale,
scaledWidth  = width * scale;

if (scaledHeight > maxHeight) {
  scaledHeight = maxHeight;
  scaledWidth  = aspectRatio * scaledHeight;
} else if (scaledWidth > maxWidth) {
  scaledWidth  = maxWidth;
  scaledHeight = scaledWidth / aspectRatio;
}

return { scaledHeight, scaledWidth };
Vinay
  • 6,204
  • 6
  • 38
  • 55