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