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