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