使用 androidx.compose.foundation.Canvas
、Jetpack Compose 的默认 Canvas 或带有 Modifier.drawBehind 的 Spacer{}
@Composable
fun Canvas(modifier: Modifier, onDraw: DrawScope.() -> Unit) =
Spacer(modifier.drawBehind(onDraw
当 mutableState Offset
更改时正确刷新 Canvas 上的绘图
var offset by remember {
mutableStateOf(Offset(bitmapWidth / 2f, bitmapHeight / 2f))
}
Canvas(modifier = canvasModifier.fillMaxSize()) {
val canvasWidth = size.width.roundToInt()
val canvasHeight = size.height.roundToInt()
drawImage(
image = dstBitmap,
srcSize = IntSize(dstBitmap.width, dstBitmap.height),
dstSize = IntSize(canvasWidth, canvasHeight)
)
drawCircle(
center = offset,
color = Color.Red,
radius = canvasHeight.coerceAtMost(canvasWidth) / 8f,
)
}
使用androidx.compose.ui.graphics.Canvas
,Canvas接受ImageBitmap作为参数并按照其描述进行绘制
Create a new Canvas instance that targets its drawing commands to the provided ImageBitmap
我添加了完整的实现来轻松测试这一点,如果您能提出解决方案,我将不胜感激。
@Composable
fun NativeCanvasSample2(imageBitmap: ImageBitmap, modifier: Modifier) {
BoxWithConstraints(modifier) {
val imageWidth = constraints.maxWidth
val imageHeight = constraints.maxHeight
val bitmapWidth = imageBitmap.width
val bitmapHeight = imageBitmap.height
var offset by remember {
mutableStateOf(Offset(bitmapWidth / 2f, bitmapHeight / 2f))
}
val canvasModifier = Modifier.pointerMotionEvents(
Unit,
onDown = {
val position = it.position
val offsetX = position.x * bitmapWidth / imageWidth
val offsetY = position.y * bitmapHeight / imageHeight
offset = Offset(offsetX, offsetY)
it.consumeDownChange()
},
onMove = {
val position = it.position
val offsetX = position.x * bitmapWidth / imageWidth
val offsetY = position.y * bitmapHeight / imageHeight
offset = Offset(offsetX, offsetY)
it.consumePositionChange()
},
delayAfterDownInMillis = 20
)
val canvas: androidx.compose.ui.graphics.Canvas = Canvas(imageBitmap)
val paint1 = remember {
Paint().apply {
color = Color.Red
}
}
canvas.apply {
val nativeCanvas = this.nativeCanvas
val canvasWidth = nativeCanvas.width.toFloat()
val canvasHeight = nativeCanvas.height.toFloat()
drawCircle(
center = offset,
radius = canvasHeight.coerceAtMost(canvasWidth) / 8,
paint = paint1
)
}
Image(
modifier = canvasModifier,
bitmap = imageBitmap,
contentDescription = null,
contentScale = ContentScale.FillBounds
)
Text(
"Offset: $offset",
modifier = Modifier.align(Alignment.BottomEnd),
color = Color.White,
fontSize = 16.sp
)
}
}
第一期,如果没有Text
或其他读取Offset
的内容,它永远不会刷新Canvas。
第二期如下图所示。它没有清除之前在图像上的绘图,我尝试了 this question thread 中的所有可能的解决方案但它们都不起作用。
我尝试使用 BlendMode、drawColor(Color.TRANSPARENT,Mode.Multiply) 和 native Canvas 绘制图像,但许多组合仍然无法使用 Jetpack Compose Canvas 获得相同的结果。
val erasePaint = remember {
Paint().apply {
color = Color.Transparent
blendMode = BlendMode.Clear
}
}
with(canvas.nativeCanvas) {
val checkPoint = saveLayer(null, null)
drawImage(imageBitmap, topLeftOffset = Offset.Zero, erasePaint)
drawCircle(
center = offset,
radius = canvasHeight.coerceAtMost(canvasWidth) / 8,
paint = paint1
)
restoreToCount(checkPoint)
}
我需要使用androidx.compose.ui.graphics.Canvas
,因为你可以看到Canvas上的操作反射(reflect)到位图上,我计划使用它为裁剪位图创建基础
最佳答案
我终于在 6 个月后弄清楚了如何做到这一点,以及如何使用 androidx.compose.ui.graphics.Canvas
修改 Bitmap 实例
首先创建一个与原始位图尺寸相同的空可变位图。这就是我们要借鉴的。这里的技巧不是发送真实的位图,而是发送空的位图
val bitmapWidth = imageBitmap.width
val bitmapHeight = imageBitmap.height
val bmp: Bitmap = remember {
Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888)
}
由于我们在底部没有绘制任何内容,因此我们可以使用drawColor(android.graphics.Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
在每次绘制时清除,然后绘制图像并使用 Paint 应用任何混合模式
val paint = remember {
Paint()
}
val erasePaint = remember {
Paint().apply {
color = Color.Red
blendMode = BlendMode.SrcIn
}
}
canvas.apply {
val nativeCanvas = this.nativeCanvas
val canvasWidth = nativeCanvas.width.toFloat()
val canvasHeight = nativeCanvas.height.toFloat()
with(canvas.nativeCanvas) {
drawColor(android.graphics.Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
drawCircle(
center = offset,
radius = 400f,
paint = paint
)
drawImageRect(
image = imageBitmap,
dstSize = IntSize(canvasWidth.toInt(), canvasHeight.toInt()),
paint = erasePaint
)
}
}
最后使用 Canvas to Image Composable 绘制我们使用的位图
Image(
modifier = canvasModifier.border(2.dp, Color.Green),
bitmap = bmp.asImageBitmap(),
contentDescription = null,
contentScale = ContentScale.FillBounds
)
或者您可以保存此修改后的带有水印的 ImageBitmap 或您在 Canvas 上绘制的任何叠加层
全面实现
@Composable
fun NativeCanvasSample2(imageBitmap: ImageBitmap, modifier: Modifier) {
BoxWithConstraints(modifier) {
val imageWidth = constraints.maxWidth
val imageHeight = constraints.maxHeight
val bitmapWidth = imageBitmap.width
val bitmapHeight = imageBitmap.height
var offset by remember {
mutableStateOf(Offset(bitmapWidth / 2f, bitmapHeight / 2f))
}
val bmp: Bitmap = remember {
Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888)
}
val canvas: Canvas = remember {
Canvas(bmp.asImageBitmap())
}
val paint = remember {
Paint()
}
val erasePaint = remember {
Paint().apply {
color = Color.Red
blendMode = BlendMode.SrcIn
}
}
canvas.apply {
val nativeCanvas = this.nativeCanvas
val canvasWidth = nativeCanvas.width.toFloat()
val canvasHeight = nativeCanvas.height.toFloat()
with(canvas.nativeCanvas) {
drawColor(android.graphics.Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
drawCircle(
center = offset,
radius = 400f,
paint = paint
)
drawImageRect(
image = imageBitmap,
dstSize = IntSize(canvasWidth.toInt(), canvasHeight.toInt()),
paint = erasePaint
)
}
}
val canvasModifier = Modifier.pointerMotionEvents(
Unit,
onDown = {
val position = it.position
val offsetX = position.x * bitmapWidth / imageWidth
val offsetY = position.y * bitmapHeight / imageHeight
offset = Offset(offsetX, offsetY)
it.consume()
},
onMove = {
val position = it.position
val offsetX = position.x * bitmapWidth / imageWidth
val offsetY = position.y * bitmapHeight / imageHeight
offset = Offset(offsetX, offsetY)
it.consume()
},
delayAfterDownInMillis = 20
)
Image(
modifier = canvasModifier.border(2.dp, Color.Green),
bitmap = bmp.asImageBitmap(),
contentDescription = null,
contentScale = ContentScale.FillBounds
)
}
}
结果
关于android - Jetpack 撰写水印或使用 androidx.compose.ui.graphics.Canvas 在位图上书写?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72168588/