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