| /* |
| * Copyright 2020 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package androidx.compose.ui.graphics.vector |
| |
| import androidx.compose.ui.graphics.Brush |
| import androidx.compose.ui.graphics.StrokeCap |
| import androidx.compose.ui.graphics.StrokeJoin |
| import androidx.ui.unit.Dp |
| |
| private inline class Stack<T>(private val backing: ArrayList<T> = ArrayList<T>()) { |
| val size: Int get() = backing.size |
| |
| fun push(value: T) = backing.add(value) |
| fun pop(): T = backing.removeAt(size - 1) |
| fun peek(): T = backing[size - 1] |
| fun isEmpty() = backing.isEmpty() |
| fun isNotEmpty() = !isEmpty() |
| fun clear() = backing.clear() |
| } |
| |
| /** |
| * Builder used to construct a Vector graphic tree. |
| * This is useful for caching the result of expensive operations used to construct |
| * a vector graphic for compose. |
| * For example, the vector graphic could be serialized and downloaded from a server and represented |
| * internally in a VectorAsset before it is composed through [VectorPainter] |
| * The generated VectorAsset is recommended to be memoized across composition calls to avoid |
| * doing redundant work |
| */ |
| class VectorAssetBuilder( |
| |
| /** |
| * Name of the vector asset |
| */ |
| val name: String = DefaultGroupName, |
| |
| /** |
| * Intrinsic width of the Vector in [Dp] |
| */ |
| val defaultWidth: Dp, |
| |
| /** |
| * Intrinsic height of the Vector in [Dp] |
| */ |
| val defaultHeight: Dp, |
| |
| /** |
| * Used to define the width of the viewport space. Viewport is basically the virtual canvas |
| * where the paths are drawn on. |
| */ |
| val viewportWidth: Float, |
| |
| /** |
| * Used to define the height of the viewport space. Viewport is basically the virtual canvas |
| * where the paths are drawn on. |
| */ |
| val viewportHeight: Float |
| ) { |
| private val nodes = Stack<VectorGroup>() |
| |
| private var root = VectorGroup() |
| private var isConsumed = false |
| |
| private val currentGroup: VectorGroup |
| get() = nodes.peek() |
| |
| init { |
| nodes.push(root) |
| } |
| |
| /** |
| * Create a new group and push it to the front of the stack of VectorAsset nodes |
| * |
| * @param name the name of the group |
| * @param rotate the rotation of the group in degrees |
| * @param pivotX the x coordinate of the pivot point to rotate or scale the group |
| * @param pivotY the y coordinate of the pivot point to rotate or scale the group |
| * @param scaleX the scale factor in the X-axis to apply to the group |
| * @param scaleY the scale factor in the Y-axis to apply to the group |
| * @param translationX the translation in virtual pixels to apply along the x-axis |
| * @param translationY the translation in virtual pixels to apply along the y-axis |
| * @param clipPathData the path information used to clip the content within the group |
| * |
| * @return This VectorAssetBuilder instance as a convenience for chaining calls |
| */ |
| fun pushGroup( |
| name: String = DefaultGroupName, |
| rotate: Float = DefaultRotation, |
| pivotX: Float = DefaultPivotX, |
| pivotY: Float = DefaultPivotY, |
| scaleX: Float = DefaultScaleX, |
| scaleY: Float = DefaultScaleY, |
| translationX: Float = DefaultTranslationX, |
| translationY: Float = DefaultTranslationY, |
| clipPathData: List<PathNode> = EmptyPath |
| ): VectorAssetBuilder { |
| ensureNotConsumed() |
| val group = VectorGroup( |
| name, |
| rotate, |
| pivotX, |
| pivotY, |
| scaleX, |
| scaleY, |
| translationX, |
| translationY, |
| clipPathData |
| ) |
| currentGroup.addNode(group) |
| nodes.push(group) |
| return this |
| } |
| |
| /** |
| * Pops the topmost VectorGroup from this VectorAssetBuilder. This is used to indicate |
| * that no additional VectorAsset nodes will be added to the current VectorGroup |
| * @return This VectorAssetBuilder instance as a convenience for chaining calls |
| */ |
| fun popGroup(): VectorAssetBuilder { |
| ensureNotConsumed() |
| nodes.pop() |
| return this |
| } |
| |
| /** |
| * Add a path to the VectorAsset graphic. This represents a leaf node in the VectorAsset graphics |
| * tree structure |
| * |
| * @param pathData path information to render the shape of the path |
| * @param name the name of the path |
| * @param fill specifies the [Brush] used to fill the path |
| * @param fillAlpha the alpha to fill the path |
| * @param stroke specifies the [Brush] used to fill the stroke |
| * @param strokeAlpha the alpha to stroke the path |
| * @param strokeLineWidth the width of the line to stroke the path |
| * @param strokeLineCap specifies the linecap for a stroked path |
| * @param strokeLineJoin specifies the linejoin for a stroked path |
| * @param strokeLineMiter specifies the miter limit for a stroked path |
| * |
| * @return This VectorAssetBuilder instance as a convenience for chaining calls |
| */ |
| fun addPath( |
| pathData: List<PathNode>, |
| name: String = DefaultPathName, |
| fill: Brush? = null, |
| fillAlpha: Float = 1.0f, |
| stroke: Brush? = null, |
| strokeAlpha: Float = 1.0f, |
| strokeLineWidth: Float = DefaultStrokeLineWidth, |
| strokeLineCap: StrokeCap = DefaultStrokeLineCap, |
| strokeLineJoin: StrokeJoin = DefaultStrokeLineJoin, |
| strokeLineMiter: Float = DefaultStrokeLineMiter |
| ): VectorAssetBuilder { |
| ensureNotConsumed() |
| currentGroup.addNode( |
| VectorPath( |
| name, |
| pathData, |
| fill, |
| fillAlpha, |
| stroke, |
| strokeAlpha, |
| strokeLineWidth, |
| strokeLineCap, |
| strokeLineJoin, |
| strokeLineMiter |
| ) |
| ) |
| return this |
| } |
| |
| /** |
| * Construct a VectorAsset. This concludes the creation process of a VectorAsset graphic |
| * This builder cannot be re-used to create additional VectorAsset instances |
| * @return The newly created VectorAsset instance |
| */ |
| fun build(): VectorAsset { |
| ensureNotConsumed() |
| val vectorImage = VectorAsset( |
| name, |
| defaultWidth, |
| defaultHeight, |
| viewportWidth, |
| viewportHeight, |
| root |
| ) |
| |
| isConsumed = true |
| |
| return vectorImage |
| } |
| |
| /** |
| * Throws IllegalStateException if the VectorAssetBuilder has already been consumed |
| */ |
| private fun ensureNotConsumed() { |
| check(!isConsumed) { |
| "VectorAssetBuilder is single use, create a new instance to create a new VectorAsset" |
| } |
| } |
| } |
| |
| /** |
| * DSL extension for adding a [VectorPath] to [this]. |
| * |
| * See [VectorAssetBuilder.addPath] for the corresponding builder function. |
| * |
| * @param name the name for this path |
| * @param fill specifies the [Brush] used to fill the path |
| * @param fillAlpha the alpha to fill the path |
| * @param stroke specifies the [Brush] used to fill the stroke |
| * @param strokeAlpha the alpha to stroke the path |
| * @param strokeLineWidth the width of the line to stroke the path |
| * @param strokeLineCap specifies the linecap for a stroked path |
| * @param strokeLineJoin specifies the linejoin for a stroked path |
| * @param strokeLineMiter specifies the miter limit for a stroked path |
| * @param pathBuilder [PathBuilder] lambda for adding [PathNode]s to this path. |
| */ |
| fun VectorAssetBuilder.path( |
| name: String = DefaultPathName, |
| fill: Brush? = null, |
| fillAlpha: Float = 1.0f, |
| stroke: Brush? = null, |
| strokeAlpha: Float = 1.0f, |
| strokeLineWidth: Float = DefaultStrokeLineWidth, |
| strokeLineCap: StrokeCap = DefaultStrokeLineCap, |
| strokeLineJoin: StrokeJoin = DefaultStrokeLineJoin, |
| strokeLineMiter: Float = DefaultStrokeLineMiter, |
| pathBuilder: PathBuilder.() -> Unit |
| ) = addPath( |
| PathData(pathBuilder), |
| name, |
| fill, |
| fillAlpha, |
| stroke, |
| strokeAlpha, |
| strokeLineWidth, |
| strokeLineCap, |
| strokeLineJoin, |
| strokeLineMiter |
| ) |
| |
| /** |
| * DSL extension for adding a [VectorGroup] to [this]. |
| * |
| * See [VectorAssetBuilder.pushGroup] for the corresponding builder function. |
| * |
| * @param name the name of the group |
| * @param rotate the rotation of the group in degrees |
| * @param pivotX the x coordinate of the pivot point to rotate or scale the group |
| * @param pivotY the y coordinate of the pivot point to rotate or scale the group |
| * @param scaleX the scale factor in the X-axis to apply to the group |
| * @param scaleY the scale factor in the Y-axis to apply to the group |
| * @param translationX the translation in virtual pixels to apply along the x-axis |
| * @param translationY the translation in virtual pixels to apply along the y-axis |
| * @param clipPathData the path information used to clip the content within the group |
| * @param block builder lambda to add children to this group |
| */ |
| fun VectorAssetBuilder.group( |
| name: String = DefaultGroupName, |
| rotate: Float = DefaultRotation, |
| pivotX: Float = DefaultPivotX, |
| pivotY: Float = DefaultPivotY, |
| scaleX: Float = DefaultScaleX, |
| scaleY: Float = DefaultScaleY, |
| translationX: Float = DefaultTranslationX, |
| translationY: Float = DefaultTranslationY, |
| clipPathData: List<PathNode> = EmptyPath, |
| block: VectorAssetBuilder.() -> Unit |
| ) = apply { |
| pushGroup( |
| name, |
| rotate, |
| pivotX, |
| pivotY, |
| scaleX, |
| scaleY, |
| translationX, |
| translationY, |
| clipPathData |
| ) |
| block() |
| popGroup() |
| } |