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