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