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