For my game project OpenPatrician I use rendered image which I created using the 3D software Blender. As it is the nature of things there are many different users out there and everyone has its own screen with its own resolution. Therefor the challenge lies therein to create a game screen that uses the available space on the screen in an optimal way.
The game has some informational parts which are constant in size, but the main screen – where the actual game is happening can vary in size. Therefor it is important to be able to use an image as optimal as possible.
The rendered images have a resolution of 1920×1920, which at least at the moment is larger than can be displayed on any screen, so the image will be down scaled. This is the better alternative, as up scaling results in loss of accuracy. The techniques described in this article is no rocket science, but it might be a bit complex to get all the aspects right due to the huge amount of combination that are possible. Therefor I chose a test driven approach to the implementation, where I defined the expected result first and then implemented the method to get there.
To achieve an optimal result there are three possible or potential methods, that might be applied to an image:
- Scaling: this works perfect, if the target dimension is simply a factorized result of the original dimension
- Cropping: The image might be designed in a way that allows one side to be cropped away a certain amount
- Bordering: This is always the last step which adds either horizontal or vertical borders to the image.
The cropping method might need a bit of further explanation: I designed the images in such a way, that allows me to cut away a portion of the image without loosing anything important. Or more correctly, I expect an image to be cropped but this allows me a bit of leeway to accommodate different resolutions.
This example rendering has a huge amount of foreground, and if it were left that way the proportions would just look odd. As I expect the width to be containing axis, meaning the target dimension is wider than it is high, I can scale the image to fit to exactly that width and then hopefully cut away enough at the bottom to match the height.
The tricky part is to figure out how to proceed to gain optimal results. The most simple cases are where no cropping is possible, or no cropping is needed as the aspect ration is already correct. This first case must be handled with scaling and bordering and the second case can be solved with scaling alone.
The more complex cases are when cropping is possible at one side. The scale factor is then dependent on:
- Do you scale up or down
- Is the scaling axis also the cropping axis
- Can be cropped away enough
This issues might influence the scaling factor:
- The scaling factor might be the other axis than the obvious, so we can crop something away
- The scaling might be reduced to the amount where we crop away the maximum
Let me illustrate the first issue. Your original image size is 100×80 (blue). Your target dimension is 25×40 (red). This results in a scale factor of 0.25 on the X-axis and 0.5 on the Y-axis. If we use the smaller scale factor the resulting dimension lacks 20 pixel in width. Therefor we might choose the scaling factor 0.5 if we are able to crop away 25 pixels at the left or right after we are done with the scaling. The amount we can crop away at the max is defined as the amount that can be cut away in the original dimensioned image, witch would mean in this example 50 pixels (green). If we can only crop at the top or bottom we are out of luck and we need to scale the image with a factor of 0.25 and add black borders.
The second case can be illustrated with the same image. As we have seen the maximal croppable amount must be 50 pixels. Let’s say we can only crop away 25 pixels on the left side. If we cut away the max and want to scale the image so we do not have any borders on the left and right side, we must adjust the scaling factor acordingly: scaleFactor = targetWidth/(originalWidth-maxCrop) which results in a scale factor of 0.3333. This also results in horizontal borders.
This utility is implemented in the ch.sahits.game.image.ImageUtilV2 class which is available as part of the rendering package or directly from svn.