Saturday, March 07, 2009

Scaling images while keeping aspect ratio and max size

In an application I am writing, I recently came across the need to accept uploaded pictures and scale them to a set size for inclusion in a table. Simple stuff, just a “let’s make all pictures the same size for easier layout” kind of deal. Say the “target” size was 800x600, and given a size originalSize, the function needs to figure out a new size for the image that meets the following demands:

1. The entire image must fit inside the target size.

2. The image must keep its’ aspect ratio.

3. The size of the new image must never be larger in either direction than the original (no upscaling)

4. The image must be as large as possible without breaking the three other rules.

After a few days of wrapping my head around this (and doing other stuff), I realized it was all the aspect ratio – which way we used to calculate the width/height that should be returned, depends entirely on which of the aspect ratios are larger; That of the original image, or that of the target size.

I of course started with the unit tests – a MbUnit RowTest with 20 rows of different combinations of picture sizes, new sizes, and expected result sizes. I had done all the calculations on paper in advance and was pretty sure they were correct.

After a little bit of experimentation, I ended up with this method:

  1: public static Size GetRenderSize(Size originalSize, Size targetSize)
  2: {
  3:     float targetRatio = targetSize.Width / (float)targetSize.Height;
  4:     float imageRatio = originalSize.Width / (float)originalSize.Height;
  5: 
  6:     if (imageRatio < targetRatio) // Target ratio is wider than the source ratio, scale by height
  7:     {
  8:         var smallerY = Math.Min(originalSize.Height, targetSize.Height);
  9:         return new Size((int)Math.Round(smallerY * imageRatio,0), smallerY);
 10:     }
 11:     // Target ratio taller than source ratio or the same - scale by width
 12:     var smallerX = Math.Min(originalSize.Width, targetSize.Width);
 13:     return new Size(smallerX, (int)Math.Round(smallerX / imageRatio,0));
 14: }


A lot simpler than I thought, and seems to give correct results on each and every calculation. :)

3 comments:

Anja said...

Hey Rune! Den andre bloggen din er nesten like morsom som denne!!

return new Size(smallerX, (int)Math.Round(smallerX / imageRatio,0));, du lissom
U crack me up!! ;)

Unknown said...

Hehe, takk takk.. Ikke noe sier komedie som tilfeldige tegn helt uten mening og betydning. :P

Unknown said...

Case in point...