Siegfried
Siegfried

Reputation: 311

Android: OpenGL texture turns white on some devices

In my app I use a GLSurfaceView to display a 2-dimensional background image which changes depending on the content displayed. The image is used as texture rendered on a simple rectangle with the same aspect ratio as the phone's screen. When the background changes, it renders an alpha-blend transition effect. If the device does not support Open GL ES 2.0, a static image is used instead. (Additional info: the image/texture is not a square and is not the size of a power of 2)

Some customers have reported that the background turns white on some devices (e.g. LG E455), making the app unusable, because the text displayed on top is white as well. I've been looking for a solution to the problem, but could not find anything.

Because I am providing the app for free without ads (I think ads do not fit the app) and I do not receive external funding, it is not an option for me to buy the device in question (LG E455) - as it is an old device - for the sole purpose of debugging this one issue.

I am using the following code to load the textures:

int getTexture(GL10 gl, int index) {
    if (BackgroundRenderer.this.textures[index] == 0) {
        gl.glGenTextures(1, BackgroundRenderer.this.textures, index);
        gl.glBindTexture(GL10.GL_TEXTURE_2D, BackgroundRenderer.this.textures[index]);

        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

        Bitmap bitmap = ResourceStore.getBackgroundBitmap(index);

        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
    }

    return BackgroundRenderer.this.textures[index];
}

Version check and initialization:

private static boolean supportsOpenGLES2(Context context) {
    ActivityManager activityManager = (ActivityManager)
            context.getSystemService(Context.ACTIVITY_SERVICE);
    ConfigurationInfo configurationInfo =
            activityManager.getDeviceConfigurationInfo();
    return configurationInfo.reqGlEsVersion >= 0x20000;
}

public BackgroundManager(Context context, ViewPager eventSource) {
    this.context = context;
    supportsRendering = BackgroundManager.supportsOpenGLES2(context);
    eventSource.setTag(id.TAG_BACKGROUND_MANAGER, this);

    if (this.supportsRendering) {
        this.backgroundSurfaceView = new GLSurfaceView(context);
        this.backgroundSurfaceView.setFocusable(false);
        this.backgroundSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
        eventSource.addOnPageChangeListener(BackgroundManager.getRenderer());
        this.backgroundSurfaceView.setRenderer(BackgroundManager.getRenderer());
        this.backgroundSurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
    } else {
        this.backgroundStillView = new View(context);
    }
}

If you need more code, please tell me.

I have thought about some possible solutions:

a) fix the problem (don't know how...)

b) disable the fancy effects in case the device does not support it, but here the question is, how can I detect if rendering would work in advance? The device passes my Open GL 2.0 version check.

c) move to a different rendering solution instead of OpenGL, as I have the feeling it is overkill for my purpose. Any suggestions?

Note: The solution proposed in Android 'white box' textures OpenGL ES 1.0 on some devices does not apply, because I use a different API and the device does support the extension as per http://specdevice.com/showspec.php?id=02c2-cba8-ffff-ffffebe99653 (although I am not sure if the information is correct/credible)

Thank you for your suggestions!

Upvotes: 0

Views: 917

Answers (1)

Gil Moshayof
Gil Moshayof

Reputation: 16761

The problem here is that some devices do not support non-power-of-two textures. This is why some devices just show a white texture.

To solve this, it's best to always use a power-of-two bitmap when generating the texture.

Power-of-two bitmaps are bitmaps who's width / height are a power of two (256, 512, 1024, etc). As things stand at the moment, you're loading your bitmap via ResourceStore, and I doubt this tool creates a PO2 bitmap.

To overcome this issue, follow these steps:

  1. Create your bitmap normally as you're doing
  2. Find the dimensions of your bitmap (lets say for example, its 500x800)
  3. Create a new bitmap which is PO2 and larger than the original bitmap (in this example, 512x1024)
  4. Copy your original bitmap onto this new bitmap, keep the original bitmap size, so that the new bitmap will have blank areas on the right and bottom.
  5. Adjust your texture coordinates - instead of 1 for the x coord, use 500/512, instead of 1 for the y coord, use 800 / 1024. This will prevent those ugly white corners from being displayed.

Here's a code example:

public void getTexture(GL10 gl, int index) {
    if (BackgroundRenderer.this.textures[index] == 0) {
        //calls to gl.glGenTextures, gl.glBindTexture etc

        Bitmap originalBitmap = ResourceStore.getBackgroundBitmap(index);

       int po2W = 1;
       int po2H = 1;

       while (po2W < originalBitmap.getWidth())
          po2W *= 2;

       while (po2H < originalBitmap.getHeight())
          po2H *= 2;

       Bitmap po2Bitmap = Bitmap.createBitmap(po2W, po2H, Config.ARGB_8888);

       Canvas canvas = new Canvas(po2Bitmap);
       canvas.drawBitmap(originalBitmap, 0, 0, myPaint);

       GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, po2Bitmap, 0);

       // these ratios need to be used for setting the texture coordinates!
       mWidthRatio = (float)orignalBitmap.getWidth() / (float)po2Bitmap.getWidth();
       mHeightRatio = (float)orignalBitmap.getHeight() / (float)po2Bitmap.getHeight();
    }

    return BackgroundRenderer.this.textures[index];
}

Now, you will need to redefine the texture coordinates:

float textureData[] = {
         0,           0,             // top left
         0,           mHeightRatio,  // bottom left
         mWidthRatio, mHeightRatio,  // bottom right
         mWidthRatio, 0              // top right
};

Now you should be good to go.

Let me know if there's any problem.

Upvotes: 3

Related Questions