How to Implement Image Scale and Crop
If you've ever worked with Adobe Photoshop or any image processing tool, you've probably resized an image to a certain frame or dimension. Suppose the original image dimension is 600x800 (3:4 aspect ratio) and you want to fit everything in a 500x750 (2:3 aspect ratio) frame. What you will do is to shrink the width from 600 to 500 and stretch the height from 800 to 750. This process of resizing is called image scaling in computer graphics.
Looking at the images above, stretching the original image without maintaining the aspect ratio distorts the resulting image. If the aspect ratio of the resulting image does not match with the original, then there's a need to crop the original image either horizontally or vertically to produce the desired outcome. Perhaps, proper scaling by maintaining the aspect ratio and cropping may be the only acceptable method in resizing an image unless the artist has a really bad taste of design.
But where do we start copying pixels and where to we end in order for us to scale and crop the resulting image and still maintain the aspect ratio?
In case the resulting frame has the same aspect ratio as the source, then we should copy everything from the source since no cropping is necessary.
Hence we can write:
double sr, tr;
sr = (double) sw / sh; // Aspect ratio of source
tr = (double) tw / th; // Aspect ratio of dest
if (sr == tr)
{
x = 0;
y = 0;
w = sw;
h = sh;
}
If the aspect ratio of the target image is greater than the source then it means it means we need to copy the entire width of the source image and crop the height accordingly from the middle of the source image to maintain the aspect ratio.
To get the height from the source using the destinations aspect ratio, we simply do cross multiplication:
h? th
-- = --
sw tw
h? = sw x th
-------
tw
To write this in program, we do:
elseif (tr > sr)
{
x = 0;
w = sw;
h = (double) sw * th / tw;
y = ((double) sy / 2) - (h / 2);
}
If the aspect ratio of the target image is less than the source then we do the reverse:
else
{
y = 0;
h = sh;
w = (double) sh * tw / th;
x = ((double) sw / 2) - (w / 2);
}
Now that we've identified the x and y coordinates where we need to start the pixel copy and the width and height, which bounds the copying from the source, we can now do proper scaling of the source image to the destination frame.
wratio = (double) tw / sw;
hratio = (double) th / sh;
for (sy = y, dy = 0; sy < y + h; ++sy)
{
for (sx = x, dx = 0; sx < x + w; ++sx)
{
rgba = getPixel(source, sy, sx);
plot(dest, dx, dy, wratio, hratio)
dx += wratio;
}
dy += hratio;
}
getPixel and plot are just hypothetical functions that you can create to scale up or scale down the image.
The getPixel function gets RGBA unsigned long value of the pixel from the source image.
The plot function sets RGBA of the destination from dx to dx + wratio and day to dy + ratio. So, in case you have a 1:2 ratio between the source and destination width then for every 1 pixel from the source, it copies it twice to the destination.
The attached source code is created using C/C++ and SDL2.0. I did not implement the actual copying of pixels from source to destination as this is already available in SDL_BlitScale() function. You can however very easily implement the same on the programming language of your choosing by following the script above.