Rendering Graphics

This engine uses the notion of sprites to represent graphical images on the drawing surface:

In computer graphics, a sprite is a two-dimensional bitmap that is integrated into a larger scene. Wikipedia

However, in RetroGraphicsEngine sprites have extended functionality. They carry also animation information and can have action logic, which can be plugged in. But before you can create and add a sprite, you need to load a bitmap resource first.

Loading Bitmaps

A vital part of displaying graphics is the ability to load images. At one point you have to access the Resources object of android. There are many possible ways:

  • RetroEngine
  • global Resources.getSystem()
  • via the current activity

You can access the Resources object at any time via the RetroEngine class once the surface of the DrawView component was created or the core class was explicitly initialised with RetroEngine.init(ACIVITY_INSTANCE) where ACIVITY_INSTANCE is the current active activity.

If you use the second approach keep the following in mind (extracted from the docs):

Warning

Return a global shared Resources object that provides access to only system resources (no application resources), and is not configured for the current screen (can not use dimension units, does not change based on orientation, etc).

However, this should be sufficient to access the drawable resources of your app.

The latter approach may be more common to you. In an activity just call getResources(). You can apply this also within a State class by getting the StateManager instance and calling manager.getParentActivity().getResources().

Loading Resources with BitmapFactory

Before we can render some sprites, we need some graphic resources. In this case we want to use bitmap resources. For bitmap resources it's important to define inScaled = true when loading them:

1
2
3
4
5
6
7
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inPreferredConfig = Config.ARGB_8888;
opt.inScaled = true;
// ... some other options

// load the bitmap
Bitmap myBitmap = BitmapFactory.decodeResource(res, R.drawable.my-drawable, opt);

Loading Large Bitmaps in Android

This sections refers to the android developer article at https://developer.android.com/topic/performance/graphics/load-bitmap.html.

It's important to be aware of the memory limits of a mobile device and load bitmaps efficiently. To load a scaled down version of the bitmap you can use the following methods:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) >= reqHeight
                && (halfWidth / inSampleSize) >= reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
        int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

Now any bitmap of arbitrarily large size can be loaded like this:

1
Bitmap background = decodeSampledBitmapFromResource(getResources(), R.drawable.bigBackground, 800, 600);

The methods are also available in the util class BitmapHelper for your convenience.

Sprites

Sprites represent the graphics displayed on the screen. They are android.graphics.Bitmaps or generated from Drawables.

Adding Sprites and ConcurrentModificationException

As rule of a thumb you should never add sprites within a state in an event method. That can lead to ConcurrentModificationException by a SpriteGroup.

Only add sprites in the State#updateLogic() method. The reason: the State#onTouchEvent(View, MotionEvent) runs in a different thread. And if you add sprites to a sprite group within this method it's very likely that you run into those kinds of exceptions.

You should use a flag or message which is set in the event methods. If the update phase is running you can evaluate those flags and create the appropriate sprites and add them to the root node.

Units and Conversion

For rendering graphics it's important to consider the units that are used to resize a sprite, or change the font size for instance. Internally the engine uses pixel values for all the calculations. If you want to use Density Independent Pixels (DP / DIP) you have to convert those values accordingly by yourself.

For more information on this topic have a look in the section Helper classes.