Home | History | Annotate | Download | only in view
      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.view;
     18 
     19 import android.graphics.Bitmap;
     20 import android.graphics.Canvas;
     21 import android.graphics.ColorFilter;
     22 import android.graphics.DrawFilter;
     23 import android.graphics.Matrix;
     24 import android.graphics.Paint;
     25 import android.graphics.PaintFlagsDrawFilter;
     26 import android.graphics.Path;
     27 import android.graphics.Picture;
     28 import android.graphics.PorterDuff;
     29 import android.graphics.Rect;
     30 import android.graphics.RectF;
     31 import android.graphics.Region;
     32 import android.graphics.Shader;
     33 import android.graphics.SurfaceTexture;
     34 import android.graphics.TemporaryBuffer;
     35 import android.text.GraphicsOperations;
     36 import android.text.SpannableString;
     37 import android.text.SpannedString;
     38 import android.text.TextUtils;
     39 
     40 /**
     41  * An implementation of Canvas on top of OpenGL ES 2.0.
     42  */
     43 class GLES20Canvas extends HardwareCanvas {
     44     // Must match modifiers used in the JNI layer
     45     private static final int MODIFIER_NONE = 0;
     46     private static final int MODIFIER_SHADOW = 1;
     47     private static final int MODIFIER_SHADER = 2;
     48     private static final int MODIFIER_COLOR_FILTER = 4;
     49 
     50     private final boolean mOpaque;
     51     private int mRenderer;
     52 
     53     // The native renderer will be destroyed when this object dies.
     54     // DO NOT overwrite this reference once it is set.
     55     @SuppressWarnings({"unused", "FieldCanBeLocal"})
     56     private CanvasFinalizer mFinalizer;
     57 
     58     private int mWidth;
     59     private int mHeight;
     60 
     61     private final float[] mPoint = new float[2];
     62     private final float[] mLine = new float[4];
     63 
     64     private final Rect mClipBounds = new Rect();
     65     private final RectF mPathBounds = new RectF();
     66 
     67     private DrawFilter mFilter;
     68 
     69     ///////////////////////////////////////////////////////////////////////////
     70     // JNI
     71     ///////////////////////////////////////////////////////////////////////////
     72 
     73     private static native boolean nIsAvailable();
     74     private static boolean sIsAvailable = nIsAvailable();
     75 
     76     static boolean isAvailable() {
     77         return sIsAvailable;
     78     }
     79 
     80     ///////////////////////////////////////////////////////////////////////////
     81     // Constructors
     82     ///////////////////////////////////////////////////////////////////////////
     83 
     84     /**
     85      * Creates a canvas to render directly on screen.
     86      */
     87     GLES20Canvas(boolean translucent) {
     88         this(false, translucent);
     89     }
     90 
     91     /**
     92      * Creates a canvas to render into an FBO.
     93      */
     94     GLES20Canvas(int layer, boolean translucent) {
     95         mOpaque = !translucent;
     96         mRenderer = nCreateLayerRenderer(layer);
     97         setupFinalizer();
     98     }
     99 
    100     protected GLES20Canvas(boolean record, boolean translucent) {
    101         mOpaque = !translucent;
    102 
    103         if (record) {
    104             mRenderer = nCreateDisplayListRenderer();
    105         } else {
    106             mRenderer = nCreateRenderer();
    107         }
    108 
    109         setupFinalizer();
    110     }
    111 
    112     private void setupFinalizer() {
    113         if (mRenderer == 0) {
    114             throw new IllegalStateException("Could not create GLES20Canvas renderer");
    115         } else {
    116             mFinalizer = new CanvasFinalizer(mRenderer);
    117         }
    118     }
    119 
    120     protected void resetDisplayListRenderer() {
    121         nResetDisplayListRenderer(mRenderer);
    122     }
    123 
    124     private static native int nCreateRenderer();
    125     private static native int nCreateLayerRenderer(int layer);
    126     private static native int nCreateDisplayListRenderer();
    127     private static native void nResetDisplayListRenderer(int renderer);
    128     private static native void nDestroyRenderer(int renderer);
    129 
    130     private static final class CanvasFinalizer {
    131         private final int mRenderer;
    132 
    133         public CanvasFinalizer(int renderer) {
    134             mRenderer = renderer;
    135         }
    136 
    137         @Override
    138         protected void finalize() throws Throwable {
    139             try {
    140                 nDestroyRenderer(mRenderer);
    141             } finally {
    142                 super.finalize();
    143             }
    144         }
    145     }
    146 
    147     ///////////////////////////////////////////////////////////////////////////
    148     // Hardware layers
    149     ///////////////////////////////////////////////////////////////////////////
    150 
    151     @Override
    152     void pushLayerUpdate(HardwareLayer layer) {
    153         nPushLayerUpdate(mRenderer, ((GLES20RenderLayer) layer).mLayer);
    154     }
    155 
    156     @Override
    157     void clearLayerUpdates() {
    158         nClearLayerUpdates(mRenderer);
    159     }
    160 
    161     static native int nCreateTextureLayer(boolean opaque, int[] layerInfo);
    162     static native int nCreateLayer(int width, int height, boolean isOpaque, int[] layerInfo);
    163     static native boolean nResizeLayer(int layerId, int width, int height, int[] layerInfo);
    164     static native void nSetOpaqueLayer(int layerId, boolean isOpaque);
    165     static native void nSetLayerPaint(int layerId, int nativePaint);
    166     static native void nSetLayerColorFilter(int layerId, int nativeColorFilter);
    167     static native void nUpdateTextureLayer(int layerId, int width, int height, boolean opaque,
    168             SurfaceTexture surface);
    169     static native void nClearLayerTexture(int layerId);
    170     static native void nSetTextureLayerTransform(int layerId, int matrix);
    171     static native void nDestroyLayer(int layerId);
    172     static native void nDestroyLayerDeferred(int layerId);
    173     static native void nUpdateRenderLayer(int layerId, int renderer, int displayList,
    174             int left, int top, int right, int bottom);
    175     static native boolean nCopyLayer(int layerId, int bitmap);
    176 
    177     private static native void nClearLayerUpdates(int renderer);
    178     private static native void nPushLayerUpdate(int renderer, int layer);
    179 
    180     ///////////////////////////////////////////////////////////////////////////
    181     // Canvas management
    182     ///////////////////////////////////////////////////////////////////////////
    183 
    184     @Override
    185     public boolean isOpaque() {
    186         return mOpaque;
    187     }
    188 
    189     @Override
    190     public int getWidth() {
    191         return mWidth;
    192     }
    193 
    194     @Override
    195     public int getHeight() {
    196         return mHeight;
    197     }
    198 
    199     @Override
    200     public int getMaximumBitmapWidth() {
    201         return nGetMaximumTextureWidth();
    202     }
    203 
    204     @Override
    205     public int getMaximumBitmapHeight() {
    206         return nGetMaximumTextureHeight();
    207     }
    208 
    209     private static native int nGetMaximumTextureWidth();
    210     private static native int nGetMaximumTextureHeight();
    211 
    212     /**
    213      * Returns the native OpenGLRenderer object.
    214      */
    215     int getRenderer() {
    216         return mRenderer;
    217     }
    218 
    219     ///////////////////////////////////////////////////////////////////////////
    220     // Setup
    221     ///////////////////////////////////////////////////////////////////////////
    222 
    223     @Override
    224     public void setViewport(int width, int height) {
    225         mWidth = width;
    226         mHeight = height;
    227 
    228         nSetViewport(mRenderer, width, height);
    229     }
    230 
    231     private static native void nSetViewport(int renderer, int width, int height);
    232 
    233     @Override
    234     public int onPreDraw(Rect dirty) {
    235         if (dirty != null) {
    236             return nPrepareDirty(mRenderer, dirty.left, dirty.top, dirty.right, dirty.bottom,
    237                     mOpaque);
    238         } else {
    239             return nPrepare(mRenderer, mOpaque);
    240         }
    241     }
    242 
    243     private static native int nPrepare(int renderer, boolean opaque);
    244     private static native int nPrepareDirty(int renderer, int left, int top, int right, int bottom,
    245             boolean opaque);
    246 
    247     @Override
    248     public void onPostDraw() {
    249         nFinish(mRenderer);
    250     }
    251 
    252     private static native void nFinish(int renderer);
    253 
    254     /**
    255      * Returns the size of the stencil buffer required by the underlying
    256      * implementation.
    257      *
    258      * @return The minimum number of bits the stencil buffer must. Always >= 0.
    259      *
    260      * @hide
    261      */
    262     public static int getStencilSize() {
    263         return nGetStencilSize();
    264     }
    265 
    266     private static native int nGetStencilSize();
    267 
    268     ///////////////////////////////////////////////////////////////////////////
    269     // Functor
    270     ///////////////////////////////////////////////////////////////////////////
    271 
    272     @Override
    273     public int callDrawGLFunction(int drawGLFunction) {
    274         return nCallDrawGLFunction(mRenderer, drawGLFunction);
    275     }
    276 
    277     private static native int nCallDrawGLFunction(int renderer, int drawGLFunction);
    278 
    279     @Override
    280     public int invokeFunctors(Rect dirty) {
    281         return nInvokeFunctors(mRenderer, dirty);
    282     }
    283 
    284     private static native int nInvokeFunctors(int renderer, Rect dirty);
    285 
    286     @Override
    287     public void detachFunctor(int functor) {
    288         nDetachFunctor(mRenderer, functor);
    289     }
    290 
    291     private static native void nDetachFunctor(int renderer, int functor);
    292 
    293     @Override
    294     public void attachFunctor(int functor) {
    295         nAttachFunctor(mRenderer, functor);
    296     }
    297 
    298     private static native void nAttachFunctor(int renderer, int functor);
    299 
    300     ///////////////////////////////////////////////////////////////////////////
    301     // Memory
    302     ///////////////////////////////////////////////////////////////////////////
    303 
    304     /**
    305      * Must match Caches::FlushMode values
    306      *
    307      * @see #flushCaches(int)
    308      */
    309     public static final int FLUSH_CACHES_LAYERS = 0;
    310 
    311     /**
    312      * Must match Caches::FlushMode values
    313      *
    314      * @see #flushCaches(int)
    315      */
    316     public static final int FLUSH_CACHES_MODERATE = 1;
    317 
    318     /**
    319      * Must match Caches::FlushMode values
    320      *
    321      * @see #flushCaches(int)
    322      */
    323     public static final int FLUSH_CACHES_FULL = 2;
    324 
    325     /**
    326      * Flush caches to reclaim as much memory as possible. The amount of memory
    327      * to reclaim is indicate by the level parameter.
    328      *
    329      * The level can be one of {@link #FLUSH_CACHES_MODERATE} or
    330      * {@link #FLUSH_CACHES_FULL}.
    331      *
    332      * @param level Hint about the amount of memory to reclaim
    333      *
    334      * @hide
    335      */
    336     public static void flushCaches(int level) {
    337         nFlushCaches(level);
    338     }
    339 
    340     private static native void nFlushCaches(int level);
    341 
    342     /**
    343      * Release all resources associated with the underlying caches. This should
    344      * only be called after a full flushCaches().
    345      *
    346      * @hide
    347      */
    348     public static void terminateCaches() {
    349         nTerminateCaches();
    350     }
    351 
    352     private static native void nTerminateCaches();
    353 
    354     /**
    355      * @hide
    356      */
    357     public static void initCaches() {
    358         nInitCaches();
    359     }
    360 
    361     private static native void nInitCaches();
    362 
    363     ///////////////////////////////////////////////////////////////////////////
    364     // Display list
    365     ///////////////////////////////////////////////////////////////////////////
    366 
    367     int getDisplayList(int displayList) {
    368         return nGetDisplayList(mRenderer, displayList);
    369     }
    370 
    371     private static native int nGetDisplayList(int renderer, int displayList);
    372 
    373     static void destroyDisplayList(int displayList) {
    374         nDestroyDisplayList(displayList);
    375     }
    376 
    377     private static native void nDestroyDisplayList(int displayList);
    378 
    379     static int getDisplayListSize(int displayList) {
    380         return nGetDisplayListSize(displayList);
    381     }
    382 
    383     private static native int nGetDisplayListSize(int displayList);
    384 
    385     static void setDisplayListName(int displayList, String name) {
    386         nSetDisplayListName(displayList, name);
    387     }
    388 
    389     private static native void nSetDisplayListName(int displayList, String name);
    390 
    391     @Override
    392     public int drawDisplayList(DisplayList displayList, Rect dirty, int flags) {
    393         return nDrawDisplayList(mRenderer, ((GLES20DisplayList) displayList).getNativeDisplayList(),
    394                 dirty, flags);
    395     }
    396 
    397     private static native int nDrawDisplayList(int renderer, int displayList,
    398             Rect dirty, int flags);
    399 
    400     @Override
    401     void outputDisplayList(DisplayList displayList) {
    402         nOutputDisplayList(mRenderer, ((GLES20DisplayList) displayList).getNativeDisplayList());
    403     }
    404 
    405     private static native void nOutputDisplayList(int renderer, int displayList);
    406 
    407     ///////////////////////////////////////////////////////////////////////////
    408     // Hardware layer
    409     ///////////////////////////////////////////////////////////////////////////
    410 
    411     void drawHardwareLayer(HardwareLayer layer, float x, float y, Paint paint) {
    412         final GLES20Layer glLayer = (GLES20Layer) layer;
    413         final int nativePaint = paint == null ? 0 : paint.mNativePaint;
    414         nDrawLayer(mRenderer, glLayer.getLayer(), x, y, nativePaint);
    415     }
    416 
    417     private static native void nDrawLayer(int renderer, int layer, float x, float y, int paint);
    418 
    419     void interrupt() {
    420         nInterrupt(mRenderer);
    421     }
    422 
    423     void resume() {
    424         nResume(mRenderer);
    425     }
    426 
    427     private static native void nInterrupt(int renderer);
    428     private static native void nResume(int renderer);
    429 
    430     ///////////////////////////////////////////////////////////////////////////
    431     // Clipping
    432     ///////////////////////////////////////////////////////////////////////////
    433 
    434     @Override
    435     public boolean clipPath(Path path) {
    436         // TODO: Implement
    437         path.computeBounds(mPathBounds, true);
    438         return nClipRect(mRenderer, mPathBounds.left, mPathBounds.top,
    439                 mPathBounds.right, mPathBounds.bottom, Region.Op.INTERSECT.nativeInt);
    440     }
    441 
    442     @Override
    443     public boolean clipPath(Path path, Region.Op op) {
    444         // TODO: Implement
    445         path.computeBounds(mPathBounds, true);
    446         return nClipRect(mRenderer, mPathBounds.left, mPathBounds.top,
    447                 mPathBounds.right, mPathBounds.bottom, op.nativeInt);
    448     }
    449 
    450     @Override
    451     public boolean clipRect(float left, float top, float right, float bottom) {
    452         return nClipRect(mRenderer, left, top, right, bottom, Region.Op.INTERSECT.nativeInt);
    453     }
    454 
    455     private static native boolean nClipRect(int renderer, float left, float top,
    456             float right, float bottom, int op);
    457 
    458     @Override
    459     public boolean clipRect(float left, float top, float right, float bottom, Region.Op op) {
    460         return nClipRect(mRenderer, left, top, right, bottom, op.nativeInt);
    461     }
    462 
    463     @Override
    464     public boolean clipRect(int left, int top, int right, int bottom) {
    465         return nClipRect(mRenderer, left, top, right, bottom, Region.Op.INTERSECT.nativeInt);
    466     }
    467 
    468     private static native boolean nClipRect(int renderer, int left, int top, int right, int bottom,
    469             int op);
    470 
    471     @Override
    472     public boolean clipRect(Rect rect) {
    473         return nClipRect(mRenderer, rect.left, rect.top, rect.right, rect.bottom,
    474                 Region.Op.INTERSECT.nativeInt);
    475     }
    476 
    477     @Override
    478     public boolean clipRect(Rect rect, Region.Op op) {
    479         return nClipRect(mRenderer, rect.left, rect.top, rect.right, rect.bottom, op.nativeInt);
    480     }
    481 
    482     @Override
    483     public boolean clipRect(RectF rect) {
    484         return nClipRect(mRenderer, rect.left, rect.top, rect.right, rect.bottom,
    485                 Region.Op.INTERSECT.nativeInt);
    486     }
    487 
    488     @Override
    489     public boolean clipRect(RectF rect, Region.Op op) {
    490         return nClipRect(mRenderer, rect.left, rect.top, rect.right, rect.bottom, op.nativeInt);
    491     }
    492 
    493     @Override
    494     public boolean clipRegion(Region region) {
    495         // TODO: Implement
    496         region.getBounds(mClipBounds);
    497         return nClipRect(mRenderer, mClipBounds.left, mClipBounds.top,
    498                 mClipBounds.right, mClipBounds.bottom, Region.Op.INTERSECT.nativeInt);
    499     }
    500 
    501     @Override
    502     public boolean clipRegion(Region region, Region.Op op) {
    503         // TODO: Implement
    504         region.getBounds(mClipBounds);
    505         return nClipRect(mRenderer, mClipBounds.left, mClipBounds.top,
    506                 mClipBounds.right, mClipBounds.bottom, op.nativeInt);
    507     }
    508 
    509     @Override
    510     public boolean getClipBounds(Rect bounds) {
    511         return nGetClipBounds(mRenderer, bounds);
    512     }
    513 
    514     private static native boolean nGetClipBounds(int renderer, Rect bounds);
    515 
    516     @Override
    517     public boolean quickReject(float left, float top, float right, float bottom, EdgeType type) {
    518         return nQuickReject(mRenderer, left, top, right, bottom, type.nativeInt);
    519     }
    520 
    521     private static native boolean nQuickReject(int renderer, float left, float top,
    522             float right, float bottom, int edge);
    523 
    524     @Override
    525     public boolean quickReject(Path path, EdgeType type) {
    526         path.computeBounds(mPathBounds, true);
    527         return nQuickReject(mRenderer, mPathBounds.left, mPathBounds.top,
    528                 mPathBounds.right, mPathBounds.bottom, type.nativeInt);
    529     }
    530 
    531     @Override
    532     public boolean quickReject(RectF rect, EdgeType type) {
    533         return nQuickReject(mRenderer, rect.left, rect.top, rect.right, rect.bottom, type.nativeInt);
    534     }
    535 
    536     ///////////////////////////////////////////////////////////////////////////
    537     // Transformations
    538     ///////////////////////////////////////////////////////////////////////////
    539 
    540     @Override
    541     public void translate(float dx, float dy) {
    542         if (dx != 0.0f || dy != 0.0f) nTranslate(mRenderer, dx, dy);
    543     }
    544 
    545     private static native void nTranslate(int renderer, float dx, float dy);
    546 
    547     @Override
    548     public void skew(float sx, float sy) {
    549         nSkew(mRenderer, sx, sy);
    550     }
    551 
    552     private static native void nSkew(int renderer, float sx, float sy);
    553 
    554     @Override
    555     public void rotate(float degrees) {
    556         nRotate(mRenderer, degrees);
    557     }
    558 
    559     private static native void nRotate(int renderer, float degrees);
    560 
    561     @Override
    562     public void scale(float sx, float sy) {
    563         nScale(mRenderer, sx, sy);
    564     }
    565 
    566     private static native void nScale(int renderer, float sx, float sy);
    567 
    568     @Override
    569     public void setMatrix(Matrix matrix) {
    570         nSetMatrix(mRenderer, matrix == null ? 0 : matrix.native_instance);
    571     }
    572 
    573     private static native void nSetMatrix(int renderer, int matrix);
    574 
    575     @SuppressWarnings("deprecation")
    576     @Override
    577     public void getMatrix(Matrix matrix) {
    578         nGetMatrix(mRenderer, matrix.native_instance);
    579     }
    580 
    581     private static native void nGetMatrix(int renderer, int matrix);
    582 
    583     @Override
    584     public void concat(Matrix matrix) {
    585         nConcatMatrix(mRenderer, matrix.native_instance);
    586     }
    587 
    588     private static native void nConcatMatrix(int renderer, int matrix);
    589 
    590     ///////////////////////////////////////////////////////////////////////////
    591     // State management
    592     ///////////////////////////////////////////////////////////////////////////
    593 
    594     @Override
    595     public int save() {
    596         return nSave(mRenderer, Canvas.CLIP_SAVE_FLAG | Canvas.MATRIX_SAVE_FLAG);
    597     }
    598 
    599     @Override
    600     public int save(int saveFlags) {
    601         return nSave(mRenderer, saveFlags);
    602     }
    603 
    604     private static native int nSave(int renderer, int flags);
    605 
    606     @Override
    607     public int saveLayer(RectF bounds, Paint paint, int saveFlags) {
    608         if (bounds != null) {
    609             return saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, paint, saveFlags);
    610         }
    611 
    612         int count;
    613         int modifier = paint != null ? setupColorFilter(paint) : MODIFIER_NONE;
    614         try {
    615             final int nativePaint = paint == null ? 0 : paint.mNativePaint;
    616             count = nSaveLayer(mRenderer, nativePaint, saveFlags);
    617         } finally {
    618             if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier);
    619         }
    620         return count;
    621     }
    622 
    623     private static native int nSaveLayer(int renderer, int paint, int saveFlags);
    624 
    625     @Override
    626     public int saveLayer(float left, float top, float right, float bottom, Paint paint,
    627             int saveFlags) {
    628         if (left < right && top < bottom) {
    629             int count;
    630             int modifier = paint != null ? setupColorFilter(paint) : MODIFIER_NONE;
    631             try {
    632                 final int nativePaint = paint == null ? 0 : paint.mNativePaint;
    633                 count = nSaveLayer(mRenderer, left, top, right, bottom, nativePaint, saveFlags);
    634             } finally {
    635                 if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier);
    636             }
    637             return count;
    638         }
    639         return save(saveFlags);
    640     }
    641 
    642     private static native int nSaveLayer(int renderer, float left, float top,
    643             float right, float bottom, int paint, int saveFlags);
    644 
    645     @Override
    646     public int saveLayerAlpha(RectF bounds, int alpha, int saveFlags) {
    647         if (bounds != null) {
    648             return saveLayerAlpha(bounds.left, bounds.top, bounds.right, bounds.bottom,
    649                     alpha, saveFlags);
    650         }
    651         return nSaveLayerAlpha(mRenderer, alpha, saveFlags);
    652     }
    653 
    654     private static native int nSaveLayerAlpha(int renderer, int alpha, int saveFlags);
    655 
    656     @Override
    657     public int saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
    658             int saveFlags) {
    659         if (left < right && top < bottom) {
    660             return nSaveLayerAlpha(mRenderer, left, top, right, bottom, alpha, saveFlags);
    661         }
    662         return save(saveFlags);
    663     }
    664 
    665     private static native int nSaveLayerAlpha(int renderer, float left, float top, float right,
    666             float bottom, int alpha, int saveFlags);
    667 
    668     @Override
    669     public void restore() {
    670         nRestore(mRenderer);
    671     }
    672 
    673     private static native void nRestore(int renderer);
    674 
    675     @Override
    676     public void restoreToCount(int saveCount) {
    677         nRestoreToCount(mRenderer, saveCount);
    678     }
    679 
    680     private static native void nRestoreToCount(int renderer, int saveCount);
    681 
    682     @Override
    683     public int getSaveCount() {
    684         return nGetSaveCount(mRenderer);
    685     }
    686 
    687     private static native int nGetSaveCount(int renderer);
    688 
    689     ///////////////////////////////////////////////////////////////////////////
    690     // Filtering
    691     ///////////////////////////////////////////////////////////////////////////
    692 
    693     @Override
    694     public void setDrawFilter(DrawFilter filter) {
    695         mFilter = filter;
    696         if (filter == null) {
    697             nResetPaintFilter(mRenderer);
    698         } else if (filter instanceof PaintFlagsDrawFilter) {
    699             PaintFlagsDrawFilter flagsFilter = (PaintFlagsDrawFilter) filter;
    700             nSetupPaintFilter(mRenderer, flagsFilter.clearBits, flagsFilter.setBits);
    701         }
    702     }
    703 
    704     private static native void nResetPaintFilter(int renderer);
    705     private static native void nSetupPaintFilter(int renderer, int clearBits, int setBits);
    706 
    707     @Override
    708     public DrawFilter getDrawFilter() {
    709         return mFilter;
    710     }
    711 
    712     ///////////////////////////////////////////////////////////////////////////
    713     // Drawing
    714     ///////////////////////////////////////////////////////////////////////////
    715 
    716     @Override
    717     public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,
    718             Paint paint) {
    719         int modifiers = setupModifiers(paint, MODIFIER_COLOR_FILTER | MODIFIER_SHADER);
    720         try {
    721             nDrawArc(mRenderer, oval.left, oval.top, oval.right, oval.bottom,
    722                     startAngle, sweepAngle, useCenter, paint.mNativePaint);
    723         } finally {
    724             if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
    725         }
    726     }
    727 
    728     private static native void nDrawArc(int renderer, float left, float top,
    729             float right, float bottom, float startAngle, float sweepAngle,
    730             boolean useCenter, int paint);
    731 
    732     @Override
    733     public void drawARGB(int a, int r, int g, int b) {
    734         drawColor((a & 0xFF) << 24 | (r & 0xFF) << 16 | (g & 0xFF) << 8 | (b & 0xFF));
    735     }
    736 
    737     @Override
    738     public void drawPatch(Bitmap bitmap, byte[] chunks, RectF dst, Paint paint) {
    739         if (bitmap.isRecycled()) throw new IllegalArgumentException("Cannot draw recycled bitmaps");
    740         // Shaders are ignored when drawing patches
    741         int modifier = paint != null ? setupColorFilter(paint) : MODIFIER_NONE;
    742         try {
    743             final int nativePaint = paint == null ? 0 : paint.mNativePaint;
    744             nDrawPatch(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, chunks,
    745                     dst.left, dst.top, dst.right, dst.bottom, nativePaint);
    746         } finally {
    747             if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier);
    748         }
    749     }
    750 
    751     private static native void nDrawPatch(int renderer, int bitmap, byte[] buffer, byte[] chunks,
    752             float left, float top, float right, float bottom, int paint);
    753 
    754     @Override
    755     public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
    756         if (bitmap.isRecycled()) throw new IllegalArgumentException("Cannot draw recycled bitmaps");
    757         // Shaders are ignored when drawing bitmaps
    758         int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
    759         try {
    760             final int nativePaint = paint == null ? 0 : paint.mNativePaint;
    761             nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, nativePaint);
    762         } finally {
    763             if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
    764         }
    765     }
    766 
    767     private static native void nDrawBitmap(
    768             int renderer, int bitmap, byte[] buffer, float left, float top, int paint);
    769 
    770     @Override
    771     public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
    772         if (bitmap.isRecycled()) throw new IllegalArgumentException("Cannot draw recycled bitmaps");
    773         // Shaders are ignored when drawing bitmaps
    774         int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
    775         try {
    776             final int nativePaint = paint == null ? 0 : paint.mNativePaint;
    777             nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer,
    778                     matrix.native_instance, nativePaint);
    779         } finally {
    780             if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
    781         }
    782     }
    783 
    784     private static native void nDrawBitmap(int renderer, int bitmap, byte[] buff,
    785             int matrix, int paint);
    786 
    787     @Override
    788     public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
    789         if (bitmap.isRecycled()) throw new IllegalArgumentException("Cannot draw recycled bitmaps");
    790         // Shaders are ignored when drawing bitmaps
    791         int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
    792         try {
    793             final int nativePaint = paint == null ? 0 : paint.mNativePaint;
    794 
    795             int left, top, right, bottom;
    796             if (src == null) {
    797                 left = top = 0;
    798                 right = bitmap.getWidth();
    799                 bottom = bitmap.getHeight();
    800             } else {
    801                 left = src.left;
    802                 right = src.right;
    803                 top = src.top;
    804                 bottom = src.bottom;
    805             }
    806 
    807             nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
    808                     dst.left, dst.top, dst.right, dst.bottom, nativePaint);
    809         } finally {
    810             if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
    811         }
    812     }
    813 
    814     @Override
    815     public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) {
    816         if (bitmap.isRecycled()) throw new IllegalArgumentException("Cannot draw recycled bitmaps");
    817         // Shaders are ignored when drawing bitmaps
    818         int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
    819         try {
    820             final int nativePaint = paint == null ? 0 : paint.mNativePaint;
    821 
    822             float left, top, right, bottom;
    823             if (src == null) {
    824                 left = top = 0;
    825                 right = bitmap.getWidth();
    826                 bottom = bitmap.getHeight();
    827             } else {
    828                 left = src.left;
    829                 right = src.right;
    830                 top = src.top;
    831                 bottom = src.bottom;
    832             }
    833 
    834             nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
    835                     dst.left, dst.top, dst.right, dst.bottom, nativePaint);
    836         } finally {
    837             if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
    838         }
    839     }
    840 
    841     private static native void nDrawBitmap(int renderer, int bitmap, byte[] buffer,
    842             float srcLeft, float srcTop, float srcRight, float srcBottom,
    843             float left, float top, float right, float bottom, int paint);
    844 
    845     @Override
    846     public void drawBitmap(int[] colors, int offset, int stride, float x, float y,
    847             int width, int height, boolean hasAlpha, Paint paint) {
    848         if (width < 0) {
    849             throw new IllegalArgumentException("width must be >= 0");
    850         }
    851 
    852         if (height < 0) {
    853             throw new IllegalArgumentException("height must be >= 0");
    854         }
    855 
    856         if (Math.abs(stride) < width) {
    857             throw new IllegalArgumentException("abs(stride) must be >= width");
    858         }
    859 
    860         int lastScanline = offset + (height - 1) * stride;
    861         int length = colors.length;
    862 
    863         if (offset < 0 || (offset + width > length) || lastScanline < 0 ||
    864                 (lastScanline + width > length)) {
    865             throw new ArrayIndexOutOfBoundsException();
    866         }
    867 
    868         // Shaders are ignored when drawing bitmaps
    869         int modifier = paint != null ? setupColorFilter(paint) : MODIFIER_NONE;
    870         try {
    871             final int nativePaint = paint == null ? 0 : paint.mNativePaint;
    872             nDrawBitmap(mRenderer, colors, offset, stride, x, y,
    873                     width, height, hasAlpha, nativePaint);
    874         } finally {
    875             if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier);
    876         }
    877     }
    878 
    879     private static native void nDrawBitmap(int renderer, int[] colors, int offset, int stride,
    880             float x, float y, int width, int height, boolean hasAlpha, int nativePaint);
    881 
    882     @Override
    883     public void drawBitmap(int[] colors, int offset, int stride, int x, int y,
    884             int width, int height, boolean hasAlpha, Paint paint) {
    885         // Shaders are ignored when drawing bitmaps
    886         drawBitmap(colors, offset, stride, (float) x, (float) y, width, height, hasAlpha, paint);
    887     }
    888 
    889     @Override
    890     public void drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts,
    891             int vertOffset, int[] colors, int colorOffset, Paint paint) {
    892         if (bitmap.isRecycled()) throw new IllegalArgumentException("Cannot draw recycled bitmaps");
    893         if (meshWidth < 0 || meshHeight < 0 || vertOffset < 0 || colorOffset < 0) {
    894             throw new ArrayIndexOutOfBoundsException();
    895         }
    896 
    897         if (meshWidth == 0 || meshHeight == 0) {
    898             return;
    899         }
    900 
    901         final int count = (meshWidth + 1) * (meshHeight + 1);
    902         checkRange(verts.length, vertOffset, count * 2);
    903 
    904         // TODO: Colors are ignored for now
    905         colors = null;
    906         colorOffset = 0;
    907 
    908         int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
    909         try {
    910             final int nativePaint = paint == null ? 0 : paint.mNativePaint;
    911             nDrawBitmapMesh(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, meshWidth, meshHeight,
    912                     verts, vertOffset, colors, colorOffset, nativePaint);
    913         } finally {
    914             if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
    915         }
    916     }
    917 
    918     private static native void nDrawBitmapMesh(int renderer, int bitmap, byte[] buffer,
    919             int meshWidth, int meshHeight, float[] verts, int vertOffset,
    920             int[] colors, int colorOffset, int paint);
    921 
    922     @Override
    923     public void drawCircle(float cx, float cy, float radius, Paint paint) {
    924         int modifiers = setupModifiers(paint, MODIFIER_COLOR_FILTER | MODIFIER_SHADER);
    925         try {
    926             nDrawCircle(mRenderer, cx, cy, radius, paint.mNativePaint);
    927         } finally {
    928             if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
    929         }
    930     }
    931 
    932     private static native void nDrawCircle(int renderer, float cx, float cy,
    933             float radius, int paint);
    934 
    935     @Override
    936     public void drawColor(int color) {
    937         drawColor(color, PorterDuff.Mode.SRC_OVER);
    938     }
    939 
    940     @Override
    941     public void drawColor(int color, PorterDuff.Mode mode) {
    942         nDrawColor(mRenderer, color, mode.nativeInt);
    943     }
    944 
    945     private static native void nDrawColor(int renderer, int color, int mode);
    946 
    947     @Override
    948     public void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) {
    949         mLine[0] = startX;
    950         mLine[1] = startY;
    951         mLine[2] = stopX;
    952         mLine[3] = stopY;
    953         drawLines(mLine, 0, 4, paint);
    954     }
    955 
    956     @Override
    957     public void drawLines(float[] pts, int offset, int count, Paint paint) {
    958         if ((offset | count) < 0 || offset + count > pts.length) {
    959             throw new IllegalArgumentException("The lines array must contain 4 elements per line.");
    960         }
    961         int modifiers = setupModifiers(paint, MODIFIER_COLOR_FILTER | MODIFIER_SHADER);
    962         try {
    963             nDrawLines(mRenderer, pts, offset, count, paint.mNativePaint);
    964         } finally {
    965             if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
    966         }
    967     }
    968 
    969     private static native void nDrawLines(int renderer, float[] points,
    970             int offset, int count, int paint);
    971 
    972     @Override
    973     public void drawLines(float[] pts, Paint paint) {
    974         drawLines(pts, 0, pts.length, paint);
    975     }
    976 
    977     @Override
    978     public void drawOval(RectF oval, Paint paint) {
    979         int modifiers = setupModifiers(paint, MODIFIER_COLOR_FILTER | MODIFIER_SHADER);
    980         try {
    981             nDrawOval(mRenderer, oval.left, oval.top, oval.right, oval.bottom, paint.mNativePaint);
    982         } finally {
    983             if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
    984         }
    985     }
    986 
    987     private static native void nDrawOval(int renderer, float left, float top,
    988             float right, float bottom, int paint);
    989 
    990     @Override
    991     public void drawPaint(Paint paint) {
    992         final Rect r = mClipBounds;
    993         nGetClipBounds(mRenderer, r);
    994         drawRect(r.left, r.top, r.right, r.bottom, paint);
    995     }
    996 
    997     @Override
    998     public void drawPath(Path path, Paint paint) {
    999         int modifiers = setupModifiers(paint, MODIFIER_COLOR_FILTER | MODIFIER_SHADER);
   1000         try {
   1001             if (path.isSimplePath) {
   1002                 if (path.rects != null) {
   1003                     nDrawRects(mRenderer, path.rects.mNativeRegion, paint.mNativePaint);
   1004                 }
   1005             } else {
   1006                 nDrawPath(mRenderer, path.mNativePath, paint.mNativePaint);
   1007             }
   1008         } finally {
   1009             if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
   1010         }
   1011     }
   1012 
   1013     private static native void nDrawPath(int renderer, int path, int paint);
   1014     private static native void nDrawRects(int renderer, int region, int paint);
   1015 
   1016     @Override
   1017     public void drawPicture(Picture picture) {
   1018         if (picture.createdFromStream) {
   1019             return;
   1020         }
   1021 
   1022         picture.endRecording();
   1023         // TODO: Implement rendering
   1024     }
   1025 
   1026     @Override
   1027     public void drawPicture(Picture picture, Rect dst) {
   1028         if (picture.createdFromStream) {
   1029             return;
   1030         }
   1031 
   1032         save();
   1033         translate(dst.left, dst.top);
   1034         if (picture.getWidth() > 0 && picture.getHeight() > 0) {
   1035             scale(dst.width() / picture.getWidth(), dst.height() / picture.getHeight());
   1036         }
   1037         drawPicture(picture);
   1038         restore();
   1039     }
   1040 
   1041     @Override
   1042     public void drawPicture(Picture picture, RectF dst) {
   1043         if (picture.createdFromStream) {
   1044             return;
   1045         }
   1046 
   1047         save();
   1048         translate(dst.left, dst.top);
   1049         if (picture.getWidth() > 0 && picture.getHeight() > 0) {
   1050             scale(dst.width() / picture.getWidth(), dst.height() / picture.getHeight());
   1051         }
   1052         drawPicture(picture);
   1053         restore();
   1054     }
   1055 
   1056     @Override
   1057     public void drawPoint(float x, float y, Paint paint) {
   1058         mPoint[0] = x;
   1059         mPoint[1] = y;
   1060         drawPoints(mPoint, 0, 2, paint);
   1061     }
   1062 
   1063     @Override
   1064     public void drawPoints(float[] pts, Paint paint) {
   1065         drawPoints(pts, 0, pts.length, paint);
   1066     }
   1067 
   1068     @Override
   1069     public void drawPoints(float[] pts, int offset, int count, Paint paint) {
   1070         int modifiers = setupModifiers(paint, MODIFIER_COLOR_FILTER | MODIFIER_SHADER);
   1071         try {
   1072             nDrawPoints(mRenderer, pts, offset, count, paint.mNativePaint);
   1073         } finally {
   1074             if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
   1075         }
   1076     }
   1077 
   1078     private static native void nDrawPoints(int renderer, float[] points,
   1079             int offset, int count, int paint);
   1080 
   1081     @SuppressWarnings("deprecation")
   1082     @Override
   1083     public void drawPosText(char[] text, int index, int count, float[] pos, Paint paint) {
   1084         if (index < 0 || index + count > text.length || count * 2 > pos.length) {
   1085             throw new IndexOutOfBoundsException();
   1086         }
   1087 
   1088         int modifiers = setupModifiers(paint);
   1089         try {
   1090             nDrawPosText(mRenderer, text, index, count, pos, paint.mNativePaint);
   1091         } finally {
   1092             if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
   1093         }
   1094     }
   1095 
   1096     private static native void nDrawPosText(int renderer, char[] text, int index, int count,
   1097             float[] pos, int paint);
   1098 
   1099     @SuppressWarnings("deprecation")
   1100     @Override
   1101     public void drawPosText(String text, float[] pos, Paint paint) {
   1102         if (text.length() * 2 > pos.length) {
   1103             throw new ArrayIndexOutOfBoundsException();
   1104         }
   1105 
   1106         int modifiers = setupModifiers(paint);
   1107         try {
   1108             nDrawPosText(mRenderer, text, 0, text.length(), pos, paint.mNativePaint);
   1109         } finally {
   1110             if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
   1111         }
   1112     }
   1113 
   1114     private static native void nDrawPosText(int renderer, String text, int start, int end,
   1115             float[] pos, int paint);
   1116 
   1117     @Override
   1118     public void drawRect(float left, float top, float right, float bottom, Paint paint) {
   1119         if (left == right || top == bottom) return;
   1120         int modifiers = setupModifiers(paint, MODIFIER_COLOR_FILTER | MODIFIER_SHADER);
   1121         try {
   1122             nDrawRect(mRenderer, left, top, right, bottom, paint.mNativePaint);
   1123         } finally {
   1124             if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
   1125         }
   1126     }
   1127 
   1128     private static native void nDrawRect(int renderer, float left, float top,
   1129             float right, float bottom, int paint);
   1130 
   1131     @Override
   1132     public void drawRect(Rect r, Paint paint) {
   1133         drawRect(r.left, r.top, r.right, r.bottom, paint);
   1134     }
   1135 
   1136     @Override
   1137     public void drawRect(RectF r, Paint paint) {
   1138         drawRect(r.left, r.top, r.right, r.bottom, paint);
   1139     }
   1140 
   1141     @Override
   1142     public void drawRGB(int r, int g, int b) {
   1143         drawColor(0xFF000000 | (r & 0xFF) << 16 | (g & 0xFF) << 8 | (b & 0xFF));
   1144     }
   1145 
   1146     @Override
   1147     public void drawRoundRect(RectF rect, float rx, float ry, Paint paint) {
   1148         int modifiers = setupModifiers(paint, MODIFIER_COLOR_FILTER | MODIFIER_SHADER);
   1149         try {
   1150             nDrawRoundRect(mRenderer, rect.left, rect.top, rect.right, rect.bottom,
   1151                     rx, ry, paint.mNativePaint);
   1152         } finally {
   1153             if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
   1154         }
   1155     }
   1156 
   1157     private static native void nDrawRoundRect(int renderer, float left, float top,
   1158             float right, float bottom, float rx, float y, int paint);
   1159 
   1160     @Override
   1161     public void drawText(char[] text, int index, int count, float x, float y, Paint paint) {
   1162         if ((index | count | (index + count) | (text.length - index - count)) < 0) {
   1163             throw new IndexOutOfBoundsException();
   1164         }
   1165 
   1166         int modifiers = setupModifiers(paint);
   1167         try {
   1168             nDrawText(mRenderer, text, index, count, x, y, paint.mBidiFlags, paint.mNativePaint);
   1169         } finally {
   1170             if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
   1171         }
   1172     }
   1173 
   1174     private static native void nDrawText(int renderer, char[] text, int index, int count,
   1175             float x, float y, int bidiFlags, int paint);
   1176 
   1177     @Override
   1178     public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) {
   1179         int modifiers = setupModifiers(paint);
   1180         try {
   1181             if (text instanceof String || text instanceof SpannedString ||
   1182                     text instanceof SpannableString) {
   1183                 nDrawText(mRenderer, text.toString(), start, end, x, y, paint.mBidiFlags,
   1184                         paint.mNativePaint);
   1185             } else if (text instanceof GraphicsOperations) {
   1186                 ((GraphicsOperations) text).drawText(this, start, end, x, y,
   1187                                                          paint);
   1188             } else {
   1189                 char[] buf = TemporaryBuffer.obtain(end - start);
   1190                 TextUtils.getChars(text, start, end, buf, 0);
   1191                 nDrawText(mRenderer, buf, 0, end - start, x, y,
   1192                         paint.mBidiFlags, paint.mNativePaint);
   1193                 TemporaryBuffer.recycle(buf);
   1194             }
   1195         } finally {
   1196             if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
   1197         }
   1198     }
   1199 
   1200     @Override
   1201     public void drawText(String text, int start, int end, float x, float y, Paint paint) {
   1202         if ((start | end | (end - start) | (text.length() - end)) < 0) {
   1203             throw new IndexOutOfBoundsException();
   1204         }
   1205 
   1206         int modifiers = setupModifiers(paint);
   1207         try {
   1208             nDrawText(mRenderer, text, start, end, x, y, paint.mBidiFlags, paint.mNativePaint);
   1209         } finally {
   1210             if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
   1211         }
   1212     }
   1213 
   1214     private static native void nDrawText(int renderer, String text, int start, int end,
   1215             float x, float y, int bidiFlags, int paint);
   1216 
   1217     @Override
   1218     public void drawText(String text, float x, float y, Paint paint) {
   1219         int modifiers = setupModifiers(paint);
   1220         try {
   1221             nDrawText(mRenderer, text, 0, text.length(), x, y, paint.mBidiFlags,
   1222                     paint.mNativePaint);
   1223         } finally {
   1224             if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
   1225         }
   1226     }
   1227 
   1228     @Override
   1229     public void drawTextOnPath(char[] text, int index, int count, Path path, float hOffset,
   1230             float vOffset, Paint paint) {
   1231         if (index < 0 || index + count > text.length) {
   1232             throw new ArrayIndexOutOfBoundsException();
   1233         }
   1234 
   1235         int modifiers = setupModifiers(paint);
   1236         try {
   1237             nDrawTextOnPath(mRenderer, text, index, count, path.mNativePath, hOffset, vOffset,
   1238                     paint.mBidiFlags, paint.mNativePaint);
   1239         } finally {
   1240             if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
   1241         }
   1242     }
   1243 
   1244     private static native void nDrawTextOnPath(int renderer, char[] text, int index, int count,
   1245             int path, float hOffset, float vOffset, int bidiFlags, int nativePaint);
   1246 
   1247     @Override
   1248     public void drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) {
   1249         if (text.length() == 0) return;
   1250 
   1251         int modifiers = setupModifiers(paint);
   1252         try {
   1253             nDrawTextOnPath(mRenderer, text, 0, text.length(), path.mNativePath, hOffset, vOffset,
   1254                     paint.mBidiFlags, paint.mNativePaint);
   1255         } finally {
   1256             if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
   1257         }
   1258     }
   1259 
   1260     private static native void nDrawTextOnPath(int renderer, String text, int start, int end,
   1261             int path, float hOffset, float vOffset, int bidiFlags, int nativePaint);
   1262 
   1263     @Override
   1264     public void drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount,
   1265             float x, float y, int dir, Paint paint) {
   1266         if ((index | count | text.length - index - count) < 0) {
   1267             throw new IndexOutOfBoundsException();
   1268         }
   1269         if (dir != DIRECTION_LTR && dir != DIRECTION_RTL) {
   1270             throw new IllegalArgumentException("Unknown direction: " + dir);
   1271         }
   1272 
   1273         int modifiers = setupModifiers(paint);
   1274         try {
   1275             nDrawTextRun(mRenderer, text, index, count, contextIndex, contextCount, x, y, dir,
   1276                     paint.mNativePaint);
   1277         } finally {
   1278             if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
   1279         }
   1280     }
   1281 
   1282     private static native void nDrawTextRun(int renderer, char[] text, int index, int count,
   1283             int contextIndex, int contextCount, float x, float y, int dir, int nativePaint);
   1284 
   1285     @Override
   1286     public void drawTextRun(CharSequence text, int start, int end, int contextStart, int contextEnd,
   1287             float x, float y, int dir, Paint paint) {
   1288         if ((start | end | end - start | text.length() - end) < 0) {
   1289             throw new IndexOutOfBoundsException();
   1290         }
   1291 
   1292         int modifiers = setupModifiers(paint);
   1293         try {
   1294             int flags = dir == 0 ? 0 : 1;
   1295             if (text instanceof String || text instanceof SpannedString ||
   1296                     text instanceof SpannableString) {
   1297                 nDrawTextRun(mRenderer, text.toString(), start, end, contextStart,
   1298                         contextEnd, x, y, flags, paint.mNativePaint);
   1299             } else if (text instanceof GraphicsOperations) {
   1300                 ((GraphicsOperations) text).drawTextRun(this, start, end,
   1301                         contextStart, contextEnd, x, y, flags, paint);
   1302             } else {
   1303                 int contextLen = contextEnd - contextStart;
   1304                 int len = end - start;
   1305                 char[] buf = TemporaryBuffer.obtain(contextLen);
   1306                 TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
   1307                 nDrawTextRun(mRenderer, buf, start - contextStart, len, 0, contextLen,
   1308                         x, y, flags, paint.mNativePaint);
   1309                 TemporaryBuffer.recycle(buf);
   1310             }
   1311         } finally {
   1312             if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
   1313         }
   1314     }
   1315 
   1316     private static native void nDrawTextRun(int renderer, String text, int start, int end,
   1317             int contextStart, int contextEnd, float x, float y, int flags, int nativePaint);
   1318 
   1319     @Override
   1320     public void drawVertices(VertexMode mode, int vertexCount, float[] verts, int vertOffset,
   1321             float[] texs, int texOffset, int[] colors, int colorOffset, short[] indices,
   1322             int indexOffset, int indexCount, Paint paint) {
   1323         // TODO: Implement
   1324     }
   1325 
   1326     private int setupModifiers(Bitmap b, Paint paint) {
   1327         if (b.getConfig() != Bitmap.Config.ALPHA_8) {
   1328             final ColorFilter filter = paint.getColorFilter();
   1329             if (filter != null) {
   1330                 nSetupColorFilter(mRenderer, filter.nativeColorFilter);
   1331                 return MODIFIER_COLOR_FILTER;
   1332             }
   1333 
   1334             return MODIFIER_NONE;
   1335         } else {
   1336             return setupModifiers(paint);
   1337         }
   1338     }
   1339 
   1340     private int setupModifiers(Paint paint) {
   1341         int modifiers = MODIFIER_NONE;
   1342 
   1343         if (paint.hasShadow) {
   1344             nSetupShadow(mRenderer, paint.shadowRadius, paint.shadowDx, paint.shadowDy,
   1345                     paint.shadowColor);
   1346             modifiers |= MODIFIER_SHADOW;
   1347         }
   1348 
   1349         final Shader shader = paint.getShader();
   1350         if (shader != null) {
   1351             nSetupShader(mRenderer, shader.native_shader);
   1352             modifiers |= MODIFIER_SHADER;
   1353         }
   1354 
   1355         final ColorFilter filter = paint.getColorFilter();
   1356         if (filter != null) {
   1357             nSetupColorFilter(mRenderer, filter.nativeColorFilter);
   1358             modifiers |= MODIFIER_COLOR_FILTER;
   1359         }
   1360 
   1361         return modifiers;
   1362     }
   1363 
   1364     private int setupModifiers(Paint paint, int flags) {
   1365         int modifiers = MODIFIER_NONE;
   1366 
   1367         if (paint.hasShadow && (flags & MODIFIER_SHADOW) != 0) {
   1368             nSetupShadow(mRenderer, paint.shadowRadius, paint.shadowDx, paint.shadowDy,
   1369                     paint.shadowColor);
   1370             modifiers |= MODIFIER_SHADOW;
   1371         }
   1372 
   1373         final Shader shader = paint.getShader();
   1374         if (shader != null && (flags & MODIFIER_SHADER) != 0) {
   1375             nSetupShader(mRenderer, shader.native_shader);
   1376             modifiers |= MODIFIER_SHADER;
   1377         }
   1378 
   1379         final ColorFilter filter = paint.getColorFilter();
   1380         if (filter != null && (flags & MODIFIER_COLOR_FILTER) != 0) {
   1381             nSetupColorFilter(mRenderer, filter.nativeColorFilter);
   1382             modifiers |= MODIFIER_COLOR_FILTER;
   1383         }
   1384 
   1385         return modifiers;
   1386     }
   1387 
   1388     private int setupColorFilter(Paint paint) {
   1389         final ColorFilter filter = paint.getColorFilter();
   1390         if (filter != null) {
   1391             nSetupColorFilter(mRenderer, filter.nativeColorFilter);
   1392             return MODIFIER_COLOR_FILTER;
   1393         }
   1394         return MODIFIER_NONE;
   1395     }
   1396 
   1397     private static native void nSetupShader(int renderer, int shader);
   1398     private static native void nSetupColorFilter(int renderer, int colorFilter);
   1399     private static native void nSetupShadow(int renderer, float radius,
   1400             float dx, float dy, int color);
   1401 
   1402     private static native void nResetModifiers(int renderer, int modifiers);
   1403 }
   1404