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