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