Images loaded with BitmapRegionDecoder are ok on simulator, but too large on device

1.8k views Asked by At

In a simple word game app I load 26 letter tile images from a PNG-image stripe with the following code:

image stripe

private static final CharacterIterator ABC = 
    new StringCharacterIterator("ABCDEFGHIJKLMNOPQRSTUVWXYZ");

private static HashMap<Character, Bitmap> sImages =
    new HashMap<Character, Bitmap>();

BitmapRegionDecoder decoder = null; 

InputStream is = sContext.getResources()
    .openRawResource(R.drawable.big_english);

try {   
        decoder = BitmapRegionDecoder.newInstance(is, false); 
} catch (IOException ex) {
}       

int h = decoder.getHeight();
Rect r = new Rect(0, 0, h, h);

for (char c = ABC.first(); 
        c != CharacterIterator.DONE; 
        c = ABC.next(), r.offset(h, 0)) {

           Bitmap bmp = decoder.decodeRegion(r, null);
           sImages.put(c, bmp);
}       

This works well in Android emulator:

emulator

But on a real Moto G device the letters are too big (Maybe by 1.5 factor? When I print sContext.getResources().getDisplayMetrics().density I for some reason get 2.0):

Moto G device

At the same time the yellow square tile backround is shown correctly.

All (big_tile.png and big_english.png and game_board.png) are PNG-images - so why the difference?

Why does it happen and how to fix this please?

Should I maybe use inDensity or any other BitmapFactory.Options?

Or is it because of my getResources().openRawResource() call - but what to use instead?

2

There are 2 answers

0
JohnaHalim On BEST ANSWER

I also have the same problem with image, android devices came with many screen resolution. you will need either put alot of image into your drawable folder or give a set of rule of the image width and height to each screen resolution

for example:
if screen_resolution = 4 inch
int img_height = 200
int img_width = 200
else if screen_resolution = 5 inch
int img_height = 300
int img_width = 300

. . and so on hope this can help you some how.

here some of my code(I use imageview not bitmap sorry):

int width, height;

   //calculate device screen
    DisplayMetrics metrics=new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(metrics);
    float height=metrics.heightPixels/metrics.xdpi;
    float width=metrics.widthPixels/metrics.ydpi;
    float inchscreen = FloatMath.sqrt(height*height+width*width);


if (inchscreen > 4.1000)
    {
        //set image height for device above 4.1 inch screen
        height = 400;
        width = 400     
    }
else {
        //set image height for device below 4.1 inch screen
        height = 280;
        width = 280
    }

if (imgfile != null) {
                    int imageResource = getResources().getIdentifier(uri, null,
                            getPackageName());
                    Drawable image = getResources().getDrawable(imageResource);
                    imageview.setImageDrawable(image);
                    imageview.getLayoutParams().height = h_display;
                    imageview.getLayoutParams().width = h_display;
                    }
0
Alexander Farber On

I will share my own solution here.

The problem has been, that the yellow background for my letter tiles is a Drawable and the foreground is a Bitmap (read with the help of BitmapRegionDecoder from a PNG-stripe).

For some weird reason I still don't understand - their dimensions differ on some Android devices.

(1) So my easiest workaround has been to scale the Bitmap (foreground with letter) into the bounds of the Drawable (yellow square background) every time the letter tile is being drawn:

private Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);

public void draw(Canvas canvas) {
    canvas.save();
    canvas.translate(left, top);
    mImage.draw(canvas);
    Bitmap bmp = getImages().get(mLetter);
    canvas.drawBitmap(bmp, null, mImage.getBounds(), mPaint);
    canvas.restore();
}

(2) My more involved workaround has been to scale the Bitmap just once - right after I read it with BitmapRegionDecoder and before I store it in a HashMap:

private static final CharacterIterator ABC = 
    new StringCharacterIterator("ABCDEFGHIJKLMNOPQRSTUVWXYZ");

private static HashMap<Character, Bitmap> sBitmaps = 
    new HashMap<Character, Bitmap>();

try {
        InputStream is = context.getResources().openRawResource(EN);
        BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(is, false);
        int h = decoder.getHeight();
        Rect r = new Rect(0, 0, h, h);
        for (char c = ABC.first();
             c != CharacterIterator.DONE;
             c = ABC.next(), r.offset(h, 0)) {
                Bitmap unscaled = decoder.decodeRegion(r, null);
                Bitmap scaled = Bitmap.createScaledBitmap(unscaled, (int) (SCALE * width), (int) (SCALE * height), true);
                sBitmaps.put(c, scaled);
        }
} catch (IOException ex) {
}

(3) Both methods worked, but on the older Google Nexus One device I couldn't see the foregrounds for some reason. At this point I've lost my nerve, stopped using BitmapRegionDecoder and just split the PNG-stripe with ImageMagick (the command is convert square.png -crop 40x40 square_%d.png). Now my app uses Drawable both for foregrounds and backgrounds of letter tiles and works on all devices starting with SDK level 8:

private static final String SQUARE = "square_";

private static final char[] LETTERS = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};

private static HashMap<Character, Drawable> sLetters = 
    new HashMap<Character, Drawable>();

for (int i = 0; i < LETTERS.length; i++) {
    char c = LETTERS[i];
    int id = context.getResources().getIdentifier(PREFIX + i, "drawable", context.getPackageName());
    Drawable d = context.getResources().getDrawable(id);
    d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
    sLetters.put(c, d);
} 

emulator