Skip to content

Commit

Permalink
offscreen blending
Browse files Browse the repository at this point in the history
  • Loading branch information
alexzhirkevich committed Jan 29, 2025
1 parent 5f3060a commit 0dda8cf
Show file tree
Hide file tree
Showing 8 changed files with 40 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ import kotlin.math.roundToInt
* it even if it contains merge paths. This feature should only be enabled for tested animations
* @param enableExpressions enable experimental expressions feature. Unsupported expressions will
* be skipped with warning.
* @param enableOffscreenBlending render animation to offscreen canvas first. It can help to fix
* blending issues caused by the background animation is rendered on but can introduce other artifacts
* */
@OptIn(InternalCompottieApi::class)
@Composable
Expand All @@ -78,6 +80,7 @@ public fun rememberLottiePainter(
enableTextGrouping : Boolean = false,
enableMergePaths: Boolean = false,
enableExpressions: Boolean = false,
enableOffscreenBlending : Boolean = false
) : Painter {

val fontFamilyResolver = LocalFontFamilyResolver.current
Expand Down Expand Up @@ -113,6 +116,7 @@ public fun rememberLottiePainter(
enableMergePaths = enableMergePaths,
enableExpressions = enableExpressions,
applyOpacityToLayers = applyOpacityToLayers,
enableOffscreenBlending = enableOffscreenBlending,
assets = assets.await(),
fonts = fonts.await()
)
Expand All @@ -126,7 +130,8 @@ public fun rememberLottiePainter(
clipToCompositionBounds,
applyOpacityToLayers,
enableMergePaths,
enableExpressions
enableExpressions,
enableOffscreenBlending
) {
painter?.let {
it.enableMergePaths = enableMergePaths
Expand All @@ -135,6 +140,7 @@ public fun rememberLottiePainter(
it.clipToCompositionBounds = clipToCompositionBounds
it.clipTextToBoundingBoxes = clipTextToBoundingBoxes
it.fontFamilyResolver = fontFamilyResolver
it.enableOffscreenBlending = enableOffscreenBlending
}
}

Expand Down Expand Up @@ -171,6 +177,7 @@ public fun rememberLottiePainter(
clipTextToBoundingBoxes: Boolean = false,
enableMergePaths: Boolean = false,
enableExpressions: Boolean = false,
enableOffscreenBlending : Boolean = false
) : Painter {

val progress = animateLottieCompositionAsState(
Expand All @@ -195,7 +202,8 @@ public fun rememberLottiePainter(
clipToCompositionBounds = clipToCompositionBounds,
clipTextToBoundingBoxes = clipTextToBoundingBoxes,
enableMergePaths = enableMergePaths,
enableExpressions = enableExpressions
enableExpressions = enableExpressions,
enableOffscreenBlending = enableOffscreenBlending
)
}

Expand All @@ -222,6 +230,7 @@ public suspend fun LottiePainter(
enableTextGrouping: Boolean = false,
enableMergePaths: Boolean = false,
enableExpressions: Boolean = true,
enableOffscreenBlending : Boolean = false
) : Painter = coroutineScope {

val dp = when (dynamicProperties) {
Expand Down Expand Up @@ -253,6 +262,7 @@ public suspend fun LottiePainter(
enableMergePaths = enableMergePaths,
enableExpressions = enableExpressions,
applyOpacityToLayers = applyOpacityToLayers,
enableOffscreenBlending = enableOffscreenBlending,
assets = assets.await().orEmpty(),
fonts = fonts.await().orEmpty()
)
Expand Down Expand Up @@ -302,6 +312,7 @@ private class LottiePainter(
clipToCompositionBounds : Boolean,
enableMergePaths : Boolean,
enableExpressions : Boolean,
enableOffscreenBlending : Boolean
) : Painter() {


Expand Down Expand Up @@ -339,7 +350,8 @@ private class LottiePainter(
enableMergePaths = enableMergePaths,
layer = compositionLayer,
enableExpressions = enableExpressions,
enableTextGrouping = enableTextGrouping
enableTextGrouping = enableTextGrouping,
enableOffscreenBlending = enableOffscreenBlending
)

fun setDynamicProperties(provider: DynamicCompositionProvider?) {
Expand All @@ -356,6 +368,7 @@ private class LottiePainter(
var fontFamilyResolver: FontFamily.Resolver by animationState::fontFamilyResolver
var enableMergePaths: Boolean by animationState::enableMergePaths
var enableExpressions: Boolean by animationState::enableExpressions
var enableOffscreenBlending: Boolean by animationState::enableOffscreenBlending

public override fun applyAlpha(alpha: Float): Boolean {
if (alpha !in 0f..1f)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public class AnimationState @PublishedApi internal constructor(
enableTextGrouping : Boolean,
enableMergePaths: Boolean,
enableExpressions: Boolean,
enableOffscreenBlending: Boolean,
layer: Layer
) {

Expand Down Expand Up @@ -79,6 +80,7 @@ public class AnimationState @PublishedApi internal constructor(
internal var enableMergePaths by mutableStateOf(enableMergePaths)
internal var enableExpressions by mutableStateOf(enableExpressions)
internal var enableTextGrouping by mutableStateOf(enableTextGrouping)
internal var enableOffscreenBlending by mutableStateOf(enableOffscreenBlending)

internal var layer: Layer = layer
private set
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package io.github.alexzhirkevich.compottie.internal.layers

import androidx.compose.ui.geometry.MutableRect
import androidx.compose.ui.geometry.toRect
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.Matrix
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.PaintingStyle
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
import androidx.compose.ui.graphics.withSaveLayer
import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.util.fastForEachIndexed
import androidx.compose.ui.util.fastForEachReversed
Expand Down Expand Up @@ -164,7 +167,7 @@ internal abstract class BaseLayer : Layer {
matteLayer == null
&& !hasMasks()
&& blendMode == LottieBlendMode.Normal
&& this !is CompositionLayer
&& (this is CompositionLayer && state.enableOffscreenBlending).not()
) {
matrix.preConcat(transform.matrix(state))
drawLayer(drawScope, matrix, alpha, state)
Expand Down Expand Up @@ -207,7 +210,7 @@ internal abstract class BaseLayer : Layer {

// val outlineMasksAndMattesPaint = Paint().apply {
// style = PaintingStyle.Stroke
// strokeWidth = 4f
// strokeWidth = drawScope.density
// color = Color.Red
// }
// canvas.drawRect(rect, outlineMasksAndMattesPaint)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import io.github.alexzhirkevich.compottie.internal.platform.addPath
import io.github.alexzhirkevich.compottie.internal.platform.set
import io.github.alexzhirkevich.compottie.internal.utils.IdentityMatrix
import io.github.alexzhirkevich.compottie.internal.utils.appendPathEffect
import io.github.alexzhirkevich.compottie.internal.utils.extendBy
import io.github.alexzhirkevich.compottie.internal.utils.set
import kotlinx.serialization.Serializable
import kotlin.jvm.JvmInline
Expand Down Expand Up @@ -97,7 +98,6 @@ internal abstract class BaseStrokeShape() : Shape, DrawingContent {

private val trimPathPath = Path()
private val path = Path()
private val rect = MutableRect(0f, 0f, 0f, 0f)
private val rawBoundsRect = MutableRect(0f, 0f, 0f, 0f)

protected val paint by lazy {
Expand Down Expand Up @@ -247,25 +247,9 @@ internal abstract class BaseStrokeShape() : Shape, DrawingContent {
path.addPath(it.getPath(state), parentMatrix)
}
}
rect.set(path.getBounds())

val width = strokeWidth.interpolated(state)

rect.set(
rect.left - width / 2f,
rect.top - width / 2f,
rect.right + width / 2f,
rect.bottom + width / 2f
)
outBounds.set(rect)

// Add padding to account for rounding errors.
outBounds.set(
outBounds.left - 1,
outBounds.top - 1,
outBounds.right + 1,
outBounds.bottom + 1
)
outBounds.set(path.getBounds())
outBounds.extendBy(strokeWidth.interpolated(state) + 1)
}

private fun applyTrimPath(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import io.github.alexzhirkevich.compottie.internal.helpers.FillRule
import io.github.alexzhirkevich.compottie.internal.helpers.asPathFillType
import io.github.alexzhirkevich.compottie.internal.platform.GradientCache
import io.github.alexzhirkevich.compottie.internal.platform.addPath
import io.github.alexzhirkevich.compottie.internal.utils.extendBy
import io.github.alexzhirkevich.compottie.internal.utils.set
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
Expand Down Expand Up @@ -145,13 +146,7 @@ internal class FillShape(
}

outBounds.set(path.getBounds())
// Add padding to account for rounding errors.
outBounds.set(
outBounds.left - 1,
outBounds.top - 1,
outBounds.right + 1,
outBounds.bottom + 1
)
outBounds.extendBy(1f)
}

override fun setDynamicProperties(basePath: String?, properties: DynamicShapeLayerProvider?) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import io.github.alexzhirkevich.compottie.internal.platform.GradientCache
import io.github.alexzhirkevich.compottie.internal.platform.GradientShader
import io.github.alexzhirkevich.compottie.internal.platform.addPath
import io.github.alexzhirkevich.compottie.internal.utils.IdentityMatrix
import io.github.alexzhirkevich.compottie.internal.utils.extendBy
import io.github.alexzhirkevich.compottie.internal.utils.firstInstanceOf
import io.github.alexzhirkevich.compottie.internal.utils.set
import kotlinx.serialization.SerialName
Expand Down Expand Up @@ -163,13 +164,7 @@ internal class GradientFillShape(
path.addPath(it.getPath(state), parentMatrix)
}
outBounds.set(path.getBounds())
// Add padding to account for rounding errors.
outBounds.set(
outBounds.left - 1,
outBounds.top - 1,
outBounds.right + 1,
outBounds.bottom + 1
)
outBounds.extendBy(1f)
}

override fun setDynamicProperties(basePath: String?, properties: DynamicShapeLayerProvider?) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,12 @@ internal fun MutableRect.set(other : Rect){
set(left = other.left, top = other.top, right = other.right, bottom = other.bottom)
}

internal fun MutableRect.extendBy(value : Float) {
set(
left = left - value,
top = top - value,
right = right + value,
bottom = bottom + value
)
}

2 changes: 1 addition & 1 deletion compottie/src/commonTest/kotlin/expressions/TestUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ internal fun MockAnimationState(
frameRate = 24f,
width = 1024f,
height = 1024f,
version = "5.0.0",
inPoint = 0f,
outPoint = durationFrames,
name = "Animation"
Expand All @@ -108,5 +107,6 @@ internal fun MockAnimationState(
clipTextToBoundingBoxes = true,
enableMergePaths = true,
enableTextGrouping = false,
enableOffscreenBlending = false,
layer = NullLayer()
)

0 comments on commit 0dda8cf

Please sign in to comment.