29

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?

dakshbhatt21
  • 3,558
  • 3
  • 31
  • 40
Fredkr
  • 723
  • 3
  • 8
  • 21

5 Answers5

13

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.

Pkmmte
  • 2,822
  • 1
  • 31
  • 41
  • 1
    This seems like it is working for me, and for even the older API 16 devices it usually takes <1ms, with 5ms being the longest I saw it take after running it quite a few times. The result shouldn't change so I saved the result after one run just to be avoid extra cpu work. – Ryan C Dec 16 '16 at 02:10
  • Be aware that some devices have multiple max texture size configs. Kindle Fire HD 7'' (2012) has configs that report a 2048 max texture size and a 4096 max texture size. – lillicoder Jul 17 '17 at 17:29
10

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.

Mathew Kurian
  • 5,949
  • 5
  • 46
  • 73
Geobits
  • 22,218
  • 6
  • 59
  • 103
  • Thanks for the reply, this is kinda what i was thinking as well. Any idea how much this changes from different devices? – Fredkr Mar 09 '13 at 20:21
  • Honestly, I'm not sure. That's why I tried two devices. If I could find my old HTC Magic, I would have tried that one as well. I know on standard OpenGL the spec says it's always >= 1024, but I'm not sure about OpenGLES. – Geobits Mar 09 '13 at 20:25
  • Okay, thanks again. Guess i'l hardcode a value of 1024 to be sure. – Fredkr Mar 09 '13 at 22:00
  • 18
    You can query OpenGL without create an OpenGL context because glGetIntegerv is a static method: `GLES10.glGetIntegerv(GL10.GL_MAX_TEXTURE_SIZE, maxSize, 0);` – Riccardo Tesio Nov 20 '13 at 11:49
  • @RiccardoTesio When I tried doing that, it sometimes gave me different results depending on whether I had created a valid context. I don't know why that would be, just my experience. – Geobits Nov 20 '13 at 13:43
  • 1
    @Geobits Probably you are right, testing again I often get the correct result, but sometimes I get simply zero. – Riccardo Tesio Nov 27 '13 at 11:12
  • 7
    Unfortunately I'm getting zeros too, using the static method. – Jaykob May 06 '14 at 09:50
  • I have the Same issues for "Bitmap too large to be uploaded into a texture (1944x2592, max=2048x2048)". I don't know how to solve this one before reading your comment, but now i have some idea only. I am not using surfaceview. For Aviary SDK used in this size image, finaly give output size for (1937X2582) same size with same quality, how does it?. please help me any way to achieve that one. – Sakthivel Appavu Jun 18 '14 at 13:38
  • @Geobits Please add the comment of RiccardoTesio to your answer: "You can query OpenGL without create an OpenGL context because glGetIntegerv is a static method: GLES10.glGetIntegerv(GL10.GL_MAX_TEXTURE_SIZE, maxSize, 0);" – tim687 Aug 04 '15 at 10:39
  • @tim687 I'm on mobile right now, but please feel free to suggest an edit to add that in. – Geobits Aug 04 '15 at 12:35
  • @Geobits The code from Riccardo Tesio isn't working for me. Sadly enough – tim687 Aug 04 '15 at 15:34
  • My samsung galaxy s8 test device gives a GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE of 16384. I imagine that is device dependant and I shouldn't use a bitmap that high, although i have been able to successfully load and show parts of 16000 by 16000 texturemap bitmaps on it. Larger than that will load but only shows black. – Androidcoder Dec 17 '17 at 19:26
  • on Galaxy S20 Ultra, GL10.GL_MAX_TEXTURE_SIZE returns 0. – tateisu Jul 15 '23 at 22:00
2

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;
}
madlymad
  • 6,367
  • 6
  • 37
  • 68
JRowan
  • 6,824
  • 8
  • 40
  • 59
2

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.

Jaap van Hengstum
  • 4,494
  • 4
  • 30
  • 34
  • 4
    I actually tried this already, it returns a value of over 30k so it doesnt help at all – Fredkr Mar 09 '13 at 23:49
  • That's because your view is not hardware accelerated. Try `canvas.isHardwareAccelerated()`, it should return `true`. If it doesn't, try `view.setLayerType(View.LAYER_TYPE_HARDWARE, null)` or enabling hardware acceleration in the manifest (android:hardwareAccelerated="true"). – Jaap van Hengstum Mar 10 '13 at 07:57
  • Tried this as well, still a value over 30k – Fredkr Mar 10 '13 at 11:18
  • 1
    `getMaximumBitmapWidth()` and `getMaximumBitmapHeight()` methods return a static value which is `Canvas.MAXMIMUM_BITMAP_SIZE`. – Halil Jul 05 '14 at 12:58
1

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.

Community
  • 1
  • 1
jedichen
  • 91
  • 1
  • 7