All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
skikoMain.androidx.compose.ui.graphics.SkiaPathIterator.skiko.kt Maven / Gradle / Ivy
/*
* Copyright 2024 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
import org.jetbrains.skia.PathVerb
actual fun PathIterator(
path: Path,
conicEvaluation: PathIterator.ConicEvaluation,
tolerance: Float
): PathIterator = SkiaPathIterator(path, conicEvaluation, tolerance)
// The code below would be used to handle conic to quadratic conversions.
// The core Skia API exposed by Skiko accepts a "power of 2" number of
// quadratics when calling Path.convertConicToQuads() but our APIs expose
// a tolerance/error value. This code computes that "power of 2" number
// (from 0 to 5) given a specified tolerance value.
//
// const val MaxConicToQuadraticSubdivisions = 5
//
// private fun Point.isFinite() = x.isFinite() && y.isFinite()
//
// private fun toleranceToSubdivisions(
// p0: Point,
// p1: Point,
// p2: Point,
// weight: Float,
// tolerance: Float
// ): Int {
// if (
// tolerance <= 0.0f ||
// !tolerance.isFinite() ||
// !p0.isFinite() ||
// !p1.isFinite() ||
// !p2.isFinite()
// ) {
// return 0
// }
//
// val a = weight - 1.0f
// val k = a / (4.0f * (2.0f + a))
// val x = k * (p0.x - 2.0f * p1.x + p2.x)
// val y = k * (p0.y - 2.0f * p1.y + p2.y)
//
// var error = sqrt(x * x + y * y);
// var subdivisions = 0
// while (subdivisions < MaxConicToQuadraticSubdivisions) {
// if (error <= tolerance) break
// error *= 0.25f
// subdivisions++
// }
// return subdivisions
// }
private class SkiaPathIterator(
override val path: Path,
override val conicEvaluation: PathIterator.ConicEvaluation,
override val tolerance: Float
) : PathIterator {
private val skiaPath = path.asSkiaPath()
private val iterator = skiaPath.iterator()
// TODO: Handle conversion from conics to quadratics
override fun calculateSize(includeConvertedConics: Boolean): Int = skiaPath.verbsCount
override fun hasNext(): Boolean = iterator.hasNext()
override fun next(points: FloatArray, offset: Int): PathSegment.Type {
check(points.size - offset >= 8) { "The points array must contain at least 8 floats" }
if (!hasNext()) return PathSegment.Type.Done
val segment = iterator.next()
requireNotNull(segment)
return when (segment.verb) {
PathVerb.MOVE -> {
points[offset] = segment.p0!!.x
points[offset + 1] = segment.p0!!.y
PathSegment.Type.Move
}
PathVerb.LINE -> {
points[offset] = segment.p0!!.x
points[offset + 1] = segment.p0!!.y
points[offset + 2] = segment.p1!!.x
points[offset + 3] = segment.p1!!.y
PathSegment.Type.Line
}
PathVerb.QUAD -> {
points[offset] = segment.p0!!.x
points[offset + 1] = segment.p0!!.y
points[offset + 2] = segment.p1!!.x
points[offset + 3] = segment.p1!!.y
points[offset + 4] = segment.p2!!.x
points[offset + 5] = segment.p2!!.y
PathSegment.Type.Quadratic
}
// TODO: convert conics to quadratics when conicEvaluation is set to AsQuadratics
PathVerb.CONIC -> {
points[offset] = segment.p0!!.x
points[offset + 1] = segment.p0!!.y
points[offset + 2] = segment.p1!!.x
points[offset + 3] = segment.p1!!.y
points[offset + 4] = segment.p2!!.x
points[offset + 5] = segment.p2!!.y
points[offset + 6] = segment.conicWeight
points[offset + 7] = segment.conicWeight
PathSegment.Type.Conic
}
PathVerb.CUBIC -> {
points[offset] = segment.p0!!.x
points[offset + 1] = segment.p0!!.y
points[offset + 2] = segment.p1!!.x
points[offset + 3] = segment.p1!!.y
points[offset + 4] = segment.p2!!.x
points[offset + 5] = segment.p2!!.y
points[offset + 6] = segment.p3!!.x
points[offset + 7] = segment.p3!!.y
PathSegment.Type.Cubic
}
PathVerb.CLOSE -> PathSegment.Type.Close
PathVerb.DONE -> PathSegment.Type.Done
}
}
override fun next(): PathSegment {
if (!hasNext()) return DoneSegment
val segment = iterator.next()
requireNotNull(segment)
return when (segment.verb) {
PathVerb.MOVE -> PathSegment(
PathSegment.Type.Move,
floatArrayOf(segment.p0!!.x, segment.p0!!.y),
0.0f
)
PathVerb.LINE -> PathSegment(
PathSegment.Type.Line,
floatArrayOf(
segment.p0!!.x, segment.p0!!.y,
segment.p1!!.x, segment.p1!!.y,
),
0.0f
)
PathVerb.QUAD -> PathSegment(
PathSegment.Type.Quadratic,
floatArrayOf(
segment.p0!!.x, segment.p0!!.y,
segment.p1!!.x, segment.p1!!.y,
segment.p2!!.x, segment.p2!!.y,
),
0.0f
)
// TODO: convert conics to quadratics when conicEvaluation is set to AsQuadratics
PathVerb.CONIC -> PathSegment(
PathSegment.Type.Quadratic,
floatArrayOf(
segment.p0!!.x, segment.p0!!.y,
segment.p1!!.x, segment.p1!!.y,
segment.p2!!.x, segment.p2!!.y,
),
segment.conicWeight
)
PathVerb.CUBIC -> PathSegment(
PathSegment.Type.Cubic,
floatArrayOf(
segment.p0!!.x, segment.p0!!.y,
segment.p1!!.x, segment.p1!!.y,
segment.p2!!.x, segment.p2!!.y,
segment.p3!!.x, segment.p3!!.y
),
0.0f
)
PathVerb.CLOSE -> CloseSegment
PathVerb.DONE -> DoneSegment
}
}
}