28
votes

Im creating an app that needs to decode large images to bitmaps to be displayed in a ImageView.

If i just try to decode them straight to a bitmap i get the following error " Bitmap too large to be uploaded into a texture (1944x2592, max=2048x2048)"

So to be able to show images with too high resolution im using:

Bitmap bitmap = BitmapFactory.decodeFile(path);

if(bitmap.getHeight()>=2048||bitmap.getWidth()>=2048){
    DisplayMetrics metrics = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(metrics);
    int width = metrics.widthPixels;
    int height = metrics.heightPixels;
    bitmap =Bitmap.createScaledBitmap(bitmap, width, height, true);             
}

This works but I don't really want to hardcode the maximum value of 2048 as I have in the if-statement now, but I cant find out how to get a the max allowed size of the bitmap for a device

Any ideas?

5

5 Answers

10
votes

This limit should be coming from the underlying OpenGL implementation. If you're already using OpenGL in your app, you can use something like this to get the maximum size:

int[] maxSize = new int[1];
gl.glGetIntegerv(GL10.GL_MAX_TEXTURE_SIZE, maxSize, 0);
// maxSize[0] now contains max size(in both dimensions)

This shows that my both my Galaxy Nexus and Galaxy S2 have a maximum of 2048x2048.

Unfortunately, if you're not already using it, the only way to get an OpenGL context to call this from is to create one(including the surfaceview, etc), which is a lot of overhead just to query a maximum size.

13
votes

Another way of getting the maximum allowed size would be to loop through all EGL10 configurations and keep track of the largest size.

public static int getMaxTextureSize() {
    // Safe minimum default size
    final int IMAGE_MAX_BITMAP_DIMENSION = 2048;

    // Get EGL Display
    EGL10 egl = (EGL10) EGLContext.getEGL();
    EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);

    // Initialise
    int[] version = new int[2];
    egl.eglInitialize(display, version);

    // Query total number of configurations
    int[] totalConfigurations = new int[1];
    egl.eglGetConfigs(display, null, 0, totalConfigurations);

    // Query actual list configurations
    EGLConfig[] configurationsList = new EGLConfig[totalConfigurations[0]];
    egl.eglGetConfigs(display, configurationsList, totalConfigurations[0], totalConfigurations);

    int[] textureSize = new int[1];
    int maximumTextureSize = 0;

    // Iterate through all the configurations to located the maximum texture size
    for (int i = 0; i < totalConfigurations[0]; i++) {
        // Only need to check for width since opengl textures are always squared
        egl.eglGetConfigAttrib(display, configurationsList[i], EGL10.EGL_MAX_PBUFFER_WIDTH, textureSize);

        // Keep track of the maximum texture size
        if (maximumTextureSize < textureSize[0])
            maximumTextureSize = textureSize[0];
    }

    // Release
    egl.eglTerminate(display);

    // Return largest texture size found, or default
    return Math.max(maximumTextureSize, IMAGE_MAX_BITMAP_DIMENSION);
}

From my testing, this is pretty reliable and doesn't require you to create an instance. Performance-wise, this took 18 milliseconds to execute on my Note 2 and only 4 milliseconds on my G3.

2
votes

this will decode and scale image before loaded into memory,just change landscape and portrait to the size you actually want

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
if(imageWidth > imageHeight) {
    options.inSampleSize = calculateInSampleSize(options,512,256);//if landscape
} else{
    options.inSampleSize = calculateInSampleSize(options,256,512);//if portrait
}
options.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeFile(path,options);

method for calculating size

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) {

      // Calculate ratios of height and width to requested height and width
      final int heightRatio = Math.round((float) height / (float) reqHeight);
      final int widthRatio = Math.round((float) width / (float) reqWidth);

      // Choose the smallest ratio as inSampleSize value, this will guarantee
      // a final image with both dimensions larger than or equal to the
      // requested height and width.
      inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
   }

   return inSampleSize;
}
2
votes

If you're on API level 14+ (ICS) you can use the getMaximumBitmapWidth and getMaximumBitmapHeight functions on the Canvas class. This would work on both hardware accelerated and software layers.

I believe the Android hardware must at least support 2048x2048, so that would be a safe lowest value. On software layers, the max size is 32766x32766.

1
votes

The 2048*2048 limit is for GN. GN is a xhdpi device and perhaps you put the image in the wrong density bucket. I moved a 720*1280 image from drawable to drawable-xhdpi and it worked.

Thanks for the answer by Romain Guy. Here's the link of his answer.