blob: 148f9e92f448fc70ed998af03efa59d9d6891b32 [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
reed@android.comdaa200e2009-06-01 21:18:36 +0000284/* create a triangle strip that strokes the specified triangle. There are 8
285 unique vertices, but we repreat the last 2 to close up. Alternatively we
286 could use an indices array, and then only send 8 verts, but not sure that
287 would be faster.
288 */
289static void setStrokeRectStrip(SkGLVertex verts[10], const SkRect& rect,
290 SkScalar width) {
291 const SkScalar rad = SkScalarHalf(width);
292
293 verts[0].setScalars(rect.fLeft + rad, rect.fTop + rad);
294 verts[1].setScalars(rect.fLeft - rad, rect.fTop - rad);
295 verts[2].setScalars(rect.fRight - rad, rect.fTop + rad);
296 verts[3].setScalars(rect.fRight + rad, rect.fTop - rad);
297 verts[4].setScalars(rect.fRight - rad, rect.fBottom - rad);
298 verts[5].setScalars(rect.fRight + rad, rect.fBottom + rad);
299 verts[6].setScalars(rect.fLeft + rad, rect.fBottom - rad);
300 verts[7].setScalars(rect.fLeft - rad, rect.fBottom + rad);
301 verts[8] = verts[0];
302 verts[9] = verts[1];
303}
304
reed@android.com8a1c16f2008-12-17 15:59:43 +0000305void SkGLDevice::drawRect(const SkDraw& draw, const SkRect& rect,
306 const SkPaint& paint) {
307 TRACE_DRAW("coreDrawRect", this, draw);
reed@android.comdaa200e2009-06-01 21:18:36 +0000308
309 bool doStroke = paint.getStyle() == SkPaint::kStroke_Style;
310
311 if (doStroke) {
312 if (paint.getStrokeJoin() != SkPaint::kMiter_Join) {
313 SkGL_unimpl("non-miter stroke rect");
314 return;
315 }
316 } else if (paint.getStrokeJoin() != SkPaint::kMiter_Join) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317 SkPath path;
318 path.addRect(rect);
319 this->drawPath(draw, path, paint);
320 return;
321 }
322
323 AutoPaintShader shader(this, paint);
reed@android.comdaa200e2009-06-01 21:18:36 +0000324 SkScalar width = paint.getStrokeWidth();
325 SkGLVertex vertex[10]; // max needed for all cases
326 int vertCount;
327 GLenum vertMode;
328
329 if (doStroke) {
330 if (width > 0) {
331 vertCount = 10;
332 vertMode = GL_TRIANGLE_STRIP;
333 setStrokeRectStrip(vertex, rect, width);
334 } else { // hairline
335 vertCount = 5;
336 vertMode = GL_LINE_STRIP;
337 vertex[0].setScalars(rect.fLeft, rect.fTop);
338 vertex[1].setScalars(rect.fRight, rect.fTop);
339 vertex[2].setScalars(rect.fRight, rect.fBottom);
340 vertex[3].setScalars(rect.fLeft, rect.fBottom);
341 vertex[4].setScalars(rect.fLeft, rect.fTop);
342 }
343 } else {
344 vertCount = 4;
345 vertMode = GL_TRIANGLE_FAN;
346 vertex->setRectFan(rect);
347 }
348
reed@android.com4a7fd2b2009-06-02 19:46:20 +0000349 const SkGLVertex* texs = shader.useTex() ? vertex : NULL;
reed@android.comdaa200e2009-06-01 21:18:36 +0000350 SkGL::DrawVertices(vertCount, vertMode, vertex, texs, NULL, NULL,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000351 this->updateMatrixClip());
352}
353
354void SkGLDevice::drawPath(const SkDraw& draw, const SkPath& path,
355 const SkPaint& paint) {
356 TRACE_DRAW("coreDrawPath", this, draw);
357 if (paint.getStyle() == SkPaint::kStroke_Style) {
358 return;
359 }
360
361 AutoPaintShader shader(this, paint);
362
363 SkGL::FillPath(path, paint, shader.useTex(), this->updateMatrixClip());
364}
365
366void SkGLDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
367 const SkMatrix& m, const SkPaint& paint) {
368 TRACE_DRAW("coreDrawBitmap", this, draw);
369
370 SkAutoLockPixels alp(bitmap);
371 if (!bitmap.readyToDraw()) {
372 return;
373 }
374
375 SkGLClipIter* iter = this->updateMatrixClip();
376
377 SkPoint max;
378 GLenum name;
379 SkAutoLockTexCache(bitmap, &name, &max);
380 // the lock has already called glBindTexture for us
381 SkGL::SetTexParamsClamp(paint.isFilterBitmap());
382
383 glMatrixMode(GL_TEXTURE);
384 glLoadIdentity();
385 glMatrixMode(GL_MODELVIEW);
386 glPushMatrix();
387 SkGL::MultMatrix(m);
388
389 SkGLVertex pts[4], tex[4];
390
391 pts->setIRectFan(0, 0, bitmap.width(), bitmap.height());
392 tex->setRectFan(0, 0, max.fX, max.fY);
393
394 // now draw the mesh
395 SkGL::SetPaintAlpha(paint);
396 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
397
398 SkGL::DrawVertices(4, GL_TRIANGLE_FAN, pts, tex, NULL, NULL, iter);
399
400 glPopMatrix();
401}
402
403// move this guy into SkGL, so we can call it from SkGLDevice
404static void gl_drawSprite(int x, int y, int w, int h, const SkPoint& max,
405 const SkPaint& paint, SkGLClipIter* iter) {
406 SkGL::SetTexParamsClamp(false);
407
408 glMatrixMode(GL_TEXTURE);
409 glLoadIdentity();
410 glMatrixMode(GL_MODELVIEW);
411 glPushMatrix();
412 glLoadIdentity();
413
414 SkGLVertex pts[4], tex[4];
415
416 // if h < 0, then the texture is bottom-to-top, but since our projection
417 // matrix always inverts Y, we have to re-invert our texture coord here
418 if (h < 0) {
419 h = -h;
420 tex->setRectFan(0, max.fY, max.fX, 0);
421 } else {
422 tex->setRectFan(0, 0, max.fX, max.fY);
423 }
424 pts->setIRectFan(x, y, x + w, y + h);
425
426 SkGL::SetPaintAlpha(paint);
427 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
428
429 // should look to use glDrawTexi() has we do for text...
430 SkGL::DrawVertices(4, GL_TRIANGLE_FAN, pts, tex, NULL, NULL, iter);
431
432 glPopMatrix();
433}
434
435void SkGLDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
436 int left, int top, const SkPaint& paint) {
437 TRACE_DRAW("coreDrawSprite", this, draw);
438
439 SkAutoLockPixels alp(bitmap);
440 if (!bitmap.readyToDraw()) {
441 return;
442 }
443
444 SkGLClipIter* iter = this->updateMatrixClip();
445
446 SkPoint max;
447 GLuint name;
448 SkAutoLockTexCache(bitmap, &name, &max);
449
450 gl_drawSprite(left, top, bitmap.width(), bitmap.height(), max, paint, iter);
451}
452
453void SkGLDevice::drawDevice(const SkDraw& draw, SkDevice* dev,
454 int x, int y, const SkPaint& paint) {
455 TRACE_DRAW("coreDrawDevice", this, draw);
456
457 SkGLDevice::TexOrientation to = ((SkGLDevice*)dev)->bindDeviceAsTexture();
458 if (SkGLDevice::kNo_TexOrientation != to) {
459 SkGLClipIter* iter = this->updateMatrixClip();
460
461 const SkBitmap& bm = dev->accessBitmap(false);
462 int w = bm.width();
463 int h = bm.height();
464 SkPoint max;
465
466 max.set(SkFixedToScalar(w << (16 - SkNextLog2(bm.rowBytesAsPixels()))),
467 SkFixedToScalar(h << (16 - SkNextLog2(h))));
468
469 if (SkGLDevice::kBottomToTop_TexOrientation == to) {
470 h = -h;
471 }
472 gl_drawSprite(x, y, w, h, max, paint, iter);
473 }
474}
475
476///////////////////////////////////////////////////////////////////////////////
477
478static const GLenum gVertexModeToGL[] = {
479 GL_TRIANGLES, // kTriangles_VertexMode,
480 GL_TRIANGLE_STRIP, // kTriangleStrip_VertexMode,
481 GL_TRIANGLE_FAN // kTriangleFan_VertexMode
482};
483
484#include "SkShader.h"
485
486void SkGLDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
487 int vertexCount, const SkPoint vertices[],
488 const SkPoint texs[], const SkColor colors[],
489 SkXfermode* xmode,
490 const uint16_t indices[], int indexCount,
491 const SkPaint& paint) {
492
493 if (false) {
494 SkRect bounds;
495 SkIRect ibounds;
496
497 bounds.set(vertices, vertexCount);
498 bounds.round(&ibounds);
499
500 SkDebugf("---- drawverts: %d pts, texs=%d colors=%d indices=%d bounds [%d %d]\n",
501 vertexCount, texs!=0, colors!=0, indexCount, ibounds.width(), ibounds.height());
502 }
503
504 SkGLClipIter* iter = this->updateMatrixClip();
505
506 SkGL::SetPaint(paint);
507
508 const SkGLVertex* glVerts;
509 const SkGLVertex* glTexs = NULL;
510
511#if GLSCALAR_IS_SCALAR
512 glVerts = (const SkGLVertex*)vertices;
513#else
514 SkAutoSTMalloc<32, SkGLVertex> storage(vertexCount);
515 storage.get()->setPoints(vertices, vertexCount);
516 glVerts = storage.get();
517#endif
518
519 uint8_t* colorArray = NULL;
520 if (colors) {
521 colorArray = (uint8_t*)sk_malloc_throw(vertexCount*4);
522 SkGL::SetRGBA(colorArray, colors, vertexCount);
523 }
524 SkAutoFree afca(colorArray);
525
526 SkGLVertex* texArray = NULL;
527 TexCache* cache = NULL;
528
529 if (texs && paint.getShader()) {
530 SkShader* shader = paint.getShader();
531
532 // if (!shader->setContext(this->accessBitmap(), paint, *draw.fMatrix)) {
533 if (!shader->setContext(*draw.fBitmap, paint, *draw.fMatrix)) {
534 goto DONE;
535 }
536
537 SkBitmap bitmap;
538 SkMatrix matrix;
539 SkShader::TileMode tileModes[2];
540 if (shader->asABitmap(&bitmap, &matrix, tileModes)) {
541 SkPoint max;
542 GLuint name;
543 cache = SkGLDevice::LockTexCache(bitmap, &name, &max);
544 if (NULL == cache) {
545 return;
546 }
547
548 matrix.postScale(max.fX / bitmap.width(), max.fY / bitmap.height());
549 glMatrixMode(GL_TEXTURE);
550 SkGL::LoadMatrix(matrix);
551 glMatrixMode(GL_MODELVIEW);
552
553#if GLSCALAR_IS_SCALAR
554 glTexs = (const SkGLVertex*)texs;
555#else
556 texArray = (SkGLVertex*)sk_malloc_throw(vertexCount * sizeof(SkGLVertex));
557 texArray->setPoints(texs, vertexCount);
558 glTexs = texArray;
559#endif
560
561 SkGL::SetPaintAlpha(paint);
562 SkGL::SetTexParams(paint.isFilterBitmap(),
563 tileModes[0], tileModes[1]);
564 }
565 }
566DONE:
567 SkAutoFree aftex(texArray);
568
569 SkGL::DrawVertices(indices ? indexCount : vertexCount,
570 gVertexModeToGL[vmode],
571 glVerts, glTexs, colorArray, indices, iter);
572
573 if (cache) {
574 SkGLDevice::UnlockTexCache(cache);
575 }
576}
577
578///////////////////////////////////////////////////////////////////////////////
579
580#include "SkGlyphCache.h"
581#include "SkGLTextCache.h"
582
583void SkGLDevice::GlyphCacheAuxProc(void* data) {
584 SkDebugf("-------------- delete text texture cache\n");
585 SkDELETE((SkGLTextCache*)data);
586}
587
588#ifdef SK_SCALAR_IS_FIXED
589#define SkDiv16ToScalar(numer, denom) (SkIntToFixed(numer) / (denom))
590#else
591#define SkDiv16ToScalar(numer, denom) SkScalarDiv(numer, denom)
592#endif
593
594// stolen from SkDraw.cpp - D1G_NoBounder_RectClip
595static void SkGL_Draw1Glyph(const SkDraw1Glyph& state, const SkGlyph& glyph,
596 int x, int y) {
597 SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
598
599 SkGLDrawProcs* procs = (SkGLDrawProcs*)state.fDraw->fProcs;
600
601 x += glyph.fLeft;
602 y += glyph.fTop;
603
604 // check if we're clipped out (nothing to draw)
605 SkIRect bounds;
606 bounds.set(x, y, x + glyph.fWidth, y + glyph.fHeight);
607 if (!SkIRect::Intersects(state.fClip->getBounds(), bounds)) {
608 return;
609 }
610
611 // now dig up our texture cache
612
613 SkGlyphCache* gcache = state.fCache;
614 void* auxData;
615 SkGLTextCache* textCache = NULL;
616
617 if (gcache->getAuxProcData(SkGLDevice::GlyphCacheAuxProc, &auxData)) {
618 textCache = (SkGLTextCache*)auxData;
619 }
620 if (NULL == textCache) {
621 // need to create one
622 textCache = SkNEW(SkGLTextCache);
623 gcache->setAuxProc(SkGLDevice::GlyphCacheAuxProc, textCache);
624 }
625
626 int offset;
627 SkGLTextCache::Strike* strike = textCache->findGlyph(glyph, &offset);
628 if (NULL == strike) {
629 // make sure the glyph has an image
630 uint8_t* aa = (uint8_t*)glyph.fImage;
631 if (NULL == aa) {
632 aa = (uint8_t*)gcache->findImage(glyph);
633 if (NULL == aa) {
634 return; // can't rasterize glyph
635 }
636 }
637 strike = textCache->addGlyphAndBind(glyph, aa, &offset);
638 if (NULL == strike) {
639 // too big to cache, need to draw as is...
640 return;
641 }
642 }
643
644 const int shiftW = strike->widthShift();
645 const int shiftH = strike->heightShift();
646
647 SkFixed left = offset << (16 - shiftW);
648 SkFixed right = (offset + glyph.fWidth) << (16 - shiftW);
649 SkFixed bottom = glyph.fHeight << (16 - shiftH);
650
651 procs->addQuad(strike->texture(), x, y, glyph, left, right, bottom);
652}
653
654#if 1
655// matches the orientation used in SkGL::setRectFan. Too bad we can't rely on
656// QUADS in android's GL
657static const uint8_t gQuadIndices[] = {
658 0, 1, 2, 0, 2, 3,
659 4, 5, 6, 4, 6, 7,
660 8, 9, 10, 8, 10, 11,
661 12, 13, 14, 12, 14, 15,
662 16, 17, 18, 16, 18, 19,
663 20, 21, 22, 20, 22, 23,
664 24, 25, 26, 24, 26, 27,
665 28, 29, 30, 28, 30, 31,
666 32, 33, 34, 32, 34, 35,
667 36, 37, 38, 36, 38, 39,
668 40, 41, 42, 40, 42, 43,
669 44, 45, 46, 44, 46, 47,
670 48, 49, 50, 48, 50, 51,
671 52, 53, 54, 52, 54, 55,
672 56, 57, 58, 56, 58, 59,
673 60, 61, 62, 60, 62, 63,
674 64, 65, 66, 64, 66, 67,
675 68, 69, 70, 68, 70, 71,
676 72, 73, 74, 72, 74, 75,
677 76, 77, 78, 76, 78, 79,
678 80, 81, 82, 80, 82, 83,
679 84, 85, 86, 84, 86, 87,
680 88, 89, 90, 88, 90, 91,
681 92, 93, 94, 92, 94, 95,
682 96, 97, 98, 96, 98, 99,
683 100, 101, 102, 100, 102, 103,
684 104, 105, 106, 104, 106, 107,
685 108, 109, 110, 108, 110, 111,
686 112, 113, 114, 112, 114, 115,
687 116, 117, 118, 116, 118, 119,
688 120, 121, 122, 120, 122, 123,
689 124, 125, 126, 124, 126, 127
690};
691#else
692static void generateQuadIndices(int n) {
693 int index = 0;
694 for (int i = 0; i < n; i++) {
695 SkDebugf(" %3d, %3d, %3d, %3d, %3d, %3d,\n",
696 index, index + 1, index + 2, index, index + 2, index + 3);
697 index += 4;
698 }
699}
700#endif
701
702void SkGLDrawProcs::drawQuads() {
703 SkASSERT(SK_ARRAY_COUNT(gQuadIndices) == MAX_QUADS * 6);
704
705 glBindTexture(GL_TEXTURE_2D, fCurrTexture);
706
707#if 0
708 static bool gOnce;
709 if (!gOnce) {
710 generateQuadIndices(MAX_QUADS);
711 gOnce = true;
712 }
713#endif
714
715 // convert from quad vertex count to triangle vertex count
716 // 6/4 * n == n + (n >> 1) since n is always a multiple of 4
717 SkASSERT((fCurrQuad & 3) == 0);
718 int count = fCurrQuad + (fCurrQuad >> 1);
719
720 if (fClip->isComplex()) {
721 SkGLClipIter iter(fViewportHeight);
722 iter.reset(*fClip);
723 while (!iter.done()) {
724 iter.scissor();
725 glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_BYTE, gQuadIndices);
726 iter.next();
727 }
728 } else {
729 glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_BYTE, gQuadIndices);
730 }
731}
732
733void SkGLDevice::setupForText(SkDraw* draw, const SkPaint& paint) {
734 // we handle complex clips in the SkDraw common code, so we don't check
735 // for it here
736 this->updateMatrixClip();
737
738 SkGL::SetPaint(paint, false);
739
740 glMatrixMode(GL_TEXTURE);
741 glLoadIdentity();
742
743 glMatrixMode(GL_MODELVIEW);
744 glPushMatrix();
745 glLoadIdentity();
746
747 // deferred allocation
748 if (NULL == fDrawProcs) {
749 fDrawProcs = SkNEW(SkGLDrawProcs);
750 fDrawProcs->fD1GProc = SkGL_Draw1Glyph;
751 }
752
753 // init our (and GL's) state
754 fDrawProcs->init(draw->fClip, this->height());
755 // assign to the caller's SkDraw
756 draw->fProcs = fDrawProcs;
757
758 glEnable(GL_TEXTURE_2D);
759 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
760 glShadeModel(GL_FLAT);
761}
762
763void SkGLDevice::drawText(const SkDraw& draw, const void* text,
764 size_t byteLength, SkScalar x, SkScalar y,
765 const SkPaint& paint) {
766 /* Currently, perspective text is draw via paths, invoked directly by
767 SkDraw. This can't work for us, since the bitmap that our draw points
768 to has no pixels, so we just abort if we're in perspective.
769
770 Better fix would be to...
771 - have a callback inside draw to handle path drawing
772 - option to have draw call the font cache, which we could patch (?)
773 */
774 if (draw.fMatrix->getType() & SkMatrix::kPerspective_Mask) {
775 return;
776 }
777
778 SkDraw myDraw(draw);
779 this->setupForText(&myDraw, paint);
780 this->INHERITED::drawText(myDraw, text, byteLength, x, y, paint);
781 fDrawProcs->flush();
782 glPopMatrix(); // GL_MODELVIEW
783}
784
785void SkGLDevice::drawPosText(const SkDraw& draw, const void* text,
786 size_t byteLength, const SkScalar pos[],
787 SkScalar constY, int scalarsPerPos,
788 const SkPaint& paint) {
789 if (draw.fMatrix->getType() & SkMatrix::kPerspective_Mask) {
790 return;
791 }
792
793 SkDraw myDraw(draw);
794 this->setupForText(&myDraw, paint);
795 this->INHERITED::drawPosText(myDraw, text, byteLength, pos, constY,
796 scalarsPerPos, paint);
797 fDrawProcs->flush();
798 glPopMatrix(); // GL_MODELVIEW
799}
800
801void SkGLDevice::drawTextOnPath(const SkDraw& draw, const void* text,
802 size_t byteLength, const SkPath& path,
803 const SkMatrix* m, const SkPaint& paint) {
804 // not supported yet
805}
806