Paint_Delegate.java revision 442aee6bc1abfb143dcfa1ba60d696e576d066c4
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        delegate.mTextSize = textSize;
484        delegate.updateFontObject();
485    }
486
487    @LayoutlibDelegate
488    /*package*/ static float getTextScaleX(Paint thisPaint) {
489        // get the delegate from the native int.
490        Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
491        if (delegate == null) {
492            return 1.f;
493        }
494
495        return delegate.mTextScaleX;
496    }
497
498    @LayoutlibDelegate
499    /*package*/ static void setTextScaleX(Paint thisPaint, float scaleX) {
500        // get the delegate from the native int.
501        Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
502        if (delegate == null) {
503            return;
504        }
505
506        delegate.mTextScaleX = scaleX;
507        delegate.updateFontObject();
508    }
509
510    @LayoutlibDelegate
511    /*package*/ static float getTextSkewX(Paint thisPaint) {
512        // get the delegate from the native int.
513        Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
514        if (delegate == null) {
515            return 1.f;
516        }
517
518        return delegate.mTextSkewX;
519    }
520
521    @LayoutlibDelegate
522    /*package*/ static void setTextSkewX(Paint thisPaint, float skewX) {
523        // get the delegate from the native int.
524        Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
525        if (delegate == null) {
526            return;
527        }
528
529        delegate.mTextSkewX = skewX;
530        delegate.updateFontObject();
531    }
532
533    @LayoutlibDelegate
534    /*package*/ static float ascent(Paint thisPaint) {
535        // get the delegate
536        Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
537        if (delegate == null) {
538            return 0;
539        }
540
541        if (delegate.mFonts.size() > 0) {
542            java.awt.FontMetrics javaMetrics = delegate.mFonts.get(0).mMetrics;
543            // Android expects negative ascent so we invert the value from Java.
544            return - javaMetrics.getAscent();
545        }
546
547        return 0;
548    }
549
550    @LayoutlibDelegate
551    /*package*/ static float descent(Paint thisPaint) {
552        // get the delegate
553        Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
554        if (delegate == null) {
555            return 0;
556        }
557
558        if (delegate.mFonts.size() > 0) {
559            java.awt.FontMetrics javaMetrics = delegate.mFonts.get(0).mMetrics;
560            return javaMetrics.getDescent();
561        }
562
563        return 0;
564
565    }
566
567    @LayoutlibDelegate
568    /*package*/ static float getFontMetrics(Paint thisPaint, FontMetrics metrics) {
569        // get the delegate
570        Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
571        if (delegate == null) {
572            return 0;
573        }
574
575        return delegate.getFontMetrics(metrics);
576    }
577
578    @LayoutlibDelegate
579    /*package*/ static int getFontMetricsInt(Paint thisPaint, FontMetricsInt fmi) {
580        // get the delegate
581        Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
582        if (delegate == null) {
583            return 0;
584        }
585
586        if (delegate.mFonts.size() > 0) {
587            java.awt.FontMetrics javaMetrics = delegate.mFonts.get(0).mMetrics;
588            if (fmi != null) {
589                // Android expects negative ascent so we invert the value from Java.
590                fmi.top = - javaMetrics.getMaxAscent();
591                fmi.ascent = - javaMetrics.getAscent();
592                fmi.descent = javaMetrics.getDescent();
593                fmi.bottom = javaMetrics.getMaxDescent();
594                fmi.leading = javaMetrics.getLeading();
595            }
596
597            return javaMetrics.getHeight();
598        }
599
600        return 0;
601    }
602
603    @LayoutlibDelegate
604    /*package*/ static float native_measureText(Paint thisPaint, char[] text, int index,
605            int count, int bidiFlags) {
606        // get the delegate
607        Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
608        if (delegate == null) {
609            return 0;
610        }
611
612        RectF bounds = delegate.measureText(text, index, count, null, 0, bidiFlags);
613        return bounds.right - bounds.left;
614    }
615
616    @LayoutlibDelegate
617    /*package*/ static float native_measureText(Paint thisPaint, String text, int start, int end,
618        int bidiFlags) {
619        return native_measureText(thisPaint, text.toCharArray(), start, end - start, bidiFlags);
620    }
621
622    @LayoutlibDelegate
623    /*package*/ static float native_measureText(Paint thisPaint, String text, int bidiFlags) {
624        return native_measureText(thisPaint, text.toCharArray(), 0, text.length(), bidiFlags);
625    }
626
627    @LayoutlibDelegate
628    /*package*/ static int native_breakText(long nativePaint, long nativeTypeface, char[] text,
629            int index, int count, float maxWidth, int bidiFlags, float[] measuredWidth) {
630
631        // get the delegate
632        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
633        if (delegate == null) {
634            return 0;
635        }
636
637        int inc = count > 0 ? 1 : -1;
638
639        int measureIndex = 0;
640        for (int i = index; i != index + count; i += inc, measureIndex++) {
641            int start, end;
642            if (i < index) {
643                start = i;
644                end = index;
645            } else {
646                start = index;
647                end = i;
648            }
649
650            // measure from start to end
651            RectF bounds = delegate.measureText(text, start, end - start + 1, null, 0, bidiFlags);
652            float res = bounds.right - bounds.left;
653
654            if (measuredWidth != null) {
655                measuredWidth[measureIndex] = res;
656            }
657
658            if (res > maxWidth) {
659                // we should not return this char index, but since it's 0-based
660                // and we need to return a count, we simply return measureIndex;
661                return measureIndex;
662            }
663
664        }
665
666        return measureIndex;
667    }
668
669    @LayoutlibDelegate
670    /*package*/ static int native_breakText(long nativePaint, long nativeTypeface, String text,
671            boolean measureForwards,
672            float maxWidth, int bidiFlags, float[] measuredWidth) {
673        return native_breakText(nativePaint, nativeTypeface, text.toCharArray(), 0, text.length(),
674                maxWidth, bidiFlags, measuredWidth);
675    }
676
677    @LayoutlibDelegate
678    /*package*/ static long native_init() {
679        Paint_Delegate newDelegate = new Paint_Delegate();
680        return sManager.addNewDelegate(newDelegate);
681    }
682
683    @LayoutlibDelegate
684    /*package*/ static long native_initWithPaint(long paint) {
685        // get the delegate from the native int.
686        Paint_Delegate delegate = sManager.getDelegate(paint);
687        if (delegate == null) {
688            return 0;
689        }
690
691        Paint_Delegate newDelegate = new Paint_Delegate(delegate);
692        return sManager.addNewDelegate(newDelegate);
693    }
694
695    @LayoutlibDelegate
696    /*package*/ static void native_reset(long native_object) {
697        // get the delegate from the native int.
698        Paint_Delegate delegate = sManager.getDelegate(native_object);
699        if (delegate == null) {
700            return;
701        }
702
703        delegate.reset();
704    }
705
706    @LayoutlibDelegate
707    /*package*/ static void native_set(long native_dst, long native_src) {
708        // get the delegate from the native int.
709        Paint_Delegate delegate_dst = sManager.getDelegate(native_dst);
710        if (delegate_dst == null) {
711            return;
712        }
713
714        // get the delegate from the native int.
715        Paint_Delegate delegate_src = sManager.getDelegate(native_src);
716        if (delegate_src == null) {
717            return;
718        }
719
720        delegate_dst.set(delegate_src);
721    }
722
723    @LayoutlibDelegate
724    /*package*/ static int native_getStyle(long native_object) {
725        // get the delegate from the native int.
726        Paint_Delegate delegate = sManager.getDelegate(native_object);
727        if (delegate == null) {
728            return 0;
729        }
730
731        return delegate.mStyle;
732    }
733
734    @LayoutlibDelegate
735    /*package*/ static void native_setStyle(long native_object, int style) {
736        // get the delegate from the native int.
737        Paint_Delegate delegate = sManager.getDelegate(native_object);
738        if (delegate == null) {
739            return;
740        }
741
742        delegate.mStyle = style;
743    }
744
745    @LayoutlibDelegate
746    /*package*/ static int native_getStrokeCap(long native_object) {
747        // get the delegate from the native int.
748        Paint_Delegate delegate = sManager.getDelegate(native_object);
749        if (delegate == null) {
750            return 0;
751        }
752
753        return delegate.mCap;
754    }
755
756    @LayoutlibDelegate
757    /*package*/ static void native_setStrokeCap(long native_object, int cap) {
758        // get the delegate from the native int.
759        Paint_Delegate delegate = sManager.getDelegate(native_object);
760        if (delegate == null) {
761            return;
762        }
763
764        delegate.mCap = cap;
765    }
766
767    @LayoutlibDelegate
768    /*package*/ static int native_getStrokeJoin(long native_object) {
769        // get the delegate from the native int.
770        Paint_Delegate delegate = sManager.getDelegate(native_object);
771        if (delegate == null) {
772            return 0;
773        }
774
775        return delegate.mJoin;
776    }
777
778    @LayoutlibDelegate
779    /*package*/ static void native_setStrokeJoin(long native_object, int join) {
780        // get the delegate from the native int.
781        Paint_Delegate delegate = sManager.getDelegate(native_object);
782        if (delegate == null) {
783            return;
784        }
785
786        delegate.mJoin = join;
787    }
788
789    @LayoutlibDelegate
790    /*package*/ static boolean native_getFillPath(long native_object, long src, long dst) {
791        Paint_Delegate paint = sManager.getDelegate(native_object);
792        if (paint == null) {
793            return false;
794        }
795
796        Path_Delegate srcPath = Path_Delegate.getDelegate(src);
797        if (srcPath == null) {
798            return true;
799        }
800
801        Path_Delegate dstPath = Path_Delegate.getDelegate(dst);
802        if (dstPath == null) {
803            return true;
804        }
805
806        Stroke stroke = paint.getJavaStroke();
807        Shape strokeShape = stroke.createStrokedShape(srcPath.getJavaShape());
808
809        dstPath.setJavaShape(strokeShape);
810
811        // FIXME figure out the return value?
812        return true;
813    }
814
815    @LayoutlibDelegate
816    /*package*/ static long native_setShader(long native_object, long shader) {
817        // get the delegate from the native int.
818        Paint_Delegate delegate = sManager.getDelegate(native_object);
819        if (delegate == null) {
820            return shader;
821        }
822
823        delegate.mShader = Shader_Delegate.getDelegate(shader);
824
825        return shader;
826    }
827
828    @LayoutlibDelegate
829    /*package*/ static long native_setColorFilter(long native_object, long filter) {
830        // get the delegate from the native int.
831        Paint_Delegate delegate = sManager.getDelegate(native_object);
832        if (delegate == null) {
833            return filter;
834        }
835
836        delegate.mColorFilter = ColorFilter_Delegate.getDelegate(filter);
837
838        // Log warning if it's not supported.
839        if (delegate.mColorFilter != null && !delegate.mColorFilter.isSupported()) {
840            Bridge.getLog().fidelityWarning(LayoutLog.TAG_COLORFILTER,
841                    delegate.mColorFilter.getSupportMessage(), null, null /*data*/);
842        }
843
844        return filter;
845    }
846
847    @LayoutlibDelegate
848    /*package*/ static long native_setXfermode(long native_object, long xfermode) {
849        // get the delegate from the native int.
850        Paint_Delegate delegate = sManager.getDelegate(native_object);
851        if (delegate == null) {
852            return xfermode;
853        }
854
855        delegate.mXfermode = Xfermode_Delegate.getDelegate(xfermode);
856
857        return xfermode;
858    }
859
860    @LayoutlibDelegate
861    /*package*/ static long native_setPathEffect(long native_object, long effect) {
862        // get the delegate from the native int.
863        Paint_Delegate delegate = sManager.getDelegate(native_object);
864        if (delegate == null) {
865            return effect;
866        }
867
868        delegate.mPathEffect = PathEffect_Delegate.getDelegate(effect);
869
870        return effect;
871    }
872
873    @LayoutlibDelegate
874    /*package*/ static long native_setMaskFilter(long native_object, long maskfilter) {
875        // get the delegate from the native int.
876        Paint_Delegate delegate = sManager.getDelegate(native_object);
877        if (delegate == null) {
878            return maskfilter;
879        }
880
881        delegate.mMaskFilter = MaskFilter_Delegate.getDelegate(maskfilter);
882
883        // since none of those are supported, display a fidelity warning right away
884        if (delegate.mMaskFilter != null && !delegate.mMaskFilter.isSupported()) {
885            Bridge.getLog().fidelityWarning(LayoutLog.TAG_MASKFILTER,
886                    delegate.mMaskFilter.getSupportMessage(), null, null /*data*/);
887        }
888
889        return maskfilter;
890    }
891
892    @LayoutlibDelegate
893    /*package*/ static long native_setTypeface(long native_object, long typeface) {
894        // get the delegate from the native int.
895        Paint_Delegate delegate = sManager.getDelegate(native_object);
896        if (delegate == null) {
897            return 0;
898        }
899
900        delegate.mTypeface = Typeface_Delegate.getDelegate(typeface);
901        delegate.mNativeTypeface = typeface;
902        delegate.updateFontObject();
903        return typeface;
904    }
905
906    @LayoutlibDelegate
907    /*package*/ static long native_setRasterizer(long native_object, long rasterizer) {
908        // get the delegate from the native int.
909        Paint_Delegate delegate = sManager.getDelegate(native_object);
910        if (delegate == null) {
911            return rasterizer;
912        }
913
914        delegate.mRasterizer = Rasterizer_Delegate.getDelegate(rasterizer);
915
916        // since none of those are supported, display a fidelity warning right away
917        if (delegate.mRasterizer != null && !delegate.mRasterizer.isSupported()) {
918            Bridge.getLog().fidelityWarning(LayoutLog.TAG_RASTERIZER,
919                    delegate.mRasterizer.getSupportMessage(), null, null /*data*/);
920        }
921
922        return rasterizer;
923    }
924
925    @LayoutlibDelegate
926    /*package*/ static int native_getTextAlign(long native_object) {
927        // get the delegate from the native int.
928        Paint_Delegate delegate = sManager.getDelegate(native_object);
929        if (delegate == null) {
930            return 0;
931        }
932
933        return delegate.mTextAlign;
934    }
935
936    @LayoutlibDelegate
937    /*package*/ static void native_setTextAlign(long native_object, int align) {
938        // get the delegate from the native int.
939        Paint_Delegate delegate = sManager.getDelegate(native_object);
940        if (delegate == null) {
941            return;
942        }
943
944        delegate.mTextAlign = align;
945    }
946
947    @LayoutlibDelegate
948    /*package*/ static void native_setTextLocale(long native_object, String locale) {
949        // get the delegate from the native int.
950        Paint_Delegate delegate = sManager.getDelegate(native_object);
951        if (delegate == null) {
952            return;
953        }
954
955        delegate.setTextLocale(locale);
956    }
957
958    @LayoutlibDelegate
959    /*package*/ static int native_getTextWidths(long native_object, long native_typeface,
960            char[] text, int index, int count, int bidiFlags, float[] widths) {
961
962        if (widths != null) {
963            for (int i = 0; i< count; i++) {
964                widths[i]=0;
965            }
966        }
967        // get the delegate from the native int.
968        Paint_Delegate delegate = sManager.getDelegate(native_object);
969        if (delegate == null) {
970            return 0;
971        }
972
973        // native_typeface is passed here since Framework's old implementation did not have the
974        // typeface object associated with the Paint. Since, we follow the new framework way,
975        // we store the typeface with the paint and use it directly.
976        assert (native_typeface == delegate.mNativeTypeface);
977
978        RectF bounds = delegate.measureText(text, index, count, widths, 0, bidiFlags);
979        return ((int) (bounds.right - bounds.left));
980    }
981
982    @LayoutlibDelegate
983    /*package*/ static int native_getTextWidths(long native_object, long native_typeface,
984            String text, int start, int end, int bidiFlags, float[] widths) {
985        return native_getTextWidths(native_object, native_typeface, text.toCharArray(), start,
986                end - start, bidiFlags, widths);
987    }
988
989    @LayoutlibDelegate
990    /* package */static int native_getTextGlyphs(long native_object, String text, int start,
991            int end, int contextStart, int contextEnd, int flags, char[] glyphs) {
992        // FIXME
993        return 0;
994    }
995
996    @LayoutlibDelegate
997    /*package*/ static float native_getTextRunAdvances(long native_object, long native_typeface,
998            char[] text, int index, int count, int contextIndex, int contextCount,
999            boolean isRtl, float[] advances, int advancesIndex) {
1000
1001        if (advances != null)
1002            for (int i = advancesIndex; i< advancesIndex+count; i++)
1003                advances[i]=0;
1004        // get the delegate from the native int.
1005        Paint_Delegate delegate = sManager.getDelegate(native_object);
1006        if (delegate == null) {
1007            return 0.f;
1008        }
1009
1010        // native_typeface is passed here since Framework's old implementation did not have the
1011        // typeface object associated with the Paint. Since, we follow the new framework way,
1012        // we store the typeface with the paint and use it directly.
1013        assert (native_typeface == delegate.mNativeTypeface);
1014
1015        RectF bounds = delegate.measureText(text, index, count, advances, advancesIndex, isRtl);
1016        return bounds.right - bounds.left;
1017    }
1018
1019    @LayoutlibDelegate
1020    /*package*/ static float native_getTextRunAdvances(long native_object, long native_typeface,
1021            String text, int start, int end, int contextStart, int contextEnd,
1022            boolean isRtl, float[] advances, int advancesIndex) {
1023        // FIXME: support contextStart and contextEnd
1024        int count = end - start;
1025        char[] buffer = TemporaryBuffer.obtain(count);
1026        TextUtils.getChars(text, start, end, buffer, 0);
1027
1028        return native_getTextRunAdvances(native_object, native_typeface, buffer, 0, count,
1029                contextStart, contextEnd - contextStart, isRtl, advances, advancesIndex);
1030    }
1031
1032    @LayoutlibDelegate
1033    /*package*/ static int native_getTextRunCursor(Paint thisPaint, long native_object, char[] text,
1034            int contextStart, int contextLength, int flags, int offset, int cursorOpt) {
1035        // FIXME
1036        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
1037                "Paint.getTextRunCursor is not supported.", null, null /*data*/);
1038        return 0;
1039    }
1040
1041    @LayoutlibDelegate
1042    /*package*/ static int native_getTextRunCursor(Paint thisPaint, long native_object, String text,
1043            int contextStart, int contextEnd, 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 void native_getTextPath(long native_object, long native_typeface,
1052            int bidiFlags, char[] text, int index, int count, float x, float y, long path) {
1053        // FIXME
1054        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
1055                "Paint.getTextPath is not supported.", null, null /*data*/);
1056    }
1057
1058    @LayoutlibDelegate
1059    /*package*/ static void native_getTextPath(long native_object, long native_typeface,
1060            int bidiFlags, String text, int start, int end, float x, float y, long path) {
1061        // FIXME
1062        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
1063                "Paint.getTextPath is not supported.", null, null /*data*/);
1064    }
1065
1066    @LayoutlibDelegate
1067    /*package*/ static void nativeGetStringBounds(long nativePaint, long native_typeface,
1068            String text, int start, int end, int bidiFlags, Rect bounds) {
1069        nativeGetCharArrayBounds(nativePaint, native_typeface, text.toCharArray(), start,
1070                end - start, bidiFlags, bounds);
1071    }
1072
1073    @LayoutlibDelegate
1074    /*package*/ static void nativeGetCharArrayBounds(long nativePaint, long native_typeface,
1075            char[] text, int index, int count, int bidiFlags, Rect bounds) {
1076
1077        // get the delegate from the native int.
1078        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
1079        if (delegate == null) {
1080            return;
1081        }
1082
1083        // assert that the typeface passed is actually the one that we had stored.
1084        assert (native_typeface == delegate.mNativeTypeface);
1085
1086        delegate.measureText(text, index, count, null, 0, bidiFlags).roundOut(bounds);
1087    }
1088
1089    @LayoutlibDelegate
1090    /*package*/ static void finalizer(long nativePaint) {
1091        sManager.removeJavaReferenceFor(nativePaint);
1092    }
1093
1094    @LayoutlibDelegate
1095    /*package*/ static float native_getLetterSpacing(long nativePaint) {
1096        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
1097        if (delegate == null) {
1098            return 0;
1099        }
1100        return delegate.mLetterSpacing;
1101    }
1102
1103    @LayoutlibDelegate
1104    /*package*/ static void native_setLetterSpacing(long nativePaint, float letterSpacing) {
1105        Bridge.getLog().fidelityWarning(LayoutLog.TAG_TEXT_RENDERING,
1106                "Paint.setLetterSpacing() not supported.", null, null);
1107        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
1108        if (delegate == null) {
1109            return;
1110        }
1111        delegate.mLetterSpacing = letterSpacing;
1112    }
1113
1114    @LayoutlibDelegate
1115    /*package*/ static void native_setFontFeatureSettings(long nativePaint, String settings) {
1116        Bridge.getLog().fidelityWarning(LayoutLog.TAG_TEXT_RENDERING,
1117                "Paint.setFontFeatureSettings() not supported.", null, null);
1118    }
1119
1120    @LayoutlibDelegate
1121    /*package*/ static int native_getHyphenEdit(long nativePaint) {
1122        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
1123        if (delegate == null) {
1124            return 0;
1125        }
1126        return delegate.mHyphenEdit;
1127    }
1128
1129    @LayoutlibDelegate
1130    /*package*/ static void native_setHyphenEdit(long nativePaint, int hyphen) {
1131        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
1132        if (delegate == null) {
1133            return;
1134        }
1135        delegate.mHyphenEdit = hyphen;
1136    }
1137
1138    @LayoutlibDelegate
1139    /*package*/ static boolean native_hasGlyph(long nativePaint, long nativeTypeface, int bidiFlags,
1140            String string) {
1141        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
1142        if (delegate == null) {
1143            return false;
1144        }
1145        if (string.length() == 0) {
1146            return false;
1147        }
1148        if (string.length() > 1) {
1149            Bridge.getLog().fidelityWarning(LayoutLog.TAG_TEXT_RENDERING,
1150                    "Paint.hasGlyph() is not supported for ligatures.", null, null);
1151            return false;
1152        }
1153        assert nativeTypeface == delegate.mNativeTypeface;
1154        Typeface_Delegate typeface_delegate = Typeface_Delegate.getDelegate(nativeTypeface);
1155
1156        char c = string.charAt(0);
1157        for (Font font : typeface_delegate.getFonts(delegate.mFontVariant)) {
1158            if (font.canDisplay(c)) {
1159                return true;
1160            }
1161        }
1162        return false;
1163    }
1164
1165
1166    @LayoutlibDelegate
1167    /*package*/ static float native_getRunAdvance(long nativePaint, long nativeTypeface,
1168            @NonNull char[] text, int start, int end, int contextStart, int contextEnd,
1169            boolean isRtl, int offset) {
1170        int count = end - start;
1171        float[] advances = new float[count];
1172        native_getTextRunAdvances(nativePaint, nativeTypeface, text, start, count,
1173                contextStart, contextEnd - contextStart, isRtl, advances, 0);
1174        float sum = 0;
1175        for (int i = 0; i < offset; i++) {
1176            sum += advances[i];
1177        }
1178        return sum;
1179    }
1180
1181    @LayoutlibDelegate
1182    /*package*/ static int native_getOffsetForAdvance(long nativePaint, long nativeTypeface,
1183            char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl,
1184            float advance) {
1185        int count = end - start;
1186        float[] advances = new float[count];
1187        native_getTextRunAdvances(nativePaint, nativeTypeface, text, start, count,
1188                contextStart, contextEnd - contextStart, isRtl, advances, 0);
1189        float sum = 0;
1190        int i;
1191        for (i = 0; i < count && sum < advance; i++) {
1192            sum += advances[i];
1193        }
1194        float distanceToI = sum - advance;
1195        float distanceToIMinus1 = advance - (sum - advances[i]);
1196        return distanceToI > distanceToIMinus1 ? i : i - 1;
1197    }
1198
1199    // ---- Private delegate/helper methods ----
1200
1201    /*package*/ Paint_Delegate() {
1202        reset();
1203    }
1204
1205    private Paint_Delegate(Paint_Delegate paint) {
1206        set(paint);
1207    }
1208
1209    private void set(Paint_Delegate paint) {
1210        mFlags = paint.mFlags;
1211        mColor = paint.mColor;
1212        mStyle = paint.mStyle;
1213        mCap = paint.mCap;
1214        mJoin = paint.mJoin;
1215        mTextAlign = paint.mTextAlign;
1216        mTypeface = paint.mTypeface;
1217        mNativeTypeface = paint.mNativeTypeface;
1218        mStrokeWidth = paint.mStrokeWidth;
1219        mStrokeMiter = paint.mStrokeMiter;
1220        mTextSize = paint.mTextSize;
1221        mTextScaleX = paint.mTextScaleX;
1222        mTextSkewX = paint.mTextSkewX;
1223        mXfermode = paint.mXfermode;
1224        mColorFilter = paint.mColorFilter;
1225        mShader = paint.mShader;
1226        mPathEffect = paint.mPathEffect;
1227        mMaskFilter = paint.mMaskFilter;
1228        mRasterizer = paint.mRasterizer;
1229        mHintingMode = paint.mHintingMode;
1230        updateFontObject();
1231    }
1232
1233    private void reset() {
1234        mFlags = Paint.HIDDEN_DEFAULT_PAINT_FLAGS;
1235        mColor = 0xFF000000;
1236        mStyle = Paint.Style.FILL.nativeInt;
1237        mCap = Paint.Cap.BUTT.nativeInt;
1238        mJoin = Paint.Join.MITER.nativeInt;
1239        mTextAlign = 0;
1240        mTypeface = Typeface_Delegate.getDelegate(Typeface.sDefaults[0].native_instance);
1241        mNativeTypeface = 0;
1242        mStrokeWidth = 1.f;
1243        mStrokeMiter = 4.f;
1244        mTextSize = 20.f;
1245        mTextScaleX = 1.f;
1246        mTextSkewX = 0.f;
1247        mXfermode = null;
1248        mColorFilter = null;
1249        mShader = null;
1250        mPathEffect = null;
1251        mMaskFilter = null;
1252        mRasterizer = null;
1253        updateFontObject();
1254        mHintingMode = Paint.HINTING_ON;
1255    }
1256
1257    /**
1258     * Update the {@link Font} object from the typeface, text size and scaling
1259     */
1260    @SuppressWarnings("deprecation")
1261    private void updateFontObject() {
1262        if (mTypeface != null) {
1263            // Get the fonts from the TypeFace object.
1264            List<Font> fonts = mTypeface.getFonts(mFontVariant);
1265
1266            // create new font objects as well as FontMetrics, based on the current text size
1267            // and skew info.
1268            ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(fonts.size());
1269            for (Font font : fonts) {
1270                if (font == null) {
1271                    // If the font is null, add null to infoList. When rendering the text, if this
1272                    // null is reached, a warning will be logged.
1273                    infoList.add(null);
1274                    continue;
1275                }
1276                FontInfo info = new FontInfo();
1277                info.mFont = font.deriveFont(mTextSize);
1278                if (mTextScaleX != 1.0 || mTextSkewX != 0) {
1279                    // TODO: support skew
1280                    info.mFont = info.mFont.deriveFont(new AffineTransform(
1281                            mTextScaleX, mTextSkewX, 0, 1, 0, 0));
1282                }
1283                // The metrics here don't have anti-aliasing set.
1284                info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont);
1285
1286                infoList.add(info);
1287            }
1288
1289            mFonts = Collections.unmodifiableList(infoList);
1290        }
1291    }
1292
1293    /*package*/ RectF measureText(char[] text, int index, int count, float[] advances,
1294            int advancesIndex, int bidiFlags) {
1295        return new BidiRenderer(null, this, text)
1296                .renderText(index, index + count, bidiFlags, advances, advancesIndex, false);
1297    }
1298
1299    /*package*/ RectF measureText(char[] text, int index, int count, float[] advances,
1300            int advancesIndex, boolean isRtl) {
1301        return new BidiRenderer(null, this, text)
1302                .renderText(index, index + count, isRtl, advances, advancesIndex, false);
1303    }
1304
1305    private float getFontMetrics(FontMetrics metrics) {
1306        if (mFonts.size() > 0) {
1307            java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
1308            if (metrics != null) {
1309                // Android expects negative ascent so we invert the value from Java.
1310                metrics.top = - javaMetrics.getMaxAscent();
1311                metrics.ascent = - javaMetrics.getAscent();
1312                metrics.descent = javaMetrics.getDescent();
1313                metrics.bottom = javaMetrics.getMaxDescent();
1314                metrics.leading = javaMetrics.getLeading();
1315            }
1316
1317            return javaMetrics.getHeight();
1318        }
1319
1320        return 0;
1321    }
1322
1323    private void setTextLocale(String locale) {
1324        mLocale = new Locale(locale);
1325    }
1326
1327    private static void setFlag(Paint thisPaint, int flagMask, boolean flagValue) {
1328        // get the delegate from the native int.
1329        Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
1330        if (delegate == null) {
1331            return;
1332        }
1333
1334        if (flagValue) {
1335            delegate.mFlags |= flagMask;
1336        } else {
1337            delegate.mFlags &= ~flagMask;
1338        }
1339    }
1340}
1341