9

可能重复如何在android中为画布制作自定义画笔?

大家好,

我太坚持为油漆应用创建这种类型的画笔,但没有找到与此相关的任何内容。

我是绘画/画布的新手,所以对于我已完成的基础知识,我对此一无所知,但对于像创建画笔这样的效果,我没有任何关于如何创建/实现它的知识。有人有这方面的例子或代码吗?

我的应用程序需要这种类型的画笔,一个简单的例子需要理解:

在此处输入图像描述

谢谢你。

4

3 回答 3

8

我想没有简单的方法。我发现这个讨论,特别是下面的帖子很有趣:

专业的计算机图形学绝非易事。这就是为什么很少有人真正解决它。更糟糕的是,专业技术很少发表。我不知道你想要付出多少努力才能得到它,但我会给你一些启示。因此,如果您愿意,您可以学习、开发并以最佳方式获得它。如果这对你来说太难了,就把它放在这里作为一种好奇心。

现在专业的毛笔制作方法是这样的:

主曲线是平滑的,因为它是基于样条曲线绘制的。要获得更专业的结果,请构建两条样条曲线:一条使用您在样条曲线上获得的点(例如,从鼠标事件中获得),另一条使用样条曲线控制点等点。所以你画的曲线就是这两条样条插值产生的曲线。这样,您就可以绘制“主曲线”。

您还应该有一个必须应用变化的“主厚度”。该厚度变化是根据您想要的结果计算的。更常见的书法毛笔就像您链接的图像中一样:弯曲区域通常比直线区域更薄。这是更常见的类型,因为大多数设计师在使用平板电脑绘图时都会得到这种结果,因此程序会模拟这种行为。尤其是这种效果通常使用基于主样条的二阶导数的函数来计算。厚度变化幅度可以是可配置的值。

细而尖的曲线尖端是在额外的计算中制作的。有时,使用样条曲线或某种“ceil 函数”平滑厚度变化可能是一个好主意。

如果你把一切都做对了,你手中就会有一条厚实的(当然是封闭的)曲线。使用您可以开发的最佳填充算法绘制它。如果可以,请使用抗锯齿。

所有这些技术都可以在用户移动鼠标时实时计算。您获得的分数越多,您进行的计算就越多,但它运作良好,因为您已经进行的大多数计算仍然有效。通常你只需要重建一个小(最后)部分。

最后一个建议:永远不要使用函数回归方法进行 2D 平滑,除非你的点真的代表一个函数(所以你需要尽可能地保持点的“数学意义”)。我无法想象一种更慢的方法来平滑没有特殊语义的点。唯一的例外是当你有非常稀疏的点并且输入顺序无关紧要时,但当有人用画笔绘图时情况并非如此。

于 2012-01-19T10:59:47.697 回答
3

您可以通过在画布上绘制位图纹理来实现此效果。我从您共享的图像中裁剪了一些纹理并将其用作画布中的纹理:-

在此处输入图像描述

纹理图像:-

在此处输入图像描述

这是我的视图类:-

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

您可以像这样在您的活动中使用此视图:-

setContentView(new CanvasBrushDrawing(MainActivity.this));

现在你只需要你的设计师提供更好的纹理文件。希望它有所帮助

您可以在 Git repo https://github.com/hiteshsahu/Dali-PaintBox上查看完整的源代码

于 2016-01-09T19:06:19.553 回答
2

虽然为时已晚,但我想分享一些东西。这可能会帮助某人。以下链接中讨论了各种画笔技术,其中包含用于 HTML 画布的 JavaScript 代码。您所要做的就是将 JavaScript 代码转换为您期望的代码。将 JavaScript Canvas 代码转换为 Android Canvas 代码非常简单。

探索画布绘画技巧

我已将“多行”技术转换为 android 的 Java 代码;您可以检查以下android视图代码。

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

}

于 2020-03-29T13:47:53.177 回答