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