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