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