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.vector 18 19 import androidx.compose.ui.graphics.BlendMode 20 import androidx.compose.ui.graphics.Canvas 21 import androidx.compose.ui.graphics.Color 22 import androidx.compose.ui.graphics.ColorFilter 23 import androidx.compose.ui.graphics.ImageBitmap 24 import androidx.compose.ui.graphics.ImageBitmapConfig 25 import androidx.compose.ui.graphics.drawscope.CanvasDrawScope 26 import androidx.compose.ui.graphics.drawscope.DrawScope 27 import androidx.compose.ui.internal.checkPrecondition 28 import androidx.compose.ui.unit.Density 29 import androidx.compose.ui.unit.IntSize 30 import androidx.compose.ui.unit.LayoutDirection 31 import androidx.compose.ui.unit.toSize 32 33 /** 34 * Creates a drawing environment that directs its drawing commands to an [ImageBitmap] which can be 35 * drawn directly in another [DrawScope] instance. This is useful to cache complicated drawing 36 * commands across frames especially if the content has not changed. Additionally some drawing 37 * operations such as rendering paths are done purely in software so it is beneficial to cache the 38 * result and render the contents directly through a texture as done by [DrawScope.drawImage] 39 */ 40 internal class DrawCache { 41 42 @PublishedApi internal var mCachedImage: ImageBitmap? = null 43 private var cachedCanvas: Canvas? = null 44 private var scopeDensity: Density? = null 45 private var layoutDirection: LayoutDirection = LayoutDirection.Ltr 46 private var size: IntSize = IntSize.Zero 47 private var config: ImageBitmapConfig = ImageBitmapConfig.Argb8888 48 49 private val cacheScope = CanvasDrawScope() 50 51 /** 52 * Draw the contents of the lambda with receiver scope into an [ImageBitmap] with the provided 53 * size. If the same size is provided across calls, the same [ImageBitmap] instance is re-used 54 * and the contents are cleared out before drawing content in it again 55 */ drawCachedImagenull56 fun drawCachedImage( 57 config: ImageBitmapConfig, 58 size: IntSize, 59 density: Density, 60 layoutDirection: LayoutDirection, 61 block: DrawScope.() -> Unit 62 ) { 63 this.scopeDensity = density 64 this.layoutDirection = layoutDirection 65 var targetImage = mCachedImage 66 var targetCanvas = cachedCanvas 67 if ( 68 targetImage == null || 69 targetCanvas == null || 70 size.width > targetImage.width || 71 size.height > targetImage.height || 72 this.config != config 73 ) { 74 targetImage = ImageBitmap(size.width, size.height, config = config) 75 targetCanvas = Canvas(targetImage) 76 77 mCachedImage = targetImage 78 cachedCanvas = targetCanvas 79 this.config = config 80 } 81 this.size = size 82 cacheScope.draw(density, layoutDirection, targetCanvas, size.toSize()) { 83 clear() 84 block() 85 } 86 targetImage.prepareToDraw() 87 } 88 89 /** Draw the cached content into the provided [DrawScope] instance */ drawIntonull90 fun drawInto(target: DrawScope, alpha: Float = 1.0f, colorFilter: ColorFilter? = null) { 91 val targetImage = mCachedImage 92 checkPrecondition(targetImage != null) { 93 "drawCachedImage must be invoked first before attempting to draw the result " + 94 "into another destination" 95 } 96 target.drawImage(targetImage, srcSize = size, alpha = alpha, colorFilter = colorFilter) 97 } 98 99 /** 100 * Helper method to clear contents of the draw environment from the given bounds of the 101 * DrawScope 102 */ DrawScopenull103 private fun DrawScope.clear() { 104 drawRect(color = Color.Black, blendMode = BlendMode.Clear) 105 } 106 } 107