Swapped red and blue colors (colours) for bitmaps displayed when OpenGL is used


#1

I updated the underlying Juce library to an app of mine and I've noticed that all my red and blue colors are swapped on my target Intel Android devices when I use OpenGL when drawing any juce::Image. The source image has the the correct ARGB info (I stepped into it).

This does not happen on the Windows or OSX build of my app. This only happens on Android. It happens on multiple Intel Android tablets (Dell Venue, Dell venue 2, multiple Intel prototypes).

So I decided to look around the OpenGL juce code and I ran across juce_OpenGLTexture.cpp and the Flipper struct. Inside the Flipper::flipper() function you have an #if JUCE_ANDROID that basically swaps the Red and Blue pixels.

// straight from the current code base

#if JUCE_ANDROID

dst[x].setARGB (s.getAlpha(), s.getBlue(), s.getGreen(), s.getRed()); // orginal code red and blue are swapped

It is coverting a pixel from the ARBG format to AGBR. That pretty much describes by issue. Ok, I changed this code to just assign the ARGB pixel to ARGB and my problem is fixed.

dst[x].setARGB (s.getAlpha(), s.getRed(), s.getGreen(), s.getBlue()); // my edit, copy pixel over as ARGB, straight up lofi gangsta style

Any thoughts? I would hate anyone else to run across this and have to debug it (it took me a minute when I could have been eating cheesy nachos). I made this edit to my local copy of the juce library and have been using it for a couple weeks with no issues.

Even better yet, the #if JUCE_ANDROID isn't really needed at all, it can be handled just like Windows and OSX handle it. At least on the devices I've tried.

I have yet to personally try it out on an Arm or Tegra based Android tablet.

Thanks again Jules for making a great framework!

 


#2

Samsung SGS (1) / Nexus 5/  Nexus 7 (orig/pre 2013)  - This corrects an issue when OpenGL demo page is opened in OpenGL Rendering mode  but causes the opposite issue (e.g. Teapot with Portmerion texture appears mostly orange instead of blue (sky)) when rendered in the default context (Software Renderer Mode).

 


#3

Ok, I'll run the Juce demo on all of my android devices and check out the teapot example.

Thanks for trying it out and confirming that I'm not crazy!

Fortunately I don't need to render 3D objects with my app.


#4

I have the same issue and just did the same debugging as you @irdbisa, unfortunately before doing a search here :)

 

On at least a Galaxy Nexus phone, and a Samsung Note 3, when running with hardware rendered OpenGl the R-B flipping is not necessary.  These are all ARM based devices, so clearly this "fix" shouldn't be in there in such a general #ifdef ANDROID case.  I have no idea what the proper fix is, assuming this is a real issue on a specific implementation of OpenGL on some specific OS version and hardware.

In the meantime, I'm just making this change in my local repository.


#5

At first glance changing the Flipper code works and it works on my 50+ devices. However you will find later on there are issues but only if you use certain other features of Juce.

 

Basically if you set any of your components alpha to anything but 0 & 1 Juce will create another openGL cache image and composite all your children into this, it will then composite this onto the scene. The problem is that in the shader code Juce Swizzles the pixels for Android which causes this cached image to be double swizzed (swap RGB colors) and you get the funky color issue back.

The changes need to fix it are pretty straight forward and presented here:

 

Leave the flipper code in-tact and go ahead and Flip the RGB values as they are. This is in juce_OpenGLTexture.cpp.

 

In juce_OpenGLGraphicsContext.cpp there is a definition for JUCE_DECLARE_SWIZZLE_FUNCTION that needs to be changed. Instead of:

 

 #if JUCE_ANDROID

    #define JUCE_DECLARE_SWIZZLE_FUNCTION "\n" JUCE_MEDIUMP " vec4 swizzleRGBOrder (in " JUCE_MEDIUMP " vec4 c) { return vec4 (c.b, c.g, c.r, c.a); }\n"

   #else

    #define JUCE_DECLARE_SWIZZLE_FUNCTION "\n" JUCE_MEDIUMP " vec4 swizzleRGBOrder (in " JUCE_MEDIUMP " vec4 c) { return c; }\n"

   #endif

You will need to disable the swizzle in the shader to not Swizzle. So:

#define JUCE_DECLARE_SWIZZLE_FUNCTION "\n" JUCE_MEDIUMP " vec4 swizzleRGBOrder (in " JUCE_MEDIUMP " vec4 c) { return c; }\n"

Is all you need wether you are on Android or not.

 

However, this will then break some of the other shader functions as they still need swizzling (unless you also change a bunch of places in other code to process the colors correctly). These will be:

RadialGradientProgram

RadialGradientMaskedProgram

LinearGradient1Program

LinearGradient1MaskedProgram

LinearGradient2Program

LinearGradient2MaskedProgram

 

You basically add back in the old swizzling functions but name them JUCE_DECLARE_SWIZZLE_FUNCTION2

#if JUCE_ANDROID

#define JUCE_DECLARE_SWIZZLE_FUNCTION2 "\n" JUCE_MEDIUMP " vec4 swizzleRGBOrder (in " JUCE_MEDIUMP " vec4 c) { return vec4 (c.b, c.g, c.r, c.a); }\n"

#else

#define JUCE_DECLARE_SWIZZLE_FUNCTION2 "\n" JUCE_MEDIUMP " vec4 swizzleRGBOrder (in " JUCE_MEDIUMP " vec4 c) { return c; }\n"

#endif

 

Then in each of the shaders listed above change it from JUCE_DECLARE_SWIZZLE_FUNCTION to JUCE_DECLARE_SWIZZLE_FUNCTION2

 

All of this will fix the regular textures as well as the cached textures.

 

 

 

However...overall there is a design flaw on Android, and this is the reason I rewrote a larger portion of OpenGL. Android does Swizzling on the CPU in the Flipper class. This means that while uploading textures to the GPU it is using the CPU to swap RGBA bytes around as needed. Ultimately this should be on the GPU whenever possible since CPU is slower than GPU. However if you do not change textures very often then its really a moot point as its milliseconds overall. But for me and my graphics editing apps I was changing textures all the time and this delay in CPU added up. As such I removed the Swizzling in the Flipper class and moved into into new shader code, delt with the image cache for components with alpha. For 99% of the cases I think the current Juce implementation with swizzling and my changes above is perfectly fine, however if you are changing textures a lot then you will need to dig in deeper and ultimately remove CPU bound flipping whenever possible.

 

 

 


#6

Thanks!  Are you going to share your re-written version of this OpenGL code?


#7

Unfoturnately most of it is now specific to my needs. However as I see issues in general I will post about them.


#8

I've noticed this on my LG too, by not swapping R&B, everything looks lok.