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