Implementing graphics scaling

In the Series 40 device offering alone, devices have a lot of different screen resolutions to take into account. When you add the screen resolutions of Symbian devices, the number of available screen resolutions gets even larger. The screen resolution can be a hindrance, if it prevents your MIDlet from running correctly on a maximum number of devices.

The Explonoid MIDlet solves the screen resolution problem by simply scaling all the graphics to fit the screen while the game is starting up. The images are scaled when the Resources object gets instantiated, and they can be obtained by accessing the public image members of the object:

        this.r = new Resources(scaling);

The game proportions are preserved during scaling, so that if the screen width/height ratio differs from the original 4/3, black bars are used on the side of the screen, as shown in the following figure.

Figure: Preserving the game proportions during scaling

The simplest way to implement scaling is to scale the final image to be painted on the screen. This approach simplifies the code, since no consideration is needed for how scaling affects the game logic, such as sprite movements. However, this approach is not feasible, since scaling, for example, a 240 x 320 background can take 200 ms, while the game loop only allows 30 ms for an update. Consequently, scaling in the Explonoid MIDlet is implemented so that most of the numeric values concerning, for example, ball movements, plate movements, and button locations, are scaled in addition to the graphics.

The Resources class offers a handy scaling function (shown below) for scaling numerical values. When scaling a multi-framed sprite, the size of the final image needs to be divisible by the number of frames. So if the sprite image has, for example, five frames vertically, like the bricks sprite, the height needs to be calculated first by scaling one fifth of the image size and then multiplying it by the number of frames.

    /**
     * Scale a value and return an integer using symmetrical rounding
     */
    public int scale(double value) {
        double result = scaling * value;
        return (result < 0) ? (int) (result - 0.5) : (int) (result + 0.5);
    }

The scaling result is only as good as the scaling algorithm. As the scaling is done only once when the MIDlet is launched, the speed of the algorithm is not as crucial as its quality. Also note that the scaling result can never beat the quality of graphics especially designed for a specific screen resolution. So if your MIDlet is designed to run only on devices with a few different screen resolutions, create different resource folders for different resolutions (like in the Battle Tank MIDlet), and select the resolution that best fits the device.