How to make custom brush for canvas in android?
Asked Answered
G

3

37

In my canvas application i want to use custom brushes like brushes in attached image.so please somebody help me fast how can i make custom brushes like attached image?

In my app i made doted line using following code:

 mPaint.setPathEffect(new DashPathEffect(new float[] { 8, 8 }, 0));

and getting Blur and Emboss effect using following code:

 mEmboss = new EmbossMaskFilter(new float[] { 1, 1, 1 }, 0.4f, 6, 3.5f);

 mBlur = new BlurMaskFilter(8, BlurMaskFilter.Blur.NORMAL);

enter image description here

Guy answered 8/12, 2011 at 9:37 Comment(0)
E
22

As you can clearly see, no trivial shader effects / rectangles / circles can accomplish this. Images / Bitmaps are used.

So simply repeatedly draw Bitmaps using canvas.drawBitmap. You draw the same Bitmap again and again while the finger moves.

For adding a custom color you can add a simple filter.

An Example

public class CanvasBrushDrawing extends View {
    private Bitmap mBitmapBrush;
    private Vector2 mBitmapBrushDimensions;

    private List<Vector2> mPositions = new ArrayList<Vector2>(100);

    private static final class Vector2 {
        public Vector2(float x, float y) {
            this.x = x;
            this.y = y;
        }

        public final float x;
        public final float y;
    }

    public CanvasBrushDrawing(Context context) {
        super(context);

// load your brush here
        mBitmapBrush = BitmapFactory.decodeResource(context.getResources(), R.drawable.splatter_brush);
        mBitmapBrushDimensions = new Vector2(mBitmapBrush.getWidth(), mBitmapBrush.getHeight());

        setBackgroundColor(0xffffffff);
    }

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

        for (Vector2 pos : mPositions) {
            canvas.drawBitmap(mBitmapBrush, pos.x, pos.y, null);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        int action = event.getAction();
        switch (action) {
        case MotionEvent.ACTION_MOVE:
            final float posX = event.getX();
            final float posY = event.getY();
            mPositions.add(new Vector2(posX - mBitmapBrushDimensions.x / 2, posY - mBitmapBrushDimensions.y / 2));
            invalidate();
        }

        return true;
    }
}
Enervated answered 2/1, 2012 at 11:3 Comment(8)
can you give some example of brush from above available in question any one?Chapin
your answer help me to draw a bitmap as brush but when i change colorfilter color then its applied to whole lineOxley
Added sample brush image and result.Vida
@Oxley Have you found any solution to resolve this problem?Lananna
Yes remove for loop from OnDraw method just keep canvas.drawBitmap() method and on action MOVE keep this mCanvas.drawBitmap(mBitmapBrush, touchX, touchY, mPaint); and invalidate()Oxley
@Oxley I'm confused at this point, what would be the x, y co-ordinates at canvas.drawBitmap() in OnDraw() method & ACTION_MOVE event?Acrefoot
@FallingIntoInfinity its coordinate of finger touch/move. when you move your finger on screen we need positions for draw. so x and y is a point where you touch on screen and its added to an array list for later use in onDraw method.Oxley
@Enervated This will lead to a serious memory issue. Since you are applying a lot of bitmaps in canvas this will make the UI heavier. Suppose I've drawn hundreds of lines where each line is containing a large number of bitmap brush.Philodendron
P
8

Though it is too late i want to share something. This might help someone. Various brush techniques are discussed in the following link with JavaScript code for HTML canvas. All you have to do is convert JavaScript code to your expected one. It is pretty simple to covert JavaScript Canvas code to Android Canvas code.

Exploring canvas drawing techniques

I have converted "Multiple lines" technique to Java code for android; You can check the following android view code.

public class MultipleLines extends View {

    private Bitmap bitmap;
    private Canvas canvas;

    private Paint mPaint;

    public MultipleLines(Context context) {
        super(context);
        init();
    }

    private void init(){
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setColor(0xFFFF0000);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setStrokeWidth(1);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        canvas = new Canvas(bitmap);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                touch_start(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_MOVE:
                touch_move(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                touch_up();
                invalidate();
                break;
        }
        return true;
    }

    private boolean isDrawing;
    private List<PointF> points = new ArrayList<>();

    private void touch_start(float touchX, float touchY) {
        isDrawing = true;
        points.add(new PointF(touchX, touchY));

        canvas.save();
    }
    private void touch_move(float touchX, float touchY) {
        if (!isDrawing) return;

        canvas.drawColor(Color.TRANSPARENT);

        points.add(new PointF(touchX, touchY));

        stroke(offsetPoints(-10));
        stroke(offsetPoints(-5));
        stroke(points);
        stroke(offsetPoints(5));
        stroke(offsetPoints(10));
    }

    private void touch_up() {
        isDrawing = false;
        points.clear();
        canvas.restore();
    }

    private List<PointF> offsetPoints(float val) {
        List<PointF> offsetPoints = new ArrayList<>();
        for (int i = 0; i < points.size(); i++) {
            PointF point = points.get(i);
            offsetPoints.add(new PointF(point.x + val, point.y + val));
        }
        return offsetPoints;
    }

    private void stroke(List<PointF> points) {
        PointF p1 = points.get(0);
        PointF p2 = points.get(1);

        Path path = new Path();
        path.moveTo(p1.x, p1.y);

        for (int i = 1; i < points.size(); i++) {
            // we pick the point between pi+1 & pi+2 as the
            // end point and p1 as our control point
            PointF midPoint = midPointBtw(p1, p2);
            path.quadTo(p1.x, p1.y, midPoint.x, midPoint.y);
            p1 = points.get(i);
            if(i+1 < points.size()) p2 = points.get(i+1);
        }
        // Draw last line as a straight line while
        // we wait for the next point to be able to calculate
        // the bezier control point
        path.lineTo(p1.x, p1.y);

        canvas.drawPath(path,mPaint);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawColor(Color.WHITE);
        canvas.drawBitmap(bitmap, 0, 0, null);
    }

    private PointF midPointBtw(PointF p1, PointF p2) {
        return new PointF(p1.x + (p2.x - p1.x) / 2.0f, p1.y + (p2.y - p1.y) / 2.0f);
    }
}
Pokeweed answered 30/3, 2020 at 5:4 Comment(2)
By the way this is the real answer, but quite trick, can you help me with more brushes please?Cilurzo
This link is really helpful, it discuessed the implementation of several commonly used brushes.Perform
E
0

I'm converting the image to SVG, then creating a vector image and using the pathData in it so that I can draw it directly using drawPath, but if you have a fast finger swipe, you'll need to deal with the missing paths separately

Epiphany answered 1/12, 2023 at 6:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.