With androidx.compose.foundation.Canvas
, default Canvas for Jetpack Compose, or Spacer with Modifier.drawBehind{} under the hood
@Composable
fun Canvas(modifier: Modifier, onDraw: DrawScope.() -> Unit) =
Spacer(modifier.drawBehind(onDraw
correctly refreshes drawing on Canvas when mutableState Offset
changes
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,
)
}
With androidx.compose.ui.graphics.Canvas
, Canvas that takes an ImageBitmap as argument and draws to as in description of it
Create a new Canvas instance that targets its drawing commands to the provided ImageBitmap
I add full implementation to test this out easily and much appreciated if you come up with a solution.
@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
)
}
}
First issue it never refreshes Canvas without Text
or something else reading Offset
.
Second issue is as in the image below. It doesn't clear previous drawing on Image, i tried every possible solution in this question thread but none of them worked.
I tried drawing image with BlendMode, drawColor(Color.TRANSPARENT,Mode.Multiply) with native canvas and many combinations still not able to have the same result with 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)
}
I need to use androidx.compose.ui.graphics.Canvas
as you can see operations on Canvas are reflected to Bitmap and using this i'm planning to create foundation for cropping Bitmap