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