Search code examples
javaandroidandroid-canvasdpiandroid-bitmap

Which Canvas and Bitmap methods get scaled by dpi (Android)?


I know that android will automatically scale Bitmaps loaded from res/drawable by dpi. I manually shut that off with BitmapFactory options when I load the resources for my game, such as the tileset.

//Loads Bitmaps for the tileset
public void loadTiles(int s) {
    Bitmap subimage = null;
    BitmapFactory.Options o = new BitmapFactory.Options();
    o.inScaled = false;
    Bitmap tileset = BitmapFactory.decodeResource(activity.getResources(), s, o);
    try {


        numTilesAcross = tileset.getWidth() / tileSize;
        tiles = new Tile[3][numTilesAcross];


        for(int col = 0; col < numTilesAcross; col++) {
            subimage = Bitmap.createBitmap(tileset,
                        col * tileSize,
                        0,
                        tileSize,
                        tileSize
                    );
            tiles[0][col] = new Tile(subimage, Tile.UNBLOCKED);
            subimage = Bitmap.createBitmap(tileset,
                        col * tileSize,
                        tileSize,
                        tileSize,
                        tileSize
                    );
            tiles[1][col] = new Tile(subimage, Tile.BLOCKED);
            subimage = Bitmap.createBitmap(tileset,
                        col * tileSize,
                        2 * tileSize,
                        tileSize,
                        tileSize
                    );
            tiles[2][col] = new Tile(subimage, Tile.TRIGGER);
        }

    }
    catch(Exception e) {
        e.printStackTrace();
    }
}

The problem is when I draw them to my Canvas (which is then drawn to a Surface View) if I test the game on any dpi lower than mdpi the tiles are scaled too small with space between, and if I test on anything higher they are drawn too big and overlap.

The main question is what methods scale using the dpi factor. I draw my images using

//tiles[r][c].getImage() returns a Bitmap
canvas.drawBitmap(
                tiles[row][col].getImage(),
                (int)x + col * tileSize,
                (int)y + row * tileSize,
                null
            );

Does canvas.drawBitmap() scale using the dpi? Or is the scaling happening with the way I am getting subimages in my loadTiles(int s) method? I checked the width/height of the bitmaps loaded in loadTiles and they are the size I wanted.

NOTE: I am scaling the final image to the screen myself, size of all bitmaps drawn to the canvas should be dpi independent.

EDIT: Here are some pictures of what is going on... This is mdpi, it is how it is SUPPOSED to look http://cdn.img42.com/e9359c298455d505c3317bd705372030.png

This is hdpi, notice how things are overscaled and overlap the tile drawn before them http://cdn.img42.com/540b7896947049250fea3f3fa5a08331.png

This is ldpi, notice how the tiles are too small and they leave space inbetween img42/420e938b3eebc503247cd0181b779402fe.png


Solution

  • I figured it out. I was reading the documentation for Canvas and I have to set the density on both the canvas AND the bitmaps when they are loaded by calling

    canvas.setDensity(DisplayMetrics.DENSITY_MEDIUM);
    bitmap.setDensity(DisplayMetrics.DENSITYMEDIUM);
    

    Relevant Documentation:

    https://developer.android.com/reference/android/graphics/Canvas.html#setDensity%28int%29

    https://developer.android.com/reference/android/graphics/Bitmap.html#setDensity%28int%29

    I also could just set all images pixel density back to the system pixel density after I load their subimages out of their sheets so that the game will look nicer and less scaled/skewed