| /* |
| * Copyright (C) 2020 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.text; |
| |
| import android.annotation.IntRange; |
| import android.annotation.NonNull; |
| import android.graphics.Paint; |
| import android.graphics.Typeface; |
| import android.graphics.fonts.Font; |
| |
| import com.android.internal.util.Preconditions; |
| |
| import dalvik.annotation.optimization.CriticalNative; |
| |
| import libcore.util.NativeAllocationRegistry; |
| |
| import java.util.ArrayList; |
| import java.util.Objects; |
| |
| /** |
| * Text shaping result object for single style text. |
| * |
| * You can get text shaping result by |
| * {@link TextRunShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)} and |
| * {@link TextRunShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, |
| * Paint)}. |
| * |
| * @see TextRunShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint) |
| * @see TextRunShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint) |
| */ |
| public final class PositionedGlyphs { |
| private static final NativeAllocationRegistry REGISTRY = |
| NativeAllocationRegistry.createMalloced( |
| Typeface.class.getClassLoader(), nReleaseFunc()); |
| |
| private final long mLayoutPtr; |
| private final float mXOffset; |
| private final float mYOffset; |
| private final ArrayList<Font> mFonts; |
| |
| /** |
| * Returns the total amount of advance consumed by this positioned glyphs. |
| * |
| * The advance is an amount of width consumed by the glyph. The total amount of advance is |
| * a total amount of advance consumed by this series of glyphs. In other words, if another |
| * glyph is placed next to this series of glyphs, it's X offset should be shifted this amount |
| * of width. |
| * |
| * @return total amount of advance |
| */ |
| public float getAdvance() { |
| return nGetTotalAdvance(mLayoutPtr); |
| } |
| |
| /** |
| * Effective ascent value of this positioned glyphs. |
| * |
| * If two or more font files are used in this series of glyphs, the effective ascent will be |
| * the minimum ascent value across the all font files. |
| * |
| * @return effective ascent value |
| */ |
| public float getAscent() { |
| return nGetAscent(mLayoutPtr); |
| } |
| |
| /** |
| * Effective descent value of this positioned glyphs. |
| * |
| * If two or more font files are used in this series of glyphs, the effective descent will be |
| * the maximum descent value across the all font files. |
| * |
| * @return effective descent value |
| */ |
| public float getDescent() { |
| return nGetDescent(mLayoutPtr); |
| } |
| |
| /** |
| * Returns the amount of X offset added to glyph position. |
| * |
| * @return The X offset added to glyph position. |
| */ |
| public float getOffsetX() { |
| return mXOffset; |
| } |
| |
| /** |
| * Returns the amount of Y offset added to glyph position. |
| * |
| * @return The Y offset added to glyph position. |
| */ |
| public float getOffsetY() { |
| return mYOffset; |
| } |
| |
| /** |
| * Returns the number of glyphs stored. |
| * |
| * @return the number of glyphs |
| */ |
| @IntRange(from = 0) |
| public int glyphCount() { |
| return nGetGlyphCount(mLayoutPtr); |
| } |
| |
| /** |
| * Returns the font object used for drawing the glyph at the given index. |
| * |
| * @param index the glyph index |
| * @return the font object used for drawing the glyph at the given index |
| */ |
| @NonNull |
| public Font getFont(@IntRange(from = 0) int index) { |
| Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index"); |
| return mFonts.get(index); |
| } |
| |
| /** |
| * Returns the glyph ID used for drawing the glyph at the given index. |
| * |
| * @param index the glyph index |
| * @return An glyph ID of the font. |
| */ |
| @IntRange(from = 0) |
| public int getGlyphId(@IntRange(from = 0) int index) { |
| Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index"); |
| return nGetGlyphId(mLayoutPtr, index); |
| } |
| |
| /** |
| * Returns the x coordinate of the glyph position at the given index. |
| * |
| * @param index the glyph index |
| * @return A X offset in pixels |
| */ |
| public float getGlyphX(@IntRange(from = 0) int index) { |
| Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index"); |
| return nGetX(mLayoutPtr, index) + mXOffset; |
| } |
| |
| /** |
| * Returns the y coordinate of the glyph position at the given index. |
| * |
| * @param index the glyph index |
| * @return A Y offset in pixels. |
| */ |
| public float getGlyphY(@IntRange(from = 0) int index) { |
| Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index"); |
| return nGetY(mLayoutPtr, index) + mYOffset; |
| } |
| |
| /** |
| * Create single style layout from native result. |
| * |
| * @hide |
| * |
| * @param layoutPtr the address of native layout object. |
| */ |
| public PositionedGlyphs(long layoutPtr, float xOffset, float yOffset) { |
| mLayoutPtr = layoutPtr; |
| int glyphCount = nGetGlyphCount(layoutPtr); |
| mFonts = new ArrayList<>(glyphCount); |
| mXOffset = xOffset; |
| mYOffset = yOffset; |
| |
| long prevPtr = 0; |
| Font prevFont = null; |
| for (int i = 0; i < glyphCount; ++i) { |
| long ptr = nGetFont(layoutPtr, i); |
| if (prevPtr != ptr) { |
| prevPtr = ptr; |
| prevFont = new Font(ptr); |
| } |
| mFonts.add(prevFont); |
| } |
| |
| REGISTRY.registerNativeAllocation(this, layoutPtr); |
| } |
| |
| @CriticalNative |
| private static native int nGetGlyphCount(long minikinLayout); |
| @CriticalNative |
| private static native float nGetTotalAdvance(long minikinLayout); |
| @CriticalNative |
| private static native float nGetAscent(long minikinLayout); |
| @CriticalNative |
| private static native float nGetDescent(long minikinLayout); |
| @CriticalNative |
| private static native int nGetGlyphId(long minikinLayout, int i); |
| @CriticalNative |
| private static native float nGetX(long minikinLayout, int i); |
| @CriticalNative |
| private static native float nGetY(long minikinLayout, int i); |
| @CriticalNative |
| private static native long nGetFont(long minikinLayout, int i); |
| @CriticalNative |
| private static native long nReleaseFunc(); |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) return true; |
| if (!(o instanceof PositionedGlyphs)) return false; |
| PositionedGlyphs that = (PositionedGlyphs) o; |
| |
| if (mXOffset != that.mXOffset || mYOffset != that.mYOffset) return false; |
| if (glyphCount() != that.glyphCount()) return false; |
| |
| for (int i = 0; i < glyphCount(); ++i) { |
| if (getGlyphId(i) != that.getGlyphId(i)) return false; |
| if (getGlyphX(i) != that.getGlyphX(i)) return false; |
| if (getGlyphY(i) != that.getGlyphY(i)) return false; |
| if (!getFont(i).equals(that.getFont(i))) return false; |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public int hashCode() { |
| int hashCode = Objects.hash(mXOffset, mYOffset); |
| for (int i = 0; i < glyphCount(); ++i) { |
| hashCode = Objects.hash(hashCode, |
| getGlyphId(i), getGlyphX(i), getGlyphY(i), getFont(i)); |
| } |
| return hashCode; |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder("["); |
| for (int i = 0; i < glyphCount(); ++i) { |
| if (i != 0) { |
| sb.append(", "); |
| } |
| sb.append("[ ID = " + getGlyphId(i) + "," |
| + " pos = (" + getGlyphX(i) + "," + getGlyphY(i) + ")" |
| + " font = " + getFont(i) + " ]"); |
| } |
| sb.append("]"); |
| return "PositionedGlyphs{" |
| + "glyphs = " + sb.toString() |
| + ", mXOffset=" + mXOffset |
| + ", mYOffset=" + mYOffset |
| + '}'; |
| } |
| } |