Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to paint with alpha?

Tags:

I want to paint graphics onto a Canvas such that the colors are additive. For example, I want to produce this:
Proper additive colors

But instead, I get this:
My results

Note that the half white, half black background is intentional, just to see how alpha interacts with both backgrounds. I will be happy to have this work with either background. Here is my code:

public class VennColorsActivity extends Activity {     @Override     public void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         class VennView extends View {             public VennView(Context context) {                 super(context);             }              @Override             protected void onDraw(Canvas canvas) {                 super.onDraw(canvas);                 int alpha = 60, val = 255;                 int ar = Color.argb(alpha, val, 0, 0);                 int ag = Color.argb(alpha, 0, val, 0);                 int ab = Color.argb(alpha, 0, 0, val);                 float w = canvas.getWidth();                 float h = canvas.getHeight();                 float cx = w / 2f;                 float cy = h / 2;                 float r = w / 5;                 float tx = (float) (r * Math.cos(30 * Math.PI / 180));                 float ty = (float) (r * Math.sin(30 * Math.PI / 180));                 float expand = 1.5f;                 Paint paint = new Paint();                 paint.setColor(Color.WHITE);                 canvas.drawRect(new Rect(0, 0, (int) w, (int) (h / 2)), paint);                 PorterDuff.Mode mode = android.graphics.PorterDuff.Mode.ADD;                 paint = new Paint(Paint.ANTI_ALIAS_FLAG);                 paint.setColorFilter(new PorterDuffColorFilter(ar, mode));                 paint.setColor(ar);                 canvas.drawCircle(cx, cy - r, expand * r, paint);                 paint.setColorFilter(new PorterDuffColorFilter(ag, mode));                 paint.setColor(ag);                 canvas.drawCircle(cx - tx, cy + ty, expand * r, paint);                 paint.setColorFilter(new PorterDuffColorFilter(ab, mode));                 paint.setColor(ab);                 canvas.drawCircle(cx + tx, cy + ty, expand * r, paint);             }         }         this.setContentView(new VennView(this));     } } 

Can someone please help me understand how to paint with additive colors in Android graphics?

like image 750
Melinda Green Avatar asked Aug 07 '12 01:08

Melinda Green


2 Answers

You are on the right track. There are 3 major issues in your code:

  • You need to set xfer mode iso color filter
  • Use temp bitmap for rendering your image
  • Alpha should be 0xFF in order to get results you are looking for

Here is what I've got by using xfer mode. What I'm doing - is drawing everything into temporary bitmap and then rendering entire bitmap to main canvas.

xfer mode rules

You ask why do you need temp bitmap? Good question! If you are drawing everything on a main canvas, your colors will be blended with main canvas background color, so all colors will get messed up. Transparent temp bitmap helps to keep your colors away of other parts of UI

Please make sure you are not allocating anything in onDraw() - you will run out memory very soon in this way.. Also make sure you have recycled your temp bitmap when you no longer need it.

package com.example.stack2;  import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PorterDuff.Mode; import android.graphics.PorterDuffXfermode; import android.os.Bundle; import android.view.View;  public class YouAreWelcome extends Activity {      Bitmap tempBmp = Bitmap.createBitmap(1, 1, Config.ARGB_8888);     Canvas c = new Canvas();     Paint paint = new Paint();      @Override     public void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         class VennView extends View {             public VennView(Context context) {                 super(context);              }              protected void onDraw(Canvas canvas) {                 super.onDraw(canvas);                 if(tempBmp.isRecycled() || tempBmp.getWidth()!=canvas.getWidth() || tempBmp.getHeight()!=canvas.getHeight())                 {                     tempBmp.recycle();                     tempBmp = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Config.ARGB_8888);                     c.setBitmap(tempBmp);                 }                      //clear previous drawings                 c.drawColor(Color.TRANSPARENT, Mode.CLEAR);                  int alpha = 255, val = 255;                 int ar = Color.argb(alpha, val, 0, 0);                 int ag = Color.argb(alpha, 0, val, 0);                 int ab = Color.argb(alpha, 0, 0, val);                 float w = canvas.getWidth();                 float h = canvas.getHeight();                 float cx = w / 2f;                 float cy = h / 2;                 float r = w / 5;                 float tx = (float) (r * Math.cos(30 * Math.PI / 180));                 float ty = (float) (r * Math.sin(30 * Math.PI / 180));                 float expand = 1.5f;                 paint.setAntiAlias(true);                 paint.setXfermode(new PorterDuffXfermode(Mode.ADD));                 paint.setColor(ar);                 c.drawCircle(cx, cy - r, expand * r, paint);                 paint.setColor(ag);                 c.drawCircle(cx - tx, cy + ty, expand * r, paint);                 paint.setColor(ab);                 c.drawCircle(cx + tx, cy + ty, expand * r, paint);                 canvas.drawBitmap(tempBmp, 0, 0, null);             }         }         this.setContentView(new VennView(this));     } } 
like image 183
Pavel Dudka Avatar answered Oct 01 '22 20:10

Pavel Dudka


Thanks again Pavel. That would have been very difficult for me to have figured out on my own. I am answering my own question to better drill into details, but I've accepted yours as the best answer.

You are right that I prefer not to have to create and manage an off-screen Bitmap (and Canvas). That is essentially why I mentioned that a black or white background would be fine to make this work.

I'm never concerned with performance before I see something working but I share your caution after that point. Editing your version to fix that and remove those members gives the implementation below.

Is this robust? Note the two calls to Canvas.drawColor() which I suspect could also be combined into one.

package com.superliminal.android.test.venn;  import android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PorterDuff.Mode; import android.graphics.PorterDuffXfermode; import android.os.Bundle; import android.view.View;  public class VennColorsActivity extends Activity {     private Paint paint = new Paint();      @Override     public void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         class VennView extends View {             public VennView(Context context) {                 super(context);             }              @Override             protected void onDraw(Canvas canvas) {                 super.onDraw(canvas);                 canvas.drawColor(Color.BLACK);                 canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);                 int alpha = 255, val = 255;                 int ar = Color.argb(alpha, val, 0, 0);                 int ag = Color.argb(alpha, 0, val, 0);                 int ab = Color.argb(alpha, 0, 0, val);                 float w = canvas.getWidth();                 float h = canvas.getHeight();                 float cx = w / 2f;                 float cy = h / 2;                 float r = w / 5;                 float tx = (float) (r * Math.cos(30 * Math.PI / 180));                 float ty = (float) (r * Math.sin(30 * Math.PI / 180));                 float expand = 1.5f;                 paint.setAntiAlias(true);                 paint.setXfermode(new PorterDuffXfermode(Mode.ADD));                 paint.setColor(ar);                 canvas.drawCircle(cx, cy - r, expand * r, paint);                 paint.setColor(ag);                 canvas.drawCircle(cx - tx, cy + ty, expand * r, paint);                 paint.setColor(ab);                 canvas.drawCircle(cx + tx, cy + ty, expand * r, paint);             }         }         setContentView(new VennView(this));     } } 
like image 43
Melinda Green Avatar answered Oct 01 '22 21:10

Melinda Green