script-astra/Android/Sdk/sources/android-35/android/graphics/PathIterator.java
localadmin 4380f00a78 init
2025-01-20 18:15:20 +03:00

298 lines
11 KiB
Java

/*
* Copyright (C) 2022 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 android.graphics;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
import android.annotation.NonNull;
import dalvik.annotation.optimization.CriticalNative;
import dalvik.system.VMRuntime;
import libcore.util.NativeAllocationRegistry;
import java.lang.annotation.Retention;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
/**
* <code>PathIterator</code> can be used to query a given {@link Path} object, to discover its
* operations and point values.
*/
public class PathIterator implements Iterator<PathIterator.Segment> {
private final float[] mPointsArray;
private final long mPointsAddress;
private int mCachedVerb = -1;
private boolean mDone = false;
private final long mNativeIterator;
private final Path mPath;
private final int mPathGenerationId;
private static final int POINT_ARRAY_SIZE = 8;
private static final NativeAllocationRegistry sRegistry =
NativeAllocationRegistry.createMalloced(
PathIterator.class.getClassLoader(), nGetFinalizer());
/**
* The <code>Verb</code> indicates the operation for a given segment of a path. These
* operations correspond exactly to the primitive operations on {@link Path}, such as
* {@link Path#moveTo(float, float)} and {@link Path#lineTo(float, float)}, except for
* {@link #VERB_DONE}, which means that there are no more operations in this path.
*/
@Retention(SOURCE)
@IntDef({VERB_MOVE, VERB_LINE, VERB_QUAD, VERB_CONIC, VERB_CUBIC, VERB_CLOSE, VERB_DONE})
@interface Verb {}
// these must match the values in SkPath.h
public static final int VERB_MOVE = 0;
public static final int VERB_LINE = 1;
public static final int VERB_QUAD = 2;
public static final int VERB_CONIC = 3;
public static final int VERB_CUBIC = 4;
public static final int VERB_CLOSE = 5;
public static final int VERB_DONE = 6;
/**
* Returns a {@link PathIterator} object for this path, which can be used to query the
* data (operations and points) in the path. Iterators can only be used on Path objects
* that have not been modified since the iterator was created. Calling
* {@link #next(float[], int)}, {@link #next()}, or {@link #hasNext()} on an
* iterator for a modified path will result in a {@link ConcurrentModificationException}.
*
* @param path The {@link Path} for which this iterator can be queried.
*/
PathIterator(@NonNull Path path) {
mPath = path;
mNativeIterator = nCreate(mPath.mNativePath);
mPathGenerationId = mPath.getGenerationId();
final VMRuntime runtime = VMRuntime.getRuntime();
mPointsArray = (float[]) runtime.newNonMovableArray(float.class, POINT_ARRAY_SIZE);
mPointsAddress = runtime.addressOf(mPointsArray);
sRegistry.registerNativeAllocation(this, mNativeIterator);
}
/**
* Returns the next verb in this iterator's {@link Path}, and fills entries in the
* <code>points</code> array with the point data (if any) for that operation.
* Each two floats represent the data for a single point of that operation.
* The number of pairs of floats supplied in the resulting array depends on the verb:
* <ul>
* <li>{@link #VERB_MOVE}: 1 pair (indices 0 to 1)</li>
* <li>{@link #VERB_LINE}: 2 pairs (indices 0 to 3)</li>
* <li>{@link #VERB_QUAD}: 3 pairs (indices 0 to 5)</li>
* <li>{@link #VERB_CONIC}: 3.5 pairs (indices 0 to 6), the seventh entry has the conic
* weight</li>
* <li>{@link #VERB_CUBIC}: 4 pairs (indices 0 to 7)</li>
* <li>{@link #VERB_CLOSE}: 0 pairs</li>
* <li>{@link #VERB_DONE}: 0 pairs</li>
* </ul>
* @param points The point data for this operation, must have at least
* 8 items available to hold up to 4 pairs of point values
* @param offset An offset into the <code>points</code> array where entries should be placed.
* @return the operation for the next element in the iteration
* @throws ArrayIndexOutOfBoundsException if the points array is too small
* @throws ConcurrentModificationException if the underlying path was modified
* since this iterator was created.
*/
@NonNull
public @Verb int next(@NonNull float[] points, int offset) {
if (points.length < offset + POINT_ARRAY_SIZE) {
throw new ArrayIndexOutOfBoundsException("points array must be able to "
+ "hold at least 8 entries");
}
@Verb int returnVerb = getReturnVerb(mCachedVerb);
mCachedVerb = -1;
System.arraycopy(mPointsArray, 0, points, offset, POINT_ARRAY_SIZE);
return returnVerb;
}
/**
* Returns true if the there are more elements in this iterator to be returned.
* A return value of <code>false</code> means there are no more elements, and an
* ensuing call to {@link #next()} or {@link #next(float[], int)} )} will return
* {@link #VERB_DONE}.
*
* @return true if there are more elements to be iterated through, false otherwise
* @throws ConcurrentModificationException if the underlying path was modified
* since this iterator was created.
*/
@Override
public boolean hasNext() {
if (mCachedVerb == -1) {
mCachedVerb = nextInternal();
}
return mCachedVerb != VERB_DONE;
}
/**
* Returns the next verb in the iteration, or {@link #VERB_DONE} if there are no more
* elements.
*
* @return the next verb in the iteration, or {@link #VERB_DONE} if there are no more
* elements
* @throws ConcurrentModificationException if the underlying path was modified
* since this iterator was created.
*/
@NonNull
public @Verb int peek() {
if (mPathGenerationId != mPath.getGenerationId()) {
throw new ConcurrentModificationException(
"Iterator cannot be used on modified Path");
}
if (mDone) {
return VERB_DONE;
}
return nPeek(mNativeIterator);
}
/**
* This is where the work is done for {@link #next()}. Using this internal method
* is helfpul for managing the cached segment used by {@link #hasNext()}.
*
* @return the segment to be returned by {@link #next()}
* @throws ConcurrentModificationException if the underlying path was modified
* since this iterator was created.
*/
@NonNull
private @Verb int nextInternal() {
if (mDone) {
return VERB_DONE;
}
if (mPathGenerationId != mPath.getGenerationId()) {
throw new ConcurrentModificationException(
"Iterator cannot be used on modified Path");
}
@Verb int verb = nNext(mNativeIterator, mPointsAddress);
if (verb == VERB_DONE) {
mDone = true;
}
return verb;
}
/**
* Returns the next {@link Segment} element in this iterator.
*
* There are two versions of <code>next()</code>. This version is slightly more
* expensive at runtime, since it allocates a new {@link Segment} object with
* every call. The other version, {@link #next(float[], int)} requires no such allocation, but
* requires a little more manual effort to use.
*
* @return the next segment in this iterator
* @throws ConcurrentModificationException if the underlying path was modified
* since this iterator was created.
*/
@NonNull
@Override
public Segment next() {
@Verb int returnVerb = getReturnVerb(mCachedVerb);
mCachedVerb = -1;
float conicWeight = 0f;
if (returnVerb == VERB_CONIC) {
conicWeight = mPointsArray[6];
}
float[] returnPoints = new float[8];
System.arraycopy(mPointsArray, 0, returnPoints, 0, POINT_ARRAY_SIZE);
return new Segment(returnVerb, returnPoints, conicWeight);
}
private @Verb int getReturnVerb(int cachedVerb) {
switch (cachedVerb) {
case VERB_MOVE: return VERB_MOVE;
case VERB_LINE: return VERB_LINE;
case VERB_QUAD: return VERB_QUAD;
case VERB_CONIC: return VERB_CONIC;
case VERB_CUBIC: return VERB_CUBIC;
case VERB_CLOSE: return VERB_CLOSE;
case VERB_DONE: return VERB_DONE;
}
return nextInternal();
}
/**
* This class holds the data for a given segment in a path, as returned by
* {@link #next()}.
*/
public static class Segment {
private final @Verb int mVerb;
private final float[] mPoints;
private final float mConicWeight;
/**
* The operation for this segment.
*
* @return the verb which indicates the operation happening in this segment
*/
@NonNull
public @Verb int getVerb() {
return mVerb;
}
/**
* The point data for this segment.
*
* Each two floats represent the data for a single point of that operation.
* The number of pairs of floats supplied in the resulting array depends on the verb:
* <ul>
* <li>{@link #VERB_MOVE}: 1 pair (indices 0 to 1)</li>
* <li>{@link #VERB_LINE}: 2 pairs (indices 0 to 3)</li>
* <li>{@link #VERB_QUAD}: 3 pairs (indices 0 to 5)</li>
* <li>{@link #VERB_CONIC}: 4 pairs (indices 0 to 7), the last pair contains the
* conic weight twice</li>
* <li>{@link #VERB_CUBIC}: 4 pairs (indices 0 to 7)</li>
* <li>{@link #VERB_CLOSE}: 0 pairs</li>
* <li>{@link #VERB_DONE}: 0 pairs</li>
* </ul>
* @return the point data for this segment
*/
@NonNull
public float[] getPoints() {
return mPoints;
}
/**
* The weight for the conic operation in this segment. If the verb in this segment
* is not equal to {@link #VERB_CONIC}, the weight value is undefined.
*
* @see Path#conicTo(float, float, float, float, float)
* @return the weight for the conic operation in this segment, if any
*/
public float getConicWeight() {
return mConicWeight;
}
Segment(@NonNull @Verb int verb, @NonNull float[] points, float conicWeight) {
mVerb = verb;
mPoints = points;
mConicWeight = conicWeight;
}
}
// ------------------ Regular JNI ------------------------
private static native long nCreate(long nativePath);
private static native long nGetFinalizer();
// ------------------ Critical JNI ------------------------
@CriticalNative
private static native int nNext(long nativeIterator, long pointsAddress);
@CriticalNative
private static native int nPeek(long nativeIterator);
}