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