blob: 70968e24284e1a1221364525b3395c25d160d8ab [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001#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