diff options
author | joshualitt <joshualitt@chromium.org> | 2015-04-06 10:53:36 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-04-06 10:53:36 -0700 |
commit | 9a27e639481882b59a8ef123e0b69a9a719f08b9 (patch) | |
tree | 1ca6f42eaa7c535f52d907a722a38280abab7a37 | |
parent | 94e50100eb747b2dfd659bacc9cf4246f6f20278 (diff) |
Handle large paths in textblobs
BUG=skia:
Review URL: https://codereview.chromium.org/1057613002
-rw-r--r-- | src/gpu/GrAtlasTextContext.cpp | 175 | ||||
-rw-r--r-- | src/gpu/GrAtlasTextContext.h | 59 |
2 files changed, 169 insertions, 65 deletions
diff --git a/src/gpu/GrAtlasTextContext.cpp b/src/gpu/GrAtlasTextContext.cpp index b2de1cf154..08e6a9a25e 100644 --- a/src/gpu/GrAtlasTextContext.cpp +++ b/src/gpu/GrAtlasTextContext.cpp @@ -93,14 +93,6 @@ bool GrAtlasTextContext::canDraw(const GrRenderTarget*, return !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix); } -inline void GrAtlasTextContext::init(GrRenderTarget* rt, const GrClip& clip, - const GrPaint& paint, const SkPaint& skPaint, - const SkIRect& regionClipBounds) { - INHERITED::init(rt, clip, paint, skPaint, regionClipBounds); - - fCurrStrike = NULL; -} - bool GrAtlasTextContext::MustRegenerateBlob(const BitmapTextBlob& blob, const SkPaint& paint, const SkMatrix& viewMatrix, SkScalar x, SkScalar y) { // We always regenerate blobs with patheffects or mask filters we could cache these @@ -192,8 +184,8 @@ void GrAtlasTextContext::drawTextBlob(GrRenderTarget* rt, const GrClip& clip, GrPaint grPaint; SkPaint2GrPaintShader(fContext, rt, skPaint, viewMatrix, true, &grPaint); - this->flush(fContext->getTextTarget(), cacheBlob, rt, grPaint, clip, viewMatrix, - fSkPaint.getAlpha()); + this->flush(fContext->getTextTarget(), blob, cacheBlob, rt, skPaint, grPaint, drawFilter, + clip, viewMatrix, clipBounds, x, y); } void GrAtlasTextContext::regenerateTextBlob(BitmapTextBlob* cacheBlob, @@ -238,6 +230,12 @@ void GrAtlasTextContext::regenerateTextBlob(BitmapTextBlob* cacheBlob, newRun.fGlyphEndIndex = lastRun.fGlyphEndIndex; } + if (SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix)) { + cacheBlob->fRuns[run].fDrawAsPaths = true; + continue; + } + cacheBlob->fRuns[run].fDrawAsPaths = false; + switch (it.positioning()) { case SkTextBlob::kDefault_Positioning: this->internalDrawText(cacheBlob, run, cache, runPaint, viewMatrix, @@ -285,7 +283,7 @@ void GrAtlasTextContext::onDrawText(GrRenderTarget* rt, const GrClip& clip, this->internalDrawText(blob, 0, cache, skPaint, viewMatrix, text, byteLength, x, y, clipRect); SkGlyphCache::AttachCache(cache); - this->flush(fContext->getTextTarget(), blob, rt, paint, clip, viewMatrix, skPaint.getAlpha()); + this->flush(fContext->getTextTarget(), blob, rt, skPaint, paint, clip, viewMatrix); } void GrAtlasTextContext::internalDrawText(BitmapTextBlob* blob, int runIndex, @@ -398,7 +396,7 @@ void GrAtlasTextContext::onDrawPosText(GrRenderTarget* rt, const GrClip& clip, scalarsPerPosition, offset, clipRect); SkGlyphCache::AttachCache(cache); - this->flush(fContext->getTextTarget(), blob, rt, paint, clip, viewMatrix, fSkPaint.getAlpha()); + this->flush(fContext->getTextTarget(), blob, rt, skPaint, paint, clip, viewMatrix); } void GrAtlasTextContext::internalDrawPosText(BitmapTextBlob* blob, int runIndex, @@ -694,9 +692,9 @@ public: GrColor fColor; }; - static GrBatch* Create(const Geometry& geometry, GrColor color, GrMaskFormat maskFormat, + static GrBatch* Create(const Geometry& geometry, GrMaskFormat maskFormat, int glyphCount, GrBatchFontCache* fontCache) { - return SkNEW_ARGS(BitmapTextBatch, (geometry, color, maskFormat, glyphCount, fontCache)); + return SkNEW_ARGS(BitmapTextBatch, (geometry, maskFormat, glyphCount, fontCache)); } const char* name() const override { return "BitmapTextBatch"; } @@ -893,14 +891,14 @@ public: SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } private: - BitmapTextBatch(const Geometry& geometry, GrColor color, GrMaskFormat maskFormat, + BitmapTextBatch(const Geometry& geometry, GrMaskFormat maskFormat, int glyphCount, GrBatchFontCache* fontCache) : fMaskFormat(maskFormat) , fPixelConfig(fontCache->getPixelConfig(maskFormat)) , fFontCache(fontCache) { this->initClassID<BitmapTextBatch>(); fGeoData.push_back(geometry); - fBatch.fColor = color; + fBatch.fColor = geometry.fColor; fBatch.fViewMatrix = geometry.fBlob->fViewMatrix; fBatch.fNumGlyphs = glyphCount; } @@ -1015,47 +1013,128 @@ private: GrBatchFontCache* fFontCache; }; -void GrAtlasTextContext::flush(GrDrawTarget* target, BitmapTextBlob* blob, GrRenderTarget* rt, - const GrPaint& paint, const GrClip& clip, - const SkMatrix& viewMatrix, int paintAlpha) { - GrPipelineBuilder pipelineBuilder; - pipelineBuilder.setFromPaint(paint, rt, clip); +void GrAtlasTextContext::flushRunAsPaths(const SkTextBlob::RunIterator& it, const SkPaint& skPaint, + SkDrawFilter* drawFilter, const SkMatrix& viewMatrix, + const SkIRect& clipBounds, SkScalar x, SkScalar y) { + SkPaint runPaint = skPaint; - GrColor color = paint.getColor(); - for (uint32_t run = 0; run < blob->fRunCount; run++) { - for (int subRun = 0; subRun < blob->fRuns[run].fSubRunInfo.count(); subRun++) { - PerSubRunInfo& info = blob->fRuns[run].fSubRunInfo[subRun]; - int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex; - if (0 == glyphCount) { - continue; - } + size_t textLen = it.glyphCount() * sizeof(uint16_t); + const SkPoint& offset = it.offset(); - GrMaskFormat format = info.fMaskFormat; - GrColor subRunColor = kARGB_GrMaskFormat == format ? - SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha) : - color; - - BitmapTextBatch::Geometry geometry; - geometry.fBlob.reset(SkRef(blob)); - geometry.fRun = run; - geometry.fSubRun = subRun; - geometry.fColor = color; - SkAutoTUnref<GrBatch> batch(BitmapTextBatch::Create(geometry, subRunColor, format, - glyphCount, - fContext->getBatchFontCache())); - - target->drawBatch(&pipelineBuilder, batch, &blob->fRuns[run].fVertexBounds); + it.applyFontToPaint(&runPaint); + + if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) { + return; + } + + runPaint.setFlags(fGpuDevice->filterTextFlags(runPaint)); + + switch (it.positioning()) { + case SkTextBlob::kDefault_Positioning: + this->drawTextAsPath(runPaint, viewMatrix, (const char *)it.glyphs(), + textLen, x + offset.x(), y + offset.y(), clipBounds); + break; + case SkTextBlob::kHorizontal_Positioning: + this->drawPosTextAsPath(runPaint, viewMatrix, (const char*)it.glyphs(), + textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()), + clipBounds); + break; + case SkTextBlob::kFull_Positioning: + this->drawPosTextAsPath(runPaint, viewMatrix, (const char*)it.glyphs(), + textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds); + break; + } +} + +inline void GrAtlasTextContext::flushRun(GrDrawTarget* target, GrPipelineBuilder* pipelineBuilder, + BitmapTextBlob* cacheBlob, int run, GrColor color, + uint8_t paintAlpha) { + for (int subRun = 0; subRun < cacheBlob->fRuns[run].fSubRunInfo.count(); subRun++) { + const PerSubRunInfo& info = cacheBlob->fRuns[run].fSubRunInfo[subRun]; + int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex; + if (0 == glyphCount) { + continue; } + + GrMaskFormat format = info.fMaskFormat; + GrColor subRunColor = kARGB_GrMaskFormat == format ? + SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha) : + color; + + BitmapTextBatch::Geometry geometry; + geometry.fBlob.reset(SkRef(cacheBlob)); + geometry.fRun = run; + geometry.fSubRun = subRun; + geometry.fColor = subRunColor; + SkAutoTUnref<GrBatch> batch(BitmapTextBatch::Create(geometry, format, glyphCount, + fContext->getBatchFontCache())); + + target->drawBatch(pipelineBuilder, batch, &cacheBlob->fRuns[run].fVertexBounds); } +} - // Now flush big glyphs - for (int i = 0; i < blob->fBigGlyphs.count(); i++) { - BitmapTextBlob::BigGlyph& bigGlyph = blob->fBigGlyphs[i]; +inline void GrAtlasTextContext::flushBigGlyphs(BitmapTextBlob* cacheBlob, GrRenderTarget* rt, + const GrPaint& grPaint, const GrClip& clip) { + for (int i = 0; i < cacheBlob->fBigGlyphs.count(); i++) { + const BitmapTextBlob::BigGlyph& bigGlyph = cacheBlob->fBigGlyphs[i]; SkMatrix translate; translate.setTranslate(SkIntToScalar(bigGlyph.fVx), SkIntToScalar(bigGlyph.fVy)); SkPath tmpPath(bigGlyph.fPath); tmpPath.transform(translate); GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle); - fContext->drawPath(rt, clip, paint, SkMatrix::I(), tmpPath, strokeInfo); + fContext->drawPath(rt, clip, grPaint, SkMatrix::I(), tmpPath, strokeInfo); } } + +void GrAtlasTextContext::flush(GrDrawTarget* target, + const SkTextBlob* blob, + BitmapTextBlob* cacheBlob, + GrRenderTarget* rt, + const SkPaint& skPaint, + const GrPaint& grPaint, + SkDrawFilter* drawFilter, + const GrClip& clip, + const SkMatrix& viewMatrix, + const SkIRect& clipBounds, + SkScalar x, + SkScalar y) { + // We loop through the runs of the blob, flushing each. If any run is too large, then we flush + // it as paths + GrPipelineBuilder pipelineBuilder; + pipelineBuilder.setFromPaint(grPaint, rt, clip); + + GrColor color = grPaint.getColor(); + uint8_t paintAlpha = skPaint.getAlpha(); + + SkTextBlob::RunIterator it(blob); + for (int run = 0; !it.done(); it.next(), run++) { + if (cacheBlob->fRuns[run].fDrawAsPaths) { + this->flushRunAsPaths(it, skPaint, drawFilter, viewMatrix, clipBounds, x, y); + continue; + } + this->flushRun(target, &pipelineBuilder, cacheBlob, run, color, paintAlpha); + } + + // Now flush big glyphs + this->flushBigGlyphs(cacheBlob, rt, grPaint, clip); +} + +void GrAtlasTextContext::flush(GrDrawTarget* target, + BitmapTextBlob* cacheBlob, + GrRenderTarget* rt, + const SkPaint& skPaint, + const GrPaint& grPaint, + const GrClip& clip, + const SkMatrix& viewMatrix) { + GrPipelineBuilder pipelineBuilder; + pipelineBuilder.setFromPaint(grPaint, rt, clip); + + GrColor color = grPaint.getColor(); + uint8_t paintAlpha = skPaint.getAlpha(); + for (int run = 0; run < cacheBlob->fRunCount; run++) { + this->flushRun(target, &pipelineBuilder, cacheBlob, run, color, paintAlpha); + } + + // Now flush big glyphs + this->flushBigGlyphs(cacheBlob, rt, grPaint, clip); +} diff --git a/src/gpu/GrAtlasTextContext.h b/src/gpu/GrAtlasTextContext.h index eb1abbac42..de07d1f474 100644 --- a/src/gpu/GrAtlasTextContext.h +++ b/src/gpu/GrAtlasTextContext.h @@ -12,6 +12,7 @@ #include "GrGeometryProcessor.h" #include "SkDescriptor.h" +#include "SkTextBlob.h" #include "SkTHash.h" class GrBatchTextStrike; @@ -45,9 +46,6 @@ private: const SkMatrix& viewMatrix, const SkTextBlob*, SkScalar x, SkScalar y, SkDrawFilter*, const SkIRect& clipBounds) override; - void init(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&, - const SkIRect& regionClipBounds); - /* * A BitmapTextBlob contains a fully processed SkTextBlob, suitable for nearly immediate drawing * on the GPU. These are initially created with valid positions and colors, but invalid @@ -60,19 +58,32 @@ private: * TODO this is currently a bug */ struct BitmapTextBlob : public SkRefCnt { - // Each Run inside of the blob can have its texture coordinates regenerated if required. - // To determine if regeneration is necessary, fAtlasGeneration is used. If there have been - // any evictions inside of the atlas, then we will simply regenerate Runs. We could track - // this at a more fine grained level, but its not clear if this is worth it, as evictions - // should be fairly rare. - // One additional point, each run can contain glyphs with any of the three mask formats. - // We call these SubRuns. Because a subrun must be a contiguous range, we have to create - // a new subrun each time the mask format changes in a run. In theory, a run can have as - // many SubRuns as it has glyphs, ie if a run alternates between color emoji and A8. In - // practice, the vast majority of runs have only a single subrun. + /* + * Each Run inside of the blob can have its texture coordinates regenerated if required. + * To determine if regeneration is necessary, fAtlasGeneration is used. If there have been + * any evictions inside of the atlas, then we will simply regenerate Runs. We could track + * this at a more fine grained level, but its not clear if this is worth it, as evictions + * should be fairly rare. + * + * One additional point, each run can contain glyphs with any of the three mask formats. + * We call these SubRuns. Because a subrun must be a contiguous range, we have to create + * a new subrun each time the mask format changes in a run. In theory, a run can have as + * many SubRuns as it has glyphs, ie if a run alternates between color emoji and A8. In + * practice, the vast majority of runs have only a single subrun. + * + * Finally, for runs where the entire thing is too large for the GrAtlasTextContext to + * handle, we have a bit to mark the run as flusahable via rendering as paths. It is worth + * pointing. It would be a bit expensive to figure out ahead of time whether or not a run + * can flush in this manner, so we always allocate vertices for the run, regardless of + * whether or not it is too large. The benefit of this strategy is that we can always reuse + * a blob allocation regardless of viewmatrix changes. We could store positions for these + * glyphs. However, its not clear if this is a win because we'd still have to either go the + * glyph cache to get the path at flush time, or hold onto the path in the cache, which + * would greatly increase the memory of these cached items. + */ struct Run { - Run() : fColor(GrColor_ILLEGAL), fInitialized(false) { + Run() : fColor(GrColor_ILLEGAL), fInitialized(false), fDrawAsPaths(false) { fVertexBounds.setLargestInverted(); // We insert the first subrun to gurantee a run always has atleast one subrun. // We do this to simplify things when we 'hand off' data from one subrun to the @@ -99,6 +110,7 @@ private: SkRect fVertexBounds; GrColor fColor; bool fInitialized; + bool fDrawAsPaths; }; struct BigGlyph { @@ -113,7 +125,7 @@ private: SkScalar fX; SkScalar fY; SkPaint::Style fStyle; - uint32_t fRunCount; + int fRunCount; // all glyph / vertex offsets are into these pools. unsigned char* fVertices; @@ -143,8 +155,21 @@ private: void appendGlyph(BitmapTextBlob*, int runIndex, GrGlyph::PackedID, int left, int top, GrColor color, GrFontScaler*, const SkIRect& clipRect); - void flush(GrDrawTarget*, BitmapTextBlob*, GrRenderTarget*, const GrPaint&, const GrClip&, - const SkMatrix& viewMatrix, int paintAlpha); + + inline void flushRunAsPaths(const SkTextBlob::RunIterator&, const SkPaint&, SkDrawFilter*, + const SkMatrix& viewMatrix, const SkIRect& clipBounds, SkScalar x, + SkScalar y); + inline void flushRun(GrDrawTarget*, GrPipelineBuilder*, BitmapTextBlob*, int run, GrColor, + uint8_t paintAlpha); + inline void flushBigGlyphs(BitmapTextBlob* cacheBlob, GrRenderTarget* rt, + const GrPaint& grPaint, const GrClip& clip); + + // We have to flush SkTextBlobs differently from drawText / drawPosText + void flush(GrDrawTarget*, const SkTextBlob*, BitmapTextBlob*, GrRenderTarget*, const SkPaint&, + const GrPaint&, SkDrawFilter*, const GrClip&, const SkMatrix& viewMatrix, + const SkIRect& clipBounds, SkScalar x, SkScalar y); + void flush(GrDrawTarget*, BitmapTextBlob*, GrRenderTarget*, const SkPaint&, + const GrPaint&, const GrClip&, const SkMatrix& viewMatrix); void internalDrawText(BitmapTextBlob*, int runIndex, SkGlyphCache*, const SkPaint&, const SkMatrix& viewMatrix, const char text[], size_t byteLength, |