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.graphics.Paint.FontMetrics;
25import android.graphics.Paint.FontMetricsInt;
26
27import java.awt.BasicStroke;
28import java.awt.Font;
29import java.awt.Shape;
30import java.awt.Stroke;
31import java.awt.Toolkit;
32import java.awt.font.FontRenderContext;
33import java.awt.geom.AffineTransform;
34import java.awt.geom.Rectangle2D;
35import java.util.ArrayList;
36import java.util.Collections;
37import java.util.List;
38
39/**
40 * Delegate implementing the native methods of android.graphics.Paint
41 *
42 * Through the layoutlib_create tool, the original native methods of Paint have been replaced
43 * by calls to methods of the same name in this delegate class.
44 *
45 * This class behaves like the original native implementation, but in Java, keeping previously
46 * native data into its own objects and mapping them to int that are sent back and forth between
47 * it and the original Paint class.
48 *
49 * @see DelegateManager
50 *
51 */
52public class Paint_Delegate {
53
54    /**
55     * Class associating a {@link Font} and it's {@link java.awt.FontMetrics}.
56     */
57    /*package*/ static final class FontInfo {
58        Font mFont;
59        java.awt.FontMetrics mMetrics;
60    }
61
62    // ---- delegate manager ----
63    private static final DelegateManager<Paint_Delegate> sManager =
64            new DelegateManager<Paint_Delegate>(Paint_Delegate.class);
65
66    // ---- delegate helper data ----
67    private List<FontInfo> mFonts;
68    private final FontRenderContext mFontContext = new FontRenderContext(
69            new AffineTransform(), true, true);
70
71    // ---- delegate data ----
72    private int mFlags;
73    private int mColor;
74    private int mStyle;
75    private int mCap;
76    private int mJoin;
77    private int mTextAlign;
78    private Typeface_Delegate mTypeface;
79    private float mStrokeWidth;
80    private float mStrokeMiter;
81    private float mTextSize;
82    private float mTextScaleX;
83    private float mTextSkewX;
84
85    private Xfermode_Delegate mXfermode;
86    private ColorFilter_Delegate mColorFilter;
87    private Shader_Delegate mShader;
88    private PathEffect_Delegate mPathEffect;
89    private MaskFilter_Delegate mMaskFilter;
90    private Rasterizer_Delegate mRasterizer;
91
92    // ---- Public Helper methods ----
93
94    public static Paint_Delegate getDelegate(int native_paint) {
95        return sManager.getDelegate(native_paint);
96    }
97
98    /**
99     * Returns the list of {@link Font} objects. The first item is the main font, the rest
100     * are fall backs for characters not present in the main font.
101     */
102    public List<FontInfo> getFonts() {
103        return mFonts;
104    }
105
106    public boolean isAntiAliased() {
107        return (mFlags & Paint.ANTI_ALIAS_FLAG) != 0;
108    }
109
110    public boolean isFilterBitmap() {
111        return (mFlags & Paint.FILTER_BITMAP_FLAG) != 0;
112    }
113
114    public int getStyle() {
115        return mStyle;
116    }
117
118    public int getColor() {
119        return mColor;
120    }
121
122    public int getAlpha() {
123        return mColor >>> 24;
124    }
125
126    public void setAlpha(int alpha) {
127        mColor = (alpha << 24) | (mColor & 0x00FFFFFF);
128    }
129
130    public int getTextAlign() {
131        return mTextAlign;
132    }
133
134    public float getStrokeWidth() {
135        return mStrokeWidth;
136    }
137
138    /**
139     * returns the value of stroke miter needed by the java api.
140     */
141    public float getJavaStrokeMiter() {
142        float miter = mStrokeMiter * mStrokeWidth;
143        if (miter < 1.f) {
144            miter = 1.f;
145        }
146        return miter;
147    }
148
149    public int getJavaCap() {
150        switch (Paint.sCapArray[mCap]) {
151            case BUTT:
152                return BasicStroke.CAP_BUTT;
153            case ROUND:
154                return BasicStroke.CAP_ROUND;
155            default:
156            case SQUARE:
157                return BasicStroke.CAP_SQUARE;
158        }
159    }
160
161    public int getJavaJoin() {
162        switch (Paint.sJoinArray[mJoin]) {
163            default:
164            case MITER:
165                return BasicStroke.JOIN_MITER;
166            case ROUND:
167                return BasicStroke.JOIN_ROUND;
168            case BEVEL:
169                return BasicStroke.JOIN_BEVEL;
170        }
171    }
172
173    public Stroke getJavaStroke() {
174        if (mPathEffect != null) {
175            if (mPathEffect.isSupported()) {
176                Stroke stroke = mPathEffect.getStroke(this);
177                assert stroke != null;
178                if (stroke != null) {
179                    return stroke;
180                }
181            } else {
182                Bridge.getLog().fidelityWarning(LayoutLog.TAG_PATHEFFECT,
183                        mPathEffect.getSupportMessage(),
184                        null, null /*data*/);
185            }
186        }
187
188        // if no custom stroke as been set, set the default one.
189        return new BasicStroke(
190                    getStrokeWidth(),
191                    getJavaCap(),
192                    getJavaJoin(),
193                    getJavaStrokeMiter());
194    }
195
196    /**
197     * Returns the {@link Xfermode} delegate or null if none have been set
198     *
199     * @return the delegate or null.
200     */
201    public Xfermode_Delegate getXfermode() {
202        return mXfermode;
203    }
204
205    /**
206     * Returns the {@link ColorFilter} delegate or null if none have been set
207     *
208     * @return the delegate or null.
209     */
210    public ColorFilter_Delegate getColorFilter() {
211        return mColorFilter;
212    }
213
214    /**
215     * Returns the {@link Shader} delegate or null if none have been set
216     *
217     * @return the delegate or null.
218     */
219    public Shader_Delegate getShader() {
220        return mShader;
221    }
222
223    /**
224     * Returns the {@link MaskFilter} delegate or null if none have been set
225     *
226     * @return the delegate or null.
227     */
228    public MaskFilter_Delegate getMaskFilter() {
229        return mMaskFilter;
230    }
231
232    /**
233     * Returns the {@link Rasterizer} delegate or null if none have been set
234     *
235     * @return the delegate or null.
236     */
237    public Rasterizer_Delegate getRasterizer() {
238        return mRasterizer;
239    }
240
241    // ---- native methods ----
242
243    @LayoutlibDelegate
244    /*package*/ static int getFlags(Paint thisPaint) {
245        // get the delegate from the native int.
246        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
247        if (delegate == null) {
248            return 0;
249        }
250
251        return delegate.mFlags;
252    }
253
254    @LayoutlibDelegate
255    /*package*/ static void setFlags(Paint thisPaint, int flags) {
256        // get the delegate from the native int.
257        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
258        if (delegate == null) {
259            return;
260        }
261
262        delegate.mFlags = flags;
263    }
264
265    @LayoutlibDelegate
266    /*package*/ static void setFilterBitmap(Paint thisPaint, boolean filter) {
267        setFlag(thisPaint, Paint.FILTER_BITMAP_FLAG, filter);
268    }
269
270    @LayoutlibDelegate
271    /*package*/ static void setAntiAlias(Paint thisPaint, boolean aa) {
272        setFlag(thisPaint, Paint.ANTI_ALIAS_FLAG, aa);
273    }
274
275    @LayoutlibDelegate
276    /*package*/ static void setSubpixelText(Paint thisPaint, boolean subpixelText) {
277        setFlag(thisPaint, Paint.SUBPIXEL_TEXT_FLAG, subpixelText);
278    }
279
280    @LayoutlibDelegate
281    /*package*/ static void setUnderlineText(Paint thisPaint, boolean underlineText) {
282        setFlag(thisPaint, Paint.UNDERLINE_TEXT_FLAG, underlineText);
283    }
284
285    @LayoutlibDelegate
286    /*package*/ static void setStrikeThruText(Paint thisPaint, boolean strikeThruText) {
287        setFlag(thisPaint, Paint.STRIKE_THRU_TEXT_FLAG, strikeThruText);
288    }
289
290    @LayoutlibDelegate
291    /*package*/ static void setFakeBoldText(Paint thisPaint, boolean fakeBoldText) {
292        setFlag(thisPaint, Paint.FAKE_BOLD_TEXT_FLAG, fakeBoldText);
293    }
294
295    @LayoutlibDelegate
296    /*package*/ static void setDither(Paint thisPaint, boolean dither) {
297        setFlag(thisPaint, Paint.DITHER_FLAG, dither);
298    }
299
300    @LayoutlibDelegate
301    /*package*/ static void setLinearText(Paint thisPaint, boolean linearText) {
302        setFlag(thisPaint, Paint.LINEAR_TEXT_FLAG, linearText);
303    }
304
305    @LayoutlibDelegate
306    /*package*/ static int getColor(Paint thisPaint) {
307        // get the delegate from the native int.
308        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
309        if (delegate == null) {
310            return 0;
311        }
312
313        return delegate.mColor;
314    }
315
316    @LayoutlibDelegate
317    /*package*/ static void setColor(Paint thisPaint, int color) {
318        // get the delegate from the native int.
319        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
320        if (delegate == null) {
321            return;
322        }
323
324        delegate.mColor = color;
325    }
326
327    @LayoutlibDelegate
328    /*package*/ static int getAlpha(Paint thisPaint) {
329        // get the delegate from the native int.
330        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
331        if (delegate == null) {
332            return 0;
333        }
334
335        return delegate.getAlpha();
336    }
337
338    @LayoutlibDelegate
339    /*package*/ static void setAlpha(Paint thisPaint, int a) {
340        // get the delegate from the native int.
341        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
342        if (delegate == null) {
343            return;
344        }
345
346        delegate.setAlpha(a);
347    }
348
349    @LayoutlibDelegate
350    /*package*/ static float getStrokeWidth(Paint thisPaint) {
351        // get the delegate from the native int.
352        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
353        if (delegate == null) {
354            return 1.f;
355        }
356
357        return delegate.mStrokeWidth;
358    }
359
360    @LayoutlibDelegate
361    /*package*/ static void setStrokeWidth(Paint thisPaint, float width) {
362        // get the delegate from the native int.
363        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
364        if (delegate == null) {
365            return;
366        }
367
368        delegate.mStrokeWidth = width;
369    }
370
371    @LayoutlibDelegate
372    /*package*/ static float getStrokeMiter(Paint thisPaint) {
373        // get the delegate from the native int.
374        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
375        if (delegate == null) {
376            return 1.f;
377        }
378
379        return delegate.mStrokeMiter;
380    }
381
382    @LayoutlibDelegate
383    /*package*/ static void setStrokeMiter(Paint thisPaint, float miter) {
384        // get the delegate from the native int.
385        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
386        if (delegate == null) {
387            return;
388        }
389
390        delegate.mStrokeMiter = miter;
391    }
392
393    @LayoutlibDelegate
394    /*package*/ static void setShadowLayer(Paint thisPaint, float radius, float dx, float dy,
395            int color) {
396        // FIXME
397        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
398                "Paint.setShadowLayer is not supported.", null, null /*data*/);
399    }
400
401    @LayoutlibDelegate
402    /*package*/ static float getTextSize(Paint thisPaint) {
403        // get the delegate from the native int.
404        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
405        if (delegate == null) {
406            return 1.f;
407        }
408
409        return delegate.mTextSize;
410    }
411
412    @LayoutlibDelegate
413    /*package*/ static void setTextSize(Paint thisPaint, float textSize) {
414        // get the delegate from the native int.
415        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
416        if (delegate == null) {
417            return;
418        }
419
420        delegate.mTextSize = textSize;
421        delegate.updateFontObject();
422    }
423
424    @LayoutlibDelegate
425    /*package*/ static float getTextScaleX(Paint thisPaint) {
426        // get the delegate from the native int.
427        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
428        if (delegate == null) {
429            return 1.f;
430        }
431
432        return delegate.mTextScaleX;
433    }
434
435    @LayoutlibDelegate
436    /*package*/ static void setTextScaleX(Paint thisPaint, float scaleX) {
437        // get the delegate from the native int.
438        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
439        if (delegate == null) {
440            return;
441        }
442
443        delegate.mTextScaleX = scaleX;
444        delegate.updateFontObject();
445    }
446
447    @LayoutlibDelegate
448    /*package*/ static float getTextSkewX(Paint thisPaint) {
449        // get the delegate from the native int.
450        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
451        if (delegate == null) {
452            return 1.f;
453        }
454
455        return delegate.mTextSkewX;
456    }
457
458    @LayoutlibDelegate
459    /*package*/ static void setTextSkewX(Paint thisPaint, float skewX) {
460        // get the delegate from the native int.
461        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
462        if (delegate == null) {
463            return;
464        }
465
466        delegate.mTextSkewX = skewX;
467        delegate.updateFontObject();
468    }
469
470    @LayoutlibDelegate
471    /*package*/ static float ascent(Paint thisPaint) {
472        // get the delegate
473        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
474        if (delegate == null) {
475            return 0;
476        }
477
478        if (delegate.mFonts.size() > 0) {
479            java.awt.FontMetrics javaMetrics = delegate.mFonts.get(0).mMetrics;
480            // Android expects negative ascent so we invert the value from Java.
481            return - javaMetrics.getAscent();
482        }
483
484        return 0;
485    }
486
487    @LayoutlibDelegate
488    /*package*/ static float descent(Paint thisPaint) {
489        // get the delegate
490        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
491        if (delegate == null) {
492            return 0;
493        }
494
495        if (delegate.mFonts.size() > 0) {
496            java.awt.FontMetrics javaMetrics = delegate.mFonts.get(0).mMetrics;
497            return javaMetrics.getDescent();
498        }
499
500        return 0;
501
502    }
503
504    @LayoutlibDelegate
505    /*package*/ static float getFontMetrics(Paint thisPaint, FontMetrics metrics) {
506        // get the delegate
507        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
508        if (delegate == null) {
509            return 0;
510        }
511
512        return delegate.getFontMetrics(metrics);
513    }
514
515    @LayoutlibDelegate
516    /*package*/ static int getFontMetricsInt(Paint thisPaint, FontMetricsInt fmi) {
517        // get the delegate
518        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
519        if (delegate == null) {
520            return 0;
521        }
522
523        if (delegate.mFonts.size() > 0) {
524            java.awt.FontMetrics javaMetrics = delegate.mFonts.get(0).mMetrics;
525            if (fmi != null) {
526                // Android expects negative ascent so we invert the value from Java.
527                fmi.top = - javaMetrics.getMaxAscent();
528                fmi.ascent = - javaMetrics.getAscent();
529                fmi.descent = javaMetrics.getDescent();
530                fmi.bottom = javaMetrics.getMaxDescent();
531                fmi.leading = javaMetrics.getLeading();
532            }
533
534            return javaMetrics.getHeight();
535        }
536
537        return 0;
538    }
539
540    @LayoutlibDelegate
541    /*package*/ static float native_measureText(Paint thisPaint, char[] text, int index,
542            int count) {
543        // get the delegate
544        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
545        if (delegate == null) {
546            return 0;
547        }
548
549        return delegate.measureText(text, index, count);
550    }
551
552    @LayoutlibDelegate
553    /*package*/ static float native_measureText(Paint thisPaint, String text, int start, int end) {
554        return native_measureText(thisPaint, text.toCharArray(), start, end - start);
555    }
556
557    @LayoutlibDelegate
558    /*package*/ static float native_measureText(Paint thisPaint, String text) {
559        return native_measureText(thisPaint, text.toCharArray(), 0, text.length());
560    }
561
562    @LayoutlibDelegate
563    /*package*/ static int native_breakText(Paint thisPaint, char[] text, int index, int count,
564            float maxWidth, float[] measuredWidth) {
565
566        // get the delegate
567        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
568        if (delegate == null) {
569            return 0;
570        }
571
572        int inc = count > 0 ? 1 : -1;
573
574        int measureIndex = 0;
575        float measureAcc = 0;
576        for (int i = index; i != index + count; i += inc, measureIndex++) {
577            int start, end;
578            if (i < index) {
579                start = i;
580                end = index;
581            } else {
582                start = index;
583                end = i;
584            }
585
586            // measure from start to end
587            float res = delegate.measureText(text, start, end - start + 1);
588
589            if (measuredWidth != null) {
590                measuredWidth[measureIndex] = res;
591            }
592
593            measureAcc += res;
594            if (res > maxWidth) {
595                // we should not return this char index, but since it's 0-based
596                // and we need to return a count, we simply return measureIndex;
597                return measureIndex;
598            }
599
600        }
601
602        return measureIndex;
603    }
604
605    @LayoutlibDelegate
606    /*package*/ static int native_breakText(Paint thisPaint, String text, boolean measureForwards,
607            float maxWidth, float[] measuredWidth) {
608        return native_breakText(thisPaint, text.toCharArray(), 0, text.length(), maxWidth,
609                measuredWidth);
610    }
611
612    @LayoutlibDelegate
613    /*package*/ static int native_init() {
614        Paint_Delegate newDelegate = new Paint_Delegate();
615        return sManager.addNewDelegate(newDelegate);
616    }
617
618    @LayoutlibDelegate
619    /*package*/ static int native_initWithPaint(int paint) {
620        // get the delegate from the native int.
621        Paint_Delegate delegate = sManager.getDelegate(paint);
622        if (delegate == null) {
623            return 0;
624        }
625
626        Paint_Delegate newDelegate = new Paint_Delegate(delegate);
627        return sManager.addNewDelegate(newDelegate);
628    }
629
630    @LayoutlibDelegate
631    /*package*/ static void native_reset(int native_object) {
632        // get the delegate from the native int.
633        Paint_Delegate delegate = sManager.getDelegate(native_object);
634        if (delegate == null) {
635            return;
636        }
637
638        delegate.reset();
639    }
640
641    @LayoutlibDelegate
642    /*package*/ static void native_set(int native_dst, int native_src) {
643        // get the delegate from the native int.
644        Paint_Delegate delegate_dst = sManager.getDelegate(native_dst);
645        if (delegate_dst == null) {
646            return;
647        }
648
649        // get the delegate from the native int.
650        Paint_Delegate delegate_src = sManager.getDelegate(native_src);
651        if (delegate_src == null) {
652            return;
653        }
654
655        delegate_dst.set(delegate_src);
656    }
657
658    @LayoutlibDelegate
659    /*package*/ static int native_getStyle(int native_object) {
660        // get the delegate from the native int.
661        Paint_Delegate delegate = sManager.getDelegate(native_object);
662        if (delegate == null) {
663            return 0;
664        }
665
666        return delegate.mStyle;
667    }
668
669    @LayoutlibDelegate
670    /*package*/ static void native_setStyle(int native_object, int style) {
671        // get the delegate from the native int.
672        Paint_Delegate delegate = sManager.getDelegate(native_object);
673        if (delegate == null) {
674            return;
675        }
676
677        delegate.mStyle = style;
678    }
679
680    @LayoutlibDelegate
681    /*package*/ static int native_getStrokeCap(int native_object) {
682        // get the delegate from the native int.
683        Paint_Delegate delegate = sManager.getDelegate(native_object);
684        if (delegate == null) {
685            return 0;
686        }
687
688        return delegate.mCap;
689    }
690
691    @LayoutlibDelegate
692    /*package*/ static void native_setStrokeCap(int native_object, int cap) {
693        // get the delegate from the native int.
694        Paint_Delegate delegate = sManager.getDelegate(native_object);
695        if (delegate == null) {
696            return;
697        }
698
699        delegate.mCap = cap;
700    }
701
702    @LayoutlibDelegate
703    /*package*/ static int native_getStrokeJoin(int native_object) {
704        // get the delegate from the native int.
705        Paint_Delegate delegate = sManager.getDelegate(native_object);
706        if (delegate == null) {
707            return 0;
708        }
709
710        return delegate.mJoin;
711    }
712
713    @LayoutlibDelegate
714    /*package*/ static void native_setStrokeJoin(int native_object, int join) {
715        // get the delegate from the native int.
716        Paint_Delegate delegate = sManager.getDelegate(native_object);
717        if (delegate == null) {
718            return;
719        }
720
721        delegate.mJoin = join;
722    }
723
724    @LayoutlibDelegate
725    /*package*/ static boolean native_getFillPath(int native_object, int src, int dst) {
726        Paint_Delegate paint = sManager.getDelegate(native_object);
727        if (paint == null) {
728            return false;
729        }
730
731        Path_Delegate srcPath = Path_Delegate.getDelegate(src);
732        if (srcPath == null) {
733            return true;
734        }
735
736        Path_Delegate dstPath = Path_Delegate.getDelegate(dst);
737        if (dstPath == null) {
738            return true;
739        }
740
741        Stroke stroke = paint.getJavaStroke();
742        Shape strokeShape = stroke.createStrokedShape(srcPath.getJavaShape());
743
744        dstPath.setJavaShape(strokeShape);
745
746        // FIXME figure out the return value?
747        return true;
748    }
749
750    @LayoutlibDelegate
751    /*package*/ static int native_setShader(int native_object, int shader) {
752        // get the delegate from the native int.
753        Paint_Delegate delegate = sManager.getDelegate(native_object);
754        if (delegate == null) {
755            return shader;
756        }
757
758        delegate.mShader = Shader_Delegate.getDelegate(shader);
759
760        return shader;
761    }
762
763    @LayoutlibDelegate
764    /*package*/ static int native_setColorFilter(int native_object, int filter) {
765        // get the delegate from the native int.
766        Paint_Delegate delegate = sManager.getDelegate(native_object);
767        if (delegate == null) {
768            return filter;
769        }
770
771        delegate.mColorFilter = ColorFilter_Delegate.getDelegate(filter);;
772
773        // since none of those are supported, display a fidelity warning right away
774        if (delegate.mColorFilter != null && delegate.mColorFilter.isSupported() == false) {
775            Bridge.getLog().fidelityWarning(LayoutLog.TAG_COLORFILTER,
776                    delegate.mColorFilter.getSupportMessage(), null, null /*data*/);
777        }
778
779        return filter;
780    }
781
782    @LayoutlibDelegate
783    /*package*/ static int native_setXfermode(int native_object, int xfermode) {
784        // get the delegate from the native int.
785        Paint_Delegate delegate = sManager.getDelegate(native_object);
786        if (delegate == null) {
787            return xfermode;
788        }
789
790        delegate.mXfermode = Xfermode_Delegate.getDelegate(xfermode);
791
792        return xfermode;
793    }
794
795    @LayoutlibDelegate
796    /*package*/ static int native_setPathEffect(int native_object, int effect) {
797        // get the delegate from the native int.
798        Paint_Delegate delegate = sManager.getDelegate(native_object);
799        if (delegate == null) {
800            return effect;
801        }
802
803        delegate.mPathEffect = PathEffect_Delegate.getDelegate(effect);
804
805        return effect;
806    }
807
808    @LayoutlibDelegate
809    /*package*/ static int native_setMaskFilter(int native_object, int maskfilter) {
810        // get the delegate from the native int.
811        Paint_Delegate delegate = sManager.getDelegate(native_object);
812        if (delegate == null) {
813            return maskfilter;
814        }
815
816        delegate.mMaskFilter = MaskFilter_Delegate.getDelegate(maskfilter);
817
818        // since none of those are supported, display a fidelity warning right away
819        if (delegate.mMaskFilter != null && delegate.mMaskFilter.isSupported() == false) {
820            Bridge.getLog().fidelityWarning(LayoutLog.TAG_MASKFILTER,
821                    delegate.mMaskFilter.getSupportMessage(), null, null /*data*/);
822        }
823
824        return maskfilter;
825    }
826
827    @LayoutlibDelegate
828    /*package*/ static int native_setTypeface(int native_object, int typeface) {
829        // get the delegate from the native int.
830        Paint_Delegate delegate = sManager.getDelegate(native_object);
831        if (delegate == null) {
832            return 0;
833        }
834
835        delegate.mTypeface = Typeface_Delegate.getDelegate(typeface);
836        delegate.updateFontObject();
837        return typeface;
838    }
839
840    @LayoutlibDelegate
841    /*package*/ static int native_setRasterizer(int native_object, int rasterizer) {
842        // get the delegate from the native int.
843        Paint_Delegate delegate = sManager.getDelegate(native_object);
844        if (delegate == null) {
845            return rasterizer;
846        }
847
848        delegate.mRasterizer = Rasterizer_Delegate.getDelegate(rasterizer);
849
850        // since none of those are supported, display a fidelity warning right away
851        if (delegate.mRasterizer != null && delegate.mRasterizer.isSupported() == false) {
852            Bridge.getLog().fidelityWarning(LayoutLog.TAG_RASTERIZER,
853                    delegate.mRasterizer.getSupportMessage(), null, null /*data*/);
854        }
855
856        return rasterizer;
857    }
858
859    @LayoutlibDelegate
860    /*package*/ static int native_getTextAlign(int native_object) {
861        // get the delegate from the native int.
862        Paint_Delegate delegate = sManager.getDelegate(native_object);
863        if (delegate == null) {
864            return 0;
865        }
866
867        return delegate.mTextAlign;
868    }
869
870    @LayoutlibDelegate
871    /*package*/ static void native_setTextAlign(int native_object, int align) {
872        // get the delegate from the native int.
873        Paint_Delegate delegate = sManager.getDelegate(native_object);
874        if (delegate == null) {
875            return;
876        }
877
878        delegate.mTextAlign = align;
879    }
880
881    @LayoutlibDelegate
882    /*package*/ static float native_getFontMetrics(int native_paint, FontMetrics metrics) {
883        // get the delegate from the native int.
884        Paint_Delegate delegate = sManager.getDelegate(native_paint);
885        if (delegate == null) {
886            return 0.f;
887        }
888
889        return delegate.getFontMetrics(metrics);
890    }
891
892    @LayoutlibDelegate
893    /*package*/ static int native_getTextWidths(int native_object, char[] text, int index,
894            int count, float[] widths) {
895        // get the delegate from the native int.
896        Paint_Delegate delegate = sManager.getDelegate(native_object);
897        if (delegate == null) {
898            return 0;
899        }
900
901        if (delegate.mFonts.size() > 0) {
902            // FIXME: handle multi-char characters (see measureText)
903            float totalAdvance = 0;
904            for (int i = 0; i < count; i++) {
905                char c = text[i + index];
906                boolean found = false;
907                for (FontInfo info : delegate.mFonts) {
908                    if (info.mFont.canDisplay(c)) {
909                        float adv = info.mMetrics.charWidth(c);
910                        totalAdvance += adv;
911                        if (widths != null) {
912                            widths[i] = adv;
913                        }
914
915                        found = true;
916                        break;
917                    }
918                }
919
920                if (found == false) {
921                    // no advance for this char.
922                    if (widths != null) {
923                        widths[i] = 0.f;
924                    }
925                }
926            }
927
928            return (int) totalAdvance;
929        }
930
931        return 0;
932    }
933
934    @LayoutlibDelegate
935    /*package*/ static int native_getTextWidths(int native_object, String text, int start,
936            int end, float[] widths) {
937        return native_getTextWidths(native_object, text.toCharArray(), start, end - start, widths);
938    }
939
940    @LayoutlibDelegate
941    /*package*/ static void native_getTextPath(int native_object,
942                char[] text, int index, int count, float x, float y, int path) {
943        // FIXME
944        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
945                "Paint.getTextPath is not supported.", null, null /*data*/);
946    }
947
948    @LayoutlibDelegate
949    /*package*/ static void native_getTextPath(int native_object,
950            String text, int start, int end, float x, float y, int path) {
951        // FIXME
952        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
953                "Paint.getTextPath is not supported.", null, null /*data*/);
954    }
955
956    @LayoutlibDelegate
957    /*package*/ static void nativeGetStringBounds(int nativePaint, String text, int start,
958            int end, Rect bounds) {
959        nativeGetCharArrayBounds(nativePaint, text.toCharArray(), start, end - start, bounds);
960    }
961
962    @LayoutlibDelegate
963    /*package*/ static void nativeGetCharArrayBounds(int nativePaint, char[] text, int index,
964            int count, Rect bounds) {
965
966        // get the delegate from the native int.
967        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
968        if (delegate == null) {
969            return;
970        }
971
972        // FIXME should test if the main font can display all those characters.
973        // See MeasureText
974        if (delegate.mFonts.size() > 0) {
975            FontInfo mainInfo = delegate.mFonts.get(0);
976
977            Rectangle2D rect = mainInfo.mFont.getStringBounds(text, index, index + count,
978                    delegate.mFontContext);
979            bounds.set(0, 0, (int) rect.getWidth(), (int) rect.getHeight());
980        }
981    }
982
983    @LayoutlibDelegate
984    /*package*/ static void finalizer(int nativePaint) {
985        sManager.removeJavaReferenceFor(nativePaint);
986    }
987
988    // ---- Private delegate/helper methods ----
989
990    /*package*/ Paint_Delegate() {
991        reset();
992    }
993
994    private Paint_Delegate(Paint_Delegate paint) {
995        set(paint);
996    }
997
998    private void set(Paint_Delegate paint) {
999        mFlags = paint.mFlags;
1000        mColor = paint.mColor;
1001        mStyle = paint.mStyle;
1002        mCap = paint.mCap;
1003        mJoin = paint.mJoin;
1004        mTextAlign = paint.mTextAlign;
1005        mTypeface = paint.mTypeface;
1006        mStrokeWidth = paint.mStrokeWidth;
1007        mStrokeMiter = paint.mStrokeMiter;
1008        mTextSize = paint.mTextSize;
1009        mTextScaleX = paint.mTextScaleX;
1010        mTextSkewX = paint.mTextSkewX;
1011        mXfermode = paint.mXfermode;
1012        mColorFilter = paint.mColorFilter;
1013        mShader = paint.mShader;
1014        mPathEffect = paint.mPathEffect;
1015        mMaskFilter = paint.mMaskFilter;
1016        mRasterizer = paint.mRasterizer;
1017        updateFontObject();
1018    }
1019
1020    private void reset() {
1021        mFlags = Paint.DEFAULT_PAINT_FLAGS;
1022        mColor = 0xFF000000;
1023        mStyle = Paint.Style.FILL.nativeInt;
1024        mCap = Paint.Cap.BUTT.nativeInt;
1025        mJoin = Paint.Join.MITER.nativeInt;
1026        mTextAlign = 0;
1027        mTypeface = Typeface_Delegate.getDelegate(Typeface.sDefaults[0].native_instance);
1028        mStrokeWidth = 1.f;
1029        mStrokeMiter = 4.f;
1030        mTextSize = 20.f;
1031        mTextScaleX = 1.f;
1032        mTextSkewX = 0.f;
1033        mXfermode = null;
1034        mColorFilter = null;
1035        mShader = null;
1036        mPathEffect = null;
1037        mMaskFilter = null;
1038        mRasterizer = null;
1039        updateFontObject();
1040    }
1041
1042    /**
1043     * Update the {@link Font} object from the typeface, text size and scaling
1044     */
1045    @SuppressWarnings("deprecation")
1046    private void updateFontObject() {
1047        if (mTypeface != null) {
1048            // Get the fonts from the TypeFace object.
1049            List<Font> fonts = mTypeface.getFonts();
1050
1051            // create new font objects as well as FontMetrics, based on the current text size
1052            // and skew info.
1053            ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(fonts.size());
1054            for (Font font : fonts) {
1055                FontInfo info = new FontInfo();
1056                info.mFont = font.deriveFont(mTextSize);
1057                if (mTextScaleX != 1.0 || mTextSkewX != 0) {
1058                    // TODO: support skew
1059                    info.mFont = info.mFont.deriveFont(new AffineTransform(
1060                            mTextScaleX, mTextSkewX, 0, 0, 1, 0));
1061                }
1062                info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont);
1063
1064                infoList.add(info);
1065            }
1066
1067            mFonts = Collections.unmodifiableList(infoList);
1068        }
1069    }
1070
1071    /*package*/ float measureText(char[] text, int index, int count) {
1072
1073        // WARNING: the logic in this method is similar to Canvas_Delegate.native_drawText
1074        // Any change to this method should be reflected there as well
1075
1076        if (mFonts.size() > 0) {
1077            FontInfo mainFont = mFonts.get(0);
1078            int i = index;
1079            int lastIndex = index + count;
1080            float total = 0f;
1081            while (i < lastIndex) {
1082                // always start with the main font.
1083                int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
1084                if (upTo == -1) {
1085                    // shortcut to exit
1086                    return total + mainFont.mMetrics.charsWidth(text, i, lastIndex - i);
1087                } else if (upTo > 0) {
1088                    total += mainFont.mMetrics.charsWidth(text, i, upTo - i);
1089                    i = upTo;
1090                    // don't call continue at this point. Since it is certain the main font
1091                    // cannot display the font a index upTo (now ==i), we move on to the
1092                    // fallback fonts directly.
1093                }
1094
1095                // no char supported, attempt to read the next char(s) with the
1096                // fallback font. In this case we only test the first character
1097                // and then go back to test with the main font.
1098                // Special test for 2-char characters.
1099                boolean foundFont = false;
1100                for (int f = 1 ; f < mFonts.size() ; f++) {
1101                    FontInfo fontInfo = mFonts.get(f);
1102
1103                    // need to check that the font can display the character. We test
1104                    // differently if the char is a high surrogate.
1105                    int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
1106                    upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
1107                    if (upTo == -1) {
1108                        total += fontInfo.mMetrics.charsWidth(text, i, charCount);
1109                        i += charCount;
1110                        foundFont = true;
1111                        break;
1112
1113                    }
1114                }
1115
1116                // in case no font can display the char, measure it with the main font.
1117                if (foundFont == false) {
1118                    int size = Character.isHighSurrogate(text[i]) ? 2 : 1;
1119                    total += mainFont.mMetrics.charsWidth(text, i, size);
1120                    i += size;
1121                }
1122            }
1123
1124            return total;
1125        }
1126
1127        return 0;
1128    }
1129
1130    private float getFontMetrics(FontMetrics metrics) {
1131        if (mFonts.size() > 0) {
1132            java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
1133            if (metrics != null) {
1134                // Android expects negative ascent so we invert the value from Java.
1135                metrics.top = - javaMetrics.getMaxAscent();
1136                metrics.ascent = - javaMetrics.getAscent();
1137                metrics.descent = javaMetrics.getDescent();
1138                metrics.bottom = javaMetrics.getMaxDescent();
1139                metrics.leading = javaMetrics.getLeading();
1140            }
1141
1142            return javaMetrics.getHeight();
1143        }
1144
1145        return 0;
1146    }
1147
1148
1149
1150    private static void setFlag(Paint thisPaint, int flagMask, boolean flagValue) {
1151        // get the delegate from the native int.
1152        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
1153        if (delegate == null) {
1154            return;
1155        }
1156
1157        if (flagValue) {
1158            delegate.mFlags |= flagMask;
1159        } else {
1160            delegate.mFlags &= ~flagMask;
1161        }
1162    }
1163}
1164