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