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