• 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)571     /*package*/ static float native_measureText(Paint thisPaint, char[] text, int index,
572             int count) {
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);
580     }
581 
582     @LayoutlibDelegate
native_measureText(Paint thisPaint, String text, int start, int end)583     /*package*/ static float native_measureText(Paint thisPaint, String text, int start, int end) {
584         return native_measureText(thisPaint, text.toCharArray(), start, end - start);
585     }
586 
587     @LayoutlibDelegate
native_measureText(Paint thisPaint, String text)588     /*package*/ static float native_measureText(Paint thisPaint, String text) {
589         return native_measureText(thisPaint, text.toCharArray(), 0, text.length());
590     }
591 
592     @LayoutlibDelegate
native_breakText(Paint thisPaint, char[] text, int index, int count, float maxWidth, float[] measuredWidth)593     /*package*/ static int native_breakText(Paint thisPaint, char[] text, int index, int count,
594             float maxWidth, float[] measuredWidth) {
595 
596         // get the delegate
597         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
598         if (delegate == null) {
599             return 0;
600         }
601 
602         int inc = count > 0 ? 1 : -1;
603 
604         int measureIndex = 0;
605         float measureAcc = 0;
606         for (int i = index; i != index + count; i += inc, measureIndex++) {
607             int start, end;
608             if (i < index) {
609                 start = i;
610                 end = index;
611             } else {
612                 start = index;
613                 end = i;
614             }
615 
616             // measure from start to end
617             float res = delegate.measureText(text, start, end - start + 1);
618 
619             if (measuredWidth != null) {
620                 measuredWidth[measureIndex] = res;
621             }
622 
623             measureAcc += res;
624             if (res > maxWidth) {
625                 // we should not return this char index, but since it's 0-based
626                 // and we need to return a count, we simply return measureIndex;
627                 return measureIndex;
628             }
629 
630         }
631 
632         return measureIndex;
633     }
634 
635     @LayoutlibDelegate
native_breakText(Paint thisPaint, String text, boolean measureForwards, float maxWidth, float[] measuredWidth)636     /*package*/ static int native_breakText(Paint thisPaint, String text, boolean measureForwards,
637             float maxWidth, float[] measuredWidth) {
638         return native_breakText(thisPaint, text.toCharArray(), 0, text.length(), maxWidth,
639                 measuredWidth);
640     }
641 
642     @LayoutlibDelegate
native_init()643     /*package*/ static int native_init() {
644         Paint_Delegate newDelegate = new Paint_Delegate();
645         return sManager.addNewDelegate(newDelegate);
646     }
647 
648     @LayoutlibDelegate
native_initWithPaint(int paint)649     /*package*/ static int native_initWithPaint(int paint) {
650         // get the delegate from the native int.
651         Paint_Delegate delegate = sManager.getDelegate(paint);
652         if (delegate == null) {
653             return 0;
654         }
655 
656         Paint_Delegate newDelegate = new Paint_Delegate(delegate);
657         return sManager.addNewDelegate(newDelegate);
658     }
659 
660     @LayoutlibDelegate
native_reset(int native_object)661     /*package*/ static void native_reset(int native_object) {
662         // get the delegate from the native int.
663         Paint_Delegate delegate = sManager.getDelegate(native_object);
664         if (delegate == null) {
665             return;
666         }
667 
668         delegate.reset();
669     }
670 
671     @LayoutlibDelegate
native_set(int native_dst, int native_src)672     /*package*/ static void native_set(int native_dst, int native_src) {
673         // get the delegate from the native int.
674         Paint_Delegate delegate_dst = sManager.getDelegate(native_dst);
675         if (delegate_dst == null) {
676             return;
677         }
678 
679         // get the delegate from the native int.
680         Paint_Delegate delegate_src = sManager.getDelegate(native_src);
681         if (delegate_src == null) {
682             return;
683         }
684 
685         delegate_dst.set(delegate_src);
686     }
687 
688     @LayoutlibDelegate
native_getStyle(int native_object)689     /*package*/ static int native_getStyle(int native_object) {
690         // get the delegate from the native int.
691         Paint_Delegate delegate = sManager.getDelegate(native_object);
692         if (delegate == null) {
693             return 0;
694         }
695 
696         return delegate.mStyle;
697     }
698 
699     @LayoutlibDelegate
native_setStyle(int native_object, int style)700     /*package*/ static void native_setStyle(int native_object, int style) {
701         // get the delegate from the native int.
702         Paint_Delegate delegate = sManager.getDelegate(native_object);
703         if (delegate == null) {
704             return;
705         }
706 
707         delegate.mStyle = style;
708     }
709 
710     @LayoutlibDelegate
native_getStrokeCap(int native_object)711     /*package*/ static int native_getStrokeCap(int native_object) {
712         // get the delegate from the native int.
713         Paint_Delegate delegate = sManager.getDelegate(native_object);
714         if (delegate == null) {
715             return 0;
716         }
717 
718         return delegate.mCap;
719     }
720 
721     @LayoutlibDelegate
native_setStrokeCap(int native_object, int cap)722     /*package*/ static void native_setStrokeCap(int native_object, int cap) {
723         // get the delegate from the native int.
724         Paint_Delegate delegate = sManager.getDelegate(native_object);
725         if (delegate == null) {
726             return;
727         }
728 
729         delegate.mCap = cap;
730     }
731 
732     @LayoutlibDelegate
native_getStrokeJoin(int native_object)733     /*package*/ static int native_getStrokeJoin(int native_object) {
734         // get the delegate from the native int.
735         Paint_Delegate delegate = sManager.getDelegate(native_object);
736         if (delegate == null) {
737             return 0;
738         }
739 
740         return delegate.mJoin;
741     }
742 
743     @LayoutlibDelegate
native_setStrokeJoin(int native_object, int join)744     /*package*/ static void native_setStrokeJoin(int native_object, int join) {
745         // get the delegate from the native int.
746         Paint_Delegate delegate = sManager.getDelegate(native_object);
747         if (delegate == null) {
748             return;
749         }
750 
751         delegate.mJoin = join;
752     }
753 
754     @LayoutlibDelegate
native_getFillPath(int native_object, int src, int dst)755     /*package*/ static boolean native_getFillPath(int native_object, int src, int dst) {
756         Paint_Delegate paint = sManager.getDelegate(native_object);
757         if (paint == null) {
758             return false;
759         }
760 
761         Path_Delegate srcPath = Path_Delegate.getDelegate(src);
762         if (srcPath == null) {
763             return true;
764         }
765 
766         Path_Delegate dstPath = Path_Delegate.getDelegate(dst);
767         if (dstPath == null) {
768             return true;
769         }
770 
771         Stroke stroke = paint.getJavaStroke();
772         Shape strokeShape = stroke.createStrokedShape(srcPath.getJavaShape());
773 
774         dstPath.setJavaShape(strokeShape);
775 
776         // FIXME figure out the return value?
777         return true;
778     }
779 
780     @LayoutlibDelegate
native_setShader(int native_object, int shader)781     /*package*/ static int native_setShader(int native_object, int shader) {
782         // get the delegate from the native int.
783         Paint_Delegate delegate = sManager.getDelegate(native_object);
784         if (delegate == null) {
785             return shader;
786         }
787 
788         delegate.mShader = Shader_Delegate.getDelegate(shader);
789 
790         return shader;
791     }
792 
793     @LayoutlibDelegate
native_setColorFilter(int native_object, int filter)794     /*package*/ static int native_setColorFilter(int native_object, int filter) {
795         // get the delegate from the native int.
796         Paint_Delegate delegate = sManager.getDelegate(native_object);
797         if (delegate == null) {
798             return filter;
799         }
800 
801         delegate.mColorFilter = ColorFilter_Delegate.getDelegate(filter);;
802 
803         // since none of those are supported, display a fidelity warning right away
804         if (delegate.mColorFilter != null && delegate.mColorFilter.isSupported() == false) {
805             Bridge.getLog().fidelityWarning(LayoutLog.TAG_COLORFILTER,
806                     delegate.mColorFilter.getSupportMessage(), null, null /*data*/);
807         }
808 
809         return filter;
810     }
811 
812     @LayoutlibDelegate
native_setXfermode(int native_object, int xfermode)813     /*package*/ static int native_setXfermode(int native_object, int xfermode) {
814         // get the delegate from the native int.
815         Paint_Delegate delegate = sManager.getDelegate(native_object);
816         if (delegate == null) {
817             return xfermode;
818         }
819 
820         delegate.mXfermode = Xfermode_Delegate.getDelegate(xfermode);
821 
822         return xfermode;
823     }
824 
825     @LayoutlibDelegate
native_setPathEffect(int native_object, int effect)826     /*package*/ static int native_setPathEffect(int native_object, int effect) {
827         // get the delegate from the native int.
828         Paint_Delegate delegate = sManager.getDelegate(native_object);
829         if (delegate == null) {
830             return effect;
831         }
832 
833         delegate.mPathEffect = PathEffect_Delegate.getDelegate(effect);
834 
835         return effect;
836     }
837 
838     @LayoutlibDelegate
native_setMaskFilter(int native_object, int maskfilter)839     /*package*/ static int native_setMaskFilter(int native_object, int maskfilter) {
840         // get the delegate from the native int.
841         Paint_Delegate delegate = sManager.getDelegate(native_object);
842         if (delegate == null) {
843             return maskfilter;
844         }
845 
846         delegate.mMaskFilter = MaskFilter_Delegate.getDelegate(maskfilter);
847 
848         // since none of those are supported, display a fidelity warning right away
849         if (delegate.mMaskFilter != null && delegate.mMaskFilter.isSupported() == false) {
850             Bridge.getLog().fidelityWarning(LayoutLog.TAG_MASKFILTER,
851                     delegate.mMaskFilter.getSupportMessage(), null, null /*data*/);
852         }
853 
854         return maskfilter;
855     }
856 
857     @LayoutlibDelegate
native_setTypeface(int native_object, int typeface)858     /*package*/ static int native_setTypeface(int native_object, int typeface) {
859         // get the delegate from the native int.
860         Paint_Delegate delegate = sManager.getDelegate(native_object);
861         if (delegate == null) {
862             return 0;
863         }
864 
865         delegate.mTypeface = Typeface_Delegate.getDelegate(typeface);
866         delegate.updateFontObject();
867         return typeface;
868     }
869 
870     @LayoutlibDelegate
native_setRasterizer(int native_object, int rasterizer)871     /*package*/ static int native_setRasterizer(int native_object, int rasterizer) {
872         // get the delegate from the native int.
873         Paint_Delegate delegate = sManager.getDelegate(native_object);
874         if (delegate == null) {
875             return rasterizer;
876         }
877 
878         delegate.mRasterizer = Rasterizer_Delegate.getDelegate(rasterizer);
879 
880         // since none of those are supported, display a fidelity warning right away
881         if (delegate.mRasterizer != null && delegate.mRasterizer.isSupported() == false) {
882             Bridge.getLog().fidelityWarning(LayoutLog.TAG_RASTERIZER,
883                     delegate.mRasterizer.getSupportMessage(), null, null /*data*/);
884         }
885 
886         return rasterizer;
887     }
888 
889     @LayoutlibDelegate
native_getTextAlign(int native_object)890     /*package*/ static int native_getTextAlign(int native_object) {
891         // get the delegate from the native int.
892         Paint_Delegate delegate = sManager.getDelegate(native_object);
893         if (delegate == null) {
894             return 0;
895         }
896 
897         return delegate.mTextAlign;
898     }
899 
900     @LayoutlibDelegate
native_setTextAlign(int native_object, int align)901     /*package*/ static void native_setTextAlign(int native_object, int align) {
902         // get the delegate from the native int.
903         Paint_Delegate delegate = sManager.getDelegate(native_object);
904         if (delegate == null) {
905             return;
906         }
907 
908         delegate.mTextAlign = align;
909     }
910 
911     @LayoutlibDelegate
native_setTextLocale(int native_object, String locale)912     /*package*/ static void native_setTextLocale(int native_object, String locale) {
913         // get the delegate from the native int.
914         Paint_Delegate delegate = sManager.getDelegate(native_object);
915         if (delegate == null) {
916             return;
917         }
918 
919         delegate.setTextLocale(locale);
920     }
921 
922     @LayoutlibDelegate
native_getTextWidths(int native_object, char[] text, int index, int count, float[] widths)923     /*package*/ static int native_getTextWidths(int native_object, char[] text, int index,
924             int count, float[] widths) {
925         // get the delegate from the native int.
926         Paint_Delegate delegate = sManager.getDelegate(native_object);
927         if (delegate == null) {
928             return 0;
929         }
930 
931         if (delegate.mFonts.size() > 0) {
932             // FIXME: handle multi-char characters (see measureText)
933             float totalAdvance = 0;
934             for (int i = 0; i < count; i++) {
935                 char c = text[i + index];
936                 boolean found = false;
937                 for (FontInfo info : delegate.mFonts) {
938                     if (info.mFont.canDisplay(c)) {
939                         float adv = info.mMetrics.charWidth(c);
940                         totalAdvance += adv;
941                         if (widths != null) {
942                             widths[i] = adv;
943                         }
944 
945                         found = true;
946                         break;
947                     }
948                 }
949 
950                 if (found == false) {
951                     // no advance for this char.
952                     if (widths != null) {
953                         widths[i] = 0.f;
954                     }
955                 }
956             }
957 
958             return (int) totalAdvance;
959         }
960 
961         return 0;
962     }
963 
964     @LayoutlibDelegate
native_getTextWidths(int native_object, String text, int start, int end, float[] widths)965     /*package*/ static int native_getTextWidths(int native_object, String text, int start,
966             int end, float[] widths) {
967         return native_getTextWidths(native_object, text.toCharArray(), start, end - start, widths);
968     }
969 
970     @LayoutlibDelegate
native_getTextGlyphs(int native_object, String text, int start, int end, int contextStart, int contextEnd, int flags, char[] glyphs)971     /* package */static int native_getTextGlyphs(int native_object, String text, int start,
972             int end, int contextStart, int contextEnd, int flags, char[] glyphs) {
973         // FIXME
974         return 0;
975     }
976 
977     @LayoutlibDelegate
native_getTextRunAdvances(int native_object, char[] text, int index, int count, int contextIndex, int contextCount, int flags, float[] advances, int advancesIndex, int reserved)978     /*package*/ static float native_getTextRunAdvances(int native_object,
979             char[] text, int index, int count, int contextIndex, int contextCount,
980             int flags, float[] advances, int advancesIndex, int reserved) {
981         // get the delegate from the native int.
982         Paint_Delegate delegate = sManager.getDelegate(native_object);
983         if (delegate == null) {
984             return 0.f;
985         }
986 
987         if (delegate.mFonts.size() > 0) {
988             // FIXME: handle multi-char characters (see measureText)
989             float totalAdvance = 0;
990             for (int i = 0; i < count; i++) {
991                 char c = text[i + index];
992                 boolean found = false;
993                 for (FontInfo info : delegate.mFonts) {
994                     if (info.mFont.canDisplay(c)) {
995                         float adv = info.mMetrics.charWidth(c);
996                         totalAdvance += adv;
997                         if (advances != null) {
998                             advances[i] = adv;
999                         }
1000 
1001                         found = true;
1002                         break;
1003                     }
1004                 }
1005 
1006                 if (found == false) {
1007                     // no advance for this char.
1008                     if (advances != null) {
1009                         advances[i] = 0.f;
1010                     }
1011                 }
1012             }
1013 
1014             return totalAdvance;
1015         }
1016 
1017         return 0;
1018 
1019     }
1020 
1021     @LayoutlibDelegate
native_getTextRunAdvances(int native_object, String text, int start, int end, int contextStart, int contextEnd, int flags, float[] advances, int advancesIndex, int reserved)1022     /*package*/ static float native_getTextRunAdvances(int native_object,
1023             String text, int start, int end, int contextStart, int contextEnd,
1024             int flags, float[] advances, int advancesIndex, int reserved) {
1025         // FIXME: support contextStart, contextEnd and direction flag
1026         int count = end - start;
1027         char[] buffer = TemporaryBuffer.obtain(count);
1028         TextUtils.getChars(text, start, end, buffer, 0);
1029 
1030         return native_getTextRunAdvances(native_object, buffer, 0, count, contextStart,
1031                 contextEnd - contextStart, flags, advances, advancesIndex, reserved);
1032     }
1033 
1034     @LayoutlibDelegate
native_getTextRunCursor(Paint thisPaint, int native_object, char[] text, int contextStart, int contextLength, int flags, int offset, int cursorOpt)1035     /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, char[] text,
1036             int contextStart, int contextLength, int flags, int offset, int cursorOpt) {
1037         // FIXME
1038         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
1039                 "Paint.getTextRunCursor is not supported.", null, null /*data*/);
1040         return 0;
1041     }
1042 
1043     @LayoutlibDelegate
native_getTextRunCursor(Paint thisPaint, int native_object, String text, int contextStart, int contextEnd, int flags, int offset, int cursorOpt)1044     /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, String text,
1045             int contextStart, int contextEnd, int flags, int offset, int cursorOpt) {
1046         // FIXME
1047         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
1048                 "Paint.getTextRunCursor is not supported.", null, null /*data*/);
1049         return 0;
1050     }
1051 
1052     @LayoutlibDelegate
native_getTextPath(int native_object, int bidiFlags, char[] text, int index, int count, float x, float y, int path)1053     /*package*/ static void native_getTextPath(int native_object, int bidiFlags,
1054                 char[] text, int index, int count, float x, float y, int path) {
1055         // FIXME
1056         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
1057                 "Paint.getTextPath is not supported.", null, null /*data*/);
1058     }
1059 
1060     @LayoutlibDelegate
native_getTextPath(int native_object, int bidiFlags, String text, int start, int end, float x, float y, int path)1061     /*package*/ static void native_getTextPath(int native_object, int bidiFlags,
1062             String text, int start, int end, float x, float y, int path) {
1063         // FIXME
1064         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
1065                 "Paint.getTextPath is not supported.", null, null /*data*/);
1066     }
1067 
1068     @LayoutlibDelegate
nativeGetStringBounds(int nativePaint, String text, int start, int end, Rect bounds)1069     /*package*/ static void nativeGetStringBounds(int nativePaint, String text, int start,
1070             int end, Rect bounds) {
1071         nativeGetCharArrayBounds(nativePaint, text.toCharArray(), start, end - start, bounds);
1072     }
1073 
1074     @LayoutlibDelegate
nativeGetCharArrayBounds(int nativePaint, char[] text, int index, int count, Rect bounds)1075     /*package*/ static void nativeGetCharArrayBounds(int nativePaint, char[] text, int index,
1076             int count, Rect bounds) {
1077 
1078         // get the delegate from the native int.
1079         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
1080         if (delegate == null) {
1081             return;
1082         }
1083 
1084         // FIXME should test if the main font can display all those characters.
1085         // See MeasureText
1086         if (delegate.mFonts.size() > 0) {
1087             FontInfo mainInfo = delegate.mFonts.get(0);
1088 
1089             Rectangle2D rect = mainInfo.mFont.getStringBounds(text, index, index + count,
1090                     delegate.mFontContext);
1091             bounds.set(0, 0, (int) rect.getWidth(), (int) rect.getHeight());
1092         }
1093     }
1094 
1095     @LayoutlibDelegate
finalizer(int nativePaint)1096     /*package*/ static void finalizer(int nativePaint) {
1097         sManager.removeJavaReferenceFor(nativePaint);
1098     }
1099 
1100     // ---- Private delegate/helper methods ----
1101 
Paint_Delegate()1102     /*package*/ Paint_Delegate() {
1103         reset();
1104     }
1105 
Paint_Delegate(Paint_Delegate paint)1106     private Paint_Delegate(Paint_Delegate paint) {
1107         set(paint);
1108     }
1109 
set(Paint_Delegate paint)1110     private void set(Paint_Delegate paint) {
1111         mFlags = paint.mFlags;
1112         mColor = paint.mColor;
1113         mStyle = paint.mStyle;
1114         mCap = paint.mCap;
1115         mJoin = paint.mJoin;
1116         mTextAlign = paint.mTextAlign;
1117         mTypeface = paint.mTypeface;
1118         mStrokeWidth = paint.mStrokeWidth;
1119         mStrokeMiter = paint.mStrokeMiter;
1120         mTextSize = paint.mTextSize;
1121         mTextScaleX = paint.mTextScaleX;
1122         mTextSkewX = paint.mTextSkewX;
1123         mXfermode = paint.mXfermode;
1124         mColorFilter = paint.mColorFilter;
1125         mShader = paint.mShader;
1126         mPathEffect = paint.mPathEffect;
1127         mMaskFilter = paint.mMaskFilter;
1128         mRasterizer = paint.mRasterizer;
1129         mHintingMode = paint.mHintingMode;
1130         updateFontObject();
1131     }
1132 
reset()1133     private void reset() {
1134         mFlags = Paint.DEFAULT_PAINT_FLAGS;
1135         mColor = 0xFF000000;
1136         mStyle = Paint.Style.FILL.nativeInt;
1137         mCap = Paint.Cap.BUTT.nativeInt;
1138         mJoin = Paint.Join.MITER.nativeInt;
1139         mTextAlign = 0;
1140         mTypeface = Typeface_Delegate.getDelegate(Typeface.sDefaults[0].native_instance);
1141         mStrokeWidth = 1.f;
1142         mStrokeMiter = 4.f;
1143         mTextSize = 20.f;
1144         mTextScaleX = 1.f;
1145         mTextSkewX = 0.f;
1146         mXfermode = null;
1147         mColorFilter = null;
1148         mShader = null;
1149         mPathEffect = null;
1150         mMaskFilter = null;
1151         mRasterizer = null;
1152         updateFontObject();
1153         mHintingMode = Paint.HINTING_ON;
1154     }
1155 
1156     /**
1157      * Update the {@link Font} object from the typeface, text size and scaling
1158      */
1159     @SuppressWarnings("deprecation")
updateFontObject()1160     private void updateFontObject() {
1161         if (mTypeface != null) {
1162             // Get the fonts from the TypeFace object.
1163             List<Font> fonts = mTypeface.getFonts();
1164 
1165             // create new font objects as well as FontMetrics, based on the current text size
1166             // and skew info.
1167             ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(fonts.size());
1168             for (Font font : fonts) {
1169                 FontInfo info = new FontInfo();
1170                 info.mFont = font.deriveFont(mTextSize);
1171                 if (mTextScaleX != 1.0 || mTextSkewX != 0) {
1172                     // TODO: support skew
1173                     info.mFont = info.mFont.deriveFont(new AffineTransform(
1174                             mTextScaleX, mTextSkewX, 0, 1, 0, 0));
1175                 }
1176                 info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont);
1177 
1178                 infoList.add(info);
1179             }
1180 
1181             mFonts = Collections.unmodifiableList(infoList);
1182         }
1183     }
1184 
measureText(char[] text, int index, int count)1185     /*package*/ float measureText(char[] text, int index, int count) {
1186 
1187         // WARNING: the logic in this method is similar to Canvas_Delegate.native_drawText
1188         // Any change to this method should be reflected there as well
1189 
1190         if (mFonts.size() > 0) {
1191             FontInfo mainFont = mFonts.get(0);
1192             int i = index;
1193             int lastIndex = index + count;
1194             float total = 0f;
1195             while (i < lastIndex) {
1196                 // always start with the main font.
1197                 int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
1198                 if (upTo == -1) {
1199                     // shortcut to exit
1200                     return total + mainFont.mMetrics.charsWidth(text, i, lastIndex - i);
1201                 } else if (upTo > 0) {
1202                     total += mainFont.mMetrics.charsWidth(text, i, upTo - i);
1203                     i = upTo;
1204                     // don't call continue at this point. Since it is certain the main font
1205                     // cannot display the font a index upTo (now ==i), we move on to the
1206                     // fallback fonts directly.
1207                 }
1208 
1209                 // no char supported, attempt to read the next char(s) with the
1210                 // fallback font. In this case we only test the first character
1211                 // and then go back to test with the main font.
1212                 // Special test for 2-char characters.
1213                 boolean foundFont = false;
1214                 for (int f = 1 ; f < mFonts.size() ; f++) {
1215                     FontInfo fontInfo = mFonts.get(f);
1216 
1217                     // need to check that the font can display the character. We test
1218                     // differently if the char is a high surrogate.
1219                     int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
1220                     upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
1221                     if (upTo == -1) {
1222                         total += fontInfo.mMetrics.charsWidth(text, i, charCount);
1223                         i += charCount;
1224                         foundFont = true;
1225                         break;
1226 
1227                     }
1228                 }
1229 
1230                 // in case no font can display the char, measure it with the main font.
1231                 if (foundFont == false) {
1232                     int size = Character.isHighSurrogate(text[i]) ? 2 : 1;
1233                     total += mainFont.mMetrics.charsWidth(text, i, size);
1234                     i += size;
1235                 }
1236             }
1237 
1238             return total;
1239         }
1240 
1241         return 0;
1242     }
1243 
getFontMetrics(FontMetrics metrics)1244     private float getFontMetrics(FontMetrics metrics) {
1245         if (mFonts.size() > 0) {
1246             java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
1247             if (metrics != null) {
1248                 // Android expects negative ascent so we invert the value from Java.
1249                 metrics.top = - javaMetrics.getMaxAscent();
1250                 metrics.ascent = - javaMetrics.getAscent();
1251                 metrics.descent = javaMetrics.getDescent();
1252                 metrics.bottom = javaMetrics.getMaxDescent();
1253                 metrics.leading = javaMetrics.getLeading();
1254             }
1255 
1256             return javaMetrics.getHeight();
1257         }
1258 
1259         return 0;
1260     }
1261 
setTextLocale(String locale)1262     private void setTextLocale(String locale) {
1263         mLocale = new Locale(locale);
1264     }
1265 
setFlag(Paint thisPaint, int flagMask, boolean flagValue)1266     private static void setFlag(Paint thisPaint, int flagMask, boolean flagValue) {
1267         // get the delegate from the native int.
1268         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
1269         if (delegate == null) {
1270             return;
1271         }
1272 
1273         if (flagValue) {
1274             delegate.mFlags |= flagMask;
1275         } else {
1276             delegate.mFlags &= ~flagMask;
1277         }
1278     }
1279 
1280 }
1281