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