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