带边框的毕加索图像蒙版变换
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>
.
视觉效果
我正在尝试使用 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>
.