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