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 21 /** 22 * Effect applied to the geometry of a drawing primitive. For example, this can be used to draw 23 * shapes as a dashed or shaped pattern, or apply a treatment around line segment intersections. 24 */ 25 interface PathEffect { 26 companion object { 27 28 /** 29 * Replaces sharp angles between line segments into rounded angles of the specified radius 30 * 31 * @param radius Rounded corner radius to apply for each angle of the drawn shape 32 */ cornerPathEffectnull33 fun cornerPathEffect(radius: Float): PathEffect = actualCornerPathEffect(radius) 34 35 /** 36 * Draws a shape as a series of dashes with the given intervals and offset into the 37 * specified interval array. The intervals must contain an even number of entries (>=2). The 38 * even indices specify "on" intervals and the odd indices represent "off" intervals. The 39 * phase parameter is the pixel offset into the intervals array (mod the sum of all of the 40 * intervals). 41 * 42 * For example: if `intervals[] = {10, 20}`, and phase = 25, this will set up a dashed path 43 * like so: 5 pixels off 10 pixels on 20 pixels off 10 pixels on 20 pixels off 44 * 45 * The phase parameter is an offset into the intervals array. The intervals array controls 46 * the length of the dashes. This is only applied for stroked shapes (ex. 47 * [PaintingStyle.Stroke] and is ignored for filled in shapes (ex. [PaintingStyle.Fill] 48 * 49 * @param intervals Array of "on" and "off" distances for the dashed line segments 50 * @param phase Pixel offset into the intervals array 51 */ 52 fun dashPathEffect(intervals: FloatArray, phase: Float = 0f): PathEffect = 53 actualDashPathEffect(intervals, phase) 54 55 /** 56 * Create a PathEffect that applies the inner effect to the path, and then applies the outer 57 * effect to the result of the inner effect. (e.g. outer(inner(path)). 58 */ 59 fun chainPathEffect(outer: PathEffect, inner: PathEffect): PathEffect = 60 actualChainPathEffect(outer, inner) 61 62 /** 63 * Dash the drawn path by stamping it with the specified shape represented as a [Path]. This 64 * is only applied to stroke shapes and will be ignored with filled shapes. The stroke width 65 * used with this [PathEffect] is ignored as well. 66 * 67 * @param shape Path to stamp along 68 * @param advance Spacing between each stamped shape 69 * @param phase Amount to offset before the first shape is stamped 70 * @param style How to transform the shape at each position as it is stamped 71 */ 72 fun stampedPathEffect( 73 shape: Path, 74 advance: Float, 75 phase: Float, 76 style: StampedPathEffectStyle 77 ): PathEffect = actualStampedPathEffect(shape, advance, phase, style) 78 } 79 } 80 81 internal expect fun actualCornerPathEffect(radius: Float): PathEffect 82 83 internal expect fun actualDashPathEffect(intervals: FloatArray, phase: Float): PathEffect 84 85 internal expect fun actualChainPathEffect(outer: PathEffect, inner: PathEffect): PathEffect 86 87 internal expect fun actualStampedPathEffect( 88 shape: Path, 89 advance: Float, 90 phase: Float, 91 style: StampedPathEffectStyle 92 ): PathEffect 93 94 /** 95 * Strategy for transforming each point of the shape along the drawn path 96 * 97 * @sample androidx.compose.ui.graphics.samples.StampedPathEffectSample 98 */ 99 @Immutable 100 @kotlin.jvm.JvmInline 101 value class StampedPathEffectStyle 102 internal constructor(@Suppress("unused") private val value: Int) { 103 104 companion object { 105 /** 106 * Translate the path shape into the specified location aligning the top left of the path 107 * with the drawn geometry. This does not modify the path itself. 108 * 109 * For example, a circle drawn with a square path and [Translate] will draw the square path 110 * repeatedly with the top left corner of each stamped square along the curvature of the 111 * circle. 112 */ 113 val Translate = StampedPathEffectStyle(0) 114 115 /** 116 * Rotates the path shape its center along the curvature of the drawn geometry. This does 117 * not modify the path itself. 118 * 119 * For example, a circle drawn with a square path and [Rotate] will draw the square path 120 * repeatedly with the center of each stamped square along the curvature of the circle as 121 * well as each square being rotated along the circumference. 122 */ 123 val Rotate = StampedPathEffectStyle(1) 124 125 /** 126 * Modifies the points within the path such that they fit within the drawn geometry. This 127 * will turn straight lines into curves. 128 * 129 * For example, a circle drawn with a square path and [Morph] will modify the straight lines 130 * of the square paths to be curves such that each stamped square is rendered as an arc 131 * around the curvature of the circle. 132 */ 133 val Morph = StampedPathEffectStyle(2) 134 } 135 136 override fun toString() = 137 when (this) { 138 Translate -> "Translate" 139 Rotate -> "Rotate" 140 Morph -> "Morph" 141 else -> "Unknown" 142 } 143 } 144