1 /*
2  * Copyright 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.compose.ui.graphics
18 
19 import androidx.compose.runtime.Immutable
20 import androidx.compose.runtime.Stable
21 import androidx.compose.ui.Modifier
22 import androidx.compose.ui.layout.Measurable
23 import androidx.compose.ui.layout.MeasureResult
24 import androidx.compose.ui.layout.MeasureScope
25 import androidx.compose.ui.node.LayoutModifierNode
26 import androidx.compose.ui.node.ModifierNodeElement
27 import androidx.compose.ui.node.Nodes
28 import androidx.compose.ui.node.requireCoordinator
29 import androidx.compose.ui.platform.InspectorInfo
30 import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
31 import androidx.compose.ui.unit.Constraints
32 
33 /**
34  * A [Modifier.Element] that makes content draw into a draw layer. The draw layer can be invalidated
35  * separately from parents. A [graphicsLayer] should be used when the content updates independently
36  * from anything above it to minimize the invalidated content.
37  *
38  * [graphicsLayer] can also be used to apply effects to content, such as scaling ([scaleX],
39  * [scaleY]), rotation ([rotationX], [rotationY], [rotationZ]), opacity ([alpha]), shadow
40  * ([shadowElevation], [shape]), and clipping ([clip], [shape]).
41  *
42  * Note that if you provide a non-zero [shadowElevation] and if the passed [shape] is concave the
43  * shadow will not be drawn on Android versions less than 10.
44  *
45  * Also note that alpha values less than 1.0f will have their contents implicitly clipped to their
46  * bounds. This is because an intermediate compositing layer is created to render contents into
47  * first before being drawn into the destination with the desired alpha. This layer is sized to the
48  * bounds of the composable this modifier is configured on, and contents outside of these bounds are
49  * omitted.
50  *
51  * If the layer parameters are backed by a [androidx.compose.runtime.State] or an animated value
52  * prefer an overload with a lambda block on [GraphicsLayerScope] as reading a state inside the
53  * block will only cause the layer properties update without triggering recomposition and relayout.
54  *
55  * @sample androidx.compose.ui.samples.ChangeOpacity
56  * @param scaleX see [GraphicsLayerScope.scaleX]
57  * @param scaleY see [GraphicsLayerScope.scaleY]
58  * @param alpha see [GraphicsLayerScope.alpha]
59  * @param translationX see [GraphicsLayerScope.translationX]
60  * @param translationY see [GraphicsLayerScope.translationY]
61  * @param shadowElevation see [GraphicsLayerScope.shadowElevation]
62  * @param rotationX see [GraphicsLayerScope.rotationX]
63  * @param rotationY see [GraphicsLayerScope.rotationY]
64  * @param rotationZ see [GraphicsLayerScope.rotationZ]
65  * @param cameraDistance see [GraphicsLayerScope.cameraDistance]
66  * @param transformOrigin see [GraphicsLayerScope.transformOrigin]
67  * @param shape see [GraphicsLayerScope.shape]
68  * @param clip see [GraphicsLayerScope.clip]
69  */
70 @Deprecated(
71     "Replace with graphicsLayer that consumes an optional RenderEffect parameter and " +
72         "shadow color parameters",
73     replaceWith =
74         ReplaceWith(
75             "Modifier.graphicsLayer(scaleX, scaleY, alpha, translationX, translationY, " +
76                 "shadowElevation, rotationX, rotationY, rotationZ, cameraDistance, transformOrigin, " +
77                 "shape, clip, null, DefaultShadowColor, DefaultShadowColor)",
78             "androidx.compose.ui.graphics"
79         ),
80     level = DeprecationLevel.HIDDEN
81 )
82 @Stable
Modifiernull83 fun Modifier.graphicsLayer(
84     scaleX: Float = 1f,
85     scaleY: Float = 1f,
86     alpha: Float = 1f,
87     translationX: Float = 0f,
88     translationY: Float = 0f,
89     shadowElevation: Float = 0f,
90     rotationX: Float = 0f,
91     rotationY: Float = 0f,
92     rotationZ: Float = 0f,
93     cameraDistance: Float = DefaultCameraDistance,
94     transformOrigin: TransformOrigin = TransformOrigin.Center,
95     shape: Shape = RectangleShape,
96     clip: Boolean = false
97 ) =
98     graphicsLayer(
99         scaleX = scaleX,
100         scaleY = scaleY,
101         alpha = alpha,
102         translationX = translationX,
103         translationY = translationY,
104         shadowElevation = shadowElevation,
105         rotationX = rotationX,
106         rotationY = rotationY,
107         rotationZ = rotationZ,
108         cameraDistance = cameraDistance,
109         transformOrigin = transformOrigin,
110         shape = shape,
111         clip = clip,
112         renderEffect = null,
113         blendMode = BlendMode.SrcOver,
114         colorFilter = null
115     )
116 
117 /**
118  * A [Modifier.Element] that makes content draw into a draw layer. The draw layer can be invalidated
119  * separately from parents. A [graphicsLayer] should be used when the content updates independently
120  * from anything above it to minimize the invalidated content.
121  *
122  * [graphicsLayer] can also be used to apply effects to content, such as scaling ([scaleX],
123  * [scaleY]), rotation ([rotationX], [rotationY], [rotationZ]), opacity ([alpha]), shadow
124  * ([shadowElevation], [shape]), clipping ([clip], [shape]), as well as altering the result of the
125  * layer with [RenderEffect].
126  *
127  * Note that if you provide a non-zero [shadowElevation] and if the passed [shape] is concave the
128  * shadow will not be drawn on Android versions less than 10.
129  *
130  * Also note that alpha values less than 1.0f will have their contents implicitly clipped to their
131  * bounds. This is because an intermediate compositing layer is created to render contents into
132  * first before being drawn into the destination with the desired alpha. This layer is sized to the
133  * bounds of the composable this modifier is configured on, and contents outside of these bounds are
134  * omitted.
135  *
136  * If the layer parameters are backed by a [androidx.compose.runtime.State] or an animated value
137  * prefer an overload with a lambda block on [GraphicsLayerScope] as reading a state inside the
138  * block will only cause the layer properties update without triggering recomposition and relayout.
139  *
140  * @sample androidx.compose.ui.samples.ChangeOpacity
141  * @param scaleX see [GraphicsLayerScope.scaleX]
142  * @param scaleY see [GraphicsLayerScope.scaleY]
143  * @param alpha see [GraphicsLayerScope.alpha]
144  * @param translationX see [GraphicsLayerScope.translationX]
145  * @param translationY see [GraphicsLayerScope.translationY]
146  * @param shadowElevation see [GraphicsLayerScope.shadowElevation]
147  * @param rotationX see [GraphicsLayerScope.rotationX]
148  * @param rotationY see [GraphicsLayerScope.rotationY]
149  * @param rotationZ see [GraphicsLayerScope.rotationZ]
150  * @param cameraDistance see [GraphicsLayerScope.cameraDistance]
151  * @param transformOrigin see [GraphicsLayerScope.transformOrigin]
152  * @param shape see [GraphicsLayerScope.shape]
153  * @param clip see [GraphicsLayerScope.clip]
154  * @param renderEffect see [GraphicsLayerScope.renderEffect]
155  */
156 @Deprecated(
157     "Replace with graphicsLayer that consumes shadow color parameters",
158     replaceWith =
159         ReplaceWith(
160             "Modifier.graphicsLayer(scaleX, scaleY, alpha, translationX, translationY, " +
161                 "shadowElevation, rotationX, rotationY, rotationZ, cameraDistance, transformOrigin, " +
162                 "shape, clip, null, DefaultShadowColor, DefaultShadowColor)",
163             "androidx.compose.ui.graphics"
164         ),
165     level = DeprecationLevel.HIDDEN
166 )
167 @Stable
168 fun Modifier.graphicsLayer(
169     scaleX: Float = 1f,
170     scaleY: Float = 1f,
171     alpha: Float = 1f,
172     translationX: Float = 0f,
173     translationY: Float = 0f,
174     shadowElevation: Float = 0f,
175     rotationX: Float = 0f,
176     rotationY: Float = 0f,
177     rotationZ: Float = 0f,
178     cameraDistance: Float = DefaultCameraDistance,
179     transformOrigin: TransformOrigin = TransformOrigin.Center,
180     shape: Shape = RectangleShape,
181     clip: Boolean = false,
182     renderEffect: RenderEffect? = null
183 ) =
184     graphicsLayer(
185         scaleX = scaleX,
186         scaleY = scaleY,
187         alpha = alpha,
188         translationX = translationX,
189         translationY = translationY,
190         shadowElevation = shadowElevation,
191         ambientShadowColor = DefaultShadowColor,
192         spotShadowColor = DefaultShadowColor,
193         rotationX = rotationX,
194         rotationY = rotationY,
195         rotationZ = rotationZ,
196         cameraDistance = cameraDistance,
197         transformOrigin = transformOrigin,
198         shape = shape,
199         clip = clip,
200         renderEffect = renderEffect,
201         compositingStrategy = CompositingStrategy.Auto,
202         blendMode = BlendMode.SrcOver,
203         colorFilter = null
204     )
205 
206 /**
207  * A [Modifier.Element] that makes content draw into a draw layer. The draw layer can be invalidated
208  * separately from parents. A [graphicsLayer] should be used when the content updates independently
209  * from anything above it to minimize the invalidated content.
210  *
211  * [graphicsLayer] can also be used to apply effects to content, such as scaling ([scaleX],
212  * [scaleY]), rotation ([rotationX], [rotationY], [rotationZ]), opacity ([alpha]), shadow
213  * ([shadowElevation], [shape]), clipping ([clip], [shape]), as well as altering the result of the
214  * layer with [RenderEffect]. Shadow color and ambient colors can be modified by configuring the
215  * [spotShadowColor] and [ambientShadowColor] respectively.
216  *
217  * Note that if you provide a non-zero [shadowElevation] and if the passed [shape] is concave the
218  * shadow will not be drawn on Android versions less than 10.
219  *
220  * Also note that alpha values less than 1.0f will have their contents implicitly clipped to their
221  * bounds. This is because an intermediate compositing layer is created to render contents into
222  * first before being drawn into the destination with the desired alpha. This layer is sized to the
223  * bounds of the composable this modifier is configured on, and contents outside of these bounds are
224  * omitted.
225  *
226  * If the layer parameters are backed by a [androidx.compose.runtime.State] or an animated value
227  * prefer an overload with a lambda block on [GraphicsLayerScope] as reading a state inside the
228  * block will only cause the layer properties update without triggering recomposition and relayout.
229  *
230  * @sample androidx.compose.ui.samples.ChangeOpacity
231  * @param scaleX see [GraphicsLayerScope.scaleX]
232  * @param scaleY see [GraphicsLayerScope.scaleY]
233  * @param alpha see [GraphicsLayerScope.alpha]
234  * @param translationX see [GraphicsLayerScope.translationX]
235  * @param translationY see [GraphicsLayerScope.translationY]
236  * @param shadowElevation see [GraphicsLayerScope.shadowElevation]
237  * @param rotationX see [GraphicsLayerScope.rotationX]
238  * @param rotationY see [GraphicsLayerScope.rotationY]
239  * @param rotationZ see [GraphicsLayerScope.rotationZ]
240  * @param cameraDistance see [GraphicsLayerScope.cameraDistance]
241  * @param transformOrigin see [GraphicsLayerScope.transformOrigin]
242  * @param shape see [GraphicsLayerScope.shape]
243  * @param clip see [GraphicsLayerScope.clip]
244  * @param renderEffect see [GraphicsLayerScope.renderEffect]
245  * @param ambientShadowColor see [GraphicsLayerScope.ambientShadowColor]
246  * @param spotShadowColor see [GraphicsLayerScope.spotShadowColor]
247  */
248 @Deprecated(
249     "Replace with graphicsLayer that consumes a compositing strategy",
250     replaceWith =
251         ReplaceWith(
252             "Modifier.graphicsLayer(scaleX, scaleY, alpha, translationX, translationY, " +
253                 "shadowElevation, rotationX, rotationY, rotationZ, cameraDistance, transformOrigin, " +
254                 "shape, clip, renderEffect, ambientShadowColor, spotShadowColor, " +
255                 "CompositingStrategy.Auto)",
256             "androidx.compose.ui.graphics"
257         ),
258     level = DeprecationLevel.HIDDEN
259 )
260 @Stable
261 fun Modifier.graphicsLayer(
262     scaleX: Float = 1f,
263     scaleY: Float = 1f,
264     alpha: Float = 1f,
265     translationX: Float = 0f,
266     translationY: Float = 0f,
267     shadowElevation: Float = 0f,
268     rotationX: Float = 0f,
269     rotationY: Float = 0f,
270     rotationZ: Float = 0f,
271     cameraDistance: Float = DefaultCameraDistance,
272     transformOrigin: TransformOrigin = TransformOrigin.Center,
273     shape: Shape = RectangleShape,
274     clip: Boolean = false,
275     renderEffect: RenderEffect? = null,
276     ambientShadowColor: Color = DefaultShadowColor,
277     spotShadowColor: Color = DefaultShadowColor,
278 ) =
279     graphicsLayer(
280         scaleX,
281         scaleY,
282         alpha,
283         translationX,
284         translationY,
285         shadowElevation,
286         rotationX,
287         rotationY,
288         rotationZ,
289         cameraDistance,
290         transformOrigin,
291         shape,
292         clip,
293         renderEffect,
294         ambientShadowColor,
295         spotShadowColor,
296         CompositingStrategy.Auto,
297         BlendMode.SrcOver,
298         null
299     )
300 
301 /**
302  * A [Modifier.Element] that makes content draw into a draw layer. The draw layer can be invalidated
303  * separately from parents. A [graphicsLayer] should be used when the content updates independently
304  * from anything above it to minimize the invalidated content.
305  *
306  * [graphicsLayer] can also be used to apply effects to content, such as scaling ([scaleX],
307  * [scaleY]), rotation ([rotationX], [rotationY], [rotationZ]), opacity ([alpha]), shadow
308  * ([shadowElevation], [shape]), clipping ([clip], [shape]), as well as altering the result of the
309  * layer with [RenderEffect]. Shadow color and ambient colors can be modified by configuring the
310  * [spotShadowColor] and [ambientShadowColor] respectively.
311  *
312  * [CompositingStrategy] determines whether or not the contents of this layer are rendered into an
313  * offscreen buffer. This is useful in order to optimize alpha usages with
314  * [CompositingStrategy.ModulateAlpha] which will skip the overhead of an offscreen buffer but can
315  * generate different rendering results depending on whether or not the contents of the layer are
316  * overlapping. Similarly leveraging [CompositingStrategy.Offscreen] is useful in situations where
317  * creating an offscreen buffer is preferred usually in conjunction with [BlendMode] usage.
318  *
319  * Note that if you provide a non-zero [shadowElevation] and if the passed [shape] is concave the
320  * shadow will not be drawn on Android versions less than 10.
321  *
322  * Also note that alpha values less than 1.0f will have their contents implicitly clipped to their
323  * bounds unless [CompositingStrategy.ModulateAlpha] is specified. This is because an intermediate
324  * compositing layer is created to render contents into first before being drawn into the
325  * destination with the desired alpha. This layer is sized to the bounds of the composable this
326  * modifier is configured on, and contents outside of these bounds are omitted.
327  *
328  * If the layer parameters are backed by a [androidx.compose.runtime.State] or an animated value
329  * prefer an overload with a lambda block on [GraphicsLayerScope] as reading a state inside the
330  * block will only cause the layer properties update without triggering recomposition and relayout.
331  *
332  * @sample androidx.compose.ui.samples.ChangeOpacity
333  * @sample androidx.compose.ui.samples.CompositingStrategyModulateAlpha
334  * @param scaleX see [GraphicsLayerScope.scaleX]
335  * @param scaleY see [GraphicsLayerScope.scaleY]
336  * @param alpha see [GraphicsLayerScope.alpha]
337  * @param translationX see [GraphicsLayerScope.translationX]
338  * @param translationY see [GraphicsLayerScope.translationY]
339  * @param shadowElevation see [GraphicsLayerScope.shadowElevation]
340  * @param rotationX see [GraphicsLayerScope.rotationX]
341  * @param rotationY see [GraphicsLayerScope.rotationY]
342  * @param rotationZ see [GraphicsLayerScope.rotationZ]
343  * @param cameraDistance see [GraphicsLayerScope.cameraDistance]
344  * @param transformOrigin see [GraphicsLayerScope.transformOrigin]
345  * @param shape see [GraphicsLayerScope.shape]
346  * @param clip see [GraphicsLayerScope.clip]
347  * @param renderEffect see [GraphicsLayerScope.renderEffect]
348  * @param ambientShadowColor see [GraphicsLayerScope.ambientShadowColor]
349  * @param spotShadowColor see [GraphicsLayerScope.spotShadowColor]
350  * @param compositingStrategy see [GraphicsLayerScope.compositingStrategy]
351  */
352 @Deprecated(
353     "Replace with graphicsLayer that consumes a blend mode and a color filter",
354     replaceWith =
355         ReplaceWith(
356             "Modifier.graphicsLayer(scaleX, scaleY, alpha, translationX, translationY, " +
357                 "shadowElevation, rotationX, rotationY, rotationZ, cameraDistance, transformOrigin, " +
358                 "shape, clip, renderEffect, ambientShadowColor, spotShadowColor, " +
359                 "compositingStrategy, BlendMode.SrcOver, null)",
360             "androidx.compose.ui.graphics"
361         ),
362     level = DeprecationLevel.HIDDEN
363 )
364 @Stable
365 fun Modifier.graphicsLayer(
366     scaleX: Float = 1f,
367     scaleY: Float = 1f,
368     alpha: Float = 1f,
369     translationX: Float = 0f,
370     translationY: Float = 0f,
371     shadowElevation: Float = 0f,
372     rotationX: Float = 0f,
373     rotationY: Float = 0f,
374     rotationZ: Float = 0f,
375     cameraDistance: Float = DefaultCameraDistance,
376     transformOrigin: TransformOrigin = TransformOrigin.Center,
377     shape: Shape = RectangleShape,
378     clip: Boolean = false,
379     renderEffect: RenderEffect? = null,
380     ambientShadowColor: Color = DefaultShadowColor,
381     spotShadowColor: Color = DefaultShadowColor,
382     compositingStrategy: CompositingStrategy = CompositingStrategy.Auto
383 ) =
384     graphicsLayer(
385         scaleX,
386         scaleY,
387         alpha,
388         translationX,
389         translationY,
390         shadowElevation,
391         rotationX,
392         rotationY,
393         rotationZ,
394         cameraDistance,
395         transformOrigin,
396         shape,
397         clip,
398         renderEffect,
399         ambientShadowColor,
400         spotShadowColor,
401         compositingStrategy,
402         BlendMode.SrcOver,
403         null
404     )
405 
406 /**
407  * A [Modifier.Element] that makes content draw into a draw layer. The draw layer can be invalidated
408  * separately from parents. A [graphicsLayer] should be used when the content updates independently
409  * from anything above it to minimize the invalidated content.
410  *
411  * [graphicsLayer] can also be used to apply effects to content, such as scaling ([scaleX],
412  * [scaleY]), rotation ([rotationX], [rotationY], [rotationZ]), opacity ([alpha]), shadow
413  * ([shadowElevation], [shape]), clipping ([clip], [shape]), as well as altering the result of the
414  * layer with [RenderEffect]. Shadow color and ambient colors can be modified by configuring the
415  * [spotShadowColor] and [ambientShadowColor] respectively.
416  *
417  * [CompositingStrategy] determines whether or not the contents of this layer are rendered into an
418  * offscreen buffer. This is useful in order to optimize alpha usages with
419  * [CompositingStrategy.ModulateAlpha] which will skip the overhead of an offscreen buffer but can
420  * generate different rendering results depending on whether or not the contents of the layer are
421  * overlapping. Similarly leveraging [CompositingStrategy.Offscreen] is useful in situations where
422  * creating an offscreen buffer is preferred usually in conjunction with [BlendMode] usage.
423  *
424  * Note that if you provide a non-zero [shadowElevation] and if the passed [shape] is concave the
425  * shadow will not be drawn on Android versions less than 10.
426  *
427  * Also note that alpha values less than 1.0f will have their contents implicitly clipped to their
428  * bounds unless [CompositingStrategy.ModulateAlpha] is specified. This is because an intermediate
429  * compositing layer is created to render contents into first before being drawn into the
430  * destination with the desired alpha. This layer is sized to the bounds of the composable this
431  * modifier is configured on, and contents outside of these bounds are omitted.
432  *
433  * If the layer parameters are backed by a [androidx.compose.runtime.State] or an animated value
434  * prefer an overload with a lambda block on [GraphicsLayerScope] as reading a state inside the
435  * block will only cause the layer properties update without triggering recomposition and relayout.
436  *
437  * @sample androidx.compose.ui.samples.ChangeOpacity
438  * @sample androidx.compose.ui.samples.CompositingStrategyModulateAlpha
439  * @param scaleX see [GraphicsLayerScope.scaleX]
440  * @param scaleY see [GraphicsLayerScope.scaleY]
441  * @param alpha see [GraphicsLayerScope.alpha]
442  * @param translationX see [GraphicsLayerScope.translationX]
443  * @param translationY see [GraphicsLayerScope.translationY]
444  * @param shadowElevation see [GraphicsLayerScope.shadowElevation]
445  * @param rotationX see [GraphicsLayerScope.rotationX]
446  * @param rotationY see [GraphicsLayerScope.rotationY]
447  * @param rotationZ see [GraphicsLayerScope.rotationZ]
448  * @param cameraDistance see [GraphicsLayerScope.cameraDistance]
449  * @param transformOrigin see [GraphicsLayerScope.transformOrigin]
450  * @param shape see [GraphicsLayerScope.shape]
451  * @param clip see [GraphicsLayerScope.clip]
452  * @param renderEffect see [GraphicsLayerScope.renderEffect]
453  * @param ambientShadowColor see [GraphicsLayerScope.ambientShadowColor]
454  * @param spotShadowColor see [GraphicsLayerScope.spotShadowColor]
455  * @param compositingStrategy see [GraphicsLayerScope.compositingStrategy]
456  * @param blendMode see [GraphicsLayerScope.blendMode]
457  * @param colorFilter see [GraphicsLayerScope.colorFilter]
458  */
459 @Stable
460 fun Modifier.graphicsLayer(
461     scaleX: Float = 1f,
462     scaleY: Float = 1f,
463     alpha: Float = 1f,
464     translationX: Float = 0f,
465     translationY: Float = 0f,
466     shadowElevation: Float = 0f,
467     rotationX: Float = 0f,
468     rotationY: Float = 0f,
469     rotationZ: Float = 0f,
470     cameraDistance: Float = DefaultCameraDistance,
471     transformOrigin: TransformOrigin = TransformOrigin.Center,
472     shape: Shape = RectangleShape,
473     clip: Boolean = false,
474     renderEffect: RenderEffect? = null,
475     ambientShadowColor: Color = DefaultShadowColor,
476     spotShadowColor: Color = DefaultShadowColor,
477     compositingStrategy: CompositingStrategy = CompositingStrategy.Auto,
478     blendMode: BlendMode = BlendMode.SrcOver,
479     colorFilter: ColorFilter? = null
480 ) =
481     this then
482         GraphicsLayerElement(
483             scaleX,
484             scaleY,
485             alpha,
486             translationX,
487             translationY,
488             shadowElevation,
489             rotationX,
490             rotationY,
491             rotationZ,
492             cameraDistance,
493             transformOrigin,
494             shape,
495             clip,
496             renderEffect,
497             ambientShadowColor,
498             spotShadowColor,
499             compositingStrategy,
500             blendMode,
501             colorFilter
502         )
503 
504 private data class GraphicsLayerElement(
505     val scaleX: Float,
506     val scaleY: Float,
507     val alpha: Float,
508     val translationX: Float,
509     val translationY: Float,
510     val shadowElevation: Float,
511     val rotationX: Float,
512     val rotationY: Float,
513     val rotationZ: Float,
514     val cameraDistance: Float,
515     val transformOrigin: TransformOrigin,
516     val shape: Shape,
517     val clip: Boolean,
518     val renderEffect: RenderEffect?,
519     val ambientShadowColor: Color,
520     val spotShadowColor: Color,
521     val compositingStrategy: CompositingStrategy,
522     val blendMode: BlendMode,
523     val colorFilter: ColorFilter?
524 ) : ModifierNodeElement<SimpleGraphicsLayerModifier>() {
525     override fun create(): SimpleGraphicsLayerModifier {
526         return SimpleGraphicsLayerModifier(
527             scaleX = scaleX,
528             scaleY = scaleY,
529             alpha = alpha,
530             translationX = translationX,
531             translationY = translationY,
532             shadowElevation = shadowElevation,
533             rotationX = rotationX,
534             rotationY = rotationY,
535             rotationZ = rotationZ,
536             cameraDistance = cameraDistance,
537             transformOrigin = transformOrigin,
538             shape = shape,
539             clip = clip,
540             renderEffect = renderEffect,
541             ambientShadowColor = ambientShadowColor,
542             spotShadowColor = spotShadowColor,
543             compositingStrategy = compositingStrategy,
544             blendMode = blendMode,
545             colorFilter = colorFilter
546         )
547     }
548 
549     override fun update(node: SimpleGraphicsLayerModifier) {
550         node.scaleX = scaleX
551         node.scaleY = scaleY
552         node.alpha = alpha
553         node.translationX = translationX
554         node.translationY = translationY
555         node.shadowElevation = shadowElevation
556         node.rotationX = rotationX
557         node.rotationY = rotationY
558         node.rotationZ = rotationZ
559         node.cameraDistance = cameraDistance
560         node.transformOrigin = transformOrigin
561         node.shape = shape
562         node.clip = clip
563         node.renderEffect = renderEffect
564         node.ambientShadowColor = ambientShadowColor
565         node.spotShadowColor = spotShadowColor
566         node.compositingStrategy = compositingStrategy
567         node.blendMode = blendMode
568         node.colorFilter = colorFilter
569         node.invalidateLayerBlock()
570     }
571 
572     override fun InspectorInfo.inspectableProperties() {
573         name = "graphicsLayer"
574         properties["scaleX"] = scaleX
575         properties["scaleY"] = scaleY
576         properties["alpha"] = alpha
577         properties["translationX"] = translationX
578         properties["translationY"] = translationY
579         properties["shadowElevation"] = shadowElevation
580         properties["rotationX"] = rotationX
581         properties["rotationY"] = rotationY
582         properties["rotationZ"] = rotationZ
583         properties["cameraDistance"] = cameraDistance
584         properties["transformOrigin"] = transformOrigin
585         properties["shape"] = shape
586         properties["clip"] = clip
587         properties["renderEffect"] = renderEffect
588         properties["ambientShadowColor"] = ambientShadowColor
589         properties["spotShadowColor"] = spotShadowColor
590         properties["compositingStrategy"] = compositingStrategy
591         properties["blendMode"] = blendMode
592         properties["colorFilter"] = colorFilter
593     }
594 }
595 
596 /**
597  * A [Modifier.Node] that makes content draw into a draw layer. The draw layer can be invalidated
598  * separately from parents. A [graphicsLayer] should be used when the content updates independently
599  * from anything above it to minimize the invalidated content.
600  *
601  * [graphicsLayer] can be used to apply effects to content, such as scaling, rotation, opacity,
602  * shadow, and clipping. Prefer this version when you have layer properties backed by a
603  * [androidx.compose.runtime.State] or an animated value as reading a state inside [block] will only
604  * cause the layer properties update without triggering recomposition and relayout.
605  *
606  * @sample androidx.compose.ui.samples.AnimateFadeIn
607  * @param block block on [GraphicsLayerScope] where you define the layer properties.
608  */
609 @Stable
Modifiernull610 fun Modifier.graphicsLayer(block: GraphicsLayerScope.() -> Unit): Modifier =
611     this then BlockGraphicsLayerElement(block)
612 
613 /**
614  * Determines when to render the contents of a layer into an offscreen buffer before being drawn to
615  * the destination.
616  */
617 @Immutable
618 @kotlin.jvm.JvmInline
619 value class CompositingStrategy internal constructor(@Suppress("unused") private val value: Int) {
620 
621     companion object {
622 
623         /**
624          * Rendering to an offscreen buffer will be determined automatically by the rest of the
625          * graphicsLayer parameters. This is the default behavior. For example, whenever an alpha
626          * value less than 1.0f is provided on [Modifier.graphicsLayer], a compositing layer is
627          * created automatically to first render the contents fully opaque, then draw this offscreen
628          * buffer to the destination with the corresponding alpha. This is necessary for correctness
629          * otherwise alpha applied to individual drawing instructions that overlap will have a
630          * different result than expected. Additionally usage of [RenderEffect] on the graphicsLayer
631          * will also render into an intermediate offscreen buffer before being drawn into the
632          * destination.
633          */
634         val Auto = CompositingStrategy(0)
635 
636         /**
637          * Rendering of content will always be rendered into an offscreen buffer first then drawn to
638          * the destination regardless of the other parameters configured on the graphics layer. This
639          * is useful for leveraging different blending algorithms for masking content. For example,
640          * the contents can be drawn into this graphics layer and masked out by drawing additional
641          * shapes with [BlendMode.Clear]
642          */
643         val Offscreen = CompositingStrategy(1)
644 
645         /**
646          * Modulates alpha for each of the drawing instructions recorded within the graphicsLayer.
647          * This avoids usage of an offscreen buffer for purposes of alpha rendering. [ModulateAlpha]
648          * is more efficient than [Auto] in performance in scenarios where an alpha value less than
649          * 1.0f is provided. Otherwise the performance is similar to that of [Auto]. However, this
650          * can provide different results than [Auto] if there is overlapping content within the
651          * layer and alpha is applied. This should only be used if the contents of the layer are
652          * known well in advance and are expected to not be overlapping.
653          */
654         val ModulateAlpha = CompositingStrategy(2)
655     }
656 }
657 
658 /**
659  * A [Modifier.Element] that adds a draw layer such that tooling can identify an element in the
660  * drawn image.
661  */
662 @Stable
toolingGraphicsLayernull663 fun Modifier.toolingGraphicsLayer() =
664     if (isDebugInspectorInfoEnabled) this.then(Modifier.graphicsLayer()) else this
665 
666 private class BlockGraphicsLayerElement(val block: GraphicsLayerScope.() -> Unit) :
667     ModifierNodeElement<BlockGraphicsLayerModifier>() {
668     override fun create() = BlockGraphicsLayerModifier(block)
669 
670     override fun update(node: BlockGraphicsLayerModifier) {
671         node.layerBlock = block
672         node.invalidateLayerBlock()
673     }
674 
675     override fun InspectorInfo.inspectableProperties() {
676         name = "graphicsLayer"
677         properties["block"] = block
678     }
679 
680     override fun equals(other: Any?): Boolean {
681         if (this === other) return true
682         if (other !is BlockGraphicsLayerElement) return false
683 
684         if (block !== other.block) return false
685 
686         return true
687     }
688 
689     override fun hashCode(): Int {
690         return block.hashCode()
691     }
692 }
693 
694 internal class BlockGraphicsLayerModifier(
695     var layerBlock: GraphicsLayerScope.() -> Unit,
696 ) : LayoutModifierNode, Modifier.Node() {
697 
698     /**
699      * We can skip remeasuring as we only need to rerun the placement block. we request it manually
700      * in the update block.
701      */
702     override val shouldAutoInvalidate: Boolean
703         get() = false
704 
invalidateLayerBlocknull705     fun invalidateLayerBlock() {
706         requireCoordinator(Nodes.Layout)
707             .wrapped
708             ?.updateLayerBlock(layerBlock, forceUpdateLayerParameters = true)
709     }
710 
measurenull711     override fun MeasureScope.measure(
712         measurable: Measurable,
713         constraints: Constraints
714     ): MeasureResult {
715         val placeable = measurable.measure(constraints)
716         return layout(placeable.width, placeable.height) {
717             placeable.placeWithLayer(0, 0, layerBlock = layerBlock)
718         }
719     }
720 
toStringnull721     override fun toString(): String = "BlockGraphicsLayerModifier(" + "block=$layerBlock)"
722 }
723 
724 private class SimpleGraphicsLayerModifier(
725     var scaleX: Float,
726     var scaleY: Float,
727     var alpha: Float,
728     var translationX: Float,
729     var translationY: Float,
730     var shadowElevation: Float,
731     var rotationX: Float,
732     var rotationY: Float,
733     var rotationZ: Float,
734     var cameraDistance: Float,
735     var transformOrigin: TransformOrigin,
736     var shape: Shape,
737     var clip: Boolean,
738     var renderEffect: RenderEffect?,
739     var ambientShadowColor: Color,
740     var spotShadowColor: Color,
741     var compositingStrategy: CompositingStrategy = CompositingStrategy.Auto,
742     var blendMode: BlendMode = BlendMode.SrcOver,
743     var colorFilter: ColorFilter? = null
744 ) : LayoutModifierNode, Modifier.Node() {
745 
746     /**
747      * We can skip remeasuring as we only need to rerun the placement block. we request it manually
748      * in the update block.
749      */
750     override val shouldAutoInvalidate: Boolean
751         get() = false
752 
753     private var layerBlock: GraphicsLayerScope.() -> Unit = {
754         scaleX = this@SimpleGraphicsLayerModifier.scaleX
755         scaleY = this@SimpleGraphicsLayerModifier.scaleY
756         alpha = this@SimpleGraphicsLayerModifier.alpha
757         translationX = this@SimpleGraphicsLayerModifier.translationX
758         translationY = this@SimpleGraphicsLayerModifier.translationY
759         shadowElevation = this@SimpleGraphicsLayerModifier.shadowElevation
760         rotationX = this@SimpleGraphicsLayerModifier.rotationX
761         rotationY = this@SimpleGraphicsLayerModifier.rotationY
762         rotationZ = this@SimpleGraphicsLayerModifier.rotationZ
763         cameraDistance = this@SimpleGraphicsLayerModifier.cameraDistance
764         transformOrigin = this@SimpleGraphicsLayerModifier.transformOrigin
765         shape = this@SimpleGraphicsLayerModifier.shape
766         clip = this@SimpleGraphicsLayerModifier.clip
767         renderEffect = this@SimpleGraphicsLayerModifier.renderEffect
768         ambientShadowColor = this@SimpleGraphicsLayerModifier.ambientShadowColor
769         spotShadowColor = this@SimpleGraphicsLayerModifier.spotShadowColor
770         compositingStrategy = this@SimpleGraphicsLayerModifier.compositingStrategy
771         blendMode = this@SimpleGraphicsLayerModifier.blendMode
772         colorFilter = this@SimpleGraphicsLayerModifier.colorFilter
773     }
774 
775     fun invalidateLayerBlock() {
776         requireCoordinator(Nodes.Layout)
777             .wrapped
778             ?.updateLayerBlock(this.layerBlock, forceUpdateLayerParameters = true)
779     }
780 
781     override fun MeasureScope.measure(
782         measurable: Measurable,
783         constraints: Constraints
784     ): MeasureResult {
785         val placeable = measurable.measure(constraints)
786         return layout(placeable.width, placeable.height) {
787             placeable.placeWithLayer(0, 0, layerBlock = layerBlock)
788         }
789     }
790 
791     override fun toString(): String =
792         "SimpleGraphicsLayerModifier(" +
793             "scaleX=$scaleX, " +
794             "scaleY=$scaleY, " +
795             "alpha = $alpha, " +
796             "translationX=$translationX, " +
797             "translationY=$translationY, " +
798             "shadowElevation=$shadowElevation, " +
799             "rotationX=$rotationX, " +
800             "rotationY=$rotationY, " +
801             "rotationZ=$rotationZ, " +
802             "cameraDistance=$cameraDistance, " +
803             "transformOrigin=$transformOrigin, " +
804             "shape=$shape, " +
805             "clip=$clip, " +
806             "renderEffect=$renderEffect, " +
807             "ambientShadowColor=$ambientShadowColor, " +
808             "spotShadowColor=$spotShadowColor, " +
809             "compositingStrategy=$compositingStrategy, " +
810             "blendMode=$blendMode, " +
811             "colorFilter=$colorFilter" +
812             ")"
813 }
814