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