Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android SurfaceView Canvas renders wrong colors

Hej guys, so this is the first time in about 7 years that I have not been able to find an answer to a programming issue, nor have I gotten close to what the issue might be in the first place.

Alright so let's begin from the start. I've followed some tutorials and examples on the Android SurfaceView and how to draw on its Canvas in another thread. So far, no issues, everything draws like I expect. I'm currently working on a scenario where I have some padding around my SurfaceView, which means that the background of the parent view (a FrameLayout) renders around the aforementioned SurfaceView. This is where things become interesting as I use the same color for the parent background as I do for clearing the SurfaceView's Canvas.

The root (=parent) FrameLayout has its background set in the theme like
<item name="android:windowBackground">@color/palette_primary_dark</item>

which is defined in colors.xml like
<color name="palette_primary_dark">#28252C</color>

and in my thread constructor, I retrieve the same color into the global variable mClearColor like
mClearColor = ContextCompat.getColor(context, R.color.palette_primary_dark); where the context is the Context I receive from my SurfaceView.

The following code is the render loop that runs in my thread

@Override
public void run() {
    while (mShouldRun) {
        Canvas canvas = null;
        try {
            canvas = mSurfaceHolder.lockCanvas();
            synchronized (mSurfaceHolder) {
                if (canvas != null) {
                    canvas.drawColor(mClearColor);
                    onDrawFrame(canvas);
                }
            }
        } finally {
            if (canvas != null) {
                mSurfaceHolder.unlockCanvasAndPost(canvas);
            }
        }
    }
}

So far, so good. No errors and everything draws; the background isn't black and it looks like the color I want. But here's the catch, it ISN'T actually the color I want. It's off by a hair. The color I defined in my XML is #28252C but according to my GIMP color picker tool it actually draws #292429. Weird.
Then I started searching further and discovered that the color I'm using to draw touch indicators (where my fingers are located on the screen) should be #FF1744 but actually is #FF1442.

This left me scratching my head and desperately trying other ways of drawing the right color on my canvas. I've tried Canvas.drawColor(), Canvas.drawARGB() and Canvas.drawRGB(), but to no avail. In drawColor() I even tried different ways of retrieving my color, like mClearColor, Color.parseColor("#28252C"), Color.argb(255, 40, 37, 44) and so on. Nothing worked.
Then I came across the fact that drawColor() uses PorterDuff.Mode.SRC_OVER by default, so I tried using PorterDuff.Mode.SRC to get the pure color I'm feeding it, but no happy faces :(

Then I just started trying stuff but nothing seemed to work. When I tried setLayerType() in my SurfaceView, something did change though. The following screenshot shows how it looked like with the default LAYER_TYPE_NONE value

LAYER_TYPE_NONE

Now when I change this to either LAYER_TYPE_SOFTWARE or LAYER_TYPE_HARDWARE the same layout looks like follows

LAYER_TYPE_SOFTWARE/HARDWARE

The weird thing is now that it actually is the correct color and renders how I want it to, it still doesn't draw the lines and info about the touch. Calls to my onTouchListener are still being made, which in its turn should provide data to draw the lines on the screen. The latter doesn't work though, it doesn't actually draw the lines. I have no clue what causes this weird issue that changes my colors... Any help would be greatly appreciated :)

Edit #1:

I have done some more research today and as per this answer by Romain Guy and the following self-answered question by Adrian Lopez, setLayerType() actually does more harm than it solves problems. So I now have even less pointers as to what could cause or solve my issue :/ I'll make sure to edit the question when I have found more information and answer it if I found a solution :)

like image 487
Thijs Koppen Avatar asked Nov 16 '15 15:11

Thijs Koppen


1 Answers

The SurfaceView's surface is configured to use RGB 565 by default.

In onCreate(), set the color format of the surface with setFormat() to get RGB 8888 instead:

mSurfaceHolder.setFormat(PixelFormat.TRANSLUCENT);
like image 165
fadden Avatar answered Oct 16 '22 03:10

fadden