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