有没有办法在位图上实现类似笔画的效果?

时间:2017-07-13 00:08:10

标签: android canvas bitmap graphics2d android-bitmap

我想在位图上实现与

相当的效果
paint.setStyle(Paint.Style.STROKE) 
canvas.drawText(string, x, y, paint);

适用于文字。

类似于BlurMaskFilter的东西,但是对于笔画,不是发光,也不是阴影。

或者,如果没有内置方式,也许有人可以建议一个算法来实现这个目标?

1 个答案:

答案 0 :(得分:1)

private Bitmap multiplyAlpha(final Bitmap bitmap, Paint paint,
        final boolean color, final float x) {
    paint = new Paint(paint.getFlags());
    Bitmap result = Bitmap.createBitmap(bitmap.getWidth(),
            bitmap.getHeight(), color ? Config.ARGB_8888 : Config.ALPHA_8);
    // ColorMatrixColorFilter requires ARGB.
    Bitmap auxiliary = Bitmap.createBitmap(bitmap.getWidth(),
            bitmap.getHeight(), Config.ARGB_8888);
    new Canvas(auxiliary).drawBitmap(bitmap, 0, 0, paint);
    // @formatter:off
    paint.setColorFilter(new ColorMatrixColorFilter(new float[] { 
               1,    0,    0,    0,    0,
               0,    1,    0,    0,    0,
               0,    0,    1,    0,    0,
               0,    0,    0,    x,    0
    }));
    // @formatter:on
    new Canvas(result).drawBitmap(auxiliary, 0, 0, paint);
    return result;
}

private Bitmap opaque(final Bitmap bitmap, Paint paint, final boolean color) {
    return multiplyAlpha(bitmap, paint, color, 255);
}

private RectF inset(final RectF rectF, final float dx, final float dy) {
    RectF result = new RectF(rectF);
    result.inset(dx, dy);
    return result;
}

private RectF offset(final RectF rectF, final float dx, final float dy) {
    RectF result = new RectF(rectF);
    result.offset(dx, dy);
    return result;
}

private Bitmap antialias(final Bitmap bitmap, Paint paint,
        final float radius) {
    Bitmap result = Bitmap.createBitmap(bitmap.getWidth(),
            bitmap.getHeight(), Config.ALPHA_8);
    if (radius > 0) {
        paint = new Paint(paint.getFlags());
        Canvas canvas = new Canvas(result);
        Bitmap opaque = opaque(bitmap, paint, false);
        canvas.drawBitmap(opaque, 0, 0, paint);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        paint.setMaskFilter(new BlurMaskFilter(radius, Blur.INNER));
        canvas.drawBitmap(opaque, 0, 0, paint);
    }
    return result;
}

private Bitmap stroke(final Bitmap bitmap, Paint paint, final float radius,
        final RectF rectF, final float dx, final float dy) {
    paint = new Paint(paint.getFlags());
    Bitmap result = Bitmap.createBitmap(
            (int) Math.ceil(rectF.width() + 2 * radius),
            (int) Math.ceil(rectF.height() + 2 * radius), Config.ALPHA_8);
    if (radius > 0) {
        paint.setMaskFilter(new BlurMaskFilter(radius, Blur.NORMAL));
        new Canvas(result).drawBitmap(opaque(bitmap, paint, false), null,
                offset(rectF, -dx, -dy), paint);
    }
    return result;
}

private Bitmap stroke(final Bitmap bitmap, Paint paint, final float radius,
        final RectF rectF, final int color, final float antialias,
        final float factor, final boolean fill, final float dx,
        final float dy) {
    paint = new Paint(paint.getFlags());
    Canvas canvas = new Canvas();
    paint.setColor(color);
    Bitmap stroke = stroke(bitmap, paint, radius, rectF, dx, dy);

    Bitmap auxiliary = Bitmap.createBitmap(stroke.getWidth(),
            stroke.getHeight(), Config.ALPHA_8);
    canvas.setBitmap(auxiliary);
    // Paint [opaque] stroke.
    canvas.drawBitmap(opaque(stroke, paint, false), 0, 0, paint);
    // Antialias stroke with outside.
    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
    Bitmap outer = multiplyAlpha(antialias(stroke, paint, antialias),
            paint, false, factor);

    canvas.drawBitmap(outer, 0, 0, paint);
    paint.setXfermode(null);

    // If FILL, leave the inside filled with color (this is the way e.g.
    // Photoshop strokes); otherwise, the stroke will be only on the outside
    // of the bitmap; the more transparent the bitmap, the more noticeable
    // the effect.
    if (!fill) {
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        canvas.drawBitmap(opaque(bitmap, paint, false), null,
                offset(rectF, -dx, -dy), paint);
        paint.setXfermode(null);
    }

    Bitmap result = Bitmap.createBitmap(auxiliary.getWidth(),
            auxiliary.getHeight(), bitmap.getConfig());
    canvas.setBitmap(result);
    RectF output = offset(rectF, -dx, -dy);
    canvas.drawBitmap(auxiliary, null, inset(output, -radius, -radius),
            paint);
    // Paint bitmap.
    canvas.drawBitmap(bitmap, null, output, paint);
    // Antialias stroke with bitmap.
    Bitmap inner = multiplyAlpha(antialias(bitmap, paint, antialias),
            paint, false, factor);
    canvas.drawBitmap(inner, null, output, paint);

    return result;
}

private void stroke(final Bitmap bitmap, Paint paint, final float radius,
        final RectF rectF, final int color, final float antialias,
        final float factor, final boolean fill, Canvas canvas) {
    float dx = rectF.left - radius;
    float dy = rectF.top - radius;
    Bitmap stroke = stroke(bitmap, paint, radius, rectF, color, antialias,
            factor, fill, dx, dy);
    canvas.drawBitmap(stroke, dx, dy, paint);
}

其中:

  • antialias是抗锯齿效果的宽度
  • factor修改了抗锯齿力
  • fill表示是 false 仅绘制之外的笔划,还是 true 也填充内部(这就是Photoshop笔画的方式); bitmap越透明,效果就越明显
  • rectF表示要绘制的RectF

用法(例如):

stroke(bmp, paint, radius, rectF, 0xffff0000, antialias, factor, fill, canvas);