rob42
01/18/2024, 3:58 PM.graphicsLayer {
compositingStrategy = CompositingStrategy.Offscreen
}
However, when I set the graphics layer to CompositingStrategy.Offscreen, my drawing is clipped to the composable's rect. Even when clip = false is set on graphicsLayer.
Does anyone know how use blend modes without this clipping?romainguy
01/18/2024, 4:01 PMrob42
01/18/2024, 4:04 PMromainguy
01/18/2024, 4:10 PMrob42
01/18/2024, 4:22 PMromainguy
01/18/2024, 4:41 PMComposeShader
that combines two shaders with a blend mode. You can then render your mask into a Bitmap
and use that bitmap to mask the second shader (which for your shadow would be another Bitmap
)romainguy
01/18/2024, 4:41 PMColton Idle
01/18/2024, 4:43 PMrob42
01/18/2024, 4:47 PMrob42
01/18/2024, 4:51 PMromainguy
01/18/2024, 4:58 PMromainguy
01/18/2024, 4:58 PMromainguy
01/18/2024, 4:58 PMromainguy
01/18/2024, 4:59 PMJonathan
01/18/2024, 5:20 PMrob42
01/18/2024, 7:04 PM@Composable
fun Modifier.cutoutShadow(
color: Color = Color.Black,
shape: Shape,
radius: Dp = 0.dp,
offsetX: Dp = 0.dp,
offsetY: Dp = 0.dp,
): Modifier {
// Creates a container big enough to hold the shadow centered within it
val padding = PaddingValues(
horizontal = radius + abs(offsetX.value).dp,
vertical = radius + abs(offsetY.value).dp
)
return outset(padding)
.graphicsLayer {
compositingStrategy = CompositingStrategy.Offscreen
}
.drawWithCache {
// The canvas bounds were enlarged above (to make room for the shadow).
// This effectively does the reverse to get back the desired size of the shape.
val shapeSize = Size(
width = size.width - (padding.calculateLeftPadding(layoutDirection) + padding.calculateRightPadding(layoutDirection)).toPx(),
height = size.height - (padding.calculateTopPadding() + padding.calculateBottomPadding()).toPx()
)
// Offsets from top left corner of enlarged canvas
val shapeOffset = Offset(
x = padding.calculateLeftPadding(layoutDirection).toPx(),
y = padding.calculateTopPadding().toPx()
)
val shadowOffset = Offset(
x = shapeOffset.x + offsetX.toPx(),
y = shapeOffset.y + offsetY.toPx(),
)
val outline = shape.createOutline(size = shapeSize, layoutDirection = layoutDirection, density = this)
val paint = Paint().apply {
val nativePaint = asFrameworkPaint()
if (radius != 0.dp) {
nativePaint.maskFilter = MaskFilter.makeBlur(FilterBlurMode.NORMAL, radius.toPx() / 2, true)
}
nativePaint.color = color.toArgb()
}
onDrawBehind {
drawIntoCanvas {
translate(left = shadowOffset.x, top = shadowOffset.y) {
it.drawOutline(outline = outline, paint = paint)
}
}
// Erase the shape from the drawn shadow
translate(left = shapeOffset.x, top = shapeOffset.y) {
drawOutline(outline = outline, color = Color.Black, blendMode = BlendMode.Clear)
}
}
}
.padding(padding)
}
fun Modifier.outset(values: PaddingValues) = layout { measurable, constraints ->
val wExtra = values.calculateLeftPadding(layoutDirection) + values.calculateRightPadding(layoutDirection)
val hExtra = values.calculateTopPadding() + values.calculateBottomPadding()
val placeable = measurable.measure(constraints.offset(
horizontal = (-values.calculateLeftPadding(layoutDirection)).roundToPx(),
vertical = (-values.calculateTopPadding()).roundToPx()
))
layout(
width = placeable.width - wExtra.roundToPx(),
height = placeable.height - hExtra.roundToPx()
) {
placeable.place(
x = -values.calculateLeftPadding(layoutDirection).roundToPx(),
y = -values.calculateTopPadding().roundToPx(),
)
}
}
fun Modifier.outset(all: Dp) = outset(values = PaddingValues(all))
fun Modifier.outset(horizontal: Dp, vertical: Dp) =
outset(values = PaddingValues(horizontal = horizontal, vertical = vertical))
fun Modifier.outset(start: Dp, top: Dp, end: Dp, bottom: Dp) =
outset(values = PaddingValues(start = start, top = top, end = end, bottom = bottom))
wwalkingg
08/09/2024, 7:16 AM