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