I want to paint graphics onto a Canvas such that the colors are additive. For example, I want to produce this:
But instead, I get this:
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?
You are on the right track. There are 3 major issues in your code:
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.
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)); } }
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)); } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With