Mansour
Mansour

Reputation: 1810

Canvas.draw() a transparent shape on opaque background results in 254 alpha

I noticed this (what appears to be a bug) when viewing saved bitmaps on my desktop and I could barely see the checkered pattern through the image file. I did a simple experiment:

Bitmap bitmap = new Bitmap(300, 200, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint  paint  = new Paint();
paint.setColor(Color.argb(150, 0, 255, 0));
canvas.drawColor(Color.RED);
canvas.drawRect(20, 20, 280, 180, paint);
Log.d("alpha-bug", "Alpha at 30, 30 is "
  + Color.alpha(bitmap.getPixel(30, 30))
);

to verify. The above logs Alpha at 30, 30 is 254. The correct alpha is 255. I get 254 regardless of what the alpha of the shape I'm drawing is. I'm assuming this is a rounding error. Has anyone else come across this? Is this expected and if so why? If not, any ideas on how to get around it?

Upvotes: 2

Views: 404

Answers (1)

Attila Tanyi
Attila Tanyi

Reputation: 5044

Yes, seems like a bug to me.

A workaround can be using another Bitmap to draw the half-transparent shape to, and then draw that bitmap using the drawBitmap method. The background of that other bitmap is transparent, and this way (apparently), the shape will be drawn properly. (Or if you're reusing the bitmap, you can fill it with transparent using drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR).)


Example code and output:

Bitmap test = Bitmap.createBitmap(250, 190, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(test);
{ // draw opaque background
    Paint p = new Paint();
    p.setARGB(255, 0, 0, 0);
    c.drawPaint(p);
}
Paint circlePaint = new Paint();
circlePaint.setARGB(128, 255, 255, 255);
Bitmap b;
c.drawText("drawCircle", 30, 40, circlePaint);
c.drawText("drawBitmap", 140, 40, circlePaint);
{ // draw a circle into a temporary bitmap as workaround
    b = MipMap.allocateBitmapSafely(100, 100);
    Canvas ca = new Canvas(b);
    ca.drawCircle(50, 50, 50, circlePaint);
    System.out.println("drawCircle (in temporary bitmap) color: " + Integer.toHexString(b.getPixel(50, 50)));
}
{ // draw circle with circle-drawing method
    c.drawCircle(70, 100, 50, circlePaint);
    System.out.println("drawCircle color: " + Integer.toHexString(test.getPixel(70, 100)));
}
{ // draw circle from the saved bitmap with bitmap-drawing method
    c.drawBitmap(b, 130, 50, null);
    System.out.println("drawBitmap color: " + Integer.toHexString(test.getPixel(170, 100)));
}
String filename = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "test.png";
FileOutputStream out = null;
try {
    out = new FileOutputStream(filename);
    test.compress(Bitmap.CompressFormat.PNG, 90, out);
} catch (Exception e) {
} finally {
    try {
        out.close();
    } catch (IOException e) {
    }
}

Console output:

drawCircle (in temporary bitmap) color: 80ffffff drawCircle color: fe818181 drawBitmap color: ff808080

Example output image

In the image, the color of the left circle is (254, 129, 129, 129), the right one is (255, 128, 128, 128). So not only is the alpha 254 instead of 255 on the left, the R, G and B values are off by 1 as well.

P.S.: We should report this to Google because this is an overkill for a workaround.

Upvotes: 1

Related Questions