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