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