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.util.ArrayList;
     36 import java.util.Collections;
     37 import java.util.List;
     38 import java.util.Locale;
     39 
     40 /**
     41  * Delegate implementing the native methods of android.graphics.Paint
     42  *
     43  * Through the layoutlib_create tool, the original native methods of Paint have been replaced
     44  * by calls to methods of the same name in this delegate class.
     45  *
     46  * This class behaves like the original native implementation, but in Java, keeping previously
     47  * native data into its own objects and mapping them to int that are sent back and forth between
     48  * it and the original Paint class.
     49  *
     50  * @see DelegateManager
     51  *
     52  */
     53 public class Paint_Delegate {
     54 
     55     /**
     56      * Class associating a {@link Font} and it's {@link java.awt.FontMetrics}.
     57      */
     58     /*package*/ static final class FontInfo {
     59         Font mFont;
     60         java.awt.FontMetrics mMetrics;
     61     }
     62 
     63     // ---- delegate manager ----
     64     private static final DelegateManager<Paint_Delegate> sManager =
     65             new DelegateManager<Paint_Delegate>(Paint_Delegate.class);
     66 
     67     // ---- delegate helper data ----
     68     private List<FontInfo> mFonts;
     69     private final FontRenderContext mFontContext = new FontRenderContext(
     70             new AffineTransform(), true, true);
     71 
     72     // ---- delegate data ----
     73     private int mFlags;
     74     private int mColor;
     75     private int mStyle;
     76     private int mCap;
     77     private int mJoin;
     78     private int mTextAlign;
     79     private Typeface_Delegate mTypeface;
     80     private float mStrokeWidth;
     81     private float mStrokeMiter;
     82     private float mTextSize;
     83     private float mTextScaleX;
     84     private float mTextSkewX;
     85     private int mHintingMode = Paint.HINTING_ON;
     86 
     87     private Xfermode_Delegate mXfermode;
     88     private ColorFilter_Delegate mColorFilter;
     89     private Shader_Delegate mShader;
     90     private PathEffect_Delegate mPathEffect;
     91     private MaskFilter_Delegate mMaskFilter;
     92     private Rasterizer_Delegate mRasterizer;
     93 
     94     private Locale mLocale = Locale.getDefault();
     95 
     96 
     97     // ---- Public Helper methods ----
     98 
     99     public static Paint_Delegate getDelegate(int native_paint) {
    100         return sManager.getDelegate(native_paint);
    101     }
    102 
    103     /**
    104      * Returns the list of {@link Font} objects. The first item is the main font, the rest
    105      * are fall backs for characters not present in the main font.
    106      */
    107     public List<FontInfo> getFonts() {
    108         return mFonts;
    109     }
    110 
    111     public boolean isAntiAliased() {
    112         return (mFlags & Paint.ANTI_ALIAS_FLAG) != 0;
    113     }
    114 
    115     public boolean isFilterBitmap() {
    116         return (mFlags & Paint.FILTER_BITMAP_FLAG) != 0;
    117     }
    118 
    119     public int getStyle() {
    120         return mStyle;
    121     }
    122 
    123     public int getColor() {
    124         return mColor;
    125     }
    126 
    127     public int getAlpha() {
    128         return mColor >>> 24;
    129     }
    130 
    131     public void setAlpha(int alpha) {
    132         mColor = (alpha << 24) | (mColor & 0x00FFFFFF);
    133     }
    134 
    135     public int getTextAlign() {
    136         return mTextAlign;
    137     }
    138 
    139     public float getStrokeWidth() {
    140         return mStrokeWidth;
    141     }
    142 
    143     /**
    144      * returns the value of stroke miter needed by the java api.
    145      */
    146     public float getJavaStrokeMiter() {
    147         float miter = mStrokeMiter * mStrokeWidth;
    148         if (miter < 1.f) {
    149             miter = 1.f;
    150         }
    151         return miter;
    152     }
    153 
    154     public int getJavaCap() {
    155         switch (Paint.sCapArray[mCap]) {
    156             case BUTT:
    157                 return BasicStroke.CAP_BUTT;
    158             case ROUND:
    159                 return BasicStroke.CAP_ROUND;
    160             default:
    161             case SQUARE:
    162                 return BasicStroke.CAP_SQUARE;
    163         }
    164     }
    165 
    166     public int getJavaJoin() {
    167         switch (Paint.sJoinArray[mJoin]) {
    168             default:
    169             case MITER:
    170                 return BasicStroke.JOIN_MITER;
    171             case ROUND:
    172                 return BasicStroke.JOIN_ROUND;
    173             case BEVEL:
    174                 return BasicStroke.JOIN_BEVEL;
    175         }
    176     }
    177 
    178     public Stroke getJavaStroke() {
    179         if (mPathEffect != null) {
    180             if (mPathEffect.isSupported()) {
    181                 Stroke stroke = mPathEffect.getStroke(this);
    182                 assert stroke != null;
    183                 if (stroke != null) {
    184                     return stroke;
    185                 }
    186             } else {
    187                 Bridge.getLog().fidelityWarning(LayoutLog.TAG_PATHEFFECT,
    188                         mPathEffect.getSupportMessage(),
    189                         null, null /*data*/);
    190             }
    191         }
    192 
    193         // if no custom stroke as been set, set the default one.
    194         return new BasicStroke(
    195                     getStrokeWidth(),
    196                     getJavaCap(),
    197                     getJavaJoin(),
    198                     getJavaStrokeMiter());
    199     }
    200 
    201     /**
    202      * Returns the {@link Xfermode} delegate or null if none have been set
    203      *
    204      * @return the delegate or null.
    205      */
    206     public Xfermode_Delegate getXfermode() {
    207         return mXfermode;
    208     }
    209 
    210     /**
    211      * Returns the {@link ColorFilter} delegate or null if none have been set
    212      *
    213      * @return the delegate or null.
    214      */
    215     public ColorFilter_Delegate getColorFilter() {
    216         return mColorFilter;
    217     }
    218 
    219     /**
    220      * Returns the {@link Shader} delegate or null if none have been set
    221      *
    222      * @return the delegate or null.
    223      */
    224     public Shader_Delegate getShader() {
    225         return mShader;
    226     }
    227 
    228     /**
    229      * Returns the {@link MaskFilter} delegate or null if none have been set
    230      *
    231      * @return the delegate or null.
    232      */
    233     public MaskFilter_Delegate getMaskFilter() {
    234         return mMaskFilter;
    235     }
    236 
    237     /**
    238      * Returns the {@link Rasterizer} delegate or null if none have been set
    239      *
    240      * @return the delegate or null.
    241      */
    242     public Rasterizer_Delegate getRasterizer() {
    243         return mRasterizer;
    244     }
    245 
    246     // ---- native methods ----
    247 
    248     @LayoutlibDelegate
    249     /*package*/ static int getFlags(Paint thisPaint) {
    250         // get the delegate from the native int.
    251         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
    252         if (delegate == null) {
    253             return 0;
    254         }
    255 
    256         return delegate.mFlags;
    257     }
    258 
    259 
    260 
    261     @LayoutlibDelegate
    262     /*package*/ static void setFlags(Paint thisPaint, int flags) {
    263         // get the delegate from the native int.
    264         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
    265         if (delegate == null) {
    266             return;
    267         }
    268 
    269         delegate.mFlags = flags;
    270     }
    271 
    272     @LayoutlibDelegate
    273     /*package*/ static void setFilterBitmap(Paint thisPaint, boolean filter) {
    274         setFlag(thisPaint, Paint.FILTER_BITMAP_FLAG, filter);
    275     }
    276 
    277     @LayoutlibDelegate
    278     /*package*/ static int getHinting(Paint thisPaint) {
    279         // get the delegate from the native int.
    280         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
    281         if (delegate == null) {
    282             return Paint.HINTING_ON;
    283         }
    284 
    285         return delegate.mHintingMode;
    286     }
    287 
    288     @LayoutlibDelegate
    289     /*package*/ static void setHinting(Paint thisPaint, int mode) {
    290         // get the delegate from the native int.
    291         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
    292         if (delegate == null) {
    293             return;
    294         }
    295 
    296         delegate.mHintingMode = mode;
    297     }
    298 
    299     @LayoutlibDelegate
    300     /*package*/ static void setAntiAlias(Paint thisPaint, boolean aa) {
    301         setFlag(thisPaint, Paint.ANTI_ALIAS_FLAG, aa);
    302     }
    303 
    304     @LayoutlibDelegate
    305     /*package*/ static void setSubpixelText(Paint thisPaint, boolean subpixelText) {
    306         setFlag(thisPaint, Paint.SUBPIXEL_TEXT_FLAG, subpixelText);
    307     }
    308 
    309     @LayoutlibDelegate
    310     /*package*/ static void setUnderlineText(Paint thisPaint, boolean underlineText) {
    311         setFlag(thisPaint, Paint.UNDERLINE_TEXT_FLAG, underlineText);
    312     }
    313 
    314     @LayoutlibDelegate
    315     /*package*/ static void setStrikeThruText(Paint thisPaint, boolean strikeThruText) {
    316         setFlag(thisPaint, Paint.STRIKE_THRU_TEXT_FLAG, strikeThruText);
    317     }
    318 
    319     @LayoutlibDelegate
    320     /*package*/ static void setFakeBoldText(Paint thisPaint, boolean fakeBoldText) {
    321         setFlag(thisPaint, Paint.FAKE_BOLD_TEXT_FLAG, fakeBoldText);
    322     }
    323 
    324     @LayoutlibDelegate
    325     /*package*/ static void setDither(Paint thisPaint, boolean dither) {
    326         setFlag(thisPaint, Paint.DITHER_FLAG, dither);
    327     }
    328 
    329     @LayoutlibDelegate
    330     /*package*/ static void setLinearText(Paint thisPaint, boolean linearText) {
    331         setFlag(thisPaint, Paint.LINEAR_TEXT_FLAG, linearText);
    332     }
    333 
    334     @LayoutlibDelegate
    335     /*package*/ static int getColor(Paint thisPaint) {
    336         // get the delegate from the native int.
    337         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
    338         if (delegate == null) {
    339             return 0;
    340         }
    341 
    342         return delegate.mColor;
    343     }
    344 
    345     @LayoutlibDelegate
    346     /*package*/ static void setColor(Paint thisPaint, int color) {
    347         // get the delegate from the native int.
    348         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
    349         if (delegate == null) {
    350             return;
    351         }
    352 
    353         delegate.mColor = color;
    354     }
    355 
    356     @LayoutlibDelegate
    357     /*package*/ static int getAlpha(Paint thisPaint) {
    358         // get the delegate from the native int.
    359         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
    360         if (delegate == null) {
    361             return 0;
    362         }
    363 
    364         return delegate.getAlpha();
    365     }
    366 
    367     @LayoutlibDelegate
    368     /*package*/ static void setAlpha(Paint thisPaint, int a) {
    369         // get the delegate from the native int.
    370         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
    371         if (delegate == null) {
    372             return;
    373         }
    374 
    375         delegate.setAlpha(a);
    376     }
    377 
    378     @LayoutlibDelegate
    379     /*package*/ static float getStrokeWidth(Paint thisPaint) {
    380         // get the delegate from the native int.
    381         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
    382         if (delegate == null) {
    383             return 1.f;
    384         }
    385 
    386         return delegate.mStrokeWidth;
    387     }
    388 
    389     @LayoutlibDelegate
    390     /*package*/ static void setStrokeWidth(Paint thisPaint, float width) {
    391         // get the delegate from the native int.
    392         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
    393         if (delegate == null) {
    394             return;
    395         }
    396 
    397         delegate.mStrokeWidth = width;
    398     }
    399 
    400     @LayoutlibDelegate
    401     /*package*/ static float getStrokeMiter(Paint thisPaint) {
    402         // get the delegate from the native int.
    403         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
    404         if (delegate == null) {
    405             return 1.f;
    406         }
    407 
    408         return delegate.mStrokeMiter;
    409     }
    410 
    411     @LayoutlibDelegate
    412     /*package*/ static void setStrokeMiter(Paint thisPaint, float miter) {
    413         // get the delegate from the native int.
    414         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
    415         if (delegate == null) {
    416             return;
    417         }
    418 
    419         delegate.mStrokeMiter = miter;
    420     }
    421 
    422     @LayoutlibDelegate
    423     /*package*/ static void nSetShadowLayer(Paint thisPaint, float radius, float dx, float dy,
    424             int color) {
    425         // FIXME
    426         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
    427                 "Paint.setShadowLayer is not supported.", null, null /*data*/);
    428     }
    429 
    430     @LayoutlibDelegate
    431     /*package*/ static float getTextSize(Paint thisPaint) {
    432         // get the delegate from the native int.
    433         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
    434         if (delegate == null) {
    435             return 1.f;
    436         }
    437 
    438         return delegate.mTextSize;
    439     }
    440 
    441     @LayoutlibDelegate
    442     /*package*/ static void setTextSize(Paint thisPaint, float textSize) {
    443         // get the delegate from the native int.
    444         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
    445         if (delegate == null) {
    446             return;
    447         }
    448 
    449         delegate.mTextSize = textSize;
    450         delegate.updateFontObject();
    451     }
    452 
    453     @LayoutlibDelegate
    454     /*package*/ static float getTextScaleX(Paint thisPaint) {
    455         // get the delegate from the native int.
    456         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
    457         if (delegate == null) {
    458             return 1.f;
    459         }
    460 
    461         return delegate.mTextScaleX;
    462     }
    463 
    464     @LayoutlibDelegate
    465     /*package*/ static void setTextScaleX(Paint thisPaint, float scaleX) {
    466         // get the delegate from the native int.
    467         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
    468         if (delegate == null) {
    469             return;
    470         }
    471 
    472         delegate.mTextScaleX = scaleX;
    473         delegate.updateFontObject();
    474     }
    475 
    476     @LayoutlibDelegate
    477     /*package*/ static float getTextSkewX(Paint thisPaint) {
    478         // get the delegate from the native int.
    479         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
    480         if (delegate == null) {
    481             return 1.f;
    482         }
    483 
    484         return delegate.mTextSkewX;
    485     }
    486 
    487     @LayoutlibDelegate
    488     /*package*/ static void setTextSkewX(Paint thisPaint, float skewX) {
    489         // get the delegate from the native int.
    490         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
    491         if (delegate == null) {
    492             return;
    493         }
    494 
    495         delegate.mTextSkewX = skewX;
    496         delegate.updateFontObject();
    497     }
    498 
    499     @LayoutlibDelegate
    500     /*package*/ static float ascent(Paint thisPaint) {
    501         // get the delegate
    502         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
    503         if (delegate == null) {
    504             return 0;
    505         }
    506 
    507         if (delegate.mFonts.size() > 0) {
    508             java.awt.FontMetrics javaMetrics = delegate.mFonts.get(0).mMetrics;
    509             // Android expects negative ascent so we invert the value from Java.
    510             return - javaMetrics.getAscent();
    511         }
    512 
    513         return 0;
    514     }
    515 
    516     @LayoutlibDelegate
    517     /*package*/ static float descent(Paint thisPaint) {
    518         // get the delegate
    519         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
    520         if (delegate == null) {
    521             return 0;
    522         }
    523 
    524         if (delegate.mFonts.size() > 0) {
    525             java.awt.FontMetrics javaMetrics = delegate.mFonts.get(0).mMetrics;
    526             return javaMetrics.getDescent();
    527         }
    528 
    529         return 0;
    530 
    531     }
    532 
    533     @LayoutlibDelegate
    534     /*package*/ static float getFontMetrics(Paint thisPaint, FontMetrics metrics) {
    535         // get the delegate
    536         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
    537         if (delegate == null) {
    538             return 0;
    539         }
    540 
    541         return delegate.getFontMetrics(metrics);
    542     }
    543 
    544     @LayoutlibDelegate
    545     /*package*/ static int getFontMetricsInt(Paint thisPaint, FontMetricsInt fmi) {
    546         // get the delegate
    547         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
    548         if (delegate == null) {
    549             return 0;
    550         }
    551 
    552         if (delegate.mFonts.size() > 0) {
    553             java.awt.FontMetrics javaMetrics = delegate.mFonts.get(0).mMetrics;
    554             if (fmi != null) {
    555                 // Android expects negative ascent so we invert the value from Java.
    556                 fmi.top = - javaMetrics.getMaxAscent();
    557                 fmi.ascent = - javaMetrics.getAscent();
    558                 fmi.descent = javaMetrics.getDescent();
    559                 fmi.bottom = javaMetrics.getMaxDescent();
    560                 fmi.leading = javaMetrics.getLeading();
    561             }
    562 
    563             return javaMetrics.getHeight();
    564         }
    565 
    566         return 0;
    567     }
    568 
    569     @LayoutlibDelegate
    570     /*package*/ static float native_measureText(Paint thisPaint, char[] text, int index,
    571             int count, int bidiFlags) {
    572         // get the delegate
    573         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
    574         if (delegate == null) {
    575             return 0;
    576         }
    577 
    578         return delegate.measureText(text, index, count, isRtl(bidiFlags));
    579     }
    580 
    581     @LayoutlibDelegate
    582     /*package*/ static float native_measureText(Paint thisPaint, String text, int start, int end,
    583         int bidiFlags) {
    584         return native_measureText(thisPaint, text.toCharArray(), start, end - start, bidiFlags);
    585     }
    586 
    587     @LayoutlibDelegate
    588     /*package*/ static float native_measureText(Paint thisPaint, String text, int bidiFlags) {
    589         return native_measureText(thisPaint, text.toCharArray(), 0, text.length(), bidiFlags);
    590     }
    591 
    592     @LayoutlibDelegate
    593     /*package*/ static int native_breakText(Paint thisPaint, char[] text, int index, int count,
    594             float maxWidth, int bidiFlags, 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, isRtl(bidiFlags));
    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, int bidiFlags, float[] measuredWidth) {
    638         return native_breakText(thisPaint, text.toCharArray(), 0, text.length(), maxWidth,
    639                 bidiFlags, 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, int bidiFlags, 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, int bidiFlags, float[] widths) {
    967         return native_getTextWidths(native_object, text.toCharArray(), start, end - start,
    968                 bidiFlags, widths);
    969     }
    970 
    971     @LayoutlibDelegate
    972     /* package */static int native_getTextGlyphs(int native_object, String text, int start,
    973             int end, int contextStart, int contextEnd, int flags, char[] glyphs) {
    974         // FIXME
    975         return 0;
    976     }
    977 
    978     @LayoutlibDelegate
    979     /*package*/ static float native_getTextRunAdvances(int native_object,
    980             char[] text, int index, int count, int contextIndex, int contextCount,
    981             int flags, float[] advances, int advancesIndex) {
    982 
    983         if (advances != null)
    984             for (int i = advancesIndex; i< advancesIndex+count; i++)
    985                 advances[i]=0;
    986         // get the delegate from the native int.
    987         Paint_Delegate delegate = sManager.getDelegate(native_object);
    988         if (delegate == null || delegate.mFonts == null || delegate.mFonts.size() == 0) {
    989             return 0.f;
    990         }
    991         boolean isRtl = isRtl(flags);
    992 
    993         int limit = index + count;
    994         return new BidiRenderer(null, delegate, text).renderText(
    995                 index, limit, isRtl, advances, advancesIndex, false, 0, 0);
    996     }
    997 
    998     @LayoutlibDelegate
    999     /*package*/ static float native_getTextRunAdvances(int native_object,
   1000             String text, int start, int end, int contextStart, int contextEnd,
   1001             int flags, float[] advances, int advancesIndex) {
   1002         // FIXME: support contextStart and contextEnd
   1003         int count = end - start;
   1004         char[] buffer = TemporaryBuffer.obtain(count);
   1005         TextUtils.getChars(text, start, end, buffer, 0);
   1006 
   1007         return native_getTextRunAdvances(native_object, buffer, 0, count, contextStart,
   1008                 contextEnd - contextStart, flags, advances, advancesIndex);
   1009     }
   1010 
   1011     @LayoutlibDelegate
   1012     /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, char[] text,
   1013             int contextStart, int contextLength, int flags, int offset, int cursorOpt) {
   1014         // FIXME
   1015         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
   1016                 "Paint.getTextRunCursor is not supported.", null, null /*data*/);
   1017         return 0;
   1018     }
   1019 
   1020     @LayoutlibDelegate
   1021     /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, String text,
   1022             int contextStart, int contextEnd, int flags, int offset, int cursorOpt) {
   1023         // FIXME
   1024         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
   1025                 "Paint.getTextRunCursor is not supported.", null, null /*data*/);
   1026         return 0;
   1027     }
   1028 
   1029     @LayoutlibDelegate
   1030     /*package*/ static void native_getTextPath(int native_object, int bidiFlags,
   1031                 char[] text, int index, int count, float x, float y, int path) {
   1032         // FIXME
   1033         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
   1034                 "Paint.getTextPath is not supported.", null, null /*data*/);
   1035     }
   1036 
   1037     @LayoutlibDelegate
   1038     /*package*/ static void native_getTextPath(int native_object, int bidiFlags,
   1039             String text, int start, int end, float x, float y, int path) {
   1040         // FIXME
   1041         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
   1042                 "Paint.getTextPath is not supported.", null, null /*data*/);
   1043     }
   1044 
   1045     @LayoutlibDelegate
   1046     /*package*/ static void nativeGetStringBounds(int nativePaint, String text, int start,
   1047             int end, int bidiFlags, Rect bounds) {
   1048         nativeGetCharArrayBounds(nativePaint, text.toCharArray(), start, end - start, bidiFlags,
   1049                 bounds);
   1050     }
   1051 
   1052     @LayoutlibDelegate
   1053     /*package*/ static void nativeGetCharArrayBounds(int nativePaint, char[] text, int index,
   1054             int count, int bidiFlags, Rect bounds) {
   1055 
   1056         // get the delegate from the native int.
   1057         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
   1058         if (delegate == null || delegate.mFonts == null || delegate.mFonts.size() == 0) {
   1059             return;
   1060         }
   1061         int w = (int) delegate.measureText(text, index, count, isRtl(bidiFlags));
   1062         int h= delegate.getFonts().get(0).mMetrics.getHeight();
   1063         bounds.set(0, 0, w, h);
   1064     }
   1065 
   1066     @LayoutlibDelegate
   1067     /*package*/ static void finalizer(int nativePaint) {
   1068         sManager.removeJavaReferenceFor(nativePaint);
   1069     }
   1070 
   1071     // ---- Private delegate/helper methods ----
   1072 
   1073     /*package*/ Paint_Delegate() {
   1074         reset();
   1075     }
   1076 
   1077     private Paint_Delegate(Paint_Delegate paint) {
   1078         set(paint);
   1079     }
   1080 
   1081     private void set(Paint_Delegate paint) {
   1082         mFlags = paint.mFlags;
   1083         mColor = paint.mColor;
   1084         mStyle = paint.mStyle;
   1085         mCap = paint.mCap;
   1086         mJoin = paint.mJoin;
   1087         mTextAlign = paint.mTextAlign;
   1088         mTypeface = paint.mTypeface;
   1089         mStrokeWidth = paint.mStrokeWidth;
   1090         mStrokeMiter = paint.mStrokeMiter;
   1091         mTextSize = paint.mTextSize;
   1092         mTextScaleX = paint.mTextScaleX;
   1093         mTextSkewX = paint.mTextSkewX;
   1094         mXfermode = paint.mXfermode;
   1095         mColorFilter = paint.mColorFilter;
   1096         mShader = paint.mShader;
   1097         mPathEffect = paint.mPathEffect;
   1098         mMaskFilter = paint.mMaskFilter;
   1099         mRasterizer = paint.mRasterizer;
   1100         mHintingMode = paint.mHintingMode;
   1101         updateFontObject();
   1102     }
   1103 
   1104     private void reset() {
   1105         mFlags = Paint.DEFAULT_PAINT_FLAGS;
   1106         mColor = 0xFF000000;
   1107         mStyle = Paint.Style.FILL.nativeInt;
   1108         mCap = Paint.Cap.BUTT.nativeInt;
   1109         mJoin = Paint.Join.MITER.nativeInt;
   1110         mTextAlign = 0;
   1111         mTypeface = Typeface_Delegate.getDelegate(Typeface.sDefaults[0].native_instance);
   1112         mStrokeWidth = 1.f;
   1113         mStrokeMiter = 4.f;
   1114         mTextSize = 20.f;
   1115         mTextScaleX = 1.f;
   1116         mTextSkewX = 0.f;
   1117         mXfermode = null;
   1118         mColorFilter = null;
   1119         mShader = null;
   1120         mPathEffect = null;
   1121         mMaskFilter = null;
   1122         mRasterizer = null;
   1123         updateFontObject();
   1124         mHintingMode = Paint.HINTING_ON;
   1125     }
   1126 
   1127     /**
   1128      * Update the {@link Font} object from the typeface, text size and scaling
   1129      */
   1130     @SuppressWarnings("deprecation")
   1131     private void updateFontObject() {
   1132         if (mTypeface != null) {
   1133             // Get the fonts from the TypeFace object.
   1134             List<Font> fonts = mTypeface.getFonts();
   1135 
   1136             // create new font objects as well as FontMetrics, based on the current text size
   1137             // and skew info.
   1138             ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(fonts.size());
   1139             for (Font font : fonts) {
   1140                 FontInfo info = new FontInfo();
   1141                 info.mFont = font.deriveFont(mTextSize);
   1142                 if (mTextScaleX != 1.0 || mTextSkewX != 0) {
   1143                     // TODO: support skew
   1144                     info.mFont = info.mFont.deriveFont(new AffineTransform(
   1145                             mTextScaleX, mTextSkewX, 0, 1, 0, 0));
   1146                 }
   1147                 // The metrics here don't have anti-aliasing set.
   1148                 info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont);
   1149 
   1150                 infoList.add(info);
   1151             }
   1152 
   1153             mFonts = Collections.unmodifiableList(infoList);
   1154         }
   1155     }
   1156 
   1157     /*package*/ float measureText(char[] text, int index, int count, boolean isRtl) {
   1158         return new BidiRenderer(null, this, text).renderText(
   1159                 index, index + count, isRtl, null, 0, false, 0, 0);
   1160     }
   1161 
   1162     private float getFontMetrics(FontMetrics metrics) {
   1163         if (mFonts.size() > 0) {
   1164             java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
   1165             if (metrics != null) {
   1166                 // Android expects negative ascent so we invert the value from Java.
   1167                 metrics.top = - javaMetrics.getMaxAscent();
   1168                 metrics.ascent = - javaMetrics.getAscent();
   1169                 metrics.descent = javaMetrics.getDescent();
   1170                 metrics.bottom = javaMetrics.getMaxDescent();
   1171                 metrics.leading = javaMetrics.getLeading();
   1172             }
   1173 
   1174             return javaMetrics.getHeight();
   1175         }
   1176 
   1177         return 0;
   1178     }
   1179 
   1180     private void setTextLocale(String locale) {
   1181         mLocale = new Locale(locale);
   1182     }
   1183 
   1184     private static void setFlag(Paint thisPaint, int flagMask, boolean flagValue) {
   1185         // get the delegate from the native int.
   1186         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
   1187         if (delegate == null) {
   1188             return;
   1189         }
   1190 
   1191         if (flagValue) {
   1192             delegate.mFlags |= flagMask;
   1193         } else {
   1194             delegate.mFlags &= ~flagMask;
   1195         }
   1196     }
   1197 
   1198     private static boolean isRtl(int flag) {
   1199         switch(flag) {
   1200         case Paint.BIDI_RTL:
   1201         case Paint.BIDI_FORCE_RTL:
   1202         case Paint.BIDI_DEFAULT_RTL:
   1203             return true;
   1204         default:
   1205             return false;
   1206         }
   1207     }
   1208 }
   1209