Is there a way to apply a ColorFilter to a Canvas?...
# compose-android
j
Is there a way to apply a ColorFilter to a Canvas? I’d like to create a generic Modifier that allows me to apply any a custom color matrix to any composable. Searching around, I don’t see documentation discussing if this is possible. Link in thread.
The closest I was able to come up with is the following:
Copy code
fun Modifier.hueRotate(degrees: Float): Modifier =
    this.drawWithCache {
        val hueMatrix = createHueRotationMatrix(degrees)
        val colorFilter = ColorFilter.colorMatrix(hueMatrix)
        onDrawWithContent {
            drawContent()
            drawRect(
                color = Color.Transparent,
                topLeft = Offset.Zero,
                size = size,
                colorFilter = colorFilter
            )
        }
    }
But because of the Color.Transparent it doesn’t seem to work.
s
I did something similar but in a different way. Here’s the code. Perhaps if you could describe what you’re trying to achieve, it would be easier to help.
😯 1
r
You have to go through a layer
You can either use Canvas.saveWithLayer (or equivalent) or use a graphicsLayer set to offscreen compositing
Note that I recently added the ability to set a color filter on a layer directly
j
I will try with graphicLayer {} and offscreen compositing
r
All you will need to do is:
Copy code
.graphicsLayer(
    colorFilter = LightingColorFilter(Color.White, Color.Red)
)
❤️ 1
So no need to create a new modifier 😄
j
What version of compose?
r
1.9 I imagine? The change landed in February, and 1.8 was already in beta
j
Hmm. I’m on 2025.03.00 BOM and I don’t see the change. Do I need to use a better version for BOM
r
that BOM is for the latest stable, so 1.7.x
Use the -alpha BOM instead
s
You can either use Canvas.saveWithLayer (or equivalent) or use a graphicsLayer set to offscreen compositing
@romainguy Is there any performance difference between canvas.saveWithLayer and graphicsLayer with the offscreen composition strategy, or are these equivalent operations but through different APIs?
👀 1
r
It's the same but saveWithLayer needs to be cached internally etc. It's better to use an explicit layer
j
@romainguy The changed graphicLayer isn’t available in the alpha 2025.03.00 bill of materials.
Using the following:
Copy code
graphicsLayer(compositingStrategy = CompositingStrategy.Offscreen)
    .drawWithCache {
    val hueMatrix = createHueRotationMatrix(degrees)
    val colorFilter = ColorFilter.colorMatrix(hueMatrix)
    onDrawWithContent {
        drawContent()
        drawRect(
            color = Color.Transparent,
            colorFilter = colorFilter,
        )
    }
}
doesn’t work a well.
If I directly apply the color matrix to my canvas calls ie drawPath the output is as expected, so I believe my color matrix is correct.
s
I provided you with a code sample that uses canvas.saveWithLayer for a similar feature. Does it not work either?
j
I haven’t tried it yet, I’m going to try now.
👍 1
s
I’d like to create a generic Modifier that allows me to apply any a custom color matrix to any composable.
Do you need to create some sort of color filter modifier? This sounds exactly like what I did with the desaturation modifier in my code example. Or am I missing something?
j
Using the saveWithLayer works for me.
@Sergey Y. The final results of your desaturation modifier looks very similar to the following desaturation ColorMatrix example.
Copy code
val saturationMatrix = ColorMatrix().apply { setToSaturation(amount) }
val saturationFilter = ColorFilter.colorMatrix(saturationMatrix)
Is there any performance gain using your solution?
s
No difference, just an alternative approach with more precise control over the color filter.
👍🏿 1
r
Should be exactly the same
Keep the code simple and use the existing APIs 🙂
1
BTW your comments are slightly incorrect, the coefficients are not luma coefficients but luminance coefficients
And just like the implementation in ColorMatrix, these are the wrong coefficients for gamma-encoded sRGB 😕
s
🥲
j
What’s the difference between luma and luminance?
r
In shot: luma is a weighted sum of color components, luminance is a weighted sum of linear color components
Unfortunately both terms and equations are used interchangeably all over the place
When done correctly, luma should be noted Y' and luminance Y
Things get even more confusing because video standards use the same coefficients for luma as sRGB does for luminance
s
Thank you for the clarification, I’ll keep that in mind.
BTW, I wish there were more presentations like

this one

with you and Chet Haase. 😅
r
Me too!
❤️ 1
But since both Chet and I have left Google now, this seems unlikely 😕
🥲 1
j
You two could just tour as comedy-developer extraordinaires.
😄 2
💯 1