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