• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 android.graphics.text;
18 
19 import static com.android.text.flags.Flags.FLAG_NEW_FONTS_FALLBACK_XML;
20 
21 import android.annotation.FlaggedApi;
22 import android.annotation.IntRange;
23 import android.annotation.NonNull;
24 import android.graphics.Paint;
25 import android.graphics.Typeface;
26 import android.graphics.fonts.Font;
27 
28 import com.android.internal.util.Preconditions;
29 import com.android.text.flags.Flags;
30 
31 import dalvik.annotation.optimization.CriticalNative;
32 
33 import libcore.util.NativeAllocationRegistry;
34 
35 import java.util.ArrayList;
36 import java.util.Objects;
37 
38 /**
39  * Text shaping result object for single style text.
40  *
41  * You can get text shaping result by
42  * {@link TextRunShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)} and
43  * {@link TextRunShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean,
44  * Paint)}.
45  *
46  * @see TextRunShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)
47  * @see TextRunShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)
48  */
49 @android.ravenwood.annotation.RavenwoodKeepWholeClass
50 public final class PositionedGlyphs {
51     private static class NoImagePreloadHolder {
52         private static final NativeAllocationRegistry REGISTRY =
53                 NativeAllocationRegistry.createMalloced(
54                         Typeface.class.getClassLoader(), nReleaseFunc());
55     }
56 
57     private final long mLayoutPtr;
58     private final float mXOffset;
59     private final float mYOffset;
60     private final ArrayList<Font> mFonts;
61 
62     /**
63      * Returns the total amount of advance consumed by this positioned glyphs.
64      *
65      * The advance is an amount of width consumed by the glyph. The total amount of advance is
66      * a total amount of advance consumed by this series of glyphs. In other words, if another
67      * glyph is placed next to this series of  glyphs, it's X offset should be shifted this amount
68      * of width.
69      *
70      * @return total amount of advance
71      */
getAdvance()72     public float getAdvance() {
73         return nGetTotalAdvance(mLayoutPtr);
74     }
75 
76     /**
77      * Effective ascent value of this positioned glyphs.
78      *
79      * If two or more font files are used in this series of glyphs, the effective ascent will be
80      * the minimum ascent value across the all font files.
81      *
82      * @return effective ascent value
83      */
getAscent()84     public float getAscent() {
85         return nGetAscent(mLayoutPtr);
86     }
87 
88     /**
89      * Effective descent value of this positioned glyphs.
90      *
91      * If two or more font files are used in this series of glyphs, the effective descent will be
92      * the maximum descent value across the all font files.
93      *
94      * @return effective descent value
95      */
getDescent()96     public float getDescent() {
97         return nGetDescent(mLayoutPtr);
98     }
99 
100     /**
101      * Returns the amount of X offset added to glyph position.
102      *
103      * @return The X offset added to glyph position.
104      */
getOffsetX()105     public float getOffsetX() {
106         return mXOffset;
107     }
108 
109     /**
110      * Returns the amount of Y offset added to glyph position.
111      *
112      * @return The Y offset added to glyph position.
113      */
getOffsetY()114     public float getOffsetY() {
115         return mYOffset;
116     }
117 
118     /**
119      * Returns the number of glyphs stored.
120      *
121      * @return the number of glyphs
122      */
123     @IntRange(from = 0)
glyphCount()124     public int glyphCount() {
125         return nGetGlyphCount(mLayoutPtr);
126     }
127 
128     /**
129      * Returns the font object used for drawing the glyph at the given index.
130      *
131      * @param index the glyph index
132      * @return the font object used for drawing the glyph at the given index
133      */
134     @NonNull
getFont(@ntRangefrom = 0) int index)135     public Font getFont(@IntRange(from = 0) int index) {
136         Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
137         if (Flags.typefaceRedesignReadonly()) {
138             return mFonts.get(nGetFontId(mLayoutPtr, index));
139         }
140         return mFonts.get(index);
141     }
142 
143     /**
144      * Returns the glyph ID used for drawing the glyph at the given index.
145      *
146      * @param index the glyph index
147      * @return A glyph ID of the font.
148      */
149     @IntRange(from = 0)
getGlyphId(@ntRangefrom = 0) int index)150     public int getGlyphId(@IntRange(from = 0) int index) {
151         Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
152         return nGetGlyphId(mLayoutPtr, index);
153     }
154 
155     /**
156      * Returns the x coordinate of the glyph position at the given index.
157      *
158      * @param index the glyph index
159      * @return A X offset in pixels
160      */
getGlyphX(@ntRangefrom = 0) int index)161     public float getGlyphX(@IntRange(from = 0) int index) {
162         Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
163         return nGetX(mLayoutPtr, index) + mXOffset;
164     }
165 
166     /**
167      * Returns the y coordinate of the glyph position at the given index.
168      *
169      * @param index the glyph index
170      * @return A Y offset in pixels.
171      */
getGlyphY(@ntRangefrom = 0) int index)172     public float getGlyphY(@IntRange(from = 0) int index) {
173         Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
174         return nGetY(mLayoutPtr, index) + mYOffset;
175     }
176 
177     /**
178      * Returns true if the fake bold option used for drawing, otherwise false.
179      *
180      * @param index the glyph index
181      * @return true if the fake bold option is on, otherwise off.
182      */
183     @FlaggedApi(FLAG_NEW_FONTS_FALLBACK_XML)
getFakeBold(@ntRangefrom = 0) int index)184     public boolean getFakeBold(@IntRange(from = 0) int index) {
185         Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
186         return nGetFakeBold(mLayoutPtr, index);
187     }
188 
189     /**
190      * Returns true if the fake italic option used for drawing, otherwise false.
191      *
192      * @param index the glyph index
193      * @return true if the fake italic option is on, otherwise off.
194      */
195     @FlaggedApi(FLAG_NEW_FONTS_FALLBACK_XML)
getFakeItalic(@ntRangefrom = 0) int index)196     public boolean getFakeItalic(@IntRange(from = 0) int index) {
197         Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
198         return nGetFakeItalic(mLayoutPtr, index);
199     }
200 
201     /**
202      * A special value returned by {@link #getWeightOverride(int)} and
203      * {@link #getItalicOverride(int)} that indicates no font variation setting is overridden.
204      */
205     @FlaggedApi(FLAG_NEW_FONTS_FALLBACK_XML)
206     public static final float NO_OVERRIDE = Float.MIN_VALUE;
207 
208     /**
209      * Returns overridden weight value if the font is variable font and `wght` value is overridden
210      * for drawing. Otherwise returns {@link #NO_OVERRIDE}.
211      *
212      * @param index the glyph index
213      * @return overridden weight value or {@link #NO_OVERRIDE}.
214      */
215     @FlaggedApi(FLAG_NEW_FONTS_FALLBACK_XML)
getWeightOverride(@ntRangefrom = 0) int index)216     public float getWeightOverride(@IntRange(from = 0) int index) {
217         Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
218         float value = nGetWeightOverride(mLayoutPtr, index);
219         if (value == -1) {
220             return NO_OVERRIDE;
221         } else {
222             return value;
223         }
224     }
225 
226     /**
227      * Returns overridden italic value if the font is variable font and `ital` value is overridden
228      * for drawing. Otherwise returns {@link #NO_OVERRIDE}.
229      *
230      * @param index the glyph index
231      * @return overridden weight value or {@link #NO_OVERRIDE}.
232      */
233     @FlaggedApi(FLAG_NEW_FONTS_FALLBACK_XML)
getItalicOverride(@ntRangefrom = 0) int index)234     public float getItalicOverride(@IntRange(from = 0) int index) {
235         Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
236         float value = nGetItalicOverride(mLayoutPtr, index);
237         if (value == -1) {
238             return NO_OVERRIDE;
239         } else {
240             return value;
241         }
242     }
243 
244     /**
245      * Create single style layout from native result.
246      *
247      * @hide
248      *
249      * @param layoutPtr the address of native layout object.
250      */
PositionedGlyphs(long layoutPtr, float xOffset, float yOffset)251     public PositionedGlyphs(long layoutPtr, float xOffset, float yOffset) {
252         mLayoutPtr = layoutPtr;
253         mXOffset = xOffset;
254         mYOffset = yOffset;
255 
256         if (Flags.typefaceRedesignReadonly()) {
257             int fontCount = nGetFontCount(layoutPtr);
258             mFonts = new ArrayList<>(fontCount);
259             for (int i = 0; i < fontCount; ++i) {
260                 mFonts.add(new Font(nGetFontRef(layoutPtr, i)));
261             }
262         } else {
263             int glyphCount = nGetGlyphCount(layoutPtr);
264             mFonts = new ArrayList<>(glyphCount);
265 
266             long prevPtr = 0;
267             Font prevFont = null;
268             for (int i = 0; i < glyphCount; ++i) {
269                 long ptr = nGetFont(layoutPtr, i);
270                 if (prevPtr != ptr) {
271                     prevPtr = ptr;
272                     prevFont = new Font(ptr);
273                 }
274                 mFonts.add(prevFont);
275             }
276         }
277 
278         NoImagePreloadHolder.REGISTRY.registerNativeAllocation(this, layoutPtr);
279     }
280 
281     @CriticalNative
nGetGlyphCount(long minikinLayout)282     private static native int nGetGlyphCount(long minikinLayout);
283     @CriticalNative
nGetTotalAdvance(long minikinLayout)284     private static native float nGetTotalAdvance(long minikinLayout);
285     @CriticalNative
nGetAscent(long minikinLayout)286     private static native float nGetAscent(long minikinLayout);
287     @CriticalNative
nGetDescent(long minikinLayout)288     private static native float nGetDescent(long minikinLayout);
289     @CriticalNative
nGetGlyphId(long minikinLayout, int i)290     private static native int nGetGlyphId(long minikinLayout, int i);
291     @CriticalNative
nGetX(long minikinLayout, int i)292     private static native float nGetX(long minikinLayout, int i);
293     @CriticalNative
nGetY(long minikinLayout, int i)294     private static native float nGetY(long minikinLayout, int i);
295     @CriticalNative
nGetFont(long minikinLayout, int i)296     private static native long nGetFont(long minikinLayout, int i);
297     @CriticalNative
nReleaseFunc()298     private static native long nReleaseFunc();
299     @CriticalNative
nGetFakeBold(long minikinLayout, int i)300     private static native boolean nGetFakeBold(long minikinLayout, int i);
301     @CriticalNative
nGetFakeItalic(long minikinLayout, int i)302     private static native boolean nGetFakeItalic(long minikinLayout, int i);
303     @CriticalNative
nGetWeightOverride(long minikinLayout, int i)304     private static native float nGetWeightOverride(long minikinLayout, int i);
305     @CriticalNative
nGetItalicOverride(long minikinLayout, int i)306     private static native float nGetItalicOverride(long minikinLayout, int i);
307     @CriticalNative
nGetFontCount(long minikinLayout)308     private static native int nGetFontCount(long minikinLayout);
309     @CriticalNative
nGetFontRef(long minikinLayout, int fontId)310     private static native long nGetFontRef(long minikinLayout, int fontId);
311     @CriticalNative
nGetFontId(long minikinLayout, int glyphIndex)312     private static native int nGetFontId(long minikinLayout, int glyphIndex);
313 
314     @Override
equals(Object o)315     public boolean equals(Object o) {
316         if (this == o) return true;
317         if (!(o instanceof PositionedGlyphs)) return false;
318         PositionedGlyphs that = (PositionedGlyphs) o;
319 
320         if (mXOffset != that.mXOffset || mYOffset != that.mYOffset) return false;
321         if (glyphCount() != that.glyphCount()) return false;
322 
323         for (int i = 0; i < glyphCount(); ++i) {
324             if (getGlyphId(i) != that.getGlyphId(i)) return false;
325             if (getGlyphX(i) != that.getGlyphX(i)) return false;
326             if (getGlyphY(i) != that.getGlyphY(i)) return false;
327             if (!getFont(i).equals(that.getFont(i))) return false;
328         }
329 
330         return true;
331     }
332 
333     @Override
hashCode()334     public int hashCode() {
335         int hashCode = Objects.hash(mXOffset, mYOffset);
336         for (int i = 0; i < glyphCount(); ++i) {
337             hashCode = Objects.hash(hashCode,
338                     getGlyphId(i), getGlyphX(i), getGlyphY(i), getFont(i));
339         }
340         return hashCode;
341     }
342 
343     @Override
toString()344     public String toString() {
345         StringBuilder sb = new StringBuilder("[");
346         for (int i = 0; i < glyphCount(); ++i) {
347             if (i != 0) {
348                 sb.append(", ");
349             }
350             sb.append("[ ID = " + getGlyphId(i) + ","
351                     + " pos = (" + getGlyphX(i) + "," + getGlyphY(i) + ")"
352                     + " font = " + getFont(i) + " ]");
353         }
354         sb.append("]");
355         return "PositionedGlyphs{"
356                 + "glyphs = " + sb.toString()
357                 + ", mXOffset=" + mXOffset
358                 + ", mYOffset=" + mYOffset
359                 + '}';
360     }
361 }
362