SkGLDevice.cpp revision 03202c9c3dfbf8c4feb0a1ee9b3680817e633f58
1#include "SkGLDevice.h"
2#include "SkGL.h"
3#include "SkDrawProcs.h"
4#include "SkRegion.h"
5#include "SkThread.h"
6
7static void TRACE_DRAW(const char func[], SkGLDevice* device,
8                       const SkDraw& draw) {
9    //    SkDebugf("--- <%s> %p %p\n", func, canvas, draw.fDevice);
10}
11
12struct SkGLDrawProcs : public SkDrawProcs {
13public:
14    void init(const SkRegion* clip, int height) {
15        fCurrQuad = 0;
16        fCurrTexture = 0;
17        fClip = clip;
18        fViewportHeight = height;
19
20        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
21        glTexCoordPointer(2, SK_TextGLType, 0, fTexs);
22        glDisableClientState(GL_COLOR_ARRAY);
23        glVertexPointer(2, SK_TextGLType, 0, fVerts);
24    }
25
26    GLenum texture() const { return fCurrTexture; }
27
28    void flush() {
29        if (fCurrQuad && fCurrTexture) {
30            this->drawQuads();
31        }
32        fCurrQuad = 0;
33    }
34
35    void addQuad(GLuint texture, int x, int y, const SkGlyph& glyph,
36                 SkFixed left, SkFixed right, SkFixed bottom) {
37        SkASSERT((size_t)fCurrQuad <= SK_ARRAY_COUNT(fVerts));
38
39        if (fCurrTexture != texture || fCurrQuad == SK_ARRAY_COUNT(fVerts)) {
40            if (fCurrQuad && fCurrTexture) {
41                this->drawQuads();
42            }
43            fCurrQuad = 0;
44            fCurrTexture = texture;
45        }
46
47        fVerts[fCurrQuad].setIRectFan(x, y,
48                                      x + glyph.fWidth, y + glyph.fHeight);
49        fTexs[fCurrQuad].setXRectFan(left, 0, right, bottom);
50        fCurrQuad += 4;
51    }
52
53    void drawQuads();
54
55private:
56    enum {
57        MAX_QUADS = 32
58    };
59
60    SkGLTextVertex fVerts[MAX_QUADS * 4];
61    SkGLTextVertex fTexs[MAX_QUADS * 4];
62
63    // these are initialized in setupForText
64    GLuint          fCurrTexture;
65    int             fCurrQuad;
66    int             fViewportHeight;
67    const SkRegion* fClip;
68};
69
70///////////////////////////////////////////////////////////////////////////////
71
72SkGLDevice::SkGLDevice(const SkBitmap& bitmap, bool offscreen)
73        : SkDevice(bitmap), fClipIter(bitmap.height()) {
74    fDrawProcs = NULL;
75}
76
77SkGLDevice::~SkGLDevice() {
78    if (fDrawProcs) {
79        SkDELETE(fDrawProcs);
80    }
81}
82
83void SkGLDevice::setMatrixClip(const SkMatrix& matrix, const SkRegion& clip) {
84    this->INHERITED::setMatrixClip(matrix, clip);
85
86    fGLMatrix.set(matrix);
87    fMatrix = matrix;
88    fClip = clip;
89    fDirty = true;
90}
91
92SkGLDevice::TexOrientation SkGLDevice::bindDeviceAsTexture() {
93    return kNo_TexOrientation;
94}
95
96void SkGLDevice::gainFocus(SkCanvas* canvas) {
97    this->INHERITED::gainFocus(canvas);
98
99    const int w = this->width();
100    const int h = this->height();
101    glViewport(0, 0, w, h);
102    glMatrixMode(GL_PROJECTION);
103    glLoadIdentity();
104    SkGL::Ortho(0, w, h, 0, -1, 1);
105    glMatrixMode(GL_MODELVIEW);
106    fDirty = true;
107}
108
109SkGLClipIter* SkGLDevice::updateMatrixClip() {
110    bool useIter = false;
111
112    // first handle the clip
113    if (fDirty || !fClip.isRect()) {
114        fClipIter.reset(fClip);
115        useIter = true;
116    } else if (fDirty) {
117        // no iter means caller is not respecting complex clips :(
118        SkGL::Scissor(fClip.getBounds(), this->height());
119    }
120    // else we're just a rect, and we've already call scissor
121
122    // now handle the matrix
123    if (fDirty) {
124        MAKE_GL(glLoadMatrix)(fGLMatrix.fMat);
125#if 0
126        SkDebugf("--- gldevice update matrix %p %p\n", this, fFBO);
127        for (int y = 0; y < 4; y++) {
128            SkDebugf(" [ ");
129            for (int x = 0; x < 4; x++) {
130                SkDebugf("%g ", fGLMatrix.fMat[y*4 + x]);
131            }
132            SkDebugf("]\n");
133        }
134#endif
135        fDirty = false;
136    }
137
138    return useIter ? &fClipIter : NULL;
139}
140
141///////////////////////////////////////////////////////////////////////////////
142
143// must be in the same order as SkXfermode::Coeff in SkXfermode.h
144SkGLDevice::AutoPaintShader::AutoPaintShader(SkGLDevice* device,
145                                             const SkPaint& paint) {
146    fDevice = device;
147    fTexCache = device->setupGLPaintShader(paint);
148}
149
150SkGLDevice::AutoPaintShader::~AutoPaintShader() {
151    if (fTexCache) {
152        SkGLDevice::UnlockTexCache(fTexCache);
153    }
154}
155
156SkGLDevice::TexCache* SkGLDevice::setupGLPaintShader(const SkPaint& paint) {
157    SkGL::SetPaint(paint);
158
159    SkShader* shader = paint.getShader();
160    if (NULL == shader) {
161        return NULL;
162    }
163
164    if (!shader->setContext(this->accessBitmap(false), paint, this->matrix())) {
165        return NULL;
166    }
167
168    SkBitmap bitmap;
169    SkMatrix matrix;
170    SkShader::TileMode tileModes[2];
171    if (!shader->asABitmap(&bitmap, &matrix, tileModes)) {
172        return NULL;
173    }
174
175    bitmap.lockPixels();
176    if (!bitmap.readyToDraw()) {
177        return NULL;
178    }
179
180    // see if we've already cached the bitmap from the shader
181    SkPoint max;
182    GLuint name;
183    TexCache* cache = SkGLDevice::LockTexCache(bitmap, &name, &max);
184    // the lock has already called glBindTexture for us
185    SkGL::SetTexParams(paint.isFilterBitmap(), tileModes[0], tileModes[1]);
186
187    // since our texture coords will be in local space, we wack the texture
188    // matrix to map them back into 0...1 before we load it
189    SkMatrix localM;
190    if (shader->getLocalMatrix(&localM)) {
191        SkMatrix inverse;
192        if (localM.invert(&inverse)) {
193            matrix.preConcat(inverse);
194        }
195    }
196
197    matrix.postScale(max.fX / bitmap.width(), max.fY / bitmap.height());
198    glMatrixMode(GL_TEXTURE);
199    SkGL::LoadMatrix(matrix);
200    glMatrixMode(GL_MODELVIEW);
201
202    // since we're going to use a shader/texture, we don't want the color,
203    // just its alpha
204    SkGL::SetAlpha(paint.getAlpha());
205    // report that we have setup the texture
206    return cache;
207}
208
209///////////////////////////////////////////////////////////////////////////////
210///////////////////////////////////////////////////////////////////////////////
211
212void SkGLDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
213    TRACE_DRAW("coreDrawPaint", this, draw);
214
215    AutoPaintShader   shader(this, paint);
216    SkGLVertex        vertex[4];
217    const SkGLVertex* texs = shader.useTex() ? vertex : NULL;
218
219    // set vert to be big enough to fill the space, but not super-huge, to we
220    // don't overflow fixed-point implementations
221    {
222        SkRect r;
223        r.set(this->clip().getBounds());
224        SkMatrix inverse;
225        if (draw.fMatrix->invert(&inverse)) {
226            inverse.mapRect(&r);
227        }
228        vertex->setRectFan(r);
229    }
230
231    SkGL::DrawVertices(4, GL_TRIANGLE_FAN, vertex, texs, NULL, NULL,
232                       this->updateMatrixClip());
233}
234
235static const GLenum gPointMode2GL[] = {
236    GL_POINTS,
237    GL_LINES,
238    GL_LINE_STRIP
239};
240
241void SkGLDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode,
242                            size_t count, const SkPoint pts[], const SkPaint& paint) {
243    TRACE_DRAW("coreDrawPoints", this, draw);
244
245    SkScalar width = paint.getStrokeWidth();
246    if (width < 0) {
247        return;
248    }
249
250    /*  We should really only use drawverts for hairlines, since gl and skia
251     treat the thickness differently...
252     */
253
254    AutoPaintShader shader(this, paint);
255
256    if (width <= 0) {
257        width = SK_Scalar1;
258    }
259
260    if (SkCanvas::kPoints_PointMode == mode) {
261        glPointSize(SkScalarToFloat(width));
262    } else {
263        glLineWidth(SkScalarToFloat(width));
264    }
265
266    const SkGLVertex* verts;
267
268#if GLSCALAR_IS_SCALAR
269    verts = (const SkGLVertex*)pts;
270#else
271    SkAutoSTMalloc<32, SkGLVertex> storage(count);
272    SkGLVertex* v = storage.get();
273
274    v->setPoints(pts, count);
275    verts = v;
276#endif
277
278    const SkGLVertex* texs = shader.useTex() ? verts : NULL;
279
280    SkGL::DrawVertices(count, gPointMode2GL[mode], verts, texs, NULL, NULL,
281                       this->updateMatrixClip());
282}
283
284void SkGLDevice::drawRect(const SkDraw& draw, const SkRect& rect,
285                          const SkPaint& paint) {
286    TRACE_DRAW("coreDrawRect", this, draw);
287
288    if (paint.getStyle() == SkPaint::kStroke_Style) {
289        return;
290    }
291
292    if (paint.getStrokeJoin() != SkPaint::kMiter_Join) {
293        SkPath  path;
294        path.addRect(rect);
295        this->drawPath(draw, path, paint);
296        return;
297    }
298
299    AutoPaintShader shader(this, paint);
300
301    SkGLVertex vertex[4];
302    vertex->setRectFan(rect);
303    const SkGLVertex* texs = shader.useTex() ? vertex : NULL;
304
305    SkGL::DrawVertices(4, GL_TRIANGLE_FAN, vertex, texs, NULL, NULL,
306                       this->updateMatrixClip());
307}
308
309void SkGLDevice::drawPath(const SkDraw& draw, const SkPath& path,
310                          const SkPaint& paint) {
311    TRACE_DRAW("coreDrawPath", this, draw);
312    if (paint.getStyle() == SkPaint::kStroke_Style) {
313        return;
314    }
315
316    AutoPaintShader shader(this, paint);
317
318    SkGL::FillPath(path, paint, shader.useTex(), this->updateMatrixClip());
319}
320
321void SkGLDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
322                            const SkMatrix& m, const SkPaint& paint) {
323    TRACE_DRAW("coreDrawBitmap", this, draw);
324
325    SkAutoLockPixels alp(bitmap);
326    if (!bitmap.readyToDraw()) {
327        return;
328    }
329
330    SkGLClipIter* iter = this->updateMatrixClip();
331
332    SkPoint max;
333    GLenum name;
334    SkAutoLockTexCache(bitmap, &name, &max);
335    // the lock has already called glBindTexture for us
336    SkGL::SetTexParamsClamp(paint.isFilterBitmap());
337
338    glMatrixMode(GL_TEXTURE);
339    glLoadIdentity();
340    glMatrixMode(GL_MODELVIEW);
341    glPushMatrix();
342    SkGL::MultMatrix(m);
343
344    SkGLVertex  pts[4], tex[4];
345
346    pts->setIRectFan(0, 0, bitmap.width(), bitmap.height());
347    tex->setRectFan(0, 0, max.fX, max.fY);
348
349    // now draw the mesh
350    SkGL::SetPaintAlpha(paint);
351    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
352
353    SkGL::DrawVertices(4, GL_TRIANGLE_FAN, pts, tex, NULL, NULL, iter);
354
355    glPopMatrix();
356}
357
358// move this guy into SkGL, so we can call it from SkGLDevice
359static void gl_drawSprite(int x, int y, int w, int h, const SkPoint& max,
360                          const SkPaint& paint, SkGLClipIter* iter) {
361    SkGL::SetTexParamsClamp(false);
362
363    glMatrixMode(GL_TEXTURE);
364    glLoadIdentity();
365    glMatrixMode(GL_MODELVIEW);
366    glPushMatrix();
367    glLoadIdentity();
368
369    SkGLVertex  pts[4], tex[4];
370
371    // if h < 0, then the texture is bottom-to-top, but since our projection
372    // matrix always inverts Y, we have to re-invert our texture coord here
373    if (h < 0) {
374        h = -h;
375        tex->setRectFan(0, max.fY, max.fX, 0);
376    } else {
377        tex->setRectFan(0, 0, max.fX, max.fY);
378    }
379    pts->setIRectFan(x, y, x + w, y + h);
380
381    SkGL::SetPaintAlpha(paint);
382    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
383
384    // should look to use glDrawTexi() has we do for text...
385    SkGL::DrawVertices(4, GL_TRIANGLE_FAN, pts, tex, NULL, NULL, iter);
386
387    glPopMatrix();
388}
389
390void SkGLDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
391                            int left, int top, const SkPaint& paint) {
392    TRACE_DRAW("coreDrawSprite", this, draw);
393
394    SkAutoLockPixels alp(bitmap);
395    if (!bitmap.readyToDraw()) {
396        return;
397    }
398
399    SkGLClipIter* iter = this->updateMatrixClip();
400
401    SkPoint max;
402    GLuint name;
403    SkAutoLockTexCache(bitmap, &name, &max);
404
405    gl_drawSprite(left, top, bitmap.width(), bitmap.height(), max, paint, iter);
406}
407
408void SkGLDevice::drawDevice(const SkDraw& draw, SkDevice* dev,
409                            int x, int y, const SkPaint& paint) {
410    TRACE_DRAW("coreDrawDevice", this, draw);
411
412    SkGLDevice::TexOrientation to = ((SkGLDevice*)dev)->bindDeviceAsTexture();
413    if (SkGLDevice::kNo_TexOrientation != to) {
414        SkGLClipIter* iter = this->updateMatrixClip();
415
416        const SkBitmap& bm = dev->accessBitmap(false);
417        int w = bm.width();
418        int h = bm.height();
419        SkPoint max;
420
421        max.set(SkFixedToScalar(w << (16 - SkNextLog2(bm.rowBytesAsPixels()))),
422                SkFixedToScalar(h << (16 - SkNextLog2(h))));
423
424        if (SkGLDevice::kBottomToTop_TexOrientation == to) {
425            h = -h;
426        }
427        gl_drawSprite(x, y, w, h, max, paint, iter);
428    }
429}
430
431///////////////////////////////////////////////////////////////////////////////
432
433static const GLenum gVertexModeToGL[] = {
434    GL_TRIANGLES,       // kTriangles_VertexMode,
435    GL_TRIANGLE_STRIP,  // kTriangleStrip_VertexMode,
436    GL_TRIANGLE_FAN     // kTriangleFan_VertexMode
437};
438
439#include "SkShader.h"
440
441void SkGLDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
442                              int vertexCount, const SkPoint vertices[],
443                              const SkPoint texs[], const SkColor colors[],
444                              SkXfermode* xmode,
445                              const uint16_t indices[], int indexCount,
446                              const SkPaint& paint) {
447
448    if (false) {
449        SkRect bounds;
450        SkIRect ibounds;
451
452        bounds.set(vertices, vertexCount);
453        bounds.round(&ibounds);
454
455        SkDebugf("---- drawverts: %d pts, texs=%d colors=%d indices=%d bounds [%d %d]\n",
456                 vertexCount, texs!=0, colors!=0, indexCount, ibounds.width(), ibounds.height());
457    }
458
459    SkGLClipIter* iter = this->updateMatrixClip();
460
461    SkGL::SetPaint(paint);
462
463    const SkGLVertex* glVerts;
464    const SkGLVertex* glTexs = NULL;
465
466#if GLSCALAR_IS_SCALAR
467    glVerts = (const SkGLVertex*)vertices;
468#else
469    SkAutoSTMalloc<32, SkGLVertex> storage(vertexCount);
470    storage.get()->setPoints(vertices, vertexCount);
471    glVerts = storage.get();
472#endif
473
474    uint8_t* colorArray = NULL;
475    if (colors) {
476        colorArray = (uint8_t*)sk_malloc_throw(vertexCount*4);
477        SkGL::SetRGBA(colorArray, colors, vertexCount);
478    }
479    SkAutoFree afca(colorArray);
480
481    SkGLVertex* texArray = NULL;
482    TexCache* cache = NULL;
483
484    if (texs && paint.getShader()) {
485        SkShader* shader = paint.getShader();
486
487        //        if (!shader->setContext(this->accessBitmap(), paint, *draw.fMatrix)) {
488        if (!shader->setContext(*draw.fBitmap, paint, *draw.fMatrix)) {
489            goto DONE;
490        }
491
492        SkBitmap bitmap;
493        SkMatrix matrix;
494        SkShader::TileMode tileModes[2];
495        if (shader->asABitmap(&bitmap, &matrix, tileModes)) {
496            SkPoint max;
497            GLuint name;
498            cache = SkGLDevice::LockTexCache(bitmap, &name, &max);
499            if (NULL == cache) {
500                return;
501            }
502
503            matrix.postScale(max.fX / bitmap.width(), max.fY / bitmap.height());
504            glMatrixMode(GL_TEXTURE);
505            SkGL::LoadMatrix(matrix);
506            glMatrixMode(GL_MODELVIEW);
507
508#if GLSCALAR_IS_SCALAR
509            glTexs = (const SkGLVertex*)texs;
510#else
511            texArray = (SkGLVertex*)sk_malloc_throw(vertexCount * sizeof(SkGLVertex));
512            texArray->setPoints(texs, vertexCount);
513            glTexs = texArray;
514#endif
515
516            SkGL::SetPaintAlpha(paint);
517            SkGL::SetTexParams(paint.isFilterBitmap(),
518                               tileModes[0], tileModes[1]);
519        }
520    }
521DONE:
522    SkAutoFree aftex(texArray);
523
524    SkGL::DrawVertices(indices ? indexCount : vertexCount,
525                       gVertexModeToGL[vmode],
526                       glVerts, glTexs, colorArray, indices, iter);
527
528    if (cache) {
529        SkGLDevice::UnlockTexCache(cache);
530    }
531}
532
533///////////////////////////////////////////////////////////////////////////////
534
535#include "SkGlyphCache.h"
536#include "SkGLTextCache.h"
537
538void SkGLDevice::GlyphCacheAuxProc(void* data) {
539    SkDebugf("-------------- delete text texture cache\n");
540    SkDELETE((SkGLTextCache*)data);
541}
542
543#ifdef SK_SCALAR_IS_FIXED
544#define SkDiv16ToScalar(numer, denom)    (SkIntToFixed(numer) / (denom))
545#else
546#define SkDiv16ToScalar(numer, denom)    SkScalarDiv(numer, denom)
547#endif
548
549// stolen from SkDraw.cpp - D1G_NoBounder_RectClip
550static void SkGL_Draw1Glyph(const SkDraw1Glyph& state, const SkGlyph& glyph,
551                            int x, int y) {
552    SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
553
554    SkGLDrawProcs* procs = (SkGLDrawProcs*)state.fDraw->fProcs;
555
556    x += glyph.fLeft;
557    y  += glyph.fTop;
558
559    // check if we're clipped out (nothing to draw)
560	SkIRect bounds;
561	bounds.set(x, y, x + glyph.fWidth, y + glyph.fHeight);
562    if (!SkIRect::Intersects(state.fClip->getBounds(), bounds)) {
563        return;
564    }
565
566    // now dig up our texture cache
567
568    SkGlyphCache* gcache = state.fCache;
569    void* auxData;
570    SkGLTextCache* textCache = NULL;
571
572    if (gcache->getAuxProcData(SkGLDevice::GlyphCacheAuxProc, &auxData)) {
573        textCache = (SkGLTextCache*)auxData;
574    }
575    if (NULL == textCache) {
576        // need to create one
577        textCache = SkNEW(SkGLTextCache);
578        gcache->setAuxProc(SkGLDevice::GlyphCacheAuxProc, textCache);
579    }
580
581    int offset;
582    SkGLTextCache::Strike* strike = textCache->findGlyph(glyph, &offset);
583    if (NULL == strike) {
584        // make sure the glyph has an image
585        uint8_t* aa = (uint8_t*)glyph.fImage;
586        if (NULL == aa) {
587            aa = (uint8_t*)gcache->findImage(glyph);
588            if (NULL == aa) {
589                return; // can't rasterize glyph
590            }
591        }
592        strike = textCache->addGlyphAndBind(glyph, aa, &offset);
593        if (NULL == strike) {
594            // too big to cache, need to draw as is...
595            return;
596        }
597    }
598
599    const int shiftW = strike->widthShift();
600    const int shiftH = strike->heightShift();
601
602    SkFixed left = offset << (16 - shiftW);
603    SkFixed right = (offset + glyph.fWidth) << (16 - shiftW);
604    SkFixed bottom = glyph.fHeight << (16 - shiftH);
605
606    procs->addQuad(strike->texture(), x, y, glyph, left, right, bottom);
607}
608
609#if 1
610// matches the orientation used in SkGL::setRectFan. Too bad we can't rely on
611// QUADS in android's GL
612static const uint8_t gQuadIndices[] = {
613    0,   1,   2,   0,   2,   3,
614    4,   5,   6,   4,   6,   7,
615    8,   9,  10,   8,  10,  11,
616    12,  13,  14,  12,  14,  15,
617    16,  17,  18,  16,  18,  19,
618    20,  21,  22,  20,  22,  23,
619    24,  25,  26,  24,  26,  27,
620    28,  29,  30,  28,  30,  31,
621    32,  33,  34,  32,  34,  35,
622    36,  37,  38,  36,  38,  39,
623    40,  41,  42,  40,  42,  43,
624    44,  45,  46,  44,  46,  47,
625    48,  49,  50,  48,  50,  51,
626    52,  53,  54,  52,  54,  55,
627    56,  57,  58,  56,  58,  59,
628    60,  61,  62,  60,  62,  63,
629    64,  65,  66,  64,  66,  67,
630    68,  69,  70,  68,  70,  71,
631    72,  73,  74,  72,  74,  75,
632    76,  77,  78,  76,  78,  79,
633    80,  81,  82,  80,  82,  83,
634    84,  85,  86,  84,  86,  87,
635    88,  89,  90,  88,  90,  91,
636    92,  93,  94,  92,  94,  95,
637    96,  97,  98,  96,  98,  99,
638    100, 101, 102, 100, 102, 103,
639    104, 105, 106, 104, 106, 107,
640    108, 109, 110, 108, 110, 111,
641    112, 113, 114, 112, 114, 115,
642    116, 117, 118, 116, 118, 119,
643    120, 121, 122, 120, 122, 123,
644    124, 125, 126, 124, 126, 127
645};
646#else
647static void generateQuadIndices(int n) {
648    int index = 0;
649    for (int i = 0; i < n; i++) {
650        SkDebugf("    %3d, %3d, %3d, %3d, %3d, %3d,\n",
651                 index, index + 1, index + 2, index, index + 2, index + 3);
652        index += 4;
653    }
654}
655#endif
656
657void SkGLDrawProcs::drawQuads() {
658    SkASSERT(SK_ARRAY_COUNT(gQuadIndices) == MAX_QUADS * 6);
659
660    glBindTexture(GL_TEXTURE_2D, fCurrTexture);
661
662#if 0
663    static bool gOnce;
664    if (!gOnce) {
665        generateQuadIndices(MAX_QUADS);
666        gOnce = true;
667    }
668#endif
669
670    // convert from quad vertex count to triangle vertex count
671    // 6/4 * n == n + (n >> 1) since n is always a multiple of 4
672    SkASSERT((fCurrQuad & 3) == 0);
673    int count = fCurrQuad + (fCurrQuad >> 1);
674
675    if (fClip->isComplex()) {
676        SkGLClipIter iter(fViewportHeight);
677        iter.reset(*fClip);
678        while (!iter.done()) {
679            iter.scissor();
680            glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_BYTE, gQuadIndices);
681            iter.next();
682        }
683    } else {
684        glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_BYTE, gQuadIndices);
685    }
686}
687
688void SkGLDevice::setupForText(SkDraw* draw, const SkPaint& paint) {
689    // we handle complex clips in the SkDraw common code, so we don't check
690    // for it here
691    this->updateMatrixClip();
692
693    SkGL::SetPaint(paint, false);
694
695    glMatrixMode(GL_TEXTURE);
696    glLoadIdentity();
697
698    glMatrixMode(GL_MODELVIEW);
699    glPushMatrix();
700    glLoadIdentity();
701
702    // deferred allocation
703    if (NULL == fDrawProcs) {
704        fDrawProcs = SkNEW(SkGLDrawProcs);
705        fDrawProcs->fD1GProc = SkGL_Draw1Glyph;
706    }
707
708    // init our (and GL's) state
709    fDrawProcs->init(draw->fClip, this->height());
710    // assign to the caller's SkDraw
711    draw->fProcs = fDrawProcs;
712
713    glEnable(GL_TEXTURE_2D);
714    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
715    glShadeModel(GL_FLAT);
716}
717
718void SkGLDevice::drawText(const SkDraw& draw, const void* text,
719                          size_t byteLength, SkScalar x, SkScalar y,
720                          const SkPaint& paint) {
721    /*  Currently, perspective text is draw via paths, invoked directly by
722     SkDraw. This can't work for us, since the bitmap that our draw points
723     to has no pixels, so we just abort if we're in perspective.
724
725     Better fix would be to...
726     - have a callback inside draw to handle path drawing
727     - option to have draw call the font cache, which we could patch (?)
728     */
729    if (draw.fMatrix->getType() & SkMatrix::kPerspective_Mask) {
730        return;
731    }
732
733    SkDraw myDraw(draw);
734    this->setupForText(&myDraw, paint);
735    this->INHERITED::drawText(myDraw, text, byteLength, x, y, paint);
736    fDrawProcs->flush();
737    glPopMatrix();  // GL_MODELVIEW
738}
739
740void SkGLDevice::drawPosText(const SkDraw& draw, const void* text,
741                             size_t byteLength, const SkScalar pos[],
742                             SkScalar constY, int scalarsPerPos,
743                             const SkPaint& paint) {
744    if (draw.fMatrix->getType() & SkMatrix::kPerspective_Mask) {
745        return;
746    }
747
748    SkDraw myDraw(draw);
749    this->setupForText(&myDraw, paint);
750    this->INHERITED::drawPosText(myDraw, text, byteLength, pos, constY,
751                                 scalarsPerPos, paint);
752    fDrawProcs->flush();
753    glPopMatrix();  // GL_MODELVIEW
754}
755
756void SkGLDevice::drawTextOnPath(const SkDraw& draw, const void* text,
757                                size_t byteLength, const SkPath& path,
758                                const SkMatrix* m, const SkPaint& paint) {
759    // not supported yet
760}
761
762