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