Paint_Delegate.java revision 42e2b28e65114175e208565d62ee0041fcd50d25
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 getTextAlign() {
118        return mTextAlign;
119    }
120
121    public float getStrokeWidth() {
122        return mStrokeWidth;
123    }
124
125    public float getStrokeMiter() {
126        return mStrokeMiter;
127    }
128
129    public int getJavaCap() {
130        switch (Paint.sCapArray[mCap]) {
131            case BUTT:
132                return BasicStroke.CAP_BUTT;
133            case ROUND:
134                return BasicStroke.CAP_ROUND;
135            default:
136            case SQUARE:
137                return BasicStroke.CAP_SQUARE;
138        }
139    }
140
141    public int getJavaJoin() {
142        switch (Paint.sJoinArray[mJoin]) {
143            default:
144            case MITER:
145                return BasicStroke.JOIN_MITER;
146            case ROUND:
147                return BasicStroke.JOIN_ROUND;
148            case BEVEL:
149                return BasicStroke.JOIN_BEVEL;
150        }
151    }
152
153    public int getXfermode() {
154        return mXfermode;
155    }
156
157    public int getColorFilter() {
158        return mColorFilter;
159    }
160
161    public int getShader() {
162        return mShader;
163    }
164
165    public int getPathEffect() {
166        return mPathEffect;
167    }
168
169    public int getMaskFilter() {
170        return mMaskFilter;
171    }
172
173    // ---- native methods ----
174
175    /*package*/ static int getFlags(Paint thisPaint) {
176        // get the delegate from the native int.
177        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
178        if (delegate == null) {
179            assert false;
180            return 0;
181        }
182
183        return delegate.mFlags;
184    }
185
186    /*package*/ static void setFlags(Paint thisPaint, int flags) {
187        // get the delegate from the native int.
188        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
189        if (delegate == null) {
190            assert false;
191            return;
192        }
193
194        delegate.mFlags = flags;
195    }
196
197    /*package*/ static void setFilterBitmap(Paint thisPaint, boolean filter) {
198        setFlag(thisPaint, Paint.FILTER_BITMAP_FLAG, filter);
199    }
200
201    /*package*/ static void setAntiAlias(Paint thisPaint, boolean aa) {
202        setFlag(thisPaint, Paint.ANTI_ALIAS_FLAG, aa);
203    }
204
205    /*package*/ static void setSubpixelText(Paint thisPaint, boolean subpixelText) {
206        setFlag(thisPaint, Paint.SUBPIXEL_TEXT_FLAG, subpixelText);
207    }
208
209    /*package*/ static void setUnderlineText(Paint thisPaint, boolean underlineText) {
210        setFlag(thisPaint, Paint.UNDERLINE_TEXT_FLAG, underlineText);
211    }
212
213    /*package*/ static void setStrikeThruText(Paint thisPaint, boolean strikeThruText) {
214        setFlag(thisPaint, Paint.STRIKE_THRU_TEXT_FLAG, strikeThruText);
215    }
216
217    /*package*/ static void setFakeBoldText(Paint thisPaint, boolean fakeBoldText) {
218        setFlag(thisPaint, Paint.FAKE_BOLD_TEXT_FLAG, fakeBoldText);
219    }
220
221    /*package*/ static void setDither(Paint thisPaint, boolean dither) {
222        setFlag(thisPaint, Paint.DITHER_FLAG, dither);
223    }
224
225    /*package*/ static void setLinearText(Paint thisPaint, boolean linearText) {
226        setFlag(thisPaint, Paint.LINEAR_TEXT_FLAG, linearText);
227    }
228
229    /*package*/ static int getColor(Paint thisPaint) {
230        // get the delegate from the native int.
231        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
232        if (delegate == null) {
233            assert false;
234            return 0;
235        }
236
237        return delegate.mColor;
238    }
239
240    /*package*/ static void setColor(Paint thisPaint, int color) {
241        // get the delegate from the native int.
242        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
243        if (delegate == null) {
244            assert false;
245            return;
246        }
247
248        delegate.mColor = color;
249    }
250
251    /*package*/ static int getAlpha(Paint thisPaint) {
252        // get the delegate from the native int.
253        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
254        if (delegate == null) {
255            assert false;
256            return 0;
257        }
258
259        return delegate.mColor >>> 24;
260    }
261
262    /*package*/ static void setAlpha(Paint thisPaint, int a) {
263        // get the delegate from the native int.
264        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
265        if (delegate == null) {
266            assert false;
267            return;
268        }
269
270        delegate.mColor = (a << 24) | (delegate.mColor & 0x00FFFFFF);
271    }
272
273    /*package*/ static float getStrokeWidth(Paint thisPaint) {
274        // get the delegate from the native int.
275        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
276        if (delegate == null) {
277            assert false;
278            return 1.f;
279        }
280
281        return delegate.mStrokeWidth;
282    }
283
284    /*package*/ static void setStrokeWidth(Paint thisPaint, float width) {
285        // get the delegate from the native int.
286        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
287        if (delegate == null) {
288            assert false;
289            return;
290        }
291
292        delegate.mStrokeWidth = width;
293    }
294
295    /*package*/ static float getStrokeMiter(Paint thisPaint) {
296        // get the delegate from the native int.
297        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
298        if (delegate == null) {
299            assert false;
300            return 1.f;
301        }
302
303        return delegate.mStrokeMiter;
304    }
305
306    /*package*/ static void setStrokeMiter(Paint thisPaint, float miter) {
307        // get the delegate from the native int.
308        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
309        if (delegate == null) {
310            assert false;
311            return;
312        }
313
314        delegate.mStrokeMiter = miter;
315    }
316
317    /*package*/ static void nSetShadowLayer(Paint thisPaint, float radius, float dx, float dy,
318            int color) {
319        // FIXME
320        throw new UnsupportedOperationException();
321    }
322
323    /*package*/ static float getTextSize(Paint thisPaint) {
324        // get the delegate from the native int.
325        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
326        if (delegate == null) {
327            assert false;
328            return 1.f;
329        }
330
331        return delegate.mTextSize;
332    }
333
334    /*package*/ static void setTextSize(Paint thisPaint, float textSize) {
335        // get the delegate from the native int.
336        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
337        if (delegate == null) {
338            assert false;
339            return;
340        }
341
342        delegate.mTextSize = textSize;
343        delegate.updateFontObject();
344    }
345
346    /*package*/ static float getTextScaleX(Paint thisPaint) {
347        // get the delegate from the native int.
348        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
349        if (delegate == null) {
350            assert false;
351            return 1.f;
352        }
353
354        return delegate.mTextScaleX;
355    }
356
357    /*package*/ static void setTextScaleX(Paint thisPaint, float scaleX) {
358        // get the delegate from the native int.
359        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
360        if (delegate == null) {
361            assert false;
362            return;
363        }
364
365        delegate.mTextScaleX = scaleX;
366        delegate.updateFontObject();
367    }
368
369    /*package*/ static float getTextSkewX(Paint thisPaint) {
370        // get the delegate from the native int.
371        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
372        if (delegate == null) {
373            assert false;
374            return 1.f;
375        }
376
377        return delegate.mTextSkewX;
378    }
379
380    /*package*/ static void setTextSkewX(Paint thisPaint, float skewX) {
381        // get the delegate from the native int.
382        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
383        if (delegate == null) {
384            assert false;
385            return;
386        }
387
388        delegate.mTextSkewX = skewX;
389        delegate.updateFontObject();
390    }
391
392    /*package*/ static float ascent(Paint thisPaint) {
393        // FIXME
394        throw new UnsupportedOperationException();
395    }
396
397    /*package*/ static float descent(Paint thisPaint) {
398        // FIXME
399        throw new UnsupportedOperationException();
400    }
401
402    /*package*/ static float getFontMetrics(Paint thisPaint, FontMetrics metrics) {
403        // get the delegate
404        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
405        if (delegate == null) {
406            assert false;
407            return 0;
408        }
409
410        if (delegate.mFonts.size() > 0) {
411            java.awt.FontMetrics javaMetrics = delegate.mFonts.get(0).mMetrics;
412            if (metrics != null) {
413                // Android expects negative ascent so we invert the value from Java.
414                metrics.top = - javaMetrics.getMaxAscent();
415                metrics.ascent = - javaMetrics.getAscent();
416                metrics.descent = javaMetrics.getDescent();
417                metrics.bottom = javaMetrics.getMaxDescent();
418                metrics.leading = javaMetrics.getLeading();
419            }
420
421            return javaMetrics.getHeight();
422        }
423
424        return 0;
425    }
426
427    /*package*/ static int getFontMetricsInt(Paint thisPaint, FontMetricsInt fmi) {
428        // get the delegate
429        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
430        if (delegate == null) {
431            assert false;
432            return 0;
433        }
434
435        if (delegate.mFonts.size() > 0) {
436            java.awt.FontMetrics javaMetrics = delegate.mFonts.get(0).mMetrics;
437            if (fmi != null) {
438                // Android expects negative ascent so we invert the value from Java.
439                fmi.top = - javaMetrics.getMaxAscent();
440                fmi.ascent = - javaMetrics.getAscent();
441                fmi.descent = javaMetrics.getDescent();
442                fmi.bottom = javaMetrics.getMaxDescent();
443                fmi.leading = javaMetrics.getLeading();
444            }
445
446            return javaMetrics.getHeight();
447        }
448
449        return 0;
450    }
451
452    /*package*/ static float native_measureText(Paint thisPaint, char[] text, int index,
453            int count) {
454        // WARNING: the logic in this method is similar to Canvas.drawText.
455        // Any change to this method should be reflected in Canvas.drawText
456
457        // get the delegate
458        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
459        if (delegate == null) {
460            assert false;
461            return 0;
462        }
463
464        return delegate.measureText(text, index, count);
465    }
466
467    /*package*/ static float native_measureText(Paint thisPaint, String text, int start, int end) {
468        return native_measureText(thisPaint, text.toCharArray(), start, end - start);
469    }
470
471    /*package*/ static float native_measureText(Paint thisPaint, String text) {
472        return native_measureText(thisPaint, text.toCharArray(), 0, text.length());
473    }
474
475    /*package*/ static int native_breakText(Paint thisPaint, char[] text, int index, int count,
476            float maxWidth, float[] measuredWidth) {
477        // FIXME
478        throw new UnsupportedOperationException();
479    }
480
481    /*package*/ static int native_breakText(Paint thisPaint, String text, boolean measureForwards,
482            float maxWidth, float[] measuredWidth) {
483        // FIXME
484        throw new UnsupportedOperationException();
485    }
486
487
488    /*package*/ static int native_init() {
489        Paint_Delegate newDelegate = new Paint_Delegate();
490        return sManager.addDelegate(newDelegate);
491    }
492
493    /*package*/ static int native_initWithPaint(int paint) {
494        // get the delegate from the native int.
495        Paint_Delegate delegate = sManager.getDelegate(paint);
496        if (delegate == null) {
497            assert false;
498            return 0;
499        }
500
501        Paint_Delegate newDelegate = new Paint_Delegate(delegate);
502        return sManager.addDelegate(newDelegate);
503    }
504
505    /*package*/ static void native_reset(int native_object) {
506        // get the delegate from the native int.
507        Paint_Delegate delegate = sManager.getDelegate(native_object);
508        if (delegate == null) {
509            assert false;
510            return;
511        }
512
513        delegate.reset();
514    }
515
516    /*package*/ static void native_set(int native_dst, int native_src) {
517        // get the delegate from the native int.
518        Paint_Delegate delegate_dst = sManager.getDelegate(native_dst);
519        if (delegate_dst == null) {
520            assert false;
521            return;
522        }
523
524        // get the delegate from the native int.
525        Paint_Delegate delegate_src = sManager.getDelegate(native_src);
526        if (delegate_src == null) {
527            assert false;
528            return;
529        }
530
531        delegate_dst.set(delegate_src);
532    }
533
534    /*package*/ static int native_getStyle(int native_object) {
535        // get the delegate from the native int.
536        Paint_Delegate delegate = sManager.getDelegate(native_object);
537        if (delegate == null) {
538            assert false;
539            return 0;
540        }
541
542        return delegate.mStyle;
543    }
544
545    /*package*/ static void native_setStyle(int native_object, int style) {
546        // get the delegate from the native int.
547        Paint_Delegate delegate = sManager.getDelegate(native_object);
548        if (delegate == null) {
549            assert false;
550            return;
551        }
552
553        delegate.mStyle = style;
554    }
555
556    /*package*/ static int native_getStrokeCap(int native_object) {
557        // get the delegate from the native int.
558        Paint_Delegate delegate = sManager.getDelegate(native_object);
559        if (delegate == null) {
560            assert false;
561            return 0;
562        }
563
564        return delegate.mCap;
565    }
566
567    /*package*/ static void native_setStrokeCap(int native_object, int cap) {
568        // get the delegate from the native int.
569        Paint_Delegate delegate = sManager.getDelegate(native_object);
570        if (delegate == null) {
571            assert false;
572            return;
573        }
574
575        delegate.mCap = cap;
576    }
577
578    /*package*/ static int native_getStrokeJoin(int native_object) {
579        // get the delegate from the native int.
580        Paint_Delegate delegate = sManager.getDelegate(native_object);
581        if (delegate == null) {
582            assert false;
583            return 0;
584        }
585
586        return delegate.mJoin;
587    }
588
589    /*package*/ static void native_setStrokeJoin(int native_object, int join) {
590        // get the delegate from the native int.
591        Paint_Delegate delegate = sManager.getDelegate(native_object);
592        if (delegate == null) {
593            assert false;
594            return;
595        }
596
597        delegate.mJoin = join;
598    }
599
600    /*package*/ static boolean native_getFillPath(int native_object, int src, int dst) {
601        // FIXME
602        throw new UnsupportedOperationException();
603    }
604
605    /*package*/ static int native_setShader(int native_object, int shader) {
606        // get the delegate from the native int.
607        Paint_Delegate delegate = sManager.getDelegate(native_object);
608        if (delegate == null) {
609            assert false;
610            return shader;
611        }
612
613        return delegate.mShader = shader;
614    }
615
616    /*package*/ static int native_setColorFilter(int native_object, int filter) {
617        // get the delegate from the native int.
618        Paint_Delegate delegate = sManager.getDelegate(native_object);
619        if (delegate == null) {
620            assert false;
621            return filter;
622        }
623
624        return delegate.mColorFilter = filter;
625    }
626
627    /*package*/ static int native_setXfermode(int native_object, int xfermode) {
628        // get the delegate from the native int.
629        Paint_Delegate delegate = sManager.getDelegate(native_object);
630        if (delegate == null) {
631            assert false;
632            return xfermode;
633        }
634
635        return delegate.mXfermode = xfermode;
636    }
637
638    /*package*/ static int native_setPathEffect(int native_object, int effect) {
639        // get the delegate from the native int.
640        Paint_Delegate delegate = sManager.getDelegate(native_object);
641        if (delegate == null) {
642            assert false;
643            return effect;
644        }
645
646        return delegate.mPathEffect = effect;
647    }
648
649    /*package*/ static int native_setMaskFilter(int native_object, int maskfilter) {
650        // get the delegate from the native int.
651        Paint_Delegate delegate = sManager.getDelegate(native_object);
652        if (delegate == null) {
653            assert false;
654            return maskfilter;
655        }
656
657        return delegate.mMaskFilter = maskfilter;
658    }
659
660    /*package*/ static int native_setTypeface(int native_object, int typeface) {
661        // get the delegate from the native int.
662        Paint_Delegate delegate = sManager.getDelegate(native_object);
663        if (delegate == null) {
664            assert false;
665            return 0;
666        }
667
668        delegate.mTypeface = typeface;
669        delegate.updateFontObject();
670        return delegate.mTypeface;
671    }
672
673    /*package*/ static int native_setRasterizer(int native_object, int rasterizer) {
674        // FIXME
675        throw new UnsupportedOperationException();
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
819    private Paint_Delegate(Paint_Delegate paint) {
820        set(paint);
821    }
822
823    private void set(Paint_Delegate paint) {
824        mFlags = paint.mFlags;
825        mColor = paint.mColor;
826        mStyle = paint.mStyle;
827        mCap = paint.mCap;
828        mJoin = paint.mJoin;
829        mTextAlign = paint.mTextAlign;
830        mTypeface = paint.mTypeface;
831        mStrokeWidth = paint.mStrokeWidth;
832        mStrokeMiter = paint.mStrokeMiter;
833        mTextSize = paint.mTextSize;
834        mTextScaleX = paint.mTextScaleX;
835        mTextSkewX = paint.mTextSkewX;
836        mXfermode = paint.mXfermode;
837        mColorFilter = paint.mColorFilter;
838        mShader = paint.mShader;
839        mPathEffect = paint.mPathEffect;
840        mMaskFilter = paint.mMaskFilter;
841        updateFontObject();
842    }
843
844    private void reset() {
845        mFlags = Paint.DEFAULT_PAINT_FLAGS;
846        mColor = 0;
847        mStyle = 0;
848        mCap = 0;
849        mJoin = 0;
850        mTextAlign = 0;
851        mTypeface = Typeface.sDefaults[0].native_instance;
852        mStrokeWidth = 1.f;
853        mStrokeMiter = 2.f;
854        mTextSize = 20.f;
855        mTextScaleX = 1.f;
856        mTextSkewX = 0.f;
857        mXfermode = 0;
858        mColorFilter = 0;
859        mShader = 0;
860        mPathEffect = 0;
861        mMaskFilter = 0;
862        updateFontObject();
863    }
864
865    /**
866     * Update the {@link Font} object from the typeface, text size and scaling
867     */
868    private void updateFontObject() {
869        if (mTypeface != 0) {
870            // Get the fonts from the TypeFace object.
871            List<Font> fonts = Typeface_Delegate.getFonts(mTypeface);
872
873            // create new font objects as well as FontMetrics, based on the current text size
874            // and skew info.
875            ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(fonts.size());
876            for (Font font : fonts) {
877                FontInfo info = new FontInfo();
878                info.mFont = font.deriveFont(mTextSize);
879                if (mTextScaleX != 1.0 || mTextSkewX != 0) {
880                    // TODO: support skew
881                    info.mFont = info.mFont.deriveFont(new AffineTransform(
882                            mTextScaleX, mTextSkewX, 0, 0, 1, 0));
883                }
884                info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont);
885
886                infoList.add(info);
887            }
888
889            mFonts = Collections.unmodifiableList(infoList);
890        }
891    }
892
893    /*package*/ float measureText(char[] text, int index, int count) {
894        if (mFonts.size() > 0) {
895            FontInfo mainFont = mFonts.get(0);
896            int i = index;
897            int lastIndex = index + count;
898            float total = 0f;
899            while (i < lastIndex) {
900                // always start with the main font.
901                int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
902                if (upTo == -1) {
903                    // shortcut to exit
904                    return total + mainFont.mMetrics.charsWidth(text, i, lastIndex - i);
905                } else if (upTo > 0) {
906                    total += mainFont.mMetrics.charsWidth(text, i, upTo - i);
907                    i = upTo;
908                    // don't call continue at this point. Since it is certain the main font
909                    // cannot display the font a index upTo (now ==i), we move on to the
910                    // fallback fonts directly.
911                }
912
913                // no char supported, attempt to read the next char(s) with the
914                // fallback font. In this case we only test the first character
915                // and then go back to test with the main font.
916                // Special test for 2-char characters.
917                boolean foundFont = false;
918                for (int f = 1 ; f < mFonts.size() ; f++) {
919                    FontInfo fontInfo = mFonts.get(f);
920
921                    // need to check that the font can display the character. We test
922                    // differently if the char is a high surrogate.
923                    int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
924                    upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
925                    if (upTo == -1) {
926                        total += fontInfo.mMetrics.charsWidth(text, i, charCount);
927                        i += charCount;
928                        foundFont = true;
929                        break;
930
931                    }
932                }
933
934                // in case no font can display the char, measure it with the main font.
935                if (foundFont == false) {
936                    int size = Character.isHighSurrogate(text[i]) ? 2 : 1;
937                    total += mainFont.mMetrics.charsWidth(text, i, size);
938                    i += size;
939                }
940            }
941        }
942
943        return 0;
944
945    }
946
947    private static void setFlag(Paint thisPaint, int flagMask, boolean flagValue) {
948        // get the delegate from the native int.
949        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
950        if (delegate == null) {
951            assert false;
952            return;
953        }
954
955        if (flagValue) {
956            delegate.mFlags |= flagMask;
957        } else {
958            delegate.mFlags &= ~flagMask;
959        }
960    }
961}
962