GLES20Canvas.java revision 57066eb64c9a190d1afc87bb060bbb2d31e5b86c
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.Path;
26import android.graphics.Picture;
27import android.graphics.PorterDuff;
28import android.graphics.Rect;
29import android.graphics.RectF;
30import android.graphics.Region;
31import android.graphics.Shader;
32import android.graphics.TemporaryBuffer;
33import android.text.GraphicsOperations;
34import android.text.SpannableString;
35import android.text.SpannedString;
36import android.text.TextUtils;
37
38/**
39 * An implementation of Canvas on top of OpenGL ES 2.0.
40 */
41class GLES20Canvas extends HardwareCanvas {
42    private final boolean mOpaque;
43    private int mRenderer;
44
45    // The native renderer will be destroyed when this object dies.
46    // DO NOT overwrite this reference once it is set.
47    private CanvasFinalizer mFinalizer;
48
49    private int mWidth;
50    private int mHeight;
51
52    private final float[] mPoint = new float[2];
53    private final float[] mLine = new float[4];
54
55    private final Rect mClipBounds = new Rect();
56
57    private DrawFilter mFilter;
58
59    private boolean mContextLocked;
60
61    ///////////////////////////////////////////////////////////////////////////
62    // JNI
63    ///////////////////////////////////////////////////////////////////////////
64
65    private static native boolean nIsAvailable();
66    private static boolean sIsAvailable = nIsAvailable();
67
68    static boolean isAvailable() {
69        return sIsAvailable;
70    }
71
72    ///////////////////////////////////////////////////////////////////////////
73    // Constructors
74    ///////////////////////////////////////////////////////////////////////////
75
76    /**
77     * Creates a canvas to render directly on screen.
78     */
79    GLES20Canvas(boolean translucent) {
80        this(false, translucent);
81    }
82
83    /**
84     * Creates a canvas to render into an FBO.
85     */
86    GLES20Canvas(int fbo, boolean translucent) {
87        mOpaque = !translucent;
88        mRenderer = nCreateLayerRenderer(fbo);
89        setupFinalizer();
90    }
91
92    protected GLES20Canvas(boolean record, boolean translucent) {
93        mOpaque = !translucent;
94
95        setupRenderer(record);
96    }
97
98    protected void setupRenderer(boolean record) {
99        if (record) {
100            mRenderer = nGetDisplayListRenderer(mRenderer);
101        } else {
102            mRenderer = nCreateRenderer();
103        }
104
105        setupFinalizer();
106    }
107
108    private void setupFinalizer() {
109        if (mRenderer == 0) {
110            throw new IllegalStateException("Could not create GLES20Canvas renderer");
111        } else {
112            mFinalizer = CanvasFinalizer.getFinalizer(mFinalizer, mRenderer);
113        }
114    }
115
116    private static native int nCreateRenderer();
117    private static native int nCreateLayerRenderer(int fbo);
118    private static native int nGetDisplayListRenderer(int renderer);
119    private static native void nDestroyRenderer(int renderer);
120
121    private static class CanvasFinalizer {
122        int mRenderer;
123
124        // Factory method returns new instance if old one is null, or old instance
125        // otherwise, destroying native renderer along the way as necessary
126        static CanvasFinalizer getFinalizer(CanvasFinalizer oldFinalizer, int renderer) {
127            if (oldFinalizer == null) {
128                return new CanvasFinalizer(renderer);
129            }
130            oldFinalizer.replaceNativeObject(renderer);
131            return oldFinalizer;
132        }
133
134        private CanvasFinalizer(int renderer) {
135            mRenderer = renderer;
136        }
137
138        private void replaceNativeObject(int newRenderer) {
139            if (mRenderer != 0 && newRenderer != mRenderer) {
140                nDestroyRenderer(mRenderer);
141            }
142            mRenderer = newRenderer;
143        }
144
145        @Override
146        protected void finalize() throws Throwable {
147            try {
148                replaceNativeObject(0);
149            } finally {
150                super.finalize();
151            }
152        }
153    }
154
155    ///////////////////////////////////////////////////////////////////////////
156    // Hardware layers
157    ///////////////////////////////////////////////////////////////////////////
158
159    static native int nCreateLayer(int width, int height, int[] layerInfo);
160    static native void nResizeLayer(int layerId, int layerTextureId, int width, int height,
161            int[] layerInfo);
162    static native void nDestroyLayer(int layerId, int layerTextureId);
163    static native void nDestroyLayerDeferred(int layerId, int layerTextureId);
164
165    ///////////////////////////////////////////////////////////////////////////
166    // Canvas management
167    ///////////////////////////////////////////////////////////////////////////
168
169    @Override
170    public boolean isOpaque() {
171        return mOpaque;
172    }
173
174    @Override
175    public int getWidth() {
176        return mWidth;
177    }
178
179    @Override
180    public int getHeight() {
181        return mHeight;
182    }
183
184    ///////////////////////////////////////////////////////////////////////////
185    // Setup
186    ///////////////////////////////////////////////////////////////////////////
187
188    @Override
189    public void setViewport(int width, int height) {
190        mWidth = width;
191        mHeight = height;
192
193        nSetViewport(mRenderer, width, height);
194    }
195
196    private native void nSetViewport(int renderer, int width, int height);
197
198    @Override
199    void onPreDraw() {
200        nPrepare(mRenderer, mOpaque);
201    }
202
203    private native void nPrepare(int renderer, boolean opaque);
204
205    @Override
206    void onPostDraw() {
207        nFinish(mRenderer);
208    }
209
210    private native void nFinish(int renderer);
211
212    @Override
213    public boolean acquireContext() {
214        if (!mContextLocked) {
215            nAcquireContext(mRenderer);
216            mContextLocked = true;
217        }
218        return mContextLocked;
219    }
220
221    private native void nAcquireContext(int renderer);
222
223    @Override
224    public void releaseContext() {
225        if (mContextLocked) {
226            nReleaseContext(mRenderer);
227            mContextLocked = false;
228        }
229    }
230
231    private native void nReleaseContext(int renderer);
232
233    ///////////////////////////////////////////////////////////////////////////
234    // Display list
235    ///////////////////////////////////////////////////////////////////////////
236
237    int getDisplayList() {
238        return nGetDisplayList(mRenderer);
239    }
240
241    private native int nGetDisplayList(int renderer);
242
243    static void destroyDisplayList(int displayList) {
244        nDestroyDisplayList(displayList);
245    }
246
247    private static native void nDestroyDisplayList(int displayList);
248
249    @Override
250    public void drawDisplayList(DisplayList displayList) {
251        nDrawDisplayList(mRenderer, ((GLES20DisplayList) displayList).mNativeDisplayList);
252    }
253
254    private native void nDrawDisplayList(int renderer, int displayList);
255
256    ///////////////////////////////////////////////////////////////////////////
257    // Hardware layer
258    ///////////////////////////////////////////////////////////////////////////
259
260    void drawHardwareLayer(float left, float top, float right, float bottom,
261            HardwareLayer layer, Paint paint) {
262        final GLES20Layer glLayer = (GLES20Layer) layer;
263        boolean hasColorFilter = paint != null && setupColorFilter(paint);
264        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
265        nDrawLayer(mRenderer, left, top, right, bottom, glLayer.mLayerTextureId,
266                glLayer.getU(), glLayer.getV(), nativePaint);
267        if (hasColorFilter) nResetModifiers(mRenderer);
268    }
269
270    private native void nDrawLayer(int renderer, float left, float top, float right, float bottom,
271            int layerTexture, float u, float v, int paint);
272
273    void interrupt() {
274        nInterrupt(mRenderer);
275    }
276
277    void resume() {
278        nResume(mRenderer);
279    }
280
281    private native void nInterrupt(int renderer);
282    private native void nResume(int renderer);
283
284    ///////////////////////////////////////////////////////////////////////////
285    // Clipping
286    ///////////////////////////////////////////////////////////////////////////
287
288    @Override
289    public boolean clipPath(Path path) {
290        throw new UnsupportedOperationException();
291    }
292
293    @Override
294    public boolean clipPath(Path path, Region.Op op) {
295        throw new UnsupportedOperationException();
296    }
297
298    @Override
299    public boolean clipRect(float left, float top, float right, float bottom) {
300        return nClipRect(mRenderer, left, top, right, bottom, Region.Op.INTERSECT.nativeInt);
301    }
302
303    private native boolean nClipRect(int renderer, float left, float top,
304            float right, float bottom, int op);
305
306    @Override
307    public boolean clipRect(float left, float top, float right, float bottom, Region.Op op) {
308        return nClipRect(mRenderer, left, top, right, bottom, op.nativeInt);
309    }
310
311    @Override
312    public boolean clipRect(int left, int top, int right, int bottom) {
313        return nClipRect(mRenderer, left, top, right, bottom, Region.Op.INTERSECT.nativeInt);
314    }
315
316    private native boolean nClipRect(int renderer, int left, int top, int right, int bottom, int op);
317
318    @Override
319    public boolean clipRect(Rect rect) {
320        return nClipRect(mRenderer, rect.left, rect.top, rect.right, rect.bottom,
321                Region.Op.INTERSECT.nativeInt);
322    }
323
324    @Override
325    public boolean clipRect(Rect rect, Region.Op op) {
326        return nClipRect(mRenderer, rect.left, rect.top, rect.right, rect.bottom, op.nativeInt);
327    }
328
329    @Override
330    public boolean clipRect(RectF rect) {
331        return nClipRect(mRenderer, rect.left, rect.top, rect.right, rect.bottom,
332                Region.Op.INTERSECT.nativeInt);
333    }
334
335    @Override
336    public boolean clipRect(RectF rect, Region.Op op) {
337        return nClipRect(mRenderer, rect.left, rect.top, rect.right, rect.bottom, op.nativeInt);
338    }
339
340    @Override
341    public boolean clipRegion(Region region) {
342        throw new UnsupportedOperationException();
343    }
344
345    @Override
346    public boolean clipRegion(Region region, Region.Op op) {
347        throw new UnsupportedOperationException();
348    }
349
350    @Override
351    public boolean getClipBounds(Rect bounds) {
352        return nGetClipBounds(mRenderer, bounds);
353    }
354
355    private native boolean nGetClipBounds(int renderer, Rect bounds);
356
357    @Override
358    public boolean quickReject(float left, float top, float right, float bottom, EdgeType type) {
359        return nQuickReject(mRenderer, left, top, right, bottom, type.nativeInt);
360    }
361
362    private native boolean nQuickReject(int renderer, float left, float top,
363            float right, float bottom, int edge);
364
365    @Override
366    public boolean quickReject(Path path, EdgeType type) {
367        throw new UnsupportedOperationException();
368    }
369
370    @Override
371    public boolean quickReject(RectF rect, EdgeType type) {
372        return quickReject(rect.left, rect.top, rect.right, rect.bottom, type);
373    }
374
375    ///////////////////////////////////////////////////////////////////////////
376    // Transformations
377    ///////////////////////////////////////////////////////////////////////////
378
379    @Override
380    public void translate(float dx, float dy) {
381        nTranslate(mRenderer, dx, dy);
382    }
383
384    private native void nTranslate(int renderer, float dx, float dy);
385
386    @Override
387    public void skew(float sx, float sy) {
388        throw new UnsupportedOperationException();
389    }
390
391    @Override
392    public void rotate(float degrees) {
393        nRotate(mRenderer, degrees);
394    }
395
396    private native void nRotate(int renderer, float degrees);
397
398    @Override
399    public void scale(float sx, float sy) {
400        nScale(mRenderer, sx, sy);
401    }
402
403    private native void nScale(int renderer, float sx, float sy);
404
405    @Override
406    public void setMatrix(Matrix matrix) {
407        nSetMatrix(mRenderer, matrix.native_instance);
408    }
409
410    private native void nSetMatrix(int renderer, int matrix);
411
412    @Override
413    public int getNativeMatrix() {
414        return nGetMatrix(mRenderer);
415    }
416
417    private native int nGetMatrix(int renderer);
418
419    @Override
420    public void getMatrix(Matrix matrix) {
421        nGetMatrix(mRenderer, matrix.native_instance);
422    }
423
424    private native void nGetMatrix(int renderer, int matrix);
425
426    @Override
427    public void concat(Matrix matrix) {
428        nConcatMatrix(mRenderer, matrix.native_instance);
429    }
430
431    private native void nConcatMatrix(int renderer, int matrix);
432
433    ///////////////////////////////////////////////////////////////////////////
434    // State management
435    ///////////////////////////////////////////////////////////////////////////
436
437    @Override
438    public int save() {
439        return nSave(mRenderer, Canvas.CLIP_SAVE_FLAG | Canvas.MATRIX_SAVE_FLAG);
440    }
441
442    @Override
443    public int save(int saveFlags) {
444        return nSave(mRenderer, saveFlags);
445    }
446
447    private native int nSave(int renderer, int flags);
448
449    @Override
450    public int saveLayer(RectF bounds, Paint paint, int saveFlags) {
451        return saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, paint, saveFlags);
452    }
453
454    @Override
455    public int saveLayer(float left, float top, float right, float bottom, Paint paint,
456            int saveFlags) {
457        boolean hasColorFilter = paint != null && setupColorFilter(paint);
458        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
459        int count = nSaveLayer(mRenderer, left, top, right, bottom, nativePaint, saveFlags);
460        if (hasColorFilter) nResetModifiers(mRenderer);
461        return count;
462    }
463
464    private native int nSaveLayer(int renderer, float left, float top, float right, float bottom,
465            int paint, int saveFlags);
466
467    @Override
468    public int saveLayerAlpha(RectF bounds, int alpha, int saveFlags) {
469        return saveLayerAlpha(bounds.left, bounds.top, bounds.right, bounds.bottom,
470                alpha, saveFlags);
471    }
472
473    @Override
474    public int saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
475            int saveFlags) {
476        return nSaveLayerAlpha(mRenderer, left, top, right, bottom, alpha, saveFlags);
477    }
478
479    private native int nSaveLayerAlpha(int renderer, float left, float top, float right,
480            float bottom, int alpha, int saveFlags);
481
482    @Override
483    public void restore() {
484        nRestore(mRenderer);
485    }
486
487    private native void nRestore(int renderer);
488
489    @Override
490    public void restoreToCount(int saveCount) {
491        nRestoreToCount(mRenderer, saveCount);
492    }
493
494    private native void nRestoreToCount(int renderer, int saveCount);
495
496    @Override
497    public int getSaveCount() {
498        return nGetSaveCount(mRenderer);
499    }
500
501    private native int nGetSaveCount(int renderer);
502
503    ///////////////////////////////////////////////////////////////////////////
504    // Filtering
505    ///////////////////////////////////////////////////////////////////////////
506
507    @Override
508    public void setDrawFilter(DrawFilter filter) {
509        mFilter = filter;
510    }
511
512    @Override
513    public DrawFilter getDrawFilter() {
514        return mFilter;
515    }
516
517    ///////////////////////////////////////////////////////////////////////////
518    // Drawing
519    ///////////////////////////////////////////////////////////////////////////
520
521    @Override
522    public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,
523            Paint paint) {
524        throw new UnsupportedOperationException();
525    }
526
527    @Override
528    public void drawARGB(int a, int r, int g, int b) {
529        drawColor((a & 0xFF) << 24 | (r & 0xFF) << 16 | (g & 0xFF) << 8 | (b & 0xFF));
530    }
531
532    @Override
533    public void drawPatch(Bitmap bitmap, byte[] chunks, RectF dst, Paint paint) {
534        // Shaders are ignored when drawing patches
535        boolean hasColorFilter = paint != null && setupColorFilter(paint);
536        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
537        nDrawPatch(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, chunks,
538                dst.left, dst.top, dst.right, dst.bottom, nativePaint);
539        if (hasColorFilter) nResetModifiers(mRenderer);
540    }
541
542    private native void nDrawPatch(int renderer, int bitmap, byte[] buffer, byte[] chunks,
543            float left, float top, float right, float bottom, int paint);
544
545    @Override
546    public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
547        // Shaders are ignored when drawing bitmaps
548        boolean hasColorFilter = paint != null && setupColorFilter(paint);
549        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
550        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, nativePaint);
551        if (hasColorFilter) nResetModifiers(mRenderer);
552    }
553
554    private native void nDrawBitmap(
555            int renderer, int bitmap, byte[] buffer, float left, float top, int paint);
556
557    @Override
558    public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
559        // Shaders are ignored when drawing bitmaps
560        boolean hasColorFilter = paint != null && setupColorFilter(paint);
561        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
562        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer,
563                matrix.native_instance, nativePaint);
564        if (hasColorFilter) nResetModifiers(mRenderer);
565    }
566
567    private native void nDrawBitmap(int renderer, int bitmap, byte[] buff, int matrix, int paint);
568
569    @Override
570    public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
571        // Shaders are ignored when drawing bitmaps
572        boolean hasColorFilter = paint != null && setupColorFilter(paint);
573        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
574
575        int left, top, right, bottom;
576        if (src == null) {
577            left = top = 0;
578            right = bitmap.getWidth();
579            bottom = bitmap.getHeight();
580        } else {
581            left = src.left;
582            right = src.right;
583            top = src.top;
584            bottom = src.bottom;
585        }
586
587        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
588                dst.left, dst.top, dst.right, dst.bottom, nativePaint);
589        if (hasColorFilter) nResetModifiers(mRenderer);
590    }
591
592    @Override
593    public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) {
594        // Shaders are ignored when drawing bitmaps
595        boolean hasColorFilter = paint != null && setupColorFilter(paint);
596        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
597        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, src.left, src.top, src.right,
598                src.bottom, dst.left, dst.top, dst.right, dst.bottom, nativePaint);
599        if (hasColorFilter) nResetModifiers(mRenderer);
600    }
601
602    private native void nDrawBitmap(int renderer, int bitmap, byte[] buffer,
603            float srcLeft, float srcTop, float srcRight, float srcBottom,
604            float left, float top, float right, float bottom, int paint);
605
606    @Override
607    public void drawBitmap(int[] colors, int offset, int stride, float x, float y,
608            int width, int height, boolean hasAlpha, Paint paint) {
609        // Shaders are ignored when drawing bitmaps
610        boolean hasColorFilter = paint != null && setupColorFilter(paint);
611        final Bitmap.Config config = hasAlpha ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
612        final Bitmap b = Bitmap.createBitmap(colors, offset, stride, width, height, config);
613        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
614        nDrawBitmap(mRenderer, b.mNativeBitmap, b.mBuffer, x, y, nativePaint);
615        b.recycle();
616        if (hasColorFilter) nResetModifiers(mRenderer);
617    }
618
619    @Override
620    public void drawBitmap(int[] colors, int offset, int stride, int x, int y,
621            int width, int height, boolean hasAlpha, Paint paint) {
622        // Shaders are ignored when drawing bitmaps
623        drawBitmap(colors, offset, stride, (float) x, (float) y, width, height, hasAlpha, paint);
624    }
625
626    @Override
627    public void drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts,
628            int vertOffset, int[] colors, int colorOffset, Paint paint) {
629        // TODO: Implement
630    }
631
632    @Override
633    public void drawCircle(float cx, float cy, float radius, Paint paint) {
634        throw new UnsupportedOperationException();
635    }
636
637    @Override
638    public void drawColor(int color) {
639        drawColor(color, PorterDuff.Mode.SRC_OVER);
640    }
641
642    @Override
643    public void drawColor(int color, PorterDuff.Mode mode) {
644        nDrawColor(mRenderer, color, mode.nativeInt);
645    }
646
647    private native void nDrawColor(int renderer, int color, int mode);
648
649    @Override
650    public void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) {
651        mLine[0] = startX;
652        mLine[1] = startY;
653        mLine[2] = stopX;
654        mLine[3] = stopY;
655        drawLines(mLine, 0, 4, paint);
656    }
657
658    @Override
659    public void drawLines(float[] pts, int offset, int count, Paint paint) {
660        if ((offset | count) < 0 || offset + count > pts.length) {
661            throw new IllegalArgumentException("The lines array must contain 4 elements per line.");
662        }
663        boolean hasModifier = setupModifiers(paint);
664        nDrawLines(mRenderer, pts, offset, count, paint.mNativePaint);
665        if (hasModifier) nResetModifiers(mRenderer);
666    }
667
668    private native void nDrawLines(int renderer, float[] points, int offset, int count, int paint);
669
670    @Override
671    public void drawLines(float[] pts, Paint paint) {
672        drawLines(pts, 0, pts.length, paint);
673    }
674
675    @Override
676    public void drawOval(RectF oval, Paint paint) {
677        throw new UnsupportedOperationException();
678    }
679
680    @Override
681    public void drawPaint(Paint paint) {
682        final Rect r = mClipBounds;
683        nGetClipBounds(mRenderer, r);
684        drawRect(r.left, r.top, r.right, r.bottom, paint);
685    }
686
687    @Override
688    public void drawPath(Path path, Paint paint) {
689        boolean hasModifier = setupModifiers(paint);
690        if (path.isSimplePath) {
691            if (path.rects != null) {
692                nDrawRects(mRenderer, path.rects.mNativeRegion, paint.mNativePaint);
693            }
694        } else {
695            nDrawPath(mRenderer, path.mNativePath, paint.mNativePaint);
696        }
697        if (hasModifier) nResetModifiers(mRenderer);
698    }
699
700    private native void nDrawPath(int renderer, int path, int paint);
701    private native void nDrawRects(int renderer, int region, int paint);
702
703    @Override
704    public void drawPicture(Picture picture) {
705        throw new UnsupportedOperationException();
706    }
707
708    @Override
709    public void drawPicture(Picture picture, Rect dst) {
710        throw new UnsupportedOperationException();
711    }
712
713    @Override
714    public void drawPicture(Picture picture, RectF dst) {
715        throw new UnsupportedOperationException();
716    }
717
718    @Override
719    public void drawPoint(float x, float y, Paint paint) {
720        mPoint[0] = x;
721        mPoint[1] = y;
722        drawPoints(mPoint, 0, 1, paint);
723    }
724
725    @Override
726    public void drawPoints(float[] pts, int offset, int count, Paint paint) {
727        // TODO: Implement
728    }
729
730    @Override
731    public void drawPoints(float[] pts, Paint paint) {
732        drawPoints(pts, 0, pts.length / 2, paint);
733    }
734
735    @Override
736    public void drawPosText(char[] text, int index, int count, float[] pos, Paint paint) {
737        throw new UnsupportedOperationException();
738    }
739
740    @Override
741    public void drawPosText(String text, float[] pos, Paint paint) {
742        throw new UnsupportedOperationException();
743    }
744
745    @Override
746    public void drawRect(float left, float top, float right, float bottom, Paint paint) {
747        boolean hasModifier = setupModifiers(paint);
748        nDrawRect(mRenderer, left, top, right, bottom, paint.mNativePaint);
749        if (hasModifier) nResetModifiers(mRenderer);
750    }
751
752    private native void nDrawRect(int renderer, float left, float top, float right, float bottom,
753            int paint);
754
755    @Override
756    public void drawRect(Rect r, Paint paint) {
757        drawRect(r.left, r.top, r.right, r.bottom, paint);
758    }
759
760    @Override
761    public void drawRect(RectF r, Paint paint) {
762        drawRect(r.left, r.top, r.right, r.bottom, paint);
763    }
764
765    @Override
766    public void drawRGB(int r, int g, int b) {
767        drawColor(0xFF000000 | (r & 0xFF) << 16 | (g & 0xFF) << 8 | (b & 0xFF));
768    }
769
770    @Override
771    public void drawRoundRect(RectF rect, float rx, float ry, Paint paint) {
772        // TODO: Implement
773    }
774
775    @Override
776    public void drawText(char[] text, int index, int count, float x, float y, Paint paint) {
777        if ((index | count | (index + count) | (text.length - index - count)) < 0) {
778            throw new IndexOutOfBoundsException();
779        }
780
781        boolean hasModifier = setupModifiers(paint);
782        try {
783            nDrawText(mRenderer, text, index, count, x, y, paint.mBidiFlags, paint.mNativePaint);
784        } finally {
785            if (hasModifier) nResetModifiers(mRenderer);
786        }
787    }
788
789    private native void nDrawText(int renderer, char[] text, int index, int count, float x, float y,
790            int bidiFlags, int paint);
791
792    @Override
793    public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) {
794        boolean hasModifier = setupModifiers(paint);
795        try {
796            if (text instanceof String || text instanceof SpannedString ||
797                    text instanceof SpannableString) {
798                nDrawText(mRenderer, text.toString(), start, end, x, y, paint.mBidiFlags,
799                        paint.mNativePaint);
800            } else if (text instanceof GraphicsOperations) {
801                ((GraphicsOperations) text).drawText(this, start, end, x, y,
802                                                         paint);
803            } else {
804                char[] buf = TemporaryBuffer.obtain(end - start);
805                TextUtils.getChars(text, start, end, buf, 0);
806                nDrawText(mRenderer, buf, 0, end - start, x, y, paint.mBidiFlags, paint.mNativePaint);
807                TemporaryBuffer.recycle(buf);
808            }
809        } finally {
810            if (hasModifier) nResetModifiers(mRenderer);
811        }
812    }
813
814    @Override
815    public void drawText(String text, int start, int end, float x, float y, Paint paint) {
816        if ((start | end | (end - start) | (text.length() - end)) < 0) {
817            throw new IndexOutOfBoundsException();
818        }
819
820        boolean hasModifier = setupModifiers(paint);
821        try {
822            nDrawText(mRenderer, text, start, end, x, y, paint.mBidiFlags, paint.mNativePaint);
823        } finally {
824            if (hasModifier) nResetModifiers(mRenderer);
825        }
826    }
827
828    private native void nDrawText(int renderer, String text, int start, int end, float x, float y,
829            int bidiFlags, int paint);
830
831    @Override
832    public void drawText(String text, float x, float y, Paint paint) {
833        boolean hasModifier = setupModifiers(paint);
834        try {
835            nDrawText(mRenderer, text, 0, text.length(), x, y, paint.mBidiFlags,
836                    paint.mNativePaint);
837        } finally {
838            if (hasModifier) nResetModifiers(mRenderer);
839        }
840    }
841
842    @Override
843    public void drawTextOnPath(char[] text, int index, int count, Path path, float hOffset,
844            float vOffset, Paint paint) {
845        throw new UnsupportedOperationException();
846    }
847
848    @Override
849    public void drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) {
850        throw new UnsupportedOperationException();
851    }
852
853    @Override
854    public void drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount,
855            float x, float y, int dir, Paint paint) {
856        if ((index | count | text.length - index - count) < 0) {
857            throw new IndexOutOfBoundsException();
858        }
859        if (dir != DIRECTION_LTR && dir != DIRECTION_RTL) {
860            throw new IllegalArgumentException("Unknown direction: " + dir);
861        }
862
863        boolean hasModifier = setupModifiers(paint);
864        try {
865            nDrawTextRun(mRenderer, text, index, count, contextIndex, contextCount, x, y, dir,
866                    paint.mNativePaint);
867        } finally {
868            if (hasModifier) nResetModifiers(mRenderer);
869        }
870    }
871
872    private native void nDrawTextRun(int renderer, char[] text, int index, int count,
873            int contextIndex, int contextCount, float x, float y, int dir, int nativePaint);
874
875    @Override
876    public void drawTextRun(CharSequence text, int start, int end, int contextStart, int contextEnd,
877            float x, float y, int dir, Paint paint) {
878        if ((start | end | end - start | text.length() - end) < 0) {
879            throw new IndexOutOfBoundsException();
880        }
881
882        boolean hasModifier = setupModifiers(paint);
883        try {
884            int flags = dir == 0 ? 0 : 1;
885            if (text instanceof String || text instanceof SpannedString ||
886                    text instanceof SpannableString) {
887                nDrawTextRun(mRenderer, text.toString(), start, end, contextStart,
888                        contextEnd, x, y, flags, paint.mNativePaint);
889            } else if (text instanceof GraphicsOperations) {
890                ((GraphicsOperations) text).drawTextRun(this, start, end,
891                        contextStart, contextEnd, x, y, flags, paint);
892            } else {
893                int contextLen = contextEnd - contextStart;
894                int len = end - start;
895                char[] buf = TemporaryBuffer.obtain(contextLen);
896                TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
897                nDrawTextRun(mRenderer, buf, start - contextStart, len, 0, contextLen,
898                        x, y, flags, paint.mNativePaint);
899                TemporaryBuffer.recycle(buf);
900            }
901        } finally {
902            if (hasModifier) nResetModifiers(mRenderer);
903        }
904    }
905
906    private native void nDrawTextRun(int renderer, String text, int start, int end,
907            int contextStart, int contextEnd, float x, float y, int flags, int nativePaint);
908
909    @Override
910    public void drawVertices(VertexMode mode, int vertexCount, float[] verts, int vertOffset,
911            float[] texs, int texOffset, int[] colors, int colorOffset, short[] indices,
912            int indexOffset, int indexCount, Paint paint) {
913        // TODO: Implement
914    }
915
916    private boolean setupModifiers(Paint paint) {
917        boolean hasModifier = false;
918
919        if (paint.hasShadow) {
920            nSetupShadow(mRenderer, paint.shadowRadius, paint.shadowDx, paint.shadowDy,
921                    paint.shadowColor);
922            hasModifier = true;
923        }
924
925        final Shader shader = paint.getShader();
926        if (shader != null) {
927            nSetupShader(mRenderer, shader.native_shader);
928            hasModifier = true;
929        }
930
931        final ColorFilter filter = paint.getColorFilter();
932        if (filter != null) {
933            nSetupColorFilter(mRenderer, filter.nativeColorFilter);
934            hasModifier = true;
935        }
936
937        return hasModifier;
938    }
939
940    private boolean setupColorFilter(Paint paint) {
941        final ColorFilter filter = paint.getColorFilter();
942        if (filter != null) {
943            nSetupColorFilter(mRenderer, filter.nativeColorFilter);
944            return true;
945        }
946        return false;
947    }
948
949    private native void nSetupShader(int renderer, int shader);
950    private native void nSetupColorFilter(int renderer, int colorFilter);
951    private native void nSetupShadow(int renderer, float radius, float dx, float dy, int color);
952
953    private native void nResetModifiers(int renderer);
954}
955