void GrAtlasTextBatch::onPrepareDraws(Target* target) const { // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix. // TODO actually only invert if we don't have RGBA SkMatrix localMatrix; if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) { SkDebugf("Cannot invert viewmatrix\n"); return; } GrTexture* texture = fFontCache->getTexture(this->maskFormat()); if (!texture) { SkDebugf("Could not allocate backing texture for atlas\n"); return; } GrMaskFormat maskFormat = this->maskFormat(); FlushInfo flushInfo; if (this->usesDistanceFields()) { flushInfo.fGeometryProcessor = this->setupDfProcessor(this->viewMatrix(), fFilteredColor, this->color(), texture); } else { GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode); flushInfo.fGeometryProcessor = GrBitmapTextGeoProc::Make(this->color(), texture, params, maskFormat, localMatrix, this->usesLocalCoords()); } flushInfo.fGlyphsToFlush = 0; size_t vertexStride = flushInfo.fGeometryProcessor->getVertexStride(); SkASSERT(vertexStride == GrAtlasTextBlob::GetVertexStride(maskFormat)); int glyphCount = this->numGlyphs(); const GrBuffer* vertexBuffer; void* vertices = target->makeVertexSpace(vertexStride, glyphCount * kVerticesPerGlyph, &vertexBuffer, &flushInfo.fVertexOffset); flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer)); flushInfo.fIndexBuffer.reset(target->resourceProvider()->refQuadIndexBuffer()); if (!vertices || !flushInfo.fVertexBuffer) { SkDebugf("Could not allocate vertices\n"); return; } unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices); GrBlobRegenHelper helper(this, target, &flushInfo); SkAutoGlyphCache glyphCache; for (int i = 0; i < fGeoCount; i++) { const Geometry& args = fGeoData[i]; Blob* blob = args.fBlob; size_t byteCount; void* blobVertices; int subRunGlyphCount; blob->regenInBatch(target, fFontCache, &helper, args.fRun, args.fSubRun, &glyphCache, vertexStride, args.fViewMatrix, args.fX, args.fY, args.fColor, &blobVertices, &byteCount, &subRunGlyphCount); // now copy all vertices memcpy(currVertex, blobVertices, byteCount); #ifdef SK_DEBUG // bounds sanity check SkRect rect; rect.setLargestInverted(); SkPoint* vertex = (SkPoint*) ((char*)blobVertices); rect.growToInclude(vertex, vertexStride, kVerticesPerGlyph * subRunGlyphCount); if (this->usesDistanceFields()) { args.fViewMatrix.mapRect(&rect); } // Allow for small numerical error in the bounds. SkRect bounds = this->bounds(); bounds.outset(0.001f, 0.001f); SkASSERT(bounds.contains(rect)); #endif currVertex += byteCount; } this->flush(target, &flushInfo); }
GrDrawAtlasBatch::GrDrawAtlasBatch(GrColor color, const SkMatrix& viewMatrix, int spriteCount, const SkRSXform* xforms, const SkRect* rects, const SkColor* colors) : INHERITED(ClassID()) { SkASSERT(xforms); SkASSERT(rects); fViewMatrix = viewMatrix; Geometry& installedGeo = fGeoData.push_back(); installedGeo.fColor = color; // Figure out stride and offsets // Order within the vertex is: position [color] texCoord size_t texOffset = sizeof(SkPoint); size_t vertexStride = 2*sizeof(SkPoint); fHasColors = SkToBool(colors); if (colors) { texOffset += sizeof(GrColor); vertexStride += sizeof(GrColor); } // Compute buffer size and alloc buffer fQuadCount = spriteCount; int allocSize = static_cast<int>(4*vertexStride*spriteCount); installedGeo.fVerts.reset(allocSize); uint8_t* currVertex = installedGeo.fVerts.begin(); SkRect bounds; bounds.setLargestInverted(); int paintAlpha = GrColorUnpackA(installedGeo.fColor); for (int spriteIndex = 0; spriteIndex < spriteCount; ++spriteIndex) { // Transform rect SkPoint quad[4]; const SkRect& currRect = rects[spriteIndex]; xforms[spriteIndex].toQuad(currRect.width(), currRect.height(), quad); // Copy colors if necessary if (colors) { // convert to GrColor SkColor color = colors[spriteIndex]; if (paintAlpha != 255) { color = SkColorSetA(color, SkMulDiv255Round(SkColorGetA(color), paintAlpha)); } GrColor grColor = SkColorToPremulGrColor(color); *(reinterpret_cast<GrColor*>(currVertex+sizeof(SkPoint))) = grColor; *(reinterpret_cast<GrColor*>(currVertex+vertexStride+sizeof(SkPoint))) = grColor; *(reinterpret_cast<GrColor*>(currVertex+2*vertexStride+sizeof(SkPoint))) = grColor; *(reinterpret_cast<GrColor*>(currVertex+3*vertexStride+sizeof(SkPoint))) = grColor; } // Copy position and uv to verts *(reinterpret_cast<SkPoint*>(currVertex)) = quad[0]; *(reinterpret_cast<SkPoint*>(currVertex+texOffset)) = SkPoint::Make(currRect.fLeft, currRect.fTop); bounds.growToInclude(quad[0].fX, quad[0].fY); currVertex += vertexStride; *(reinterpret_cast<SkPoint*>(currVertex)) = quad[1]; *(reinterpret_cast<SkPoint*>(currVertex+texOffset)) = SkPoint::Make(currRect.fRight, currRect.fTop); bounds.growToInclude(quad[1].fX, quad[1].fY); currVertex += vertexStride; *(reinterpret_cast<SkPoint*>(currVertex)) = quad[2]; *(reinterpret_cast<SkPoint*>(currVertex+texOffset)) = SkPoint::Make(currRect.fRight, currRect.fBottom); bounds.growToInclude(quad[2].fX, quad[2].fY); currVertex += vertexStride; *(reinterpret_cast<SkPoint*>(currVertex)) = quad[3]; *(reinterpret_cast<SkPoint*>(currVertex+texOffset)) = SkPoint::Make(currRect.fLeft, currRect.fBottom); bounds.growToInclude(quad[3].fX, quad[3].fY); currVertex += vertexStride; } this->setTransformedBounds(bounds, viewMatrix, HasAABloat::kNo, IsZeroArea::kNo); }
void GrAtlasTextBatch::onPrepareDraws(Target* target) const { // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix. // TODO actually only invert if we don't have RGBA SkMatrix localMatrix; if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) { SkDebugf("Cannot invert viewmatrix\n"); return; } GrTexture* texture = fFontCache->getTexture(this->maskFormat()); if (!texture) { SkDebugf("Could not allocate backing texture for atlas\n"); return; } GrMaskFormat maskFormat = this->maskFormat(); SkAutoTUnref<const GrGeometryProcessor> gp; if (this->usesDistanceFields()) { gp.reset(this->setupDfProcessor(this->viewMatrix(), fFilteredColor, this->color(), texture)); } else { GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode); gp.reset(GrBitmapTextGeoProc::Create(this->color(), texture, params, maskFormat, localMatrix, this->usesLocalCoords())); } FlushInfo flushInfo; flushInfo.fGlyphsToFlush = 0; size_t vertexStride = gp->getVertexStride(); SkASSERT(vertexStride == GrAtlasTextBlob::GetVertexStride(maskFormat)); target->initDraw(gp); int glyphCount = this->numGlyphs(); const GrBuffer* vertexBuffer; void* vertices = target->makeVertexSpace(vertexStride, glyphCount * kVerticesPerGlyph, &vertexBuffer, &flushInfo.fVertexOffset); flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer)); flushInfo.fIndexBuffer.reset(target->resourceProvider()->refQuadIndexBuffer()); if (!vertices || !flushInfo.fVertexBuffer) { SkDebugf("Could not allocate vertices\n"); return; } unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices); // We cache some values to avoid going to the glyphcache for the same fontScaler twice // in a row const SkDescriptor* desc = nullptr; SkGlyphCache* cache = nullptr; GrFontScaler* scaler = nullptr; SkTypeface* typeface = nullptr; GrBlobRegenHelper helper(this, target, &flushInfo, gp); for (int i = 0; i < fGeoCount; i++) { const Geometry& args = fGeoData[i]; Blob* blob = args.fBlob; size_t byteCount; void* blobVertices; int subRunGlyphCount; blob->regenInBatch(target, fFontCache, &helper, args.fRun, args.fSubRun, &cache, &typeface, &scaler, &desc, vertexStride, args.fViewMatrix, args.fX, args.fY, args.fColor, &blobVertices, &byteCount, &subRunGlyphCount); // now copy all vertices memcpy(currVertex, blobVertices, byteCount); #ifdef SK_DEBUG // bounds sanity check SkRect rect; rect.setLargestInverted(); SkPoint* vertex = (SkPoint*) ((char*)blobVertices); rect.growToInclude(vertex, vertexStride, kVerticesPerGlyph * subRunGlyphCount); if (this->usesDistanceFields()) { args.fViewMatrix.mapRect(&rect); } SkASSERT(fBounds.contains(rect)); #endif currVertex += byteCount; } // Make sure to attach the last cache if applicable if (cache) { SkGlyphCache::AttachCache(cache); } this->flush(target, &flushInfo); }