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