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