Home | History | Annotate | Download | only in graphics
      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 
    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      */
    108     public List<FontInfo> getFonts() {
    109         return mFonts;
    110     }
    111 
    112     public boolean isAntiAliased() {
    113         return (mFlags & Paint.ANTI_ALIAS_FLAG) != 0;
    114     }
    115 
    116     public boolean isFilterBitmap() {
    117         return (mFlags & Paint.FILTER_BITMAP_FLAG) != 0;
    118     }
    119 
    120     public int getStyle() {
    121         return mStyle;
    122     }
    123 
    124     public int getColor() {
    125         return mColor;
    126     }
    127 
    128     public int getAlpha() {
    129         return mColor >>> 24;
    130     }
    131 
    132     public void setAlpha(int alpha) {
    133         mColor = (alpha << 24) | (mColor & 0x00FFFFFF);
    134     }
    135 
    136     public int getTextAlign() {
    137         return mTextAlign;
    138     }
    139 
    140     public float getStrokeWidth() {
    141         return mStrokeWidth;
    142     }
    143 
    144     /**
    145      * returns the value of stroke miter needed by the java api.
    146      */
    147     public float getJavaStrokeMiter() {
    148         float miter = mStrokeMiter * mStrokeWidth;
    149         if (miter < 1.f) {
    150             miter = 1.f;
    151         }
    152         return miter;
    153     }
    154 
    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 
    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 
    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      */
    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      */
    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      */
    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      */
    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      */
    243     public Rasterizer_Delegate getRasterizer() {
    244         return mRasterizer;
    245     }
    246 
    247     // ---- native methods ----
    248 
    249     @LayoutlibDelegate
    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
    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
    274     /*package*/ static void setFilterBitmap(Paint thisPaint, boolean filter) {
    275         setFlag(thisPaint, Paint.FILTER_BITMAP_FLAG, filter);
    276     }
    277 
    278     @LayoutlibDelegate
    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
    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
    301     /*package*/ static void setAntiAlias(Paint thisPaint, boolean aa) {
    302         setFlag(thisPaint, Paint.ANTI_ALIAS_FLAG, aa);
    303     }
    304 
    305     @LayoutlibDelegate
    306     /*package*/ static void setSubpixelText(Paint thisPaint, boolean subpixelText) {
    307         setFlag(thisPaint, Paint.SUBPIXEL_TEXT_FLAG, subpixelText);
    308     }
    309 
    310     @LayoutlibDelegate
    311     /*package*/ static void setUnderlineText(Paint thisPaint, boolean underlineText) {
    312         setFlag(thisPaint, Paint.UNDERLINE_TEXT_FLAG, underlineText);
    313     }
    314 
    315     @LayoutlibDelegate
    316     /*package*/ static void setStrikeThruText(Paint thisPaint, boolean strikeThruText) {
    317         setFlag(thisPaint, Paint.STRIKE_THRU_TEXT_FLAG, strikeThruText);
    318     }
    319 
    320     @LayoutlibDelegate
    321     /*package*/ static void setFakeBoldText(Paint thisPaint, boolean fakeBoldText) {
    322         setFlag(thisPaint, Paint.FAKE_BOLD_TEXT_FLAG, fakeBoldText);
    323     }
    324 
    325     @LayoutlibDelegate
    326     /*package*/ static void setDither(Paint thisPaint, boolean dither) {
    327         setFlag(thisPaint, Paint.DITHER_FLAG, dither);
    328     }
    329 
    330     @LayoutlibDelegate
    331     /*package*/ static void setLinearText(Paint thisPaint, boolean linearText) {
    332         setFlag(thisPaint, Paint.LINEAR_TEXT_FLAG, linearText);
    333     }
    334 
    335     @LayoutlibDelegate
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    588     /*package*/ static float native_measureText(Paint thisPaint, String text) {
    589         return native_measureText(thisPaint, text.toCharArray(), 0, text.length());
    590     }
    591 
    592     @LayoutlibDelegate
    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
    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
    643     /*package*/ static int native_init() {
    644         Paint_Delegate newDelegate = new Paint_Delegate();
    645         return sManager.addNewDelegate(newDelegate);
    646     }
    647 
    648     @LayoutlibDelegate
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
   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
   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
   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
   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
   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
   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
   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
   1096     /*package*/ static void finalizer(int nativePaint) {
   1097         sManager.removeJavaReferenceFor(nativePaint);
   1098     }
   1099 
   1100     // ---- Private delegate/helper methods ----
   1101 
   1102     /*package*/ Paint_Delegate() {
   1103         reset();
   1104     }
   1105 
   1106     private Paint_Delegate(Paint_Delegate paint) {
   1107         set(paint);
   1108     }
   1109 
   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 
   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")
   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 
   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 
   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 
   1262     private void setTextLocale(String locale) {
   1263         mLocale = new Locale(locale);
   1264     }
   1265 
   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