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