【问题标题】:How to create this type of brush for paint in android如何在android中创建这种类型的油漆刷
【发布时间】:2012-01-19 10:47:32
【问题描述】:

可能重复How to make custom brush for canvas in android?

朋友们好,

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

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

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

谢谢。

【问题讨论】:

  • 嘿,pratik,你有没有找到任何解决方案。你能帮我实现同样的目标吗?
  • @skygeek 和其他任何人您有什么解决方案吗?任何帮助将不胜感激。

标签: android paint android-canvas brush


【解决方案1】:

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

Exploring canvas drawing techniques

我已将“多行”技术转换为 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);
}

}

【讨论】:

【解决方案2】:

我想没有简单的方法。我找到了this discussion,尤其是下面的帖子很有趣:

专业的计算机图形学绝非易事。这就是为什么有 很少有人真正解决它。更糟糕的是,专业 技术很少发表。不知道你付出了多少努力 渴望得到它,但我会给你一些光明。所以,如果你 想要,你可以学习、开发和获得它的最佳方式。如果它看起来太 对你来说很难,让它在这里作为一个好奇心。

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

主曲线是平滑的,因为它是基于样条曲线绘制的。至 获得更专业的结果,构造两条样条曲线:一条使用 你得到的点(例如,来自鼠标事件)位于样条线上 另一个使用样条控制点等点。所以 您绘制的曲线是从这些插值生成的曲线 两条样条线。这样,您就可以绘制“主曲线”了。

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

细而尖的曲线尖端是在额外计算中产生的。 有时,即使厚度平滑也是一个好主意 样条曲线或某种“细胞函数”的变化。

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

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

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

【讨论】:

    【解决方案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看到完整的源代码

    【讨论】:

    • Hi Hitesh,请不要添加重复的答案,回答一个问题。如果您发现重复标记它们,如果它们没有重复,请留下评论,指出您提供一次的可能解决方案。
    • @Hitesh Sahu 我正在使用路径进行撤消/重做操作,但是当我将 for 循环放入 onDraw() 方法时,会多次应用自定义画笔。请在这里查看我的问题,stackoverflow.com/questions/38995187/…
    • 这不是一个好的画笔,因为有很多空白。还有其他帮助吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-02
    • 2020-01-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多