9

Possible duplicate How to make custom brush for canvas in android?

Hello friends,

I am too stuck to create this type of brush for paint application, but didn't find anything related to this.

I am new to paint/canvas so I don't have knowledge about this for the basic I have completed but for the effect like creating brush I didn't have anything like how to create/implement it. Does anybody have example of or code for this?

I need this type of brush for my application simple one example need for understanding:

enter image description here

Thank you.

Community
  • 1
  • 1
Pratik
  • 30,639
  • 18
  • 84
  • 159

3 Answers3

8

I guess there is no easy way. I found this discussion and particularly the following post is interesting:

Professional Computer Graphics is never easy. That's why there are so few people really tackling it. To make things worse, professional techniques are rarely published. I don't know how much effort you desire to make to get it, but I will give you some light. So, if you want, you can study, develop and get it the best way. If it seem too hard for you, let it here as a curiosity.

The professional way to make calligraphic brushes nowadays is like that:

The master curve is smooth because it's drawn based on spline(s). To get the more professional result, construct two splines: one using the points you got (for example, from mouse events) lying over the spline and another using the points like the spline control points. So the curve you draw is the curve generated from the interpolation of these two splines. This way, you have a "master curve" to draw.

You should also have a "master thickness" on which a variation must be applied. This thickness variation is calculated according to the result you want. The more common kind of calligraphic brush is just like in the image you linked: the curved regions usually are thinner than the straight ones. It's the more usual type because most designers get this kind of result when drawing with a tablet, so programs emulate this behavior. This effect in particular is usually calculated using a function based on the second derivate of the master spline. The thickness variation amplitude can be a configurable value.

The thin and sharp curve tips are made in a extra calculation. Sometimes it can be a good idea smoothing even the thickness variations with splines or some kind of "ceil function".

If you made everything right, you have a thick (and of course closed) curve in your hands. Draw it using the best filling algorithm you can develop. Use anti-aliasing if you are able to.

All these techniques can be calculated in real time while the user moves the mouse. The more points you get, the more calculations you make, but it works well because most calculations you already made are still valid. Usually you just need to reconstruct a small (last) part.

One last suggestion: never make 2D smoothing using function regression methods, unless your points really represent a function (so you need to keep the "math meaning" of the points as much as possible). I can not imagine a slower way to smooth points that have no special semantics. The only exception is when you have very very sparse points and the input order doesn't matter, but it's not the case when somebody is drawing with brushes.

Caner
  • 57,267
  • 35
  • 174
  • 180
3

You can achieved this effect by drawing bitmap texture on a canvas. I cropped a little texture from image you shared and used that as texture in canvas :-

enter image description here

Texture image :-

enter image description here

Here is my view class :-

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.view.MotionEvent;
import android.view.View;

import com.serveroverload.dali.R;

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.ic_launcher);
        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;
    }
}

You can use this view in your activity like this :-

setContentView(new CanvasBrushDrawing(MainActivity.this));

Now You just need better texture files from your designer. Hope it helped

You can see complete source code on Git repo https://github.com/hiteshsahu/Dali-PaintBox

Hitesh Sahu
  • 41,955
  • 17
  • 205
  • 154
  • 1
    Hi Hitesh, please do not add duplicate answers, answer one question. If you find duplicates flag them, if they are no duplicates leave a comment pointing to the possible solution you provided **once**. – bummi Jan 09 '16 at 19:20
  • @Hitesh Sahu I'm using paths for undo/redo operation but when I put the for loop inside onDraw() method, custom brushes are applied multiple time. Please check my question here, http://stackoverflow.com/questions/38995187/custom-brush-with-undo-redo-operation-in-android-canvas/38995449?noredirect=1#comment65385129_38995449 – Falling Into Infinity Aug 18 '16 at 17:18
  • This is not a good brush drawing because there are lots of blank spaces. Any other help? – Bhaven Shah Oct 17 '20 at 19:57
2

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);
}

}

ZeroOneZeroR
  • 667
  • 1
  • 7
  • 12
  • This is the real answer, can you please help me with this conversion from js to android? – Mubashir Murtaza Aug 25 '20 at 13:05
  • Yeah sure. @MubashirMurtaza – ZeroOneZeroR Sep 01 '20 at 04:46
  • the link you share i need these brushes ,, i already converted some of them but they are not exactly same, but i want these burshes , please help , Fur (rotating strokes),Trail effectRandom radius, opacity,Randomize everything! help me with any of one or two please – Mubashir Murtaza Sep 01 '20 at 09:17
  • Actually I did not try all of those. Some are easy and some are quite complex but possible I guess. You have to do it yourself. Sorry. – ZeroOneZeroR Sep 01 '20 at 12:02
  • 1
    See Colored Pixels code. https://gist.github.com/rajib010/fb707ff115396a723662542aea43138e – ZeroOneZeroR Sep 01 '20 at 12:44
  • 1
    See 'Randomize Everything' https://gist.github.com/rajib010/fb707ff115396a723662542aea43138e – ZeroOneZeroR Sep 01 '20 at 13:26
  • thanks for links, and i want to add one thing here,,the example you shared here is best but any one trying this without bitmap, mean if they are drawing using path in canvas , donot forgot to add path.reset in motion move or else the path will be drawn too slowly, case MotionEvent.ACTION_MOVE: //herePath.reset touch_move(x, y); invalidate(); – Mubashir Murtaza Sep 01 '20 at 13:57
  • Yeah. There are a lot to be improved in performance, I just showed you the conversion technique. – ZeroOneZeroR Sep 01 '20 at 16:35
  • this helped me alot but the brushes act differently when i move my finger fast and slow, so it doesn't look professional, i tried storing points in a list and than using for-loop i drawn a path, but still not working, so i conclude that may be when finger moves fast less points are stored in list! but the path.addline or (line using canvas) drawn very well even you move finger fast or slow , line draw professional, so may be i need to draw everything using line but how!? do you have any idea? – Mubashir Murtaza Sep 02 '20 at 11:57
  • 1
    I have modified Colored Pixels. Now see the gist. – ZeroOneZeroR Sep 03 '20 at 05:59
  • You are wining my heart bro <3, it is working but still need improvement, now when i move finger fast the points didn't miss but on slow moving finger there are more point than fast moving finger, what i want is in both cases the brush should act same. i really appreciate your help and time for slack community before today i never had idea that slack members are that much helping. Please do me this favor i hope this one will be last :p, – Mubashir Murtaza Sep 03 '20 at 11:37
  • here you can see the differece of what i talked about in previous comment [link]( https://drive.google.com/file/d/1w24jdiZ_-h-uhzee2v0xA0RhE6kPpZs6/view?usp=sharing) – Mubashir Murtaza Sep 03 '20 at 11:43
  • Here i posted Question with complete details, you already help me alot it just need bit improvement. https://stackoverflow.com/questions/63705422/how-to-draw-brushes-or-shape-using-path-when-moving-finger-fast-in-andorid-canva – Mubashir Murtaza Sep 03 '20 at 12:09
  • Bro will you help me out please!? – Mubashir Murtaza Sep 04 '20 at 10:39
  • I will try. Give me some time please. – ZeroOneZeroR Sep 04 '20 at 11:59
  • Thankyou brother, im waiting :p and its better if you check my question i attached link in previous comment and answer there so i could mark it right so anyone can get help for similar issue from there as in comments we cannot go to details. – Mubashir Murtaza Sep 04 '20 at 12:03
  • I solved it :D https://stackoverflow.com/questions/8924734/how-to-create-this-type-of-brush-for-paint-in-android/60915121?noredirect=1#comment112714322_60915121 – Mubashir Murtaza Sep 05 '20 at 12:37