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