1 /*
2 * Copyright 2023 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.graphics.path.PathIterator as PlatformPathIterator
20 import androidx.graphics.path.PathIterator.ConicEvaluation as PlatformConicEvaluation
21 import androidx.graphics.path.PathSegment.Type as PlatformPathSegmentType
22
PathIteratornull23 actual fun PathIterator(
24 path: Path,
25 conicEvaluation: PathIterator.ConicEvaluation,
26 tolerance: Float
27 ): PathIterator = AndroidPathIterator(path, conicEvaluation, tolerance)
28
29 private class AndroidPathIterator(
30 override val path: Path,
31 override val conicEvaluation: PathIterator.ConicEvaluation,
32 override val tolerance: Float
33 ) : PathIterator {
34 private val segmentPoints = FloatArray(8)
35
36 private val implementation =
37 PlatformPathIterator(
38 path.asAndroidPath(),
39 when (conicEvaluation) {
40 PathIterator.ConicEvaluation.AsConic -> PlatformConicEvaluation.AsConic
41 PathIterator.ConicEvaluation.AsQuadratics -> PlatformConicEvaluation.AsQuadratics
42 },
43 tolerance
44 )
45
46 override fun calculateSize(includeConvertedConics: Boolean): Int =
47 implementation.calculateSize(includeConvertedConics)
48
49 override fun hasNext(): Boolean = implementation.hasNext()
50
51 override fun next(outPoints: FloatArray, offset: Int): PathSegment.Type =
52 implementation.next(outPoints, offset).toPathSegmentType()
53
54 override fun next(): PathSegment {
55 val p = segmentPoints
56 // Compiler hint to bypass bound checks
57 if (p.size < 8) return DoneSegment
58
59 val type = implementation.next(p, 0).toPathSegmentType()
60 if (type == PathSegment.Type.Done) return DoneSegment
61 if (type == PathSegment.Type.Close) return CloseSegment
62
63 val points =
64 when (type) {
65 PathSegment.Type.Move -> floatArrayOf(p[0], p[1])
66 PathSegment.Type.Line -> floatArrayOf(p[0], p[1], p[2], p[3])
67 PathSegment.Type.Quadratic -> floatArrayOf(p[0], p[1], p[2], p[3], p[4], p[5])
68 PathSegment.Type.Conic -> floatArrayOf(p[0], p[1], p[2], p[3], p[4], p[5])
69 PathSegment.Type.Cubic ->
70 floatArrayOf(p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7])
71 else -> FloatArray(0) // Unreachable since we tested for Done and Close above
72 }
73
74 return PathSegment(type, points, if (type == PathSegment.Type.Conic) p[6] else 0.0f)
75 }
76 }
77
toPathSegmentTypenull78 private fun PlatformPathSegmentType.toPathSegmentType() =
79 when (this) {
80 PlatformPathSegmentType.Move -> PathSegment.Type.Move
81 PlatformPathSegmentType.Line -> PathSegment.Type.Line
82 PlatformPathSegmentType.Quadratic -> PathSegment.Type.Quadratic
83 PlatformPathSegmentType.Conic -> PathSegment.Type.Conic
84 PlatformPathSegmentType.Cubic -> PathSegment.Type.Cubic
85 PlatformPathSegmentType.Close -> PathSegment.Type.Close
86 PlatformPathSegmentType.Done -> PathSegment.Type.Done
87 }
88