Thracian
Thracian

Reputation: 66899

Android Canvas change color of intersection of shapes and texts

I'm trying to change color of intersection of shapes and text using Canvas. As you can see in the imagei have 2 shapes and 1 text with different paints.

This is without any PorterDuffXfermode, and PorterDuffColorFilter mode added to any paint. I want intersection of shapes to be particular color, for example white, and text red or white like the images below.

In my example, text is at the bottom, circle middle and rectangle above but it's insignificant, i'm just trying to figure out how it works and how should intersection can be set to particular color.

As you can see in this imageimage background is black, circles are white and text is black. When circles overlap intersection of circles turns to black and overlapping section of text turns to white.

I can achieve this with

canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_OUT));

if circle and rectangle are both white and background is black. When i change background color to any other color, text is not visible, shapes are black.

I also wonder how can i get similar results like in this imageon the image Can overlapping sections for circle, rectangle and text can be set to specific color?

Upvotes: 3

Views: 3661

Answers (1)

Jon Goodwin
Jon Goodwin

Reputation: 9153

Android Canvas change color of intersection of shapes and texts

and:

"If circle and rectangle are both white and background is black. When I change background color to any other color, text is not visible, shapes are black."

PorterDuff

  • (1) Yes we can with PorterDuff.Mode (Mode.ADD):

    PorterDuff.Mode mode = Mode.ADD;

  • (2) The Canvas background must be transparent for the compositing to work. If you need any other background color then we need to do the compositing on a separate Canvas. Then we copy the drawing to the original Canvas.

"I also wonder how can i get similar results like in this image"

Here is the code output (PorterDuff decides the blend color ;O( ):

code outputcode output

(Q1:) "Can overlapping sections for circle, rectangle and text can be set to specific color?"

  • (A1:) Yes it can be done (with much effort). Almost finished but confident it can. here are the almost finished images (I had to use almost all of the stuff in this answer, there must be an easier way...):

enter image description hereenter image description here

Code

@Override
protected void onDraw(Canvas canvas) 
{
    super.onDraw(canvas);

    Paint paint          = new Paint();
    Paint paintClear     = new Paint();
    TextPaint textPaint  = new TextPaint();
    int width            = getWidth();
    int height           = getHeight();
    int x                = 100;
    int y                = 100;
    int radius           = 100;

    PorterDuff.Mode mode        = Mode.ADD;      // mode Mode.ADD

    paintClear.setStyle(Style.FILL); 
    paint.setStyle(Style.FILL); 
    textPaint.setAntiAlias(true); 
    textPaint.setTextSize(100 * getResources().getDisplayMetrics().density);
    textPaint.setColor(Color.GREEN);    
    textPaint.setStrokeWidth(3);     

    // ** clear canvas backgound to white**
    paintClear.setColor(Color.WHITE);
    canvas.drawPaint(paintClear); 
    //canvas.save();

    Bitmap compositeBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
    Canvas compositeCanvas = new Canvas(compositeBitmap);
    paintClear.setColor(Color.TRANSPARENT);
    compositeCanvas.drawPaint(paintClear); 

    // ** draw destination circle in red **
    paint.setColor(Color.RED);
    compositeCanvas.drawCircle(x+100, y+100, radius, paint);

    // ** set Xfermode **
    paint.setXfermode(new PorterDuffXfermode(mode));
    textPaint.setXfermode(new PorterDuffXfermode(mode));

    // ** draw source circle in blue **
    paint.setColor(Color.BLUE);
    compositeCanvas.drawCircle(x-0, y-0, radius, paint);

    // ** draw text in Green **
    compositeCanvas.save();
    compositeCanvas.rotate(-45, x, y+150);
    compositeCanvas.drawText("- 65,6", x, y+150, textPaint);
    compositeCanvas.restore();

    //copy compositeCanvas to canvas
    canvas.drawBitmap(compositeBitmap, 0, 0, null);
    //canvas.restore();
}//onDraw

Intersection Testing

Using RectF contains and intersects:

public Rect  mBound = new Rect(0  , 0  , 10 , 10);
public RectF a1     = new Rect(0f , 0f , 10f, 10f);
public RectF b1     = new Rect(5f , 5f , 20f, 20f);
public RectF c1     = new Rect(30f, 30f, 40f, 40f);

int boolean hit       = false;
int boolean intersect = false;
hit = hitTest(20,15); // returns false
hit = hitTest(5,5);   // returns true 
intersect = intersectsTest(a1,b1);// returns true
intersect = intersectsTest(a1,c1);// returns false

public boolean hitTest(int x, int y) 
{
    return mBound.contains(x, y); 
}//
// Returns true if the two specified rectangles intersect
public boolean intersectsTest(RectF a, RectF b) 
{
    return intersects ( a, b);
}//

Notes

You indicated you wanted to know more about color drawing, here are some things I toyed with.

Experiments with setColorFilter and ColorMatrixColorFilter:

textPaint.setColorFilter(new ColorMatrixColorFilter(getColorMatrix5()));//custom 5

//custom 5
private ColorMatrix getColorMatrix5() 
{
    ColorMatrix colorMatrix = new ColorMatrix();
    colorMatrix.setSaturation(0);//make it greyscale
    ColorMatrix blueMatrix = new ColorMatrix(new float[] {
        0, 0, 0, 0, 0, // red
        0, 0, 0, 0, 0, // green
        1, 1, 1, 1, 1, // blue
        1, 1, 1, 1, 1  // alpha
    });
    // Convert, then scale and clamp
    colorMatrix.postConcat(blueMatrix);
    return colorMatrix;
}//getColorMatrix1

Customise PorterDuff

It sounds like you need a Custom PorterDuff. Here's some code to give you an idea of how to write one (Android uses a library written in C++).

public class MyPorterDuffMode
{

    public Bitmap applyOverlayMode(Bitmap srcBmp, Bitmap destBmp)
    {
        int width          = srcBmp.getWidth();
        int height         = srcBmp.getHeight();
        int srcPixels[]    = new int[width * height];
        int destPixels[]   = new int[width * height];
        int resultPixels[] = new int[width * height];
        int aS   = 0, rS = 0, gS = 0, bS = 0;
        int rgbS = 0;
        int aD   = 0, rD = 0, gD = 0, bD = 0;
        int rgbD = 0;

        try
        {
            srcBmp.getPixels(srcPixels, 0, width, 0, 0, width, height);
            destBmp.getPixels(destPixels, 0, width, 0, 0, width, height);
            srcBmp.recycle();
            destBmp.recycle();
        }
        catch(IllegalArgumentException e)
        {
        }
        catch(ArrayIndexOutOfBoundsException e)
        {
        }

        for(int y = 0; y < height; y++)
        {
            for(int x = 0; x < width; x++)
            {
                rgbS = srcPixels[y*width + x];
                aS = (rgbS >> 24) & 0xff;
                rS = (rgbS >> 16) & 0xff;
                gS = (rgbS >>  8) & 0xff;
                bS = (rgbS      ) & 0xff;

                rgbD = destPixels[y*width + x];
                aD = ((rgbD >> 24) & 0xff);
                rD = (rgbD >> 16) & 0xff;
                gD = (rgbD >>  8) & 0xff;
                bD = (rgbD      )  & 0xff;

                //overlay-mode
                rS = overlay_byte(rD, rS, aS, aD);
                gS = overlay_byte(gD, gS, aS, aD);
                bS = overlay_byte(bD, bS, aS, aD);
                aS = aS + aD - Math.round((aS * aD)/255f);

                resultPixels[y*width + x] = ((int)aS << 24) | ((int)rS << 16) | ((int)gS << 8) | (int)bS;
            }
        }
        return Bitmap.createBitmap(resultPixels, width, height, srcBmp.getConfig());
    }

    // kOverlay_Mode
    int overlay_byte(int sc, int dc, int sa, int da) 
    {
        int tmp = sc * (255 - da) + dc * (255 - sa);
        int rc;
        if (2 * dc <= da) 
        {
            rc = 2 * sc * dc;
        } 
        else 
        {
            rc = sa * da - 2 * (da - dc) * (sa - sc);
        }
        return clamp_div255round(rc + tmp);
    }

    int clamp_div255round(int prod) 
    {
        if (prod <= 0) 
        {
            return 0;
        } 
        else if (prod >= 255*255) 
        {
            return 255;
        } 
        else 
        {
            return Math.round((float)prod/255);
        }
    }

}//class MyPorterDuffMode

Code Reference

See PorterDuff, PorterDuff.Mode,PorterDuffXfermode, ColorFilter,ColorMatrix, ColorMatrixColorFilter, PorterDuffColorFilter, Canvas, Color.

Upvotes: 9

Related Questions