1 /*
2  * Copyright (C) 2010 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;
18 
19 import com.android.ide.common.rendering.api.LayoutLog;
20 import com.android.layoutlib.bridge.Bridge;
21 import com.android.layoutlib.bridge.impl.DelegateManager;
22 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
23 
24 import android.annotation.NonNull;
25 import android.annotation.Nullable;
26 import android.graphics.FontFamily_Delegate.FontVariant;
27 import android.graphics.Paint.FontMetrics;
28 import android.graphics.Paint.FontMetricsInt;
29 import android.text.TextUtils;
30 
31 import java.awt.BasicStroke;
32 import java.awt.Font;
33 import java.awt.Shape;
34 import java.awt.Stroke;
35 import java.awt.Toolkit;
36 import java.awt.geom.AffineTransform;
37 import java.util.Collections;
38 import java.util.List;
39 import java.util.Locale;
40 import java.util.stream.Collectors;
41 import java.util.stream.StreamSupport;
42 
43 import libcore.util.NativeAllocationRegistry_Delegate;
44 
45 /**
46  * Delegate implementing the native methods of android.graphics.Paint
47  *
48  * Through the layoutlib_create tool, the original native methods of Paint have been replaced
49  * by calls to methods of the same name in this delegate class.
50  *
51  * This class behaves like the original native implementation, but in Java, keeping previously
52  * native data into its own objects and mapping them to int that are sent back and forth between
53  * it and the original Paint class.
54  *
55  * @see DelegateManager
56  *
57  */
58 public class Paint_Delegate {
59     private static final float DEFAULT_TEXT_SIZE = 20.f;
60     private static final float DEFAULT_TEXT_SCALE_X = 1.f;
61     private static final float DEFAULT_TEXT_SKEW_X = 0.f;
62 
63     /**
64      * Class associating a {@link Font} and its {@link java.awt.FontMetrics}.
65      */
66     /*package*/ static final class FontInfo {
67         final Font mFont;
68         final java.awt.FontMetrics mMetrics;
69 
FontInfo(@onNull Font font, @NonNull java.awt.FontMetrics fontMetrics)70         FontInfo(@NonNull Font font, @NonNull java.awt.FontMetrics fontMetrics) {
71             this.mFont = font;
72             this.mMetrics = fontMetrics;
73         }
74     }
75 
76     // ---- delegate manager ----
77     private static final DelegateManager<Paint_Delegate> sManager =
78             new DelegateManager<>(Paint_Delegate.class);
79     private static long sFinalizer = -1;
80 
81     // ---- delegate helper data ----
82 
83     // This list can contain null elements.
84     @Nullable
85     private List<FontInfo> mFonts;
86 
87     // ---- delegate data ----
88     private int mFlags;
89     private int mColor;
90     private int mStyle;
91     private int mCap;
92     private int mJoin;
93     private int mTextAlign;
94     private Typeface_Delegate mTypeface;
95     private float mStrokeWidth;
96     private float mStrokeMiter;
97     private float mTextSize;
98     private float mTextScaleX;
99     private float mTextSkewX;
100     private int mHintingMode = Paint.HINTING_ON;
101     private int mHyphenEdit;
102     private float mLetterSpacing;  // not used in actual text rendering.
103     private float mWordSpacing;  // not used in actual text rendering.
104     // Variant of the font. A paint's variant can only be compact or elegant.
105     private FontVariant mFontVariant = FontVariant.COMPACT;
106 
107     private int mPorterDuffMode = Xfermode.DEFAULT;
108     private ColorFilter_Delegate mColorFilter;
109     private Shader_Delegate mShader;
110     private PathEffect_Delegate mPathEffect;
111     private MaskFilter_Delegate mMaskFilter;
112 
113     @SuppressWarnings("FieldCanBeLocal") // Used to store the locale for future use
114     private Locale mLocale = Locale.getDefault();
115 
116     // ---- Public Helper methods ----
117 
118     @Nullable
getDelegate(long native_paint)119     public static Paint_Delegate getDelegate(long native_paint) {
120         return sManager.getDelegate(native_paint);
121     }
122 
123     /**
124      * Returns the list of {@link Font} objects.
125      */
126     @NonNull
getFonts()127     public List<FontInfo> getFonts() {
128         Typeface_Delegate typeface = mTypeface;
129         if (typeface == null) {
130             if (Typeface.sDefaultTypeface == null) {
131                 return Collections.emptyList();
132             }
133 
134             typeface = Typeface_Delegate.getDelegate(Typeface.sDefaultTypeface.native_instance);
135         }
136 
137         if (mFonts != null) {
138             return mFonts;
139         }
140 
141         // Apply an optional transformation for skew and scale
142         AffineTransform affineTransform = mTextScaleX != 1.0 || mTextSkewX != 0 ?
143                 new AffineTransform(mTextScaleX, mTextSkewX, 0, 1, 0, 0) :
144                 null;
145 
146         List<FontInfo> infoList = StreamSupport.stream(typeface.getFonts(mFontVariant).spliterator
147                 (), false)
148                 .map(font -> getFontInfo(font, mTextSize, affineTransform))
149                 .collect(Collectors.toList());
150         mFonts = Collections.unmodifiableList(infoList);
151 
152         return mFonts;
153     }
154 
isAntiAliased()155     public boolean isAntiAliased() {
156         return (mFlags & Paint.ANTI_ALIAS_FLAG) != 0;
157     }
158 
isFilterBitmap()159     public boolean isFilterBitmap() {
160         return (mFlags & Paint.FILTER_BITMAP_FLAG) != 0;
161     }
162 
getStyle()163     public int getStyle() {
164         return mStyle;
165     }
166 
getColor()167     public int getColor() {
168         return mColor;
169     }
170 
getAlpha()171     public int getAlpha() {
172         return mColor >>> 24;
173     }
174 
setAlpha(int alpha)175     public void setAlpha(int alpha) {
176         mColor = (alpha << 24) | (mColor & 0x00FFFFFF);
177     }
178 
getTextAlign()179     public int getTextAlign() {
180         return mTextAlign;
181     }
182 
getStrokeWidth()183     public float getStrokeWidth() {
184         return mStrokeWidth;
185     }
186 
187     /**
188      * returns the value of stroke miter needed by the java api.
189      */
getJavaStrokeMiter()190     public float getJavaStrokeMiter() {
191         return mStrokeMiter;
192     }
193 
getJavaCap()194     public int getJavaCap() {
195         switch (Paint.sCapArray[mCap]) {
196             case BUTT:
197                 return BasicStroke.CAP_BUTT;
198             case ROUND:
199                 return BasicStroke.CAP_ROUND;
200             default:
201             case SQUARE:
202                 return BasicStroke.CAP_SQUARE;
203         }
204     }
205 
getJavaJoin()206     public int getJavaJoin() {
207         switch (Paint.sJoinArray[mJoin]) {
208             default:
209             case MITER:
210                 return BasicStroke.JOIN_MITER;
211             case ROUND:
212                 return BasicStroke.JOIN_ROUND;
213             case BEVEL:
214                 return BasicStroke.JOIN_BEVEL;
215         }
216     }
217 
getJavaStroke()218     public Stroke getJavaStroke() {
219         if (mPathEffect != null) {
220             if (mPathEffect.isSupported()) {
221                 Stroke stroke = mPathEffect.getStroke(this);
222                 assert stroke != null;
223                 //noinspection ConstantConditions
224                 if (stroke != null) {
225                     return stroke;
226                 }
227             } else {
228                 Bridge.getLog().fidelityWarning(LayoutLog.TAG_PATHEFFECT,
229                         mPathEffect.getSupportMessage(),
230                         null, null /*data*/);
231             }
232         }
233 
234         // if no custom stroke as been set, set the default one.
235         return new BasicStroke(
236                     getStrokeWidth(),
237                     getJavaCap(),
238                     getJavaJoin(),
239                     getJavaStrokeMiter());
240     }
241 
242     /**
243      * Returns the {@link PorterDuff.Mode} as an int
244      */
getPorterDuffMode()245     public int getPorterDuffMode() {
246         return mPorterDuffMode;
247     }
248 
249     /**
250      * Returns the {@link ColorFilter} delegate or null if none have been set
251      *
252      * @return the delegate or null.
253      */
getColorFilter()254     public ColorFilter_Delegate getColorFilter() {
255         return mColorFilter;
256     }
257 
setColorFilter(long colorFilterPtr)258     public void setColorFilter(long colorFilterPtr) {
259         mColorFilter = ColorFilter_Delegate.getDelegate(colorFilterPtr);
260     }
261 
setShader(long shaderPtr)262     public void setShader(long shaderPtr) {
263         mShader = Shader_Delegate.getDelegate(shaderPtr);
264     }
265 
266     /**
267      * Returns the {@link Shader} delegate or null if none have been set
268      *
269      * @return the delegate or null.
270      */
getShader()271     public Shader_Delegate getShader() {
272         return mShader;
273     }
274 
275     /**
276      * Returns the {@link MaskFilter} delegate or null if none have been set
277      *
278      * @return the delegate or null.
279      */
getMaskFilter()280     public MaskFilter_Delegate getMaskFilter() {
281         return mMaskFilter;
282     }
283 
284     // ---- native methods ----
285 
286     @LayoutlibDelegate
nGetFlags(long nativePaint)287     /*package*/ static int nGetFlags(long nativePaint) {
288         // get the delegate from the native int.
289         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
290         if (delegate == null) {
291             return 0;
292         }
293 
294         return delegate.mFlags;
295     }
296 
297 
298 
299     @LayoutlibDelegate
nSetFlags(long nativePaint, int flags)300     /*package*/ static void nSetFlags(long nativePaint, int flags) {
301         // get the delegate from the native int.
302         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
303         if (delegate == null) {
304             return;
305         }
306 
307         delegate.mFlags = flags;
308     }
309 
310     @LayoutlibDelegate
nSetFilterBitmap(long nativePaint, boolean filter)311     /*package*/ static void nSetFilterBitmap(long nativePaint, boolean filter) {
312         setFlag(nativePaint, Paint.FILTER_BITMAP_FLAG, filter);
313     }
314 
315     @LayoutlibDelegate
nGetHinting(long nativePaint)316     /*package*/ static int nGetHinting(long nativePaint) {
317         // get the delegate from the native int.
318         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
319         if (delegate == null) {
320             return Paint.HINTING_ON;
321         }
322 
323         return delegate.mHintingMode;
324     }
325 
326     @LayoutlibDelegate
nSetHinting(long nativePaint, int mode)327     /*package*/ static void nSetHinting(long nativePaint, int mode) {
328         // get the delegate from the native int.
329         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
330         if (delegate == null) {
331             return;
332         }
333 
334         delegate.mHintingMode = mode;
335     }
336 
337     @LayoutlibDelegate
nSetAntiAlias(long nativePaint, boolean aa)338     /*package*/ static void nSetAntiAlias(long nativePaint, boolean aa) {
339         setFlag(nativePaint, Paint.ANTI_ALIAS_FLAG, aa);
340     }
341 
342     @LayoutlibDelegate
nSetSubpixelText(long nativePaint, boolean subpixelText)343     /*package*/ static void nSetSubpixelText(long nativePaint,
344             boolean subpixelText) {
345         setFlag(nativePaint, Paint.SUBPIXEL_TEXT_FLAG, subpixelText);
346     }
347 
348     @LayoutlibDelegate
nSetUnderlineText(long nativePaint, boolean underlineText)349     /*package*/ static void nSetUnderlineText(long nativePaint,
350             boolean underlineText) {
351         setFlag(nativePaint, Paint.UNDERLINE_TEXT_FLAG, underlineText);
352     }
353 
354     @LayoutlibDelegate
nSetStrikeThruText(long nativePaint, boolean strikeThruText)355     /*package*/ static void nSetStrikeThruText(long nativePaint,
356             boolean strikeThruText) {
357         setFlag(nativePaint, Paint.STRIKE_THRU_TEXT_FLAG, strikeThruText);
358     }
359 
360     @LayoutlibDelegate
nSetFakeBoldText(long nativePaint, boolean fakeBoldText)361     /*package*/ static void nSetFakeBoldText(long nativePaint,
362             boolean fakeBoldText) {
363         setFlag(nativePaint, Paint.FAKE_BOLD_TEXT_FLAG, fakeBoldText);
364     }
365 
366     @LayoutlibDelegate
nSetDither(long nativePaint, boolean dither)367     /*package*/ static void nSetDither(long nativePaint, boolean dither) {
368         setFlag(nativePaint, Paint.DITHER_FLAG, dither);
369     }
370 
371     @LayoutlibDelegate
nSetLinearText(long nativePaint, boolean linearText)372     /*package*/ static void nSetLinearText(long nativePaint, boolean linearText) {
373         setFlag(nativePaint, Paint.LINEAR_TEXT_FLAG, linearText);
374     }
375 
376     @LayoutlibDelegate
nGetColor(long nativePaint)377     /*package*/ static int nGetColor(long nativePaint) {
378         // get the delegate from the native int.
379         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
380         if (delegate == null) {
381             return 0;
382         }
383 
384         return delegate.mColor;
385     }
386 
387     @LayoutlibDelegate
nSetColor(long nativePaint, int color)388     /*package*/ static void nSetColor(long nativePaint, int color) {
389         // get the delegate from the native int.
390         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
391         if (delegate == null) {
392             return;
393         }
394 
395         delegate.mColor = color;
396     }
397 
398     @LayoutlibDelegate
nGetAlpha(long nativePaint)399     /*package*/ static int nGetAlpha(long nativePaint) {
400         // get the delegate from the native int.
401         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
402         if (delegate == null) {
403             return 0;
404         }
405 
406         return delegate.getAlpha();
407     }
408 
409     @LayoutlibDelegate
nSetAlpha(long nativePaint, int a)410     /*package*/ static void nSetAlpha(long nativePaint, int a) {
411         // get the delegate from the native int.
412         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
413         if (delegate == null) {
414             return;
415         }
416 
417         delegate.setAlpha(a);
418     }
419 
420     @LayoutlibDelegate
nGetStrokeWidth(long nativePaint)421     /*package*/ static float nGetStrokeWidth(long nativePaint) {
422         // get the delegate from the native int.
423         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
424         if (delegate == null) {
425             return 1.f;
426         }
427 
428         return delegate.mStrokeWidth;
429     }
430 
431     @LayoutlibDelegate
nSetStrokeWidth(long nativePaint, float width)432     /*package*/ static void nSetStrokeWidth(long nativePaint, float width) {
433         // get the delegate from the native int.
434         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
435         if (delegate == null) {
436             return;
437         }
438 
439         delegate.mStrokeWidth = width;
440     }
441 
442     @LayoutlibDelegate
nGetStrokeMiter(long nativePaint)443     /*package*/ static float nGetStrokeMiter(long nativePaint) {
444         // get the delegate from the native int.
445         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
446         if (delegate == null) {
447             return 1.f;
448         }
449 
450         return delegate.mStrokeMiter;
451     }
452 
453     @LayoutlibDelegate
nSetStrokeMiter(long nativePaint, float miter)454     /*package*/ static void nSetStrokeMiter(long nativePaint, float miter) {
455         // get the delegate from the native int.
456         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
457         if (delegate == null) {
458             return;
459         }
460 
461         delegate.mStrokeMiter = miter;
462     }
463 
464     @LayoutlibDelegate
nSetShadowLayer(long paint, float radius, float dx, float dy, int color)465     /*package*/ static void nSetShadowLayer(long paint, float radius, float dx, float dy,
466             int color) {
467         // FIXME
468         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
469                 "Paint.setShadowLayer is not supported.", null, null /*data*/);
470     }
471 
472     @LayoutlibDelegate
nHasShadowLayer(long paint)473     /*package*/ static boolean nHasShadowLayer(long paint) {
474         // FIXME
475         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
476                 "Paint.hasShadowLayer is not supported.", null, null /*data*/);
477         return false;
478     }
479 
480     @LayoutlibDelegate
nIsElegantTextHeight(long nativePaint)481     /*package*/ static boolean nIsElegantTextHeight(long nativePaint) {
482         // get the delegate from the native int.
483         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
484         return delegate != null && delegate.mFontVariant == FontVariant.ELEGANT;
485     }
486 
487     @LayoutlibDelegate
nSetElegantTextHeight(long nativePaint, boolean elegant)488     /*package*/ static void nSetElegantTextHeight(long nativePaint,
489             boolean elegant) {
490         // get the delegate from the native int.
491         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
492         if (delegate == null) {
493             return;
494         }
495 
496         delegate.mFontVariant = elegant ? FontVariant.ELEGANT : FontVariant.COMPACT;
497     }
498 
499     @LayoutlibDelegate
nGetTextSize(long nativePaint)500     /*package*/ static float nGetTextSize(long nativePaint) {
501         // get the delegate from the native int.
502         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
503         if (delegate == null) {
504             return 1.f;
505         }
506 
507         return delegate.mTextSize;
508     }
509 
510     @LayoutlibDelegate
nSetTextSize(long nativePaint, float textSize)511     /*package*/ static void nSetTextSize(long nativePaint, float textSize) {
512         // get the delegate from the native int.
513         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
514         if (delegate == null) {
515             return;
516         }
517 
518         if (delegate.mTextSize != textSize) {
519             delegate.mTextSize = textSize;
520             delegate.invalidateFonts();
521         }
522     }
523 
524     @LayoutlibDelegate
nGetTextScaleX(long nativePaint)525     /*package*/ static float nGetTextScaleX(long nativePaint) {
526         // get the delegate from the native int.
527         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
528         if (delegate == null) {
529             return 1.f;
530         }
531 
532         return delegate.mTextScaleX;
533     }
534 
535     @LayoutlibDelegate
nSetTextScaleX(long nativePaint, float scaleX)536     /*package*/ static void nSetTextScaleX(long nativePaint, float scaleX) {
537         // get the delegate from the native int.
538         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
539         if (delegate == null) {
540             return;
541         }
542 
543         if (delegate.mTextScaleX != scaleX) {
544             delegate.mTextScaleX = scaleX;
545             delegate.invalidateFonts();
546         }
547     }
548 
549     @LayoutlibDelegate
nGetTextSkewX(long nativePaint)550     /*package*/ static float nGetTextSkewX(long nativePaint) {
551         // get the delegate from the native int.
552         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
553         if (delegate == null) {
554             return 1.f;
555         }
556 
557         return delegate.mTextSkewX;
558     }
559 
560     @LayoutlibDelegate
nSetTextSkewX(long nativePaint, float skewX)561     /*package*/ static void nSetTextSkewX(long nativePaint, float skewX) {
562         // get the delegate from the native int.
563         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
564         if (delegate == null) {
565             return;
566         }
567 
568         if (delegate.mTextSkewX != skewX) {
569             delegate.mTextSkewX = skewX;
570             delegate.invalidateFonts();
571         }
572     }
573 
574     @LayoutlibDelegate
nAscent(long nativePaint)575     /*package*/ static float nAscent(long nativePaint) {
576         // get the delegate
577         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
578         if (delegate == null) {
579             return 0;
580         }
581 
582         List<FontInfo> fonts = delegate.getFonts();
583         if (fonts.size() > 0) {
584             java.awt.FontMetrics javaMetrics = fonts.get(0).mMetrics;
585             // Android expects negative ascent so we invert the value from Java.
586             return - javaMetrics.getAscent();
587         }
588 
589         return 0;
590     }
591 
592     @LayoutlibDelegate
nDescent(long nativePaint)593     /*package*/ static float nDescent(long nativePaint) {
594         // get the delegate
595         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
596         if (delegate == null) {
597             return 0;
598         }
599 
600         List<FontInfo> fonts = delegate.getFonts();
601         if (fonts.size() > 0) {
602             java.awt.FontMetrics javaMetrics = fonts.get(0).mMetrics;
603             return javaMetrics.getDescent();
604         }
605 
606         return 0;
607 
608     }
609 
610     @LayoutlibDelegate
nGetFontMetrics(long nativePaint, FontMetrics metrics)611     /*package*/ static float nGetFontMetrics(long nativePaint,
612             FontMetrics metrics) {
613         // get the delegate
614         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
615         if (delegate == null) {
616             return 0;
617         }
618 
619         return delegate.getFontMetrics(metrics);
620     }
621 
622     @LayoutlibDelegate
nGetFontMetricsInt(long nativePaint, FontMetricsInt fmi)623     /*package*/ static int nGetFontMetricsInt(long nativePaint, FontMetricsInt fmi) {
624         // get the delegate
625         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
626         if (delegate == null) {
627             return 0;
628         }
629 
630         List<FontInfo> fonts = delegate.getFonts();
631         if (fonts.size() > 0) {
632             java.awt.FontMetrics javaMetrics = fonts.get(0).mMetrics;
633             if (fmi != null) {
634                 // Android expects negative ascent so we invert the value from Java.
635                 fmi.top = (int)(- javaMetrics.getMaxAscent() * 1.15);
636                 fmi.ascent = - javaMetrics.getAscent();
637                 fmi.descent = javaMetrics.getDescent();
638                 fmi.bottom = (int)(javaMetrics.getMaxDescent() * 1.15);
639                 fmi.leading = javaMetrics.getLeading();
640             }
641 
642             return javaMetrics.getHeight();
643         }
644 
645         return 0;
646     }
647 
648     @LayoutlibDelegate
nBreakText(long nativePaint, char[] text, int index, int count, float maxWidth, int bidiFlags, float[] measuredWidth)649     /*package*/ static int nBreakText(long nativePaint, char[] text,
650             int index, int count, float maxWidth, int bidiFlags, float[] measuredWidth) {
651 
652         // get the delegate
653         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
654         if (delegate == null) {
655             return 0;
656         }
657 
658         int inc = count > 0 ? 1 : -1;
659 
660         int measureIndex = 0;
661         for (int i = index; i != index + count; i += inc, measureIndex++) {
662             int start, end;
663             if (i < index) {
664                 start = i;
665                 end = index;
666             } else {
667                 start = index;
668                 end = i;
669             }
670 
671             // measure from start to end
672             RectF bounds = delegate.measureText(text, start, end - start + 1, null, 0, bidiFlags);
673             float res = bounds.right - bounds.left;
674 
675             if (measuredWidth != null) {
676                 measuredWidth[measureIndex] = res;
677             }
678 
679             if (res > maxWidth) {
680                 // we should not return this char index, but since it's 0-based
681                 // and we need to return a count, we simply return measureIndex;
682                 return measureIndex;
683             }
684 
685         }
686 
687         return measureIndex;
688     }
689 
690     @LayoutlibDelegate
nBreakText(long nativePaint, String text, boolean measureForwards, float maxWidth, int bidiFlags, float[] measuredWidth)691     /*package*/ static int nBreakText(long nativePaint, String text, boolean measureForwards,
692             float maxWidth, int bidiFlags, float[] measuredWidth) {
693         return nBreakText(nativePaint, text.toCharArray(), 0, text.length(),
694                 maxWidth, bidiFlags, measuredWidth);
695     }
696 
697     @LayoutlibDelegate
nInit()698     /*package*/ static long nInit() {
699         Paint_Delegate newDelegate = new Paint_Delegate();
700         return sManager.addNewDelegate(newDelegate);
701     }
702 
703     @LayoutlibDelegate
nInitWithPaint(long paint)704     /*package*/ static long nInitWithPaint(long paint) {
705         // get the delegate from the native int.
706         Paint_Delegate delegate = sManager.getDelegate(paint);
707         if (delegate == null) {
708             return 0;
709         }
710 
711         Paint_Delegate newDelegate = new Paint_Delegate(delegate);
712         return sManager.addNewDelegate(newDelegate);
713     }
714 
715     @LayoutlibDelegate
nReset(long native_object)716     /*package*/ static void nReset(long native_object) {
717         // get the delegate from the native int.
718         Paint_Delegate delegate = sManager.getDelegate(native_object);
719         if (delegate == null) {
720             return;
721         }
722 
723         delegate.reset();
724     }
725 
726     @LayoutlibDelegate
nSet(long native_dst, long native_src)727     /*package*/ static void nSet(long native_dst, long native_src) {
728         // get the delegate from the native int.
729         Paint_Delegate delegate_dst = sManager.getDelegate(native_dst);
730         if (delegate_dst == null) {
731             return;
732         }
733 
734         // get the delegate from the native int.
735         Paint_Delegate delegate_src = sManager.getDelegate(native_src);
736         if (delegate_src == null) {
737             return;
738         }
739 
740         delegate_dst.set(delegate_src);
741     }
742 
743     @LayoutlibDelegate
nGetStyle(long native_object)744     /*package*/ static int nGetStyle(long native_object) {
745         // get the delegate from the native int.
746         Paint_Delegate delegate = sManager.getDelegate(native_object);
747         if (delegate == null) {
748             return 0;
749         }
750 
751         return delegate.mStyle;
752     }
753 
754     @LayoutlibDelegate
nSetStyle(long native_object, int style)755     /*package*/ static void nSetStyle(long native_object, int style) {
756         // get the delegate from the native int.
757         Paint_Delegate delegate = sManager.getDelegate(native_object);
758         if (delegate == null) {
759             return;
760         }
761 
762         delegate.mStyle = style;
763     }
764 
765     @LayoutlibDelegate
nGetStrokeCap(long native_object)766     /*package*/ static int nGetStrokeCap(long native_object) {
767         // get the delegate from the native int.
768         Paint_Delegate delegate = sManager.getDelegate(native_object);
769         if (delegate == null) {
770             return 0;
771         }
772 
773         return delegate.mCap;
774     }
775 
776     @LayoutlibDelegate
nSetStrokeCap(long native_object, int cap)777     /*package*/ static void nSetStrokeCap(long native_object, int cap) {
778         // get the delegate from the native int.
779         Paint_Delegate delegate = sManager.getDelegate(native_object);
780         if (delegate == null) {
781             return;
782         }
783 
784         delegate.mCap = cap;
785     }
786 
787     @LayoutlibDelegate
nGetStrokeJoin(long native_object)788     /*package*/ static int nGetStrokeJoin(long native_object) {
789         // get the delegate from the native int.
790         Paint_Delegate delegate = sManager.getDelegate(native_object);
791         if (delegate == null) {
792             return 0;
793         }
794 
795         return delegate.mJoin;
796     }
797 
798     @LayoutlibDelegate
nSetStrokeJoin(long native_object, int join)799     /*package*/ static void nSetStrokeJoin(long native_object, int join) {
800         // get the delegate from the native int.
801         Paint_Delegate delegate = sManager.getDelegate(native_object);
802         if (delegate == null) {
803             return;
804         }
805 
806         delegate.mJoin = join;
807     }
808 
809     @LayoutlibDelegate
nGetFillPath(long native_object, long src, long dst)810     /*package*/ static boolean nGetFillPath(long native_object, long src, long dst) {
811         Paint_Delegate paint = sManager.getDelegate(native_object);
812         if (paint == null) {
813             return false;
814         }
815 
816         Path_Delegate srcPath = Path_Delegate.getDelegate(src);
817         if (srcPath == null) {
818             return true;
819         }
820 
821         Path_Delegate dstPath = Path_Delegate.getDelegate(dst);
822         if (dstPath == null) {
823             return true;
824         }
825 
826         Stroke stroke = paint.getJavaStroke();
827         Shape strokeShape = stroke.createStrokedShape(srcPath.getJavaShape());
828 
829         dstPath.setJavaShape(strokeShape);
830 
831         // FIXME figure out the return value?
832         return true;
833     }
834 
835     @LayoutlibDelegate
nSetShader(long native_object, long shader)836     /*package*/ static long nSetShader(long native_object, long shader) {
837         // get the delegate from the native int.
838         Paint_Delegate delegate = sManager.getDelegate(native_object);
839         if (delegate == null) {
840             return shader;
841         }
842 
843         delegate.mShader = Shader_Delegate.getDelegate(shader);
844 
845         return shader;
846     }
847 
848     @LayoutlibDelegate
nSetColorFilter(long native_object, long filter)849     /*package*/ static long nSetColorFilter(long native_object, long filter) {
850         // get the delegate from the native int.
851         Paint_Delegate delegate = sManager.getDelegate(native_object);
852         if (delegate == null) {
853             return filter;
854         }
855 
856         delegate.mColorFilter = ColorFilter_Delegate.getDelegate(filter);
857 
858         // Log warning if it's not supported.
859         if (delegate.mColorFilter != null && !delegate.mColorFilter.isSupported()) {
860             Bridge.getLog().fidelityWarning(LayoutLog.TAG_COLORFILTER,
861                     delegate.mColorFilter.getSupportMessage(), null, null /*data*/);
862         }
863 
864         return filter;
865     }
866 
867     @LayoutlibDelegate
nSetXfermode(long native_object, int xfermode)868     /*package*/ static void nSetXfermode(long native_object, int xfermode) {
869         Paint_Delegate delegate = sManager.getDelegate(native_object);
870         if (delegate == null) {
871             return;
872         }
873         delegate.mPorterDuffMode = xfermode;
874     }
875 
876     @LayoutlibDelegate
nSetPathEffect(long native_object, long effect)877     /*package*/ static long nSetPathEffect(long native_object, long effect) {
878         // get the delegate from the native int.
879         Paint_Delegate delegate = sManager.getDelegate(native_object);
880         if (delegate == null) {
881             return effect;
882         }
883 
884         delegate.mPathEffect = PathEffect_Delegate.getDelegate(effect);
885 
886         return effect;
887     }
888 
889     @LayoutlibDelegate
nSetMaskFilter(long native_object, long maskfilter)890     /*package*/ static long nSetMaskFilter(long native_object, long maskfilter) {
891         // get the delegate from the native int.
892         Paint_Delegate delegate = sManager.getDelegate(native_object);
893         if (delegate == null) {
894             return maskfilter;
895         }
896 
897         delegate.mMaskFilter = MaskFilter_Delegate.getDelegate(maskfilter);
898 
899         // since none of those are supported, display a fidelity warning right away
900         if (delegate.mMaskFilter != null && !delegate.mMaskFilter.isSupported()) {
901             Bridge.getLog().fidelityWarning(LayoutLog.TAG_MASKFILTER,
902                     delegate.mMaskFilter.getSupportMessage(), null, null /*data*/);
903         }
904 
905         return maskfilter;
906     }
907 
908     @LayoutlibDelegate
nSetTypeface(long native_object, long typeface)909     /*package*/ static void nSetTypeface(long native_object, long typeface) {
910         // get the delegate from the native int.
911         Paint_Delegate delegate = sManager.getDelegate(native_object);
912         if (delegate == null) {
913             return;
914         }
915 
916         Typeface_Delegate typefaceDelegate = Typeface_Delegate.getDelegate(typeface);
917         if (delegate.mTypeface != typefaceDelegate) {
918             delegate.mTypeface = typefaceDelegate;
919             delegate.invalidateFonts();
920         }
921     }
922 
923     @LayoutlibDelegate
nGetTextAlign(long native_object)924     /*package*/ static int nGetTextAlign(long native_object) {
925         // get the delegate from the native int.
926         Paint_Delegate delegate = sManager.getDelegate(native_object);
927         if (delegate == null) {
928             return 0;
929         }
930 
931         return delegate.mTextAlign;
932     }
933 
934     @LayoutlibDelegate
nSetTextAlign(long native_object, int align)935     /*package*/ static void nSetTextAlign(long native_object, int align) {
936         // get the delegate from the native int.
937         Paint_Delegate delegate = sManager.getDelegate(native_object);
938         if (delegate == null) {
939             return;
940         }
941 
942         delegate.mTextAlign = align;
943     }
944 
945     @LayoutlibDelegate
nSetTextLocales(long native_object, String locale)946     /*package*/ static int nSetTextLocales(long native_object, String locale) {
947         // get the delegate from the native int.
948         Paint_Delegate delegate = sManager.getDelegate(native_object);
949         if (delegate == null) {
950             return 0;
951         }
952 
953         delegate.setTextLocale(locale);
954         return 0;
955     }
956 
957     @LayoutlibDelegate
nSetTextLocalesByMinikinLocaleListId(long paintPtr, int mMinikinLangListId)958     /*package*/ static void nSetTextLocalesByMinikinLocaleListId(long paintPtr,
959             int mMinikinLangListId) {
960         // FIXME
961     }
962 
963     @LayoutlibDelegate
nGetTextAdvances(long native_object, char[] text, int index, int count, int contextIndex, int contextCount, int bidiFlags, float[] advances, int advancesIndex)964     /*package*/ static float nGetTextAdvances(long native_object, char[] text, int index,
965             int count, int contextIndex, int contextCount,
966             int bidiFlags, float[] advances, int advancesIndex) {
967 
968         if (advances != null)
969             for (int i = advancesIndex; i< advancesIndex+count; i++)
970                 advances[i]=0;
971         // get the delegate from the native int.
972         Paint_Delegate delegate = sManager.getDelegate(native_object);
973         if (delegate == null) {
974             return 0.f;
975         }
976 
977         RectF bounds = delegate.measureText(text, index, count, advances, advancesIndex, bidiFlags);
978         return bounds.right - bounds.left;
979     }
980 
981     @LayoutlibDelegate
nGetTextAdvances(long native_object, String text, int start, int end, int contextStart, int contextEnd, int bidiFlags, float[] advances, int advancesIndex)982     /*package*/ static float nGetTextAdvances(long native_object, String text, int start, int end,
983             int contextStart, int contextEnd, int bidiFlags, float[] advances, int advancesIndex) {
984         // FIXME: support contextStart and contextEnd
985         int count = end - start;
986         char[] buffer = TemporaryBuffer.obtain(count);
987         TextUtils.getChars(text, start, end, buffer, 0);
988 
989         return nGetTextAdvances(native_object, buffer, 0, count,
990                 contextStart, contextEnd - contextStart, bidiFlags, advances, advancesIndex);
991     }
992 
993     @LayoutlibDelegate
nGetTextRunCursor(Paint paint, long native_object, char[] text, int contextStart, int contextLength, int flags, int offset, int cursorOpt)994     /*package*/ static int nGetTextRunCursor(Paint paint, long native_object, char[] text,
995             int contextStart, int contextLength, int flags, int offset, int cursorOpt) {
996         // FIXME
997         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
998                 "Paint.getTextRunCursor is not supported.", null, null /*data*/);
999         return 0;
1000     }
1001 
1002     @LayoutlibDelegate
nGetTextRunCursor(Paint paint, long native_object, String text, int contextStart, int contextEnd, int flags, int offset, int cursorOpt)1003     /*package*/ static int nGetTextRunCursor(Paint paint, long native_object, String text,
1004             int contextStart, int contextEnd, int flags, int offset, int cursorOpt) {
1005         // FIXME
1006         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
1007                 "Paint.getTextRunCursor is not supported.", null, null /*data*/);
1008         return 0;
1009     }
1010 
1011     @LayoutlibDelegate
nGetTextPath(long native_object, int bidiFlags, char[] text, int index, int count, float x, float y, long path)1012     /*package*/ static void nGetTextPath(long native_object, int bidiFlags, char[] text,
1013             int index, int count, float x, float y, long path) {
1014         // FIXME
1015         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
1016                 "Paint.getTextPath is not supported.", null, null /*data*/);
1017     }
1018 
1019     @LayoutlibDelegate
nGetTextPath(long native_object, int bidiFlags, String text, int start, int end, float x, float y, long path)1020     /*package*/ static void nGetTextPath(long native_object, int bidiFlags, String text, int start,
1021             int end, float x, float y, long path) {
1022         // FIXME
1023         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
1024                 "Paint.getTextPath is not supported.", null, null /*data*/);
1025     }
1026 
1027     @LayoutlibDelegate
nGetStringBounds(long nativePaint, String text, int start, int end, int bidiFlags, Rect bounds)1028     /*package*/ static void nGetStringBounds(long nativePaint, String text, int start, int end,
1029             int bidiFlags, Rect bounds) {
1030         nGetCharArrayBounds(nativePaint, text.toCharArray(), start,
1031                 end - start, bidiFlags, bounds);
1032     }
1033 
1034     @LayoutlibDelegate
nGetCharArrayBounds(long nativePaint, char[] text, int index, int count, int bidiFlags, Rect bounds)1035     /*package*/ static void nGetCharArrayBounds(long nativePaint, char[] text, int index,
1036             int count, int bidiFlags, Rect bounds) {
1037 
1038         // get the delegate from the native int.
1039         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
1040         if (delegate == null) {
1041             return;
1042         }
1043 
1044         delegate.measureText(text, index, count, null, 0, bidiFlags).roundOut(bounds);
1045     }
1046 
1047     @LayoutlibDelegate
nGetNativeFinalizer()1048     /*package*/ static long nGetNativeFinalizer() {
1049         synchronized (Paint_Delegate.class) {
1050             if (sFinalizer == -1) {
1051                 sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
1052                         sManager::removeJavaReferenceFor);
1053             }
1054         }
1055         return sFinalizer;
1056     }
1057 
1058     @LayoutlibDelegate
nGetLetterSpacing(long nativePaint)1059     /*package*/ static float nGetLetterSpacing(long nativePaint) {
1060         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
1061         if (delegate == null) {
1062             return 0;
1063         }
1064         return delegate.mLetterSpacing;
1065     }
1066 
1067     @LayoutlibDelegate
nSetLetterSpacing(long nativePaint, float letterSpacing)1068     /*package*/ static void nSetLetterSpacing(long nativePaint, float letterSpacing) {
1069         Bridge.getLog().fidelityWarning(LayoutLog.TAG_TEXT_RENDERING,
1070                 "Paint.setLetterSpacing() not supported.", null, null);
1071         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
1072         if (delegate == null) {
1073             return;
1074         }
1075         delegate.mLetterSpacing = letterSpacing;
1076     }
1077 
1078     @LayoutlibDelegate
nGetWordSpacing(long nativePaint)1079     /*package*/ static float nGetWordSpacing(long nativePaint) {
1080         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
1081         if (delegate == null) {
1082             return 0;
1083         }
1084         return delegate.mWordSpacing;
1085     }
1086 
1087     @LayoutlibDelegate
nSetWordSpacing(long nativePaint, float wordSpacing)1088     /*package*/ static void nSetWordSpacing(long nativePaint, float wordSpacing) {
1089         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
1090         if (delegate == null) {
1091             return;
1092         }
1093         delegate.mWordSpacing = wordSpacing;
1094     }
1095 
1096     @LayoutlibDelegate
nSetFontFeatureSettings(long nativePaint, String settings)1097     /*package*/ static void nSetFontFeatureSettings(long nativePaint, String settings) {
1098         Bridge.getLog().fidelityWarning(LayoutLog.TAG_TEXT_RENDERING,
1099                 "Paint.setFontFeatureSettings() not supported.", null, null);
1100     }
1101 
1102     @LayoutlibDelegate
nGetHyphenEdit(long nativePaint)1103     /*package*/ static int nGetHyphenEdit(long nativePaint) {
1104         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
1105         if (delegate == null) {
1106             return 0;
1107         }
1108         return delegate.mHyphenEdit;
1109     }
1110 
1111     @LayoutlibDelegate
nSetHyphenEdit(long nativePaint, int hyphen)1112     /*package*/ static void nSetHyphenEdit(long nativePaint, int hyphen) {
1113         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
1114         if (delegate == null) {
1115             return;
1116         }
1117         delegate.mHyphenEdit = hyphen;
1118     }
1119 
1120     @LayoutlibDelegate
nHasGlyph(long nativePaint, int bidiFlags, String string)1121     /*package*/ static boolean nHasGlyph(long nativePaint, int bidiFlags, String string) {
1122         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
1123         if (delegate == null) {
1124             return false;
1125         }
1126         if (string.length() == 0) {
1127             return false;
1128         }
1129         if (string.length() > 1) {
1130             Bridge.getLog().fidelityWarning(LayoutLog.TAG_TEXT_RENDERING,
1131                     "Paint.hasGlyph() is not supported for ligatures.", null, null);
1132             return false;
1133         }
1134 
1135         char c = string.charAt(0);
1136         for (Font font : delegate.mTypeface.getFonts(delegate.mFontVariant)) {
1137             if (font.canDisplay(c)) {
1138                 return true;
1139             }
1140         }
1141         return false;
1142     }
1143 
1144 
1145     @LayoutlibDelegate
nGetRunAdvance(long nativePaint, @NonNull char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset)1146     /*package*/ static float nGetRunAdvance(long nativePaint, @NonNull char[] text, int start,
1147             int end, int contextStart, int contextEnd,
1148             boolean isRtl, int offset) {
1149         int count = end - start;
1150         float[] advances = new float[count];
1151         int bidiFlags = isRtl ? Paint.BIDI_FORCE_RTL : Paint.BIDI_FORCE_LTR;
1152         nGetTextAdvances(nativePaint, text, start, count, contextStart,
1153                 contextEnd - contextStart, bidiFlags, advances, 0);
1154         int startOffset = offset - start;  // offset from start.
1155         float sum = 0;
1156         for (int i = 0; i < startOffset; i++) {
1157             sum += advances[i];
1158         }
1159         return sum;
1160     }
1161 
1162     @LayoutlibDelegate
nGetOffsetForAdvance(long nativePaint, char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, float advance)1163     /*package*/ static int nGetOffsetForAdvance(long nativePaint, char[] text, int start,
1164             int end, int contextStart, int contextEnd, boolean isRtl, float advance) {
1165         int count = end - start;
1166         float[] advances = new float[count];
1167         int bidiFlags = isRtl ? Paint.BIDI_FORCE_RTL : Paint.BIDI_FORCE_LTR;
1168         nGetTextAdvances(nativePaint, text, start, count, contextStart,
1169                 contextEnd - contextStart, bidiFlags, advances, 0);
1170         float sum = 0;
1171         int i;
1172         for (i = 0; i < count && sum < advance; i++) {
1173             sum += advances[i];
1174         }
1175         float distanceToI = sum - advance;
1176         float distanceToIMinus1 = advance - (sum - advances[i]);
1177         return distanceToI > distanceToIMinus1 ? i : i - 1;
1178     }
1179 
1180     @LayoutlibDelegate
nGetUnderlinePosition(long paintPtr)1181     /*package*/ static float nGetUnderlinePosition(long paintPtr) {
1182         return (1.0f / 9.0f) * nGetTextSize(paintPtr);
1183     }
1184 
1185     @LayoutlibDelegate
nGetUnderlineThickness(long paintPtr)1186     /*package*/ static float nGetUnderlineThickness(long paintPtr) {
1187         return (1.0f / 18.0f) * nGetTextSize(paintPtr);
1188     }
1189 
1190     @LayoutlibDelegate
nGetStrikeThruPosition(long paintPtr)1191     /*package*/ static float nGetStrikeThruPosition(long paintPtr) {
1192         return (-79.0f / 252.0f) * nGetTextSize(paintPtr);
1193     }
1194 
1195     @LayoutlibDelegate
nGetStrikeThruThickness(long paintPtr)1196     /*package*/ static float nGetStrikeThruThickness(long paintPtr) {
1197         return (1.0f / 18.0f) * nGetTextSize(paintPtr);
1198     }
1199 
1200     @LayoutlibDelegate
nEqualsForTextMeasurement(long leftPaintPtr, long rightPaintPtr)1201     /*package*/ static boolean nEqualsForTextMeasurement(long leftPaintPtr, long rightPaintPtr) {
1202         return leftPaintPtr == rightPaintPtr;
1203     }
1204 
1205     // ---- Private delegate/helper methods ----
1206 
Paint_Delegate()1207     /*package*/ Paint_Delegate() {
1208         reset();
1209     }
1210 
Paint_Delegate(Paint_Delegate paint)1211     private Paint_Delegate(Paint_Delegate paint) {
1212         set(paint);
1213     }
1214 
set(Paint_Delegate paint)1215     private void set(Paint_Delegate paint) {
1216         mFlags = paint.mFlags;
1217         mColor = paint.mColor;
1218         mStyle = paint.mStyle;
1219         mCap = paint.mCap;
1220         mJoin = paint.mJoin;
1221         mTextAlign = paint.mTextAlign;
1222 
1223         if (mTypeface != paint.mTypeface) {
1224             mTypeface = paint.mTypeface;
1225             invalidateFonts();
1226         }
1227 
1228         if (mTextSize != paint.mTextSize) {
1229             mTextSize = paint.mTextSize;
1230             invalidateFonts();
1231         }
1232 
1233         if (mTextScaleX != paint.mTextScaleX) {
1234             mTextScaleX = paint.mTextScaleX;
1235             invalidateFonts();
1236         }
1237 
1238         if (mTextSkewX != paint.mTextSkewX) {
1239             mTextSkewX = paint.mTextSkewX;
1240             invalidateFonts();
1241         }
1242 
1243         mStrokeWidth = paint.mStrokeWidth;
1244         mStrokeMiter = paint.mStrokeMiter;
1245         mPorterDuffMode = paint.mPorterDuffMode;
1246         mColorFilter = paint.mColorFilter;
1247         mShader = paint.mShader;
1248         mPathEffect = paint.mPathEffect;
1249         mMaskFilter = paint.mMaskFilter;
1250         mHintingMode = paint.mHintingMode;
1251     }
1252 
reset()1253     private void reset() {
1254         Typeface_Delegate defaultTypeface =
1255                 Typeface_Delegate.getDelegate(Typeface.sDefaults[0].native_instance);
1256 
1257         mFlags = Paint.HIDDEN_DEFAULT_PAINT_FLAGS;
1258         mColor = 0xFF000000;
1259         mStyle = Paint.Style.FILL.nativeInt;
1260         mCap = Paint.Cap.BUTT.nativeInt;
1261         mJoin = Paint.Join.MITER.nativeInt;
1262         mTextAlign = 0;
1263 
1264         if (mTypeface != defaultTypeface) {
1265             mTypeface = defaultTypeface;
1266             invalidateFonts();
1267         }
1268 
1269         mStrokeWidth = 1.f;
1270         mStrokeMiter = 4.f;
1271 
1272         if (mTextSize != DEFAULT_TEXT_SIZE) {
1273             mTextSize = DEFAULT_TEXT_SIZE;
1274             invalidateFonts();
1275         }
1276 
1277         if (mTextScaleX != DEFAULT_TEXT_SCALE_X) {
1278             mTextScaleX = DEFAULT_TEXT_SCALE_X;
1279             invalidateFonts();
1280         }
1281 
1282         if (mTextSkewX != DEFAULT_TEXT_SKEW_X) {
1283             mTextSkewX = DEFAULT_TEXT_SKEW_X;
1284             invalidateFonts();
1285         }
1286 
1287         mPorterDuffMode = Xfermode.DEFAULT;
1288         mColorFilter = null;
1289         mShader = null;
1290         mPathEffect = null;
1291         mMaskFilter = null;
1292         mHintingMode = Paint.HINTING_ON;
1293     }
1294 
invalidateFonts()1295     private void invalidateFonts() {
1296         mFonts = null;
1297     }
1298 
1299     @Nullable
getFontInfo(@ullable Font font, float textSize, @Nullable AffineTransform transform)1300     private static FontInfo getFontInfo(@Nullable Font font, float textSize,
1301             @Nullable AffineTransform transform) {
1302         if (font == null) {
1303             return null;
1304         }
1305 
1306         Font transformedFont = font.deriveFont(textSize);
1307         if (transform != null) {
1308             // TODO: support skew
1309             transformedFont = transformedFont.deriveFont(transform);
1310         }
1311 
1312         // The metrics here don't have anti-aliasing set.
1313         return new FontInfo(transformedFont,
1314                 Toolkit.getDefaultToolkit().getFontMetrics(transformedFont));
1315     }
1316 
measureText(char[] text, int index, int count, float[] advances, int advancesIndex, int bidiFlags)1317     /*package*/ RectF measureText(char[] text, int index, int count, float[] advances,
1318             int advancesIndex, int bidiFlags) {
1319         return new BidiRenderer(null, this, text)
1320                 .renderText(index, index + count, bidiFlags, advances, advancesIndex, false);
1321     }
1322 
measureText(char[] text, int index, int count, float[] advances, int advancesIndex, boolean isRtl)1323     /*package*/ RectF measureText(char[] text, int index, int count, float[] advances,
1324             int advancesIndex, boolean isRtl) {
1325         return new BidiRenderer(null, this, text)
1326                 .renderText(index, index + count, isRtl, advances, advancesIndex, false);
1327     }
1328 
getFontMetrics(FontMetrics metrics)1329     private float getFontMetrics(FontMetrics metrics) {
1330         List<FontInfo> fonts = getFonts();
1331         if (fonts.size() > 0) {
1332             java.awt.FontMetrics javaMetrics = fonts.get(0).mMetrics;
1333             if (metrics != null) {
1334                 // Android expects negative ascent so we invert the value from Java.
1335                 metrics.top = - javaMetrics.getMaxAscent();
1336                 metrics.ascent = - javaMetrics.getAscent();
1337                 metrics.descent = javaMetrics.getDescent();
1338                 metrics.bottom = javaMetrics.getMaxDescent();
1339                 metrics.leading = javaMetrics.getLeading();
1340             }
1341 
1342             return javaMetrics.getHeight();
1343         }
1344 
1345         return 0;
1346     }
1347 
setTextLocale(String locale)1348     private void setTextLocale(String locale) {
1349         mLocale = new Locale(locale);
1350     }
1351 
setFlag(long nativePaint, int flagMask, boolean flagValue)1352     private static void setFlag(long nativePaint, int flagMask, boolean flagValue) {
1353         // get the delegate from the native int.
1354         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
1355         if (delegate == null) {
1356             return;
1357         }
1358 
1359         if (flagValue) {
1360             delegate.mFlags |= flagMask;
1361         } else {
1362             delegate.mFlags &= ~flagMask;
1363         }
1364     }
1365 }
1366