带边框的毕加索图像蒙版变换

Picasso image mask transform with border

我正在尝试使用 Picasso Transformation 在带有鼠形蒙版的图像上绘制边框。鼠形遮罩是一个 VectorDrawable。

我认为最简单和最灵活的方法是首先使用 canvas.drawPaint 绘制一个具有所需边框颜色的较大圆圈。然后使用 canvas.drawBitmap 使用照片位图绘制一个较小的圆圈。我可以分别绘制它们,我可以缩放位图并成功地使用蒙版绘制它,但是任何时候我尝试将两者结合起来它都会在 canvas.drawBitmap 调用中丢失蒙版。关于我可能做错了什么有什么想法吗?

我尝试了很多混合模式选项,但我认为这不是问题所在。

override fun transform(source: Bitmap): Bitmap {
    val width = source.width
    val height = source.height
    val borderWidth = 100

    val output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
    val canvas = Canvas(output)

    //Draw a full size, red squircle
    val paint = Paint()
    paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
    paint.color = Color.RED
    val mask = context.getDrawable(maskID)
    mask.setBounds(0, 0, width, height)
    mask.draw(canvas)
    canvas.drawPaint(paint)

    //Draw a masked, scaled down bitmap of the photo on top
    val bitmapPaint = Paint()
    bitmapPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
    val bitmapMask = context.getDrawable(maskID)
    bitmapMask.setBounds(borderWidth / 2, borderWidth / 2, width - borderWidth / 2, height - borderWidth / 2)
    bitmapMask.draw(canvas)

    val sourceDrawable = source.toDrawable(context.resources)
    sourceDrawable.setBounds(borderWidth / 2, borderWidth / 2, width - borderWidth / 2, height - borderWidth / 2)
    canvas.drawBitmap(sourceDrawable.bitmap, null,
           Rect(borderWidth / 2, borderWidth / 2, width - borderWidth / 2, height - borderWidth / 2),
           bitmapPaint)

    source.recycle()

    return output
}

结果:

如果我注释掉对 drawBitmap 的调用,我会得到这个结果(差不多了!):

2018 年 11 月 27 日更新

我已经解决了这个问题,方法是在临时 canvas 上用所需的掩码绘制图片,然后在主 canvas 上绘制结果位图。源代码和可视化结果包含在这里:

MaskTransformation.kt

import android.content.Context
import android.graphics.*
import android.graphics.drawable.BitmapDrawable
import android.support.annotation.DrawableRes
import android.support.v4.content.ContextCompat
import com.squareup.picasso.Transformation


class MaskTransformation(
    private val context: Context,
    @DrawableRes private val maskID: Int
) : Transformation {

    override fun key(): String {
        return "mask"
    }

    override fun transform(source: Bitmap): Bitmap {
        val width = source.width
        val height = source.height
        val borderWidth = 400

        val output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
        val canvas = Canvas(output)

        //Draw a full size, red squircle
        val paint = Paint()
        paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
        paint.color = Color.RED
        val mask = ContextCompat.getDrawable(context, maskID)!!
        mask.setBounds(0, 0, width, height)
        mask.draw(canvas)
        canvas.drawPaint(paint)

        //Draw a masked, scaled down bitmap of the photo on top
        val maskingPaint = Paint()
        maskingPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
        val maskDrawable = ContextCompat.getDrawable(context, maskID)!!
        maskDrawable.setBounds(borderWidth / 2, borderWidth / 2, width - borderWidth / 2, height - borderWidth / 2)

        val overlayBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
        val overlayCanvas = Canvas(overlayBitmap)
        maskDrawable.draw(overlayCanvas)

        val pictureBitmap = Bitmap.createBitmap(width - borderWidth, height - borderWidth, Bitmap.Config.ARGB_8888)
        val pictureCanvas = Canvas(pictureBitmap)

        val sourceDrawable = BitmapDrawable(context.resources, source)
        sourceDrawable.setBounds(borderWidth / 2, borderWidth / 2, width - borderWidth / 2, height - borderWidth / 2)
        pictureCanvas.drawBitmap(
            sourceDrawable.bitmap,
            null,
            Rect(0, 0, width - borderWidth, height - borderWidth),
            Paint()
        )

        overlayCanvas.drawBitmap(pictureBitmap, (borderWidth / 2).toFloat(), (borderWidth / 2).toFloat(), maskingPaint)

        canvas.drawBitmap(overlayBitmap, 0f, 0f, Paint())

        source.recycle()

        return output
    }

}

MainActivity.kt

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import com.squareup.picasso.Picasso
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        Picasso.get()
            .load(R.drawable.img_aminography)
            .transform(MaskTransformation(this, R.drawable.ic_squircle))
            .into(imageView)

    }
}

ic_squircle.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="24dp"
        android:height="24dp"
        android:viewportWidth="32"
        android:viewportHeight="32">

    <path android:fillColor="#000000"
          android:pathData="M31.2,14.3v3.5c0,9.8,-5.9,13.5,-13.4,13.5h-3.5c-7.7,0,-13.5,-3.4,-13.5,-13.5v-3.5c0,-10.8,6,-13.5,13.5,-13.5h3.5C25.2,0.8,31.2,4.1,31.2,14.3"/>

</vector>

.

视觉效果