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