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