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