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