Paint_Delegate.java revision 9f63ff263b0a97f0fa63e97136c18f6abccbfc68
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.DelegateManager;
20
21import android.graphics.Paint.FontMetrics;
22import android.graphics.Paint.FontMetricsInt;
23
24import java.awt.Font;
25import java.awt.Toolkit;
26import java.awt.font.FontRenderContext;
27import java.awt.geom.AffineTransform;
28import java.util.ArrayList;
29import java.util.Collections;
30import java.util.List;
31
32/**
33 * Delegate implementing the native methods of android.graphics.Paint
34 *
35 * Through the layoutlib_create tool, the original native methods of Paint have been replaced
36 * by calls to methods of the same name in this delegate class.
37 *
38 * This class behaves like the original native implementation, but in Java, keeping previously
39 * native data into its own objects and mapping them to int that are sent back and forth between
40 * it and the original Paint class.
41 *
42 * @see DelegateManager
43 *
44 */
45public class Paint_Delegate {
46
47    /**
48     * Class associating a {@link Font} and it's {@link java.awt.FontMetrics}.
49     */
50    public static final class FontInfo {
51        Font mFont;
52        java.awt.FontMetrics mMetrics;
53    }
54
55    // ---- delegate manager ----
56    private static final DelegateManager<Paint_Delegate> sManager =
57            new DelegateManager<Paint_Delegate>();
58
59    // ---- delegate helper data ----
60    private List<FontInfo> mFonts;
61    private final FontRenderContext mFontContext = new FontRenderContext(
62            new AffineTransform(), true, true);
63
64    // ---- delegate data ----
65    private int mFlags;
66    private int mColor;
67    private int mStyle;
68    private int mCap;
69    private int mJoin;
70    private int mAlign;
71    private int mTypeface;
72    private float mStrokeWidth;
73    private float mStrokeMiter;
74    private float mTextSize;
75    private float mTextScaleX;
76    private float mTextSkewX;
77
78
79    // ---- Public Helper methods ----
80
81    /**
82     * Returns the list of {@link Font} objects. The first item is the main font, the rest
83     * are fall backs for characters not present in the main font.
84     */
85    public List<FontInfo> getFonts() {
86        return mFonts;
87    }
88
89
90    // ---- native methods ----
91
92    /*package*/ static int getFlags(Paint thisPaint) {
93        // get the delegate from the native int.
94        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
95        if (delegate == null) {
96            assert false;
97            return 0;
98        }
99
100        return delegate.mFlags;
101    }
102
103    /*package*/ static void setFlags(Paint thisPaint, int flags) {
104        // get the delegate from the native int.
105        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
106        if (delegate == null) {
107            assert false;
108            return;
109        }
110
111        delegate.mFlags = flags;
112    }
113
114    /*package*/ static void setFilterBitmap(Paint thisPaint, boolean filter) {
115        // FIXME
116        throw new UnsupportedOperationException();
117    }
118
119    /*package*/ static void setAntiAlias(Paint thisPaint, boolean aa) {
120        setFlag(thisPaint, Paint.ANTI_ALIAS_FLAG, aa);
121    }
122
123    /*package*/ static void setSubpixelText(Paint thisPaint, boolean subpixelText) {
124        setFlag(thisPaint, Paint.SUBPIXEL_TEXT_FLAG, subpixelText);
125    }
126
127    /*package*/ static void setUnderlineText(Paint thisPaint, boolean underlineText) {
128        setFlag(thisPaint, Paint.UNDERLINE_TEXT_FLAG, underlineText);
129    }
130
131    /*package*/ static void setStrikeThruText(Paint thisPaint, boolean strikeThruText) {
132        setFlag(thisPaint, Paint.STRIKE_THRU_TEXT_FLAG, strikeThruText);
133    }
134
135    /*package*/ static void setFakeBoldText(Paint thisPaint, boolean fakeBoldText) {
136        setFlag(thisPaint, Paint.FAKE_BOLD_TEXT_FLAG, fakeBoldText);
137    }
138
139    /*package*/ static void setDither(Paint thisPaint, boolean dither) {
140        setFlag(thisPaint, Paint.DITHER_FLAG, dither);
141    }
142
143    /*package*/ static void setLinearText(Paint thisPaint, boolean linearText) {
144        setFlag(thisPaint, Paint.LINEAR_TEXT_FLAG, linearText);
145    }
146
147    /*package*/ static int getColor(Paint thisPaint) {
148        // get the delegate from the native int.
149        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
150        if (delegate == null) {
151            assert false;
152            return 0;
153        }
154
155        return delegate.mColor;
156    }
157
158    /*package*/ static void setColor(Paint thisPaint, int color) {
159        // get the delegate from the native int.
160        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
161        if (delegate == null) {
162            assert false;
163            return;
164        }
165
166        delegate.mColor = color;
167    }
168
169    /*package*/ static int getAlpha(Paint thisPaint) {
170        // get the delegate from the native int.
171        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
172        if (delegate == null) {
173            assert false;
174            return 0;
175        }
176
177        return delegate.mColor >>> 24;
178    }
179
180    /*package*/ static void setAlpha(Paint thisPaint, int a) {
181        // get the delegate from the native int.
182        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
183        if (delegate == null) {
184            assert false;
185            return;
186        }
187
188        delegate.mColor = (a << 24) | (delegate.mColor & 0x00FFFFFF);
189    }
190
191    /*package*/ static float getStrokeWidth(Paint thisPaint) {
192        // get the delegate from the native int.
193        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
194        if (delegate == null) {
195            assert false;
196            return 1.f;
197        }
198
199        return delegate.mStrokeWidth;
200    }
201
202    /*package*/ static void setStrokeWidth(Paint thisPaint, float width) {
203        // get the delegate from the native int.
204        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
205        if (delegate == null) {
206            assert false;
207            return;
208        }
209
210        delegate.mStrokeWidth = width;
211    }
212
213    /*package*/ static float getStrokeMiter(Paint thisPaint) {
214        // get the delegate from the native int.
215        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
216        if (delegate == null) {
217            assert false;
218            return 1.f;
219        }
220
221        return delegate.mStrokeMiter;
222    }
223
224    /*package*/ static void setStrokeMiter(Paint thisPaint, float miter) {
225        // get the delegate from the native int.
226        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
227        if (delegate == null) {
228            assert false;
229            return;
230        }
231
232        delegate.mStrokeMiter = miter;
233    }
234
235    /*package*/ static void nSetShadowLayer(Paint thisPaint, float radius, float dx, float dy,
236            int color) {
237        // FIXME
238        throw new UnsupportedOperationException();
239    }
240
241    /*package*/ static float getTextSize(Paint thisPaint) {
242        // get the delegate from the native int.
243        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
244        if (delegate == null) {
245            assert false;
246            return 1.f;
247        }
248
249        return delegate.mTextSize;
250    }
251
252    /*package*/ static void setTextSize(Paint thisPaint, float textSize) {
253        // get the delegate from the native int.
254        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
255        if (delegate == null) {
256            assert false;
257            return;
258        }
259
260        delegate.mTextSize = textSize;
261    }
262
263    /*package*/ static float getTextScaleX(Paint thisPaint) {
264        // get the delegate from the native int.
265        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
266        if (delegate == null) {
267            assert false;
268            return 1.f;
269        }
270
271        return delegate.mTextScaleX;
272    }
273
274    /*package*/ static void setTextScaleX(Paint thisPaint, float scaleX) {
275        // get the delegate from the native int.
276        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
277        if (delegate == null) {
278            assert false;
279            return;
280        }
281
282        delegate.mTextScaleX = scaleX;
283    }
284
285    /*package*/ static float getTextSkewX(Paint thisPaint) {
286        // get the delegate from the native int.
287        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
288        if (delegate == null) {
289            assert false;
290            return 1.f;
291        }
292
293        return delegate.mTextSkewX;
294    }
295
296    /*package*/ static void setTextSkewX(Paint thisPaint, float skewX) {
297        // get the delegate from the native int.
298        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
299        if (delegate == null) {
300            assert false;
301            return;
302        }
303
304        delegate.mTextSkewX = skewX;
305    }
306
307    /*package*/ static float ascent(Paint thisPaint) {
308        // FIXME
309        throw new UnsupportedOperationException();
310    }
311
312    /*package*/ static float descent(Paint thisPaint) {
313        // FIXME
314        throw new UnsupportedOperationException();
315    }
316
317    /*package*/ static float getFontMetrics(Paint thisPaint, FontMetrics metrics) {
318        // FIXME
319        throw new UnsupportedOperationException();
320    }
321
322    /*package*/ static int getFontMetricsInt(Paint thisPaint, FontMetricsInt fmi) {
323        // FIXME
324        throw new UnsupportedOperationException();
325    }
326
327    /*package*/ static float native_measureText(Paint thisPaint, char[] text, int index,
328            int count) {
329        // WARNING: the logic in this method is similar to Canvas.drawText.
330        // Any change to this method should be reflected in Canvas.drawText
331
332        // get the delegate
333        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
334        if (delegate == null) {
335            assert false;
336            return 0;
337        }
338
339        if (delegate.mFonts.size() > 0) {
340            FontInfo mainFont = delegate.mFonts.get(0);
341            int i = index;
342            int lastIndex = index + count;
343            float total = 0f;
344            while (i < lastIndex) {
345                // always start with the main font.
346                int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
347                if (upTo == -1) {
348                    // shortcut to exit
349                    return total + mainFont.mMetrics.charsWidth(text, i, lastIndex - i);
350                } else if (upTo > 0) {
351                    total += mainFont.mMetrics.charsWidth(text, i, upTo - i);
352                    i = upTo;
353                    // don't call continue at this point. Since it is certain the main font
354                    // cannot display the font a index upTo (now ==i), we move on to the
355                    // fallback fonts directly.
356                }
357
358                // no char supported, attempt to read the next char(s) with the
359                // fallback font. In this case we only test the first character
360                // and then go back to test with the main font.
361                // Special test for 2-char characters.
362                boolean foundFont = false;
363                for (int f = 1 ; f < delegate.mFonts.size() ; f++) {
364                    FontInfo fontInfo = delegate.mFonts.get(f);
365
366                    // need to check that the font can display the character. We test
367                    // differently if the char is a high surrogate.
368                    int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
369                    upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
370                    if (upTo == -1) {
371                        total += fontInfo.mMetrics.charsWidth(text, i, charCount);
372                        i += charCount;
373                        foundFont = true;
374                        break;
375
376                    }
377                }
378
379                // in case no font can display the char, measure it with the main font.
380                if (foundFont == false) {
381                    int size = Character.isHighSurrogate(text[i]) ? 2 : 1;
382                    total += mainFont.mMetrics.charsWidth(text, i, size);
383                    i += size;
384                }
385            }
386        }
387
388        return 0;
389    }
390
391    /*package*/ static float native_measureText(Paint thisPaint, String text, int start, int end) {
392        return native_measureText(thisPaint, text.toCharArray(), start, end - start);
393    }
394
395    /*package*/ static float native_measureText(Paint thisPaint, String text) {
396        return native_measureText(thisPaint, text.toCharArray(), 0, text.length());
397    }
398
399    /*package*/ static int native_breakText(Paint thisPaint, char[] text, int index, int count,
400            float maxWidth, float[] measuredWidth) {
401        // FIXME
402        throw new UnsupportedOperationException();
403    }
404
405    /*package*/ static int native_breakText(Paint thisPaint, String text, boolean measureForwards,
406            float maxWidth, float[] measuredWidth) {
407        // FIXME
408        throw new UnsupportedOperationException();
409    }
410
411
412    /*package*/ static int native_init() {
413        Paint_Delegate newDelegate = new Paint_Delegate();
414        return sManager.addDelegate(newDelegate);
415    }
416
417    /*package*/ static int native_initWithPaint(int paint) {
418        // get the delegate from the native int.
419        Paint_Delegate delegate = sManager.getDelegate(paint);
420        if (delegate == null) {
421            assert false;
422            return 0;
423        }
424
425        Paint_Delegate newDelegate = new Paint_Delegate(delegate);
426        return sManager.addDelegate(newDelegate);
427    }
428
429    /*package*/ static void native_reset(int native_object) {
430        // get the delegate from the native int.
431        Paint_Delegate delegate = sManager.getDelegate(native_object);
432        if (delegate == null) {
433            assert false;
434            return;
435        }
436
437        delegate.reset();
438    }
439
440    /*package*/ static void native_set(int native_dst, int native_src) {
441        // get the delegate from the native int.
442        Paint_Delegate delegate_dst = sManager.getDelegate(native_dst);
443        if (delegate_dst == null) {
444            assert false;
445            return;
446        }
447
448        // get the delegate from the native int.
449        Paint_Delegate delegate_src = sManager.getDelegate(native_src);
450        if (delegate_src == null) {
451            assert false;
452            return;
453        }
454
455        delegate_dst.set(delegate_src);
456    }
457
458    /*package*/ static int native_getStyle(int native_object) {
459        // get the delegate from the native int.
460        Paint_Delegate delegate = sManager.getDelegate(native_object);
461        if (delegate == null) {
462            assert false;
463            return 0;
464        }
465
466        return delegate.mStyle;
467    }
468
469    /*package*/ static void native_setStyle(int native_object, int style) {
470        // get the delegate from the native int.
471        Paint_Delegate delegate = sManager.getDelegate(native_object);
472        if (delegate == null) {
473            assert false;
474            return;
475        }
476
477        delegate.mStyle = style;
478    }
479
480    /*package*/ static int native_getStrokeCap(int native_object) {
481        // get the delegate from the native int.
482        Paint_Delegate delegate = sManager.getDelegate(native_object);
483        if (delegate == null) {
484            assert false;
485            return 0;
486        }
487
488        return delegate.mCap;
489    }
490
491    /*package*/ static void native_setStrokeCap(int native_object, int cap) {
492        // get the delegate from the native int.
493        Paint_Delegate delegate = sManager.getDelegate(native_object);
494        if (delegate == null) {
495            assert false;
496            return;
497        }
498
499        delegate.mCap = cap;
500    }
501
502    /*package*/ static int native_getStrokeJoin(int native_object) {
503        // get the delegate from the native int.
504        Paint_Delegate delegate = sManager.getDelegate(native_object);
505        if (delegate == null) {
506            assert false;
507            return 0;
508        }
509
510        return delegate.mJoin;
511    }
512
513    /*package*/ static void native_setStrokeJoin(int native_object, int join) {
514        // get the delegate from the native int.
515        Paint_Delegate delegate = sManager.getDelegate(native_object);
516        if (delegate == null) {
517            assert false;
518            return;
519        }
520
521        delegate.mJoin = join;
522    }
523
524    /*package*/ static boolean native_getFillPath(int native_object, int src, int dst) {
525        // FIXME
526        throw new UnsupportedOperationException();
527    }
528
529    /*package*/ static int native_setShader(int native_object, int shader) {
530        // FIXME
531        throw new UnsupportedOperationException();
532    }
533
534    /*package*/ static int native_setColorFilter(int native_object, int filter) {
535        // FIXME
536        throw new UnsupportedOperationException();
537    }
538
539    /*package*/ static int native_setXfermode(int native_object, int xfermode) {
540        // FIXME
541        throw new UnsupportedOperationException();
542    }
543
544    /*package*/ static int native_setPathEffect(int native_object, int effect) {
545        // FIXME
546        throw new UnsupportedOperationException();
547    }
548
549    /*package*/ static int native_setMaskFilter(int native_object, int maskfilter) {
550        // FIXME
551        throw new UnsupportedOperationException();
552    }
553
554    /*package*/ static int native_setTypeface(int native_object, int typeface) {
555        // get the delegate from the native int.
556        Paint_Delegate delegate = sManager.getDelegate(native_object);
557        if (delegate == null) {
558            assert false;
559            return 0;
560        }
561
562        return delegate.mTypeface = typeface;
563    }
564
565    /*package*/ static int native_setRasterizer(int native_object, int rasterizer) {
566        // FIXME
567        throw new UnsupportedOperationException();
568    }
569
570
571    /*package*/ static int native_getTextAlign(int native_object) {
572        // get the delegate from the native int.
573        Paint_Delegate delegate = sManager.getDelegate(native_object);
574        if (delegate == null) {
575            assert false;
576            return 0;
577        }
578
579        return delegate.mAlign;
580    }
581
582    /*package*/ static void native_setTextAlign(int native_object, int align) {
583        // get the delegate from the native int.
584        Paint_Delegate delegate = sManager.getDelegate(native_object);
585        if (delegate == null) {
586            assert false;
587            return;
588        }
589
590        delegate.mAlign = align;
591    }
592
593    /*package*/ static float native_getFontMetrics(int native_paint, FontMetrics metrics) {
594        // FIXME
595        throw new UnsupportedOperationException();
596    }
597
598    /*package*/ static int native_getTextWidths(int native_object, char[] text, int index,
599            int count, float[] widths) {
600        // FIXME
601        throw new UnsupportedOperationException();
602    }
603
604    /*package*/ static int native_getTextWidths(int native_object, String text, int start,
605            int end, float[] widths) {
606        // FIXME
607        throw new UnsupportedOperationException();
608    }
609
610    /*package*/ static float native_getTextRunAdvances(int native_object,
611            char[] text, int index, int count, int contextIndex, int contextCount,
612            int flags, float[] advances, int advancesIndex) {
613        // FIXME
614        throw new UnsupportedOperationException();
615    }
616
617    /*package*/ static float native_getTextRunAdvances(int native_object,
618            String text, int start, int end, int contextStart, int contextEnd,
619            int flags, float[] advances, int advancesIndex) {
620        // FIXME
621        throw new UnsupportedOperationException();
622    }
623
624    /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, char[] text,
625            int contextStart, int contextLength, int flags, int offset, int cursorOpt) {
626        // FIXME
627        throw new UnsupportedOperationException();
628    }
629
630    /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, String text,
631            int contextStart, int contextEnd, int flags, int offset, int cursorOpt) {
632        // FIXME
633        throw new UnsupportedOperationException();
634    }
635
636    /*package*/ static void native_getTextPath(int native_object, int bidiFlags,
637                char[] text, int index, int count, float x, float y, int path) {
638        // FIXME
639        throw new UnsupportedOperationException();
640    }
641
642    /*package*/ static void native_getTextPath(int native_object, int bidiFlags,
643            String text, int start, int end, float x, float y, int path) {
644        // FIXME
645        throw new UnsupportedOperationException();
646    }
647
648    /*package*/ static void nativeGetStringBounds(int nativePaint, String text, int start,
649            int end, Rect bounds) {
650        // FIXME
651        throw new UnsupportedOperationException();
652    }
653
654    /*package*/ static void nativeGetCharArrayBounds(int nativePaint, char[] text, int index,
655            int count, Rect bounds) {
656        // FIXME
657        throw new UnsupportedOperationException();
658    }
659
660    /*package*/ static void finalizer(int nativePaint) {
661        sManager.removeDelegate(nativePaint);
662    }
663
664    // ---- Private delegate/helper methods ----
665
666    private Paint_Delegate() {
667        reset();
668
669        mTypeface = Typeface.sDefaults[0].native_instance;
670        updateFontObject();
671    }
672
673    private Paint_Delegate(Paint_Delegate paint) {
674        set(paint);
675        updateFontObject();
676    }
677
678    private void set(Paint_Delegate paint) {
679        mFlags = paint.mFlags;
680        mColor = paint.mColor;
681        mStyle = paint.mStyle;
682        mCap = paint.mCap;
683        mJoin = paint.mJoin;
684        mAlign = paint.mAlign;
685        mTypeface = paint.mTypeface;
686        mStrokeWidth = paint.mStrokeWidth;
687        mStrokeMiter = paint.mStrokeMiter;
688        mTextSize = paint.mTextSize;
689        mTextScaleX = paint.mTextScaleX;
690        mTextSkewX = paint.mTextSkewX;
691    }
692
693    private void reset() {
694        mFlags = Paint.DEFAULT_PAINT_FLAGS;
695        mColor = 0;
696        mStyle = 0;
697        mCap = 0;
698        mJoin = 0;
699        mAlign = 0;
700        mTypeface = 0;
701        mStrokeWidth = 1.f;
702        mStrokeMiter = 2.f;
703        mTextSize = 20.f;
704        mTextScaleX = 1.f;
705        mTextSkewX = 0.f;
706    }
707
708    /**
709     * Update the {@link Font} object from the typeface, text size and scaling
710     */
711    private void updateFontObject() {
712        if (mTypeface != 0) {
713            // Get the fonts from the TypeFace object.
714            List<Font> fonts = Typeface_Delegate.getFonts(mTypeface);
715
716            // create new font objects as well as FontMetrics, based on the current text size
717            // and skew info.
718            ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(fonts.size());
719            for (Font font : fonts) {
720                FontInfo info = new FontInfo();
721                info.mFont = font.deriveFont(mTextSize);
722                if (mTextScaleX != 1.0 || mTextSkewX != 0) {
723                    // TODO: support skew
724                    info.mFont = info.mFont.deriveFont(new AffineTransform(
725                            mTextScaleX, mTextSkewX, 0, 0, 1, 0));
726                }
727                info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont);
728
729                infoList.add(info);
730            }
731
732            mFonts = Collections.unmodifiableList(infoList);
733        }
734    }
735
736    private static void setFlag(Paint thisPaint, int flagMask, boolean flagValue) {
737        // get the delegate from the native int.
738        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
739        if (delegate == null) {
740            assert false;
741            return;
742        }
743
744        if (flagValue) {
745            delegate.mFlags |= flagMask;
746        } else {
747            delegate.mFlags &= ~flagMask;
748        }
749    }
750}
751