blob: d4606d936adb0b3347226e673107c1e89e5b9c7b [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
reed@android.com8a1c16f2008-12-17 15:59:43 +00008#include "SkGLDevice.h"
9#include "SkGL.h"
10#include "SkDrawProcs.h"
11#include "SkRegion.h"
12#include "SkThread.h"
13
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +000014#ifdef SK_GL_DEVICE_FBO
15 #define USE_FBO_DEVICE
16 #include "SkGLDevice_FBO.h"
17#else
18 #define USE_SWLAYER_DEVICE
19 #include "SkGLDevice_SWLayer.h"
20#endif
21
22// maximum number of entries in our texture cache (before purging)
23#define kTexCountMax_Default 256
24// maximum number of bytes used (by gl) for the texture cache (before purging)
25#define kTexSizeMax_Default (4 * 1024 * 1024)
26
reed@android.com8a1c16f2008-12-17 15:59:43 +000027static void TRACE_DRAW(const char func[], SkGLDevice* device,
28 const SkDraw& draw) {
29 // SkDebugf("--- <%s> %p %p\n", func, canvas, draw.fDevice);
30}
31
32struct SkGLDrawProcs : public SkDrawProcs {
33public:
34 void init(const SkRegion* clip, int height) {
35 fCurrQuad = 0;
36 fCurrTexture = 0;
37 fClip = clip;
38 fViewportHeight = height;
39
40 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
41 glTexCoordPointer(2, SK_TextGLType, 0, fTexs);
42 glDisableClientState(GL_COLOR_ARRAY);
43 glVertexPointer(2, SK_TextGLType, 0, fVerts);
44 }
45
46 GLenum texture() const { return fCurrTexture; }
47
48 void flush() {
49 if (fCurrQuad && fCurrTexture) {
50 this->drawQuads();
51 }
52 fCurrQuad = 0;
53 }
54
55 void addQuad(GLuint texture, int x, int y, const SkGlyph& glyph,
56 SkFixed left, SkFixed right, SkFixed bottom) {
57 SkASSERT((size_t)fCurrQuad <= SK_ARRAY_COUNT(fVerts));
58
59 if (fCurrTexture != texture || fCurrQuad == SK_ARRAY_COUNT(fVerts)) {
60 if (fCurrQuad && fCurrTexture) {
61 this->drawQuads();
62 }
63 fCurrQuad = 0;
64 fCurrTexture = texture;
65 }
66
67 fVerts[fCurrQuad].setIRectFan(x, y,
68 x + glyph.fWidth, y + glyph.fHeight);
69 fTexs[fCurrQuad].setXRectFan(left, 0, right, bottom);
70 fCurrQuad += 4;
71 }
72
73 void drawQuads();
74
75private:
76 enum {
77 MAX_QUADS = 32
78 };
79
80 SkGLTextVertex fVerts[MAX_QUADS * 4];
81 SkGLTextVertex fTexs[MAX_QUADS * 4];
82
83 // these are initialized in setupForText
84 GLuint fCurrTexture;
85 int fCurrQuad;
86 int fViewportHeight;
87 const SkRegion* fClip;
88};
89
90///////////////////////////////////////////////////////////////////////////////
91
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +000092SkDevice* SkGLDeviceFactory::newDevice(SkBitmap::Config config, int width,
93 int height, bool isOpaque,
94 bool isForLayer) {
95 SkBitmap bitmap;
96
97 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
98 bitmap.setIsOpaque(isOpaque);
99
100#ifdef USE_FBO_DEVICE
101 return SkNEW_ARGS(SkGLDevice_FBO, (bitmap, isForLayer));
102#elif defined(USE_SWLAYER_DEVICE)
103 if (isForLayer) {
104 bitmap.allocPixels();
105 if (!bitmap.isOpaque()) {
106 bitmap.eraseColor(0);
107 }
108 return SkNEW_ARGS(SkGLDevice_SWLayer, (bitmap));
109 } else {
110 return SkNEW_ARGS(SkGLDevice, (bitmap, isForLayer));
111 }
112#else
113 return SkNEW_ARGS(SkGLDevice, (bitmap, isForLayer));
114#endif
115}
116
reed@android.com8a1c16f2008-12-17 15:59:43 +0000117SkGLDevice::SkGLDevice(const SkBitmap& bitmap, bool offscreen)
118 : SkDevice(bitmap), fClipIter(bitmap.height()) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000119 glEnable(GL_TEXTURE_2D);
120 glEnable(GL_SCISSOR_TEST);
121 glEnableClientState(GL_VERTEX_ARRAY);
122
123 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
124
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125 fDrawProcs = NULL;
126}
127
128SkGLDevice::~SkGLDevice() {
129 if (fDrawProcs) {
130 SkDELETE(fDrawProcs);
131 }
132}
133
134void SkGLDevice::setMatrixClip(const SkMatrix& matrix, const SkRegion& clip) {
135 this->INHERITED::setMatrixClip(matrix, clip);
136
137 fGLMatrix.set(matrix);
138 fMatrix = matrix;
139 fClip = clip;
140 fDirty = true;
141}
142
143SkGLDevice::TexOrientation SkGLDevice::bindDeviceAsTexture() {
144 return kNo_TexOrientation;
145}
146
147void SkGLDevice::gainFocus(SkCanvas* canvas) {
148 this->INHERITED::gainFocus(canvas);
149
150 const int w = this->width();
151 const int h = this->height();
152 glViewport(0, 0, w, h);
153 glMatrixMode(GL_PROJECTION);
154 glLoadIdentity();
155 SkGL::Ortho(0, w, h, 0, -1, 1);
156 glMatrixMode(GL_MODELVIEW);
157 fDirty = true;
158}
159
160SkGLClipIter* SkGLDevice::updateMatrixClip() {
161 bool useIter = false;
162
163 // first handle the clip
164 if (fDirty || !fClip.isRect()) {
165 fClipIter.reset(fClip);
166 useIter = true;
167 } else if (fDirty) {
168 // no iter means caller is not respecting complex clips :(
169 SkGL::Scissor(fClip.getBounds(), this->height());
170 }
171 // else we're just a rect, and we've already call scissor
172
173 // now handle the matrix
174 if (fDirty) {
175 MAKE_GL(glLoadMatrix)(fGLMatrix.fMat);
176#if 0
177 SkDebugf("--- gldevice update matrix %p %p\n", this, fFBO);
178 for (int y = 0; y < 4; y++) {
179 SkDebugf(" [ ");
180 for (int x = 0; x < 4; x++) {
181 SkDebugf("%g ", fGLMatrix.fMat[y*4 + x]);
182 }
183 SkDebugf("]\n");
184 }
185#endif
186 fDirty = false;
187 }
188
189 return useIter ? &fClipIter : NULL;
190}
191
192///////////////////////////////////////////////////////////////////////////////
193
194// must be in the same order as SkXfermode::Coeff in SkXfermode.h
195SkGLDevice::AutoPaintShader::AutoPaintShader(SkGLDevice* device,
196 const SkPaint& paint) {
197 fDevice = device;
198 fTexCache = device->setupGLPaintShader(paint);
199}
200
201SkGLDevice::AutoPaintShader::~AutoPaintShader() {
202 if (fTexCache) {
203 SkGLDevice::UnlockTexCache(fTexCache);
204 }
205}
206
207SkGLDevice::TexCache* SkGLDevice::setupGLPaintShader(const SkPaint& paint) {
208 SkGL::SetPaint(paint);
209
210 SkShader* shader = paint.getShader();
211 if (NULL == shader) {
212 return NULL;
213 }
214
215 if (!shader->setContext(this->accessBitmap(false), paint, this->matrix())) {
216 return NULL;
217 }
218
219 SkBitmap bitmap;
220 SkMatrix matrix;
221 SkShader::TileMode tileModes[2];
222 if (!shader->asABitmap(&bitmap, &matrix, tileModes)) {
reed@android.com6b82d1a2009-06-03 02:35:01 +0000223 SkGL_unimpl("shader->asABitmap() == false");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000224 return NULL;
225 }
226
227 bitmap.lockPixels();
228 if (!bitmap.readyToDraw()) {
229 return NULL;
230 }
231
232 // see if we've already cached the bitmap from the shader
233 SkPoint max;
234 GLuint name;
235 TexCache* cache = SkGLDevice::LockTexCache(bitmap, &name, &max);
236 // the lock has already called glBindTexture for us
237 SkGL::SetTexParams(paint.isFilterBitmap(), tileModes[0], tileModes[1]);
238
239 // since our texture coords will be in local space, we wack the texture
240 // matrix to map them back into 0...1 before we load it
241 SkMatrix localM;
242 if (shader->getLocalMatrix(&localM)) {
243 SkMatrix inverse;
244 if (localM.invert(&inverse)) {
245 matrix.preConcat(inverse);
246 }
247 }
248
249 matrix.postScale(max.fX / bitmap.width(), max.fY / bitmap.height());
250 glMatrixMode(GL_TEXTURE);
251 SkGL::LoadMatrix(matrix);
252 glMatrixMode(GL_MODELVIEW);
253
254 // since we're going to use a shader/texture, we don't want the color,
255 // just its alpha
256 SkGL::SetAlpha(paint.getAlpha());
257 // report that we have setup the texture
258 return cache;
259}
260
261///////////////////////////////////////////////////////////////////////////////
262///////////////////////////////////////////////////////////////////////////////
263
264void SkGLDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
265 TRACE_DRAW("coreDrawPaint", this, draw);
266
267 AutoPaintShader shader(this, paint);
268 SkGLVertex vertex[4];
269 const SkGLVertex* texs = shader.useTex() ? vertex : NULL;
270
271 // set vert to be big enough to fill the space, but not super-huge, to we
272 // don't overflow fixed-point implementations
273 {
274 SkRect r;
275 r.set(this->clip().getBounds());
276 SkMatrix inverse;
277 if (draw.fMatrix->invert(&inverse)) {
278 inverse.mapRect(&r);
279 }
280 vertex->setRectFan(r);
281 }
282
283 SkGL::DrawVertices(4, GL_TRIANGLE_FAN, vertex, texs, NULL, NULL,
284 this->updateMatrixClip());
285}
286
reed@android.com6b82d1a2009-06-03 02:35:01 +0000287// must be in SkCanvas::PointMode order
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288static const GLenum gPointMode2GL[] = {
289 GL_POINTS,
290 GL_LINES,
291 GL_LINE_STRIP
292};
293
294void SkGLDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode,
295 size_t count, const SkPoint pts[], const SkPaint& paint) {
296 TRACE_DRAW("coreDrawPoints", this, draw);
297
298 SkScalar width = paint.getStrokeWidth();
299 if (width < 0) {
300 return;
301 }
302
303 /* We should really only use drawverts for hairlines, since gl and skia
304 treat the thickness differently...
305 */
306
307 AutoPaintShader shader(this, paint);
308
309 if (width <= 0) {
310 width = SK_Scalar1;
311 }
312
313 if (SkCanvas::kPoints_PointMode == mode) {
314 glPointSize(SkScalarToFloat(width));
315 } else {
316 glLineWidth(SkScalarToFloat(width));
317 }
318
319 const SkGLVertex* verts;
320
321#if GLSCALAR_IS_SCALAR
322 verts = (const SkGLVertex*)pts;
323#else
324 SkAutoSTMalloc<32, SkGLVertex> storage(count);
325 SkGLVertex* v = storage.get();
326
327 v->setPoints(pts, count);
328 verts = v;
329#endif
330
331 const SkGLVertex* texs = shader.useTex() ? verts : NULL;
332
333 SkGL::DrawVertices(count, gPointMode2GL[mode], verts, texs, NULL, NULL,
334 this->updateMatrixClip());
335}
336
reed@android.comdaa200e2009-06-01 21:18:36 +0000337/* create a triangle strip that strokes the specified triangle. There are 8
338 unique vertices, but we repreat the last 2 to close up. Alternatively we
339 could use an indices array, and then only send 8 verts, but not sure that
340 would be faster.
341 */
342static void setStrokeRectStrip(SkGLVertex verts[10], const SkRect& rect,
343 SkScalar width) {
344 const SkScalar rad = SkScalarHalf(width);
345
346 verts[0].setScalars(rect.fLeft + rad, rect.fTop + rad);
347 verts[1].setScalars(rect.fLeft - rad, rect.fTop - rad);
348 verts[2].setScalars(rect.fRight - rad, rect.fTop + rad);
349 verts[3].setScalars(rect.fRight + rad, rect.fTop - rad);
350 verts[4].setScalars(rect.fRight - rad, rect.fBottom - rad);
351 verts[5].setScalars(rect.fRight + rad, rect.fBottom + rad);
352 verts[6].setScalars(rect.fLeft + rad, rect.fBottom - rad);
353 verts[7].setScalars(rect.fLeft - rad, rect.fBottom + rad);
354 verts[8] = verts[0];
355 verts[9] = verts[1];
356}
357
reed@android.com8a1c16f2008-12-17 15:59:43 +0000358void SkGLDevice::drawRect(const SkDraw& draw, const SkRect& rect,
359 const SkPaint& paint) {
360 TRACE_DRAW("coreDrawRect", this, draw);
reed@android.comdaa200e2009-06-01 21:18:36 +0000361
362 bool doStroke = paint.getStyle() == SkPaint::kStroke_Style;
363
364 if (doStroke) {
365 if (paint.getStrokeJoin() != SkPaint::kMiter_Join) {
366 SkGL_unimpl("non-miter stroke rect");
367 return;
368 }
369 } else if (paint.getStrokeJoin() != SkPaint::kMiter_Join) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000370 SkPath path;
371 path.addRect(rect);
372 this->drawPath(draw, path, paint);
373 return;
374 }
375
376 AutoPaintShader shader(this, paint);
reed@android.comdaa200e2009-06-01 21:18:36 +0000377 SkScalar width = paint.getStrokeWidth();
378 SkGLVertex vertex[10]; // max needed for all cases
379 int vertCount;
380 GLenum vertMode;
381
382 if (doStroke) {
383 if (width > 0) {
384 vertCount = 10;
385 vertMode = GL_TRIANGLE_STRIP;
386 setStrokeRectStrip(vertex, rect, width);
387 } else { // hairline
388 vertCount = 5;
389 vertMode = GL_LINE_STRIP;
390 vertex[0].setScalars(rect.fLeft, rect.fTop);
391 vertex[1].setScalars(rect.fRight, rect.fTop);
392 vertex[2].setScalars(rect.fRight, rect.fBottom);
393 vertex[3].setScalars(rect.fLeft, rect.fBottom);
394 vertex[4].setScalars(rect.fLeft, rect.fTop);
reed@android.com6b82d1a2009-06-03 02:35:01 +0000395 glLineWidth(1);
reed@android.comdaa200e2009-06-01 21:18:36 +0000396 }
397 } else {
398 vertCount = 4;
399 vertMode = GL_TRIANGLE_FAN;
400 vertex->setRectFan(rect);
401 }
402
reed@android.com4a7fd2b2009-06-02 19:46:20 +0000403 const SkGLVertex* texs = shader.useTex() ? vertex : NULL;
reed@android.comdaa200e2009-06-01 21:18:36 +0000404 SkGL::DrawVertices(vertCount, vertMode, vertex, texs, NULL, NULL,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000405 this->updateMatrixClip());
406}
407
408void SkGLDevice::drawPath(const SkDraw& draw, const SkPath& path,
409 const SkPaint& paint) {
410 TRACE_DRAW("coreDrawPath", this, draw);
411 if (paint.getStyle() == SkPaint::kStroke_Style) {
reed@android.com6b82d1a2009-06-03 02:35:01 +0000412 SkGL_unimpl("stroke path");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000413 return;
414 }
415
416 AutoPaintShader shader(this, paint);
417
418 SkGL::FillPath(path, paint, shader.useTex(), this->updateMatrixClip());
419}
420
421void SkGLDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
422 const SkMatrix& m, const SkPaint& paint) {
423 TRACE_DRAW("coreDrawBitmap", this, draw);
424
425 SkAutoLockPixels alp(bitmap);
426 if (!bitmap.readyToDraw()) {
427 return;
428 }
429
430 SkGLClipIter* iter = this->updateMatrixClip();
431
432 SkPoint max;
433 GLenum name;
434 SkAutoLockTexCache(bitmap, &name, &max);
435 // the lock has already called glBindTexture for us
436 SkGL::SetTexParamsClamp(paint.isFilterBitmap());
437
438 glMatrixMode(GL_TEXTURE);
439 glLoadIdentity();
440 glMatrixMode(GL_MODELVIEW);
441 glPushMatrix();
442 SkGL::MultMatrix(m);
443
444 SkGLVertex pts[4], tex[4];
445
446 pts->setIRectFan(0, 0, bitmap.width(), bitmap.height());
447 tex->setRectFan(0, 0, max.fX, max.fY);
448
449 // now draw the mesh
450 SkGL::SetPaintAlpha(paint);
451 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
452
453 SkGL::DrawVertices(4, GL_TRIANGLE_FAN, pts, tex, NULL, NULL, iter);
454
455 glPopMatrix();
456}
457
458// move this guy into SkGL, so we can call it from SkGLDevice
459static void gl_drawSprite(int x, int y, int w, int h, const SkPoint& max,
460 const SkPaint& paint, SkGLClipIter* iter) {
461 SkGL::SetTexParamsClamp(false);
462
463 glMatrixMode(GL_TEXTURE);
464 glLoadIdentity();
465 glMatrixMode(GL_MODELVIEW);
466 glPushMatrix();
467 glLoadIdentity();
468
469 SkGLVertex pts[4], tex[4];
470
471 // if h < 0, then the texture is bottom-to-top, but since our projection
472 // matrix always inverts Y, we have to re-invert our texture coord here
473 if (h < 0) {
474 h = -h;
475 tex->setRectFan(0, max.fY, max.fX, 0);
476 } else {
477 tex->setRectFan(0, 0, max.fX, max.fY);
478 }
479 pts->setIRectFan(x, y, x + w, y + h);
480
481 SkGL::SetPaintAlpha(paint);
482 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
483
484 // should look to use glDrawTexi() has we do for text...
485 SkGL::DrawVertices(4, GL_TRIANGLE_FAN, pts, tex, NULL, NULL, iter);
486
487 glPopMatrix();
488}
489
490void SkGLDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
491 int left, int top, const SkPaint& paint) {
492 TRACE_DRAW("coreDrawSprite", this, draw);
493
494 SkAutoLockPixels alp(bitmap);
495 if (!bitmap.readyToDraw()) {
496 return;
497 }
498
499 SkGLClipIter* iter = this->updateMatrixClip();
500
501 SkPoint max;
502 GLuint name;
503 SkAutoLockTexCache(bitmap, &name, &max);
504
505 gl_drawSprite(left, top, bitmap.width(), bitmap.height(), max, paint, iter);
506}
507
508void SkGLDevice::drawDevice(const SkDraw& draw, SkDevice* dev,
509 int x, int y, const SkPaint& paint) {
510 TRACE_DRAW("coreDrawDevice", this, draw);
511
512 SkGLDevice::TexOrientation to = ((SkGLDevice*)dev)->bindDeviceAsTexture();
513 if (SkGLDevice::kNo_TexOrientation != to) {
514 SkGLClipIter* iter = this->updateMatrixClip();
515
516 const SkBitmap& bm = dev->accessBitmap(false);
517 int w = bm.width();
518 int h = bm.height();
519 SkPoint max;
520
521 max.set(SkFixedToScalar(w << (16 - SkNextLog2(bm.rowBytesAsPixels()))),
522 SkFixedToScalar(h << (16 - SkNextLog2(h))));
523
524 if (SkGLDevice::kBottomToTop_TexOrientation == to) {
525 h = -h;
526 }
527 gl_drawSprite(x, y, w, h, max, paint, iter);
528 }
529}
530
531///////////////////////////////////////////////////////////////////////////////
532
533static const GLenum gVertexModeToGL[] = {
534 GL_TRIANGLES, // kTriangles_VertexMode,
535 GL_TRIANGLE_STRIP, // kTriangleStrip_VertexMode,
536 GL_TRIANGLE_FAN // kTriangleFan_VertexMode
537};
538
539#include "SkShader.h"
540
541void SkGLDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
542 int vertexCount, const SkPoint vertices[],
543 const SkPoint texs[], const SkColor colors[],
544 SkXfermode* xmode,
545 const uint16_t indices[], int indexCount,
546 const SkPaint& paint) {
547
548 if (false) {
549 SkRect bounds;
550 SkIRect ibounds;
551
552 bounds.set(vertices, vertexCount);
553 bounds.round(&ibounds);
554
555 SkDebugf("---- drawverts: %d pts, texs=%d colors=%d indices=%d bounds [%d %d]\n",
556 vertexCount, texs!=0, colors!=0, indexCount, ibounds.width(), ibounds.height());
557 }
558
559 SkGLClipIter* iter = this->updateMatrixClip();
560
561 SkGL::SetPaint(paint);
562
563 const SkGLVertex* glVerts;
564 const SkGLVertex* glTexs = NULL;
565
566#if GLSCALAR_IS_SCALAR
567 glVerts = (const SkGLVertex*)vertices;
568#else
569 SkAutoSTMalloc<32, SkGLVertex> storage(vertexCount);
570 storage.get()->setPoints(vertices, vertexCount);
571 glVerts = storage.get();
572#endif
573
574 uint8_t* colorArray = NULL;
575 if (colors) {
576 colorArray = (uint8_t*)sk_malloc_throw(vertexCount*4);
577 SkGL::SetRGBA(colorArray, colors, vertexCount);
578 }
579 SkAutoFree afca(colorArray);
580
581 SkGLVertex* texArray = NULL;
582 TexCache* cache = NULL;
583
584 if (texs && paint.getShader()) {
585 SkShader* shader = paint.getShader();
586
587 // if (!shader->setContext(this->accessBitmap(), paint, *draw.fMatrix)) {
588 if (!shader->setContext(*draw.fBitmap, paint, *draw.fMatrix)) {
589 goto DONE;
590 }
591
592 SkBitmap bitmap;
593 SkMatrix matrix;
594 SkShader::TileMode tileModes[2];
595 if (shader->asABitmap(&bitmap, &matrix, tileModes)) {
596 SkPoint max;
597 GLuint name;
598 cache = SkGLDevice::LockTexCache(bitmap, &name, &max);
599 if (NULL == cache) {
600 return;
601 }
602
603 matrix.postScale(max.fX / bitmap.width(), max.fY / bitmap.height());
604 glMatrixMode(GL_TEXTURE);
605 SkGL::LoadMatrix(matrix);
606 glMatrixMode(GL_MODELVIEW);
607
608#if GLSCALAR_IS_SCALAR
609 glTexs = (const SkGLVertex*)texs;
610#else
611 texArray = (SkGLVertex*)sk_malloc_throw(vertexCount * sizeof(SkGLVertex));
612 texArray->setPoints(texs, vertexCount);
613 glTexs = texArray;
614#endif
615
616 SkGL::SetPaintAlpha(paint);
617 SkGL::SetTexParams(paint.isFilterBitmap(),
618 tileModes[0], tileModes[1]);
619 }
620 }
621DONE:
622 SkAutoFree aftex(texArray);
623
624 SkGL::DrawVertices(indices ? indexCount : vertexCount,
625 gVertexModeToGL[vmode],
626 glVerts, glTexs, colorArray, indices, iter);
627
628 if (cache) {
629 SkGLDevice::UnlockTexCache(cache);
630 }
631}
632
633///////////////////////////////////////////////////////////////////////////////
634
635#include "SkGlyphCache.h"
636#include "SkGLTextCache.h"
637
638void SkGLDevice::GlyphCacheAuxProc(void* data) {
639 SkDebugf("-------------- delete text texture cache\n");
640 SkDELETE((SkGLTextCache*)data);
641}
642
643#ifdef SK_SCALAR_IS_FIXED
644#define SkDiv16ToScalar(numer, denom) (SkIntToFixed(numer) / (denom))
645#else
646#define SkDiv16ToScalar(numer, denom) SkScalarDiv(numer, denom)
647#endif
648
649// stolen from SkDraw.cpp - D1G_NoBounder_RectClip
650static void SkGL_Draw1Glyph(const SkDraw1Glyph& state, const SkGlyph& glyph,
651 int x, int y) {
652 SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
653
654 SkGLDrawProcs* procs = (SkGLDrawProcs*)state.fDraw->fProcs;
655
656 x += glyph.fLeft;
reed@android.com6b82d1a2009-06-03 02:35:01 +0000657 y += glyph.fTop;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000658
659 // check if we're clipped out (nothing to draw)
660 SkIRect bounds;
661 bounds.set(x, y, x + glyph.fWidth, y + glyph.fHeight);
662 if (!SkIRect::Intersects(state.fClip->getBounds(), bounds)) {
663 return;
664 }
665
666 // now dig up our texture cache
667
668 SkGlyphCache* gcache = state.fCache;
669 void* auxData;
670 SkGLTextCache* textCache = NULL;
671
672 if (gcache->getAuxProcData(SkGLDevice::GlyphCacheAuxProc, &auxData)) {
673 textCache = (SkGLTextCache*)auxData;
674 }
675 if (NULL == textCache) {
676 // need to create one
677 textCache = SkNEW(SkGLTextCache);
678 gcache->setAuxProc(SkGLDevice::GlyphCacheAuxProc, textCache);
679 }
680
681 int offset;
682 SkGLTextCache::Strike* strike = textCache->findGlyph(glyph, &offset);
683 if (NULL == strike) {
684 // make sure the glyph has an image
685 uint8_t* aa = (uint8_t*)glyph.fImage;
686 if (NULL == aa) {
687 aa = (uint8_t*)gcache->findImage(glyph);
688 if (NULL == aa) {
689 return; // can't rasterize glyph
690 }
691 }
692 strike = textCache->addGlyphAndBind(glyph, aa, &offset);
693 if (NULL == strike) {
reed@android.com6b82d1a2009-06-03 02:35:01 +0000694 SkGL_unimpl("addGlyphAndBind failed, too big");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000695 // too big to cache, need to draw as is...
696 return;
697 }
698 }
699
700 const int shiftW = strike->widthShift();
701 const int shiftH = strike->heightShift();
702
703 SkFixed left = offset << (16 - shiftW);
704 SkFixed right = (offset + glyph.fWidth) << (16 - shiftW);
705 SkFixed bottom = glyph.fHeight << (16 - shiftH);
706
707 procs->addQuad(strike->texture(), x, y, glyph, left, right, bottom);
708}
709
710#if 1
711// matches the orientation used in SkGL::setRectFan. Too bad we can't rely on
712// QUADS in android's GL
713static const uint8_t gQuadIndices[] = {
714 0, 1, 2, 0, 2, 3,
715 4, 5, 6, 4, 6, 7,
716 8, 9, 10, 8, 10, 11,
717 12, 13, 14, 12, 14, 15,
718 16, 17, 18, 16, 18, 19,
719 20, 21, 22, 20, 22, 23,
720 24, 25, 26, 24, 26, 27,
721 28, 29, 30, 28, 30, 31,
722 32, 33, 34, 32, 34, 35,
723 36, 37, 38, 36, 38, 39,
724 40, 41, 42, 40, 42, 43,
725 44, 45, 46, 44, 46, 47,
726 48, 49, 50, 48, 50, 51,
727 52, 53, 54, 52, 54, 55,
728 56, 57, 58, 56, 58, 59,
729 60, 61, 62, 60, 62, 63,
730 64, 65, 66, 64, 66, 67,
731 68, 69, 70, 68, 70, 71,
732 72, 73, 74, 72, 74, 75,
733 76, 77, 78, 76, 78, 79,
734 80, 81, 82, 80, 82, 83,
735 84, 85, 86, 84, 86, 87,
736 88, 89, 90, 88, 90, 91,
737 92, 93, 94, 92, 94, 95,
738 96, 97, 98, 96, 98, 99,
739 100, 101, 102, 100, 102, 103,
740 104, 105, 106, 104, 106, 107,
741 108, 109, 110, 108, 110, 111,
742 112, 113, 114, 112, 114, 115,
743 116, 117, 118, 116, 118, 119,
744 120, 121, 122, 120, 122, 123,
745 124, 125, 126, 124, 126, 127
746};
747#else
748static void generateQuadIndices(int n) {
749 int index = 0;
750 for (int i = 0; i < n; i++) {
751 SkDebugf(" %3d, %3d, %3d, %3d, %3d, %3d,\n",
752 index, index + 1, index + 2, index, index + 2, index + 3);
753 index += 4;
754 }
755}
756#endif
757
758void SkGLDrawProcs::drawQuads() {
759 SkASSERT(SK_ARRAY_COUNT(gQuadIndices) == MAX_QUADS * 6);
760
761 glBindTexture(GL_TEXTURE_2D, fCurrTexture);
762
763#if 0
764 static bool gOnce;
765 if (!gOnce) {
766 generateQuadIndices(MAX_QUADS);
767 gOnce = true;
768 }
769#endif
770
771 // convert from quad vertex count to triangle vertex count
772 // 6/4 * n == n + (n >> 1) since n is always a multiple of 4
773 SkASSERT((fCurrQuad & 3) == 0);
774 int count = fCurrQuad + (fCurrQuad >> 1);
775
776 if (fClip->isComplex()) {
777 SkGLClipIter iter(fViewportHeight);
778 iter.reset(*fClip);
779 while (!iter.done()) {
780 iter.scissor();
781 glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_BYTE, gQuadIndices);
782 iter.next();
783 }
784 } else {
785 glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_BYTE, gQuadIndices);
786 }
787}
788
789void SkGLDevice::setupForText(SkDraw* draw, const SkPaint& paint) {
790 // we handle complex clips in the SkDraw common code, so we don't check
791 // for it here
792 this->updateMatrixClip();
793
794 SkGL::SetPaint(paint, false);
795
796 glMatrixMode(GL_TEXTURE);
797 glLoadIdentity();
798
799 glMatrixMode(GL_MODELVIEW);
800 glPushMatrix();
801 glLoadIdentity();
802
803 // deferred allocation
804 if (NULL == fDrawProcs) {
805 fDrawProcs = SkNEW(SkGLDrawProcs);
806 fDrawProcs->fD1GProc = SkGL_Draw1Glyph;
807 }
808
809 // init our (and GL's) state
810 fDrawProcs->init(draw->fClip, this->height());
811 // assign to the caller's SkDraw
812 draw->fProcs = fDrawProcs;
813
814 glEnable(GL_TEXTURE_2D);
815 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
816 glShadeModel(GL_FLAT);
817}
818
819void SkGLDevice::drawText(const SkDraw& draw, const void* text,
820 size_t byteLength, SkScalar x, SkScalar y,
821 const SkPaint& paint) {
822 /* Currently, perspective text is draw via paths, invoked directly by
823 SkDraw. This can't work for us, since the bitmap that our draw points
824 to has no pixels, so we just abort if we're in perspective.
825
826 Better fix would be to...
827 - have a callback inside draw to handle path drawing
828 - option to have draw call the font cache, which we could patch (?)
829 */
830 if (draw.fMatrix->getType() & SkMatrix::kPerspective_Mask) {
reed@android.com6b82d1a2009-06-03 02:35:01 +0000831 SkGL_unimpl("drawText in perspective");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000832 return;
833 }
834
835 SkDraw myDraw(draw);
836 this->setupForText(&myDraw, paint);
837 this->INHERITED::drawText(myDraw, text, byteLength, x, y, paint);
838 fDrawProcs->flush();
839 glPopMatrix(); // GL_MODELVIEW
840}
841
842void SkGLDevice::drawPosText(const SkDraw& draw, const void* text,
843 size_t byteLength, const SkScalar pos[],
844 SkScalar constY, int scalarsPerPos,
845 const SkPaint& paint) {
846 if (draw.fMatrix->getType() & SkMatrix::kPerspective_Mask) {
reed@android.com6b82d1a2009-06-03 02:35:01 +0000847 SkGL_unimpl("drawPosText in perspective");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000848 return;
849 }
850
851 SkDraw myDraw(draw);
852 this->setupForText(&myDraw, paint);
853 this->INHERITED::drawPosText(myDraw, text, byteLength, pos, constY,
854 scalarsPerPos, paint);
855 fDrawProcs->flush();
856 glPopMatrix(); // GL_MODELVIEW
857}
858
859void SkGLDevice::drawTextOnPath(const SkDraw& draw, const void* text,
860 size_t byteLength, const SkPath& path,
861 const SkMatrix* m, const SkPaint& paint) {
reed@android.com6b82d1a2009-06-03 02:35:01 +0000862 SkGL_unimpl("drawTextOnPath");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000863}
864
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000865///////////////////////////////////////////////////////////////////////////////
866
867#include "SkTextureCache.h"
868#include "SkThread.h"
869
digit@google.com1771cbf2012-01-26 21:26:40 +0000870SK_DECLARE_STATIC_MUTEX(gTextureCacheMutex);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000871static SkTextureCache gTextureCache(kTexCountMax_Default, kTexSizeMax_Default);
872
873SkGLDevice::TexCache* SkGLDevice::LockTexCache(const SkBitmap& bitmap,
874 GLuint* name, SkPoint* size) {
875 SkAutoMutexAcquire amc(gTextureCacheMutex);
876
877 SkTextureCache::Entry* entry = gTextureCache.lock(bitmap);
878 if (NULL != entry) {
879 if (name) {
880 *name = entry->name();
881 }
882 if (size) {
883 *size = entry->texSize();
884 }
885 }
886 return (TexCache*)entry;
887}
888
889void SkGLDevice::UnlockTexCache(TexCache* cache) {
890 SkAutoMutexAcquire amc(gTextureCacheMutex);
891 gTextureCache.unlock((SkTextureCache::Entry*)cache);
892}
893
894// public exposure of texture cache settings
895
896size_t SkGLDevice::GetTextureCacheMaxCount() {
897 SkAutoMutexAcquire amc(gTextureCacheMutex);
898 return gTextureCache.getMaxCount();
899}
900
901size_t SkGLDevice::GetTextureCacheMaxSize() {
902 SkAutoMutexAcquire amc(gTextureCacheMutex);
903 return gTextureCache.getMaxSize();
904}
905
906void SkGLDevice::SetTextureCacheMaxCount(size_t count) {
907 SkAutoMutexAcquire amc(gTextureCacheMutex);
908 gTextureCache.setMaxCount(count);
909}
910
911void SkGLDevice::SetTextureCacheMaxSize(size_t size) {
912 SkAutoMutexAcquire amc(gTextureCacheMutex);
913 gTextureCache.setMaxSize(size);
914}
915
916///////////////////////////////////////////////////////////////////////////////
917
918#include "SkGLTextCache.h"
919
920static bool deleteCachesProc(SkGlyphCache* cache, void* texturesAreValid) {
921 void* auxData;
922 if (cache->getAuxProcData(SkGLDevice::GlyphCacheAuxProc, &auxData)) {
923 bool valid = texturesAreValid != NULL;
924 SkGLTextCache* textCache = static_cast<SkGLTextCache*>(auxData);
925 // call this before delete, in case valid is false
926 textCache->deleteAllStrikes(valid);
927 // now free the memory for the cache itself
928 SkDELETE(textCache);
929 // now remove the entry in the glyphcache (does not call the proc)
930 cache->removeAuxProc(SkGLDevice::GlyphCacheAuxProc);
931 }
932 return false; // keep going
933}
934
935void SkGLDevice::DeleteAllTextures() {
936 // free the textures in our cache
937
938 gTextureCacheMutex.acquire();
939 gTextureCache.deleteAllCaches(true);
940 gTextureCacheMutex.release();
941
942 // now free the textures in the font cache
943
944 SkGlyphCache::VisitAllCaches(deleteCachesProc, reinterpret_cast<void*>(true)
945);
946}
947
948void SkGLDevice::AbandonAllTextures() {
949 // abandon the textures in our cache
950
951 gTextureCacheMutex.acquire();
952 gTextureCache.deleteAllCaches(false);
953 gTextureCacheMutex.release();
954
955 // abandon the textures in the font cache
956
957 SkGlyphCache::VisitAllCaches(deleteCachesProc, reinterpret_cast<void*>(false
958));
959}
960