Search code examples
androidandroid-canvasporter-duff

Draw overlapping areas on one path


I have a Path that crosses over itself and I want to change the color of the areas that are gone over more than once. Like below:

desired behavior

So I set up my paint.

    highlighterPaint = new Paint();
    highlighterPaint.setAntiAlias(true);
    strokeWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20,
            displayMetrics);
    highlighterPaint.setStrokeWidth(strokeWidth);
    highlighterPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DARKEN));
    highlighterPaint.setAlpha(200);
    highlighterPaint.setStyle(Paint.Style.STROKE);
    highlighterPaint.setStrokeJoin(Paint.Join.ROUND);

But when I call canvas.drawPath(mPath1, highlighterPaint) and canvas.drawPath(mPath2, highlighterPaint) I get the below image. There are two Paths in this picture with their endpoints labeled.

actual behavior

I'm drawing each path onto a Canvas.

Separate Paths correctly darken their shared area, but a single Path does not. How can I achieve an effect similar to the first image?


Solution

  • Path simply cannot do this. Never fear, there is a better way!

    The trick is to split the path into many smaller sections and draw each section separately.

    In my case, I was creating a path from a series of points (generated from touch input) and drawing quadratic beziers to connect the points. Here is a quick outline:

    int numPoints = 0;
    for (Point p : points) {
        p1X = p2X;
        p1Y = p2Y;
        p2X = p3X;
        p2Y = p3Y;
        p3X = p.x;
        p3Y = p.y;
    
        numPoints++;
        if (numPoints >= 3) {
            startX = (p1X + p2X) / 2.0f;
            startY = (p1Y + p2Y) / 2.0f;
            controlX = p2X;
            controlY = p2Y;
            endX = (p2X + p3X) / 2.0f;
            endY = (p2Y + p3Y) / 2.0f;
            path.rewind();
            path.moveTo(startX, startY);
            path.quadTo(controlX, controlY, endX, endY);
            canvas.drawPath(path, paint);
        }
    }