Cleanup of large text rendering, take 2.
* Switch all path glyphs to be rendered as part of the fBigGlyphs
(now fPathGlyphs) system
* Use one flush command for both cached and throwaway blobs
* Store and render path glyphs per run rather than one list for the
entire blob (fixes a layering bug)
* Fix bug with scaled fallback glyphs, where fallback glyphs were
trying to use more than one descriptor
* Set paint flags correctly for paths
Bug: skia:7562
Change-Id: I9455eda2867860a713fbdbbda79c74109e95f9f2
Reviewed-on: https://skia-review.googlesource.com/105020
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Jim Van Verth <jvanverth@google.com>
diff --git a/src/gpu/text/GrAtlasTextBlob.cpp b/src/gpu/text/GrAtlasTextBlob.cpp
index 887addd..e805761 100644
--- a/src/gpu/text/GrAtlasTextBlob.cpp
+++ b/src/gpu/text/GrAtlasTextBlob.cpp
@@ -15,6 +15,7 @@
#include "SkGlyphCache.h"
#include "SkMaskFilterBase.h"
#include "SkTextBlobRunIterator.h"
+#include "SkTextToPathIter.h"
#include "ops/GrAtlasTextOp.h"
sk_sp<GrAtlasTextBlob> GrAtlasTextBlob::Make(GrMemoryPool* pool, int glyphCount, int runCount) {
@@ -73,14 +74,22 @@
GrAtlasTextStrike* strike,
GrGlyph* glyph,
SkGlyphCache* cache, const SkGlyph& skGlyph,
- SkScalar x, SkScalar y, SkScalar scale, bool treatAsBMP) {
+ SkScalar x, SkScalar y, SkScalar scale, bool preTransformed) {
if (positions.isEmpty()) {
return;
}
// If the glyph is too large we fall back to paths
if (glyph->fTooLargeForAtlas) {
- this->appendBigGlyph(glyph, cache, skGlyph, x, y, scale, treatAsBMP);
+ if (nullptr == glyph->fPath) {
+ const SkPath* glyphPath = cache->findPath(skGlyph);
+ if (!glyphPath) {
+ return;
+ }
+
+ glyph->fPath = new SkPath(*glyphPath);
+ }
+ this->appendPathGlyph(runIndex, *glyph->fPath, x, y, scale, preTransformed);
return;
}
@@ -140,17 +149,10 @@
subRun->glyphAppended();
}
-void GrAtlasTextBlob::appendBigGlyph(GrGlyph* glyph, SkGlyphCache* cache, const SkGlyph& skGlyph,
- SkScalar x, SkScalar y, SkScalar scale, bool treatAsBMP) {
- if (nullptr == glyph->fPath) {
- const SkPath* glyphPath = cache->findPath(skGlyph);
- if (!glyphPath) {
- return;
- }
-
- glyph->fPath = new SkPath(*glyphPath);
- }
- fBigGlyphs.push_back(GrAtlasTextBlob::BigGlyph(*glyph->fPath, x, y, scale, treatAsBMP));
+void GrAtlasTextBlob::appendPathGlyph(int runIndex, const SkPath& path, SkScalar x, SkScalar y,
+ SkScalar scale, bool preTransformed) {
+ Run& run = fRuns[runIndex];
+ run.fPathGlyphs.push_back(GrAtlasTextBlob::Run::PathGlyph(path, x, y, scale, preTransformed));
}
bool GrAtlasTextBlob::mustRegenerate(const GrTextUtils::Paint& paint,
@@ -273,62 +275,6 @@
return op;
}
-inline void GrAtlasTextBlob::flushRun(GrTextUtils::Target* target, const GrClip& clip, int run,
- const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
- const GrTextUtils::Paint& paint, const SkSurfaceProps& props,
- const GrDistanceFieldAdjustTable* distanceAdjustTable,
- GrAtlasGlyphCache* cache) {
- // GrAtlasTextBlob::makeOp only takes uint16_t values for run and subRun indices.
- // Encountering something larger than this is highly unlikely, so we'll just not draw it.
- if (run >= (1 << 16)) {
- return;
- }
- int lastRun = SkTMin(fRuns[run].fSubRunInfo.count(), 1 << 16) - 1;
- for (int subRun = 0; subRun <= lastRun; subRun++) {
- const Run::SubRunInfo& info = fRuns[run].fSubRunInfo[subRun];
- int glyphCount = info.glyphCount();
- if (0 == glyphCount) {
- continue;
- }
-
- bool skipClip = false;
- bool submitOp = true;
- SkIRect clipRect = SkIRect::MakeEmpty();
- SkRect rtBounds = SkRect::MakeWH(target->width(), target->height());
- SkRRect clipRRect;
- GrAA aa;
- // We can clip geometrically if we're not using SDFs,
- // and we have an axis-aligned rectangular non-AA clip
- if (!info.drawAsDistanceFields() && clip.isRRect(rtBounds, &clipRRect, &aa) &&
- clipRRect.isRect() && GrAA::kNo == aa) {
- skipClip = true;
- // We only need to do clipping work if the subrun isn't contained by the clip
- SkRect subRunBounds;
- this->computeSubRunBounds(&subRunBounds, run, subRun, viewMatrix, x, y);
- if (!clipRRect.getBounds().contains(subRunBounds)) {
- // If the subrun is completely outside, don't add an op for it
- if (!clipRRect.getBounds().intersects(subRunBounds)) {
- submitOp = false;
- } else {
- clipRRect.getBounds().round(&clipRect);
- }
- }
- }
-
- if (submitOp) {
- auto op = this->makeOp(info, glyphCount, run, subRun, viewMatrix, x, y, clipRect,
- std::move(paint), props, distanceAdjustTable, cache, target);
- if (op) {
- if (skipClip) {
- target->addDrawOp(GrNoClip(), std::move(op));
- } else {
- target->addDrawOp(clip, std::move(op));
- }
- }
- }
- }
-}
-
static void calculate_translation(bool applyVM,
const SkMatrix& newViewMatrix, SkScalar newX, SkScalar newY,
const SkMatrix& currentViewMatrix, SkScalar currentX,
@@ -349,94 +295,95 @@
}
}
-void GrAtlasTextBlob::flushBigGlyphs(GrTextUtils::Target* target,
- const GrClip& clip, const SkPaint& paint,
- const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
- const SkIRect& clipBounds) {
- SkScalar transX, transY;
- for (int i = 0; i < fBigGlyphs.count(); i++) {
- GrAtlasTextBlob::BigGlyph& bigGlyph = fBigGlyphs[i];
- calculate_translation(bigGlyph.fTreatAsBMP, viewMatrix, x, y,
- fInitialViewMatrix, fInitialX, fInitialY, &transX, &transY);
- SkMatrix ctm;
- ctm.setScale(bigGlyph.fScale, bigGlyph.fScale);
- ctm.postTranslate(bigGlyph.fX + transX, bigGlyph.fY + transY);
- if (!bigGlyph.fTreatAsBMP) {
- ctm.postConcat(viewMatrix);
+void GrAtlasTextBlob::flush(GrAtlasGlyphCache* atlasGlyphCache, GrTextUtils::Target* target,
+ const SkSurfaceProps& props,
+ const GrDistanceFieldAdjustTable* distanceAdjustTable,
+ const GrTextUtils::Paint& paint, const GrClip& clip,
+ const SkMatrix& viewMatrix, const SkIRect& clipBounds,
+ SkScalar x, SkScalar y) {
+
+ // GrAtlasTextBlob::makeOp only takes uint16_t values for run and subRun indices.
+ // Encountering something larger than this is highly unlikely, so we'll just not draw it.
+ int lastRun = SkTMin(fRunCount, (1 << 16)) - 1;
+ GrTextUtils::RunPaint runPaint(&paint, nullptr, props);
+ for (int runIndex = 0; runIndex <= lastRun; runIndex++) {
+ Run& run = fRuns[runIndex];
+
+ // first flush any path glyphs
+ if (run.fPathGlyphs.count()) {
+ SkScalar transX, transY;
+ uint16_t paintFlags = run.fPaintFlags;
+ if (!runPaint.modifyForRun(
+ [paintFlags](SkPaint* p) {
+ p->setFlags((p->getFlags() & ~Run::kPaintFlagsMask) | paintFlags);
+ })) {
+ continue;
+ }
+ for (int i = 0; i < run.fPathGlyphs.count(); i++) {
+ GrAtlasTextBlob::Run::PathGlyph& pathGlyph = run.fPathGlyphs[i];
+ calculate_translation(pathGlyph.fPreTransformed, viewMatrix, x, y,
+ fInitialViewMatrix, fInitialX, fInitialY, &transX, &transY);
+ const SkMatrix& ctm = pathGlyph.fPreTransformed ? SkMatrix::I() : viewMatrix;
+ SkMatrix pathMatrix;
+ pathMatrix.setScale(pathGlyph.fScale, pathGlyph.fScale);
+ pathMatrix.postTranslate(pathGlyph.fX + transX, pathGlyph.fY + transY);
+ target->drawPath(clip, pathGlyph.fPath, runPaint, ctm, &pathMatrix, clipBounds);
+ }
}
- target->drawPath(clip, bigGlyph.fPath, paint, ctm, nullptr, clipBounds);
- }
-}
-void GrAtlasTextBlob::flushBigRun(GrTextUtils::Target* target,
- const SkSurfaceProps& props, const SkTextBlobRunIterator& it,
- const GrClip& clip, const GrTextUtils::Paint& paint,
- SkDrawFilter* drawFilter, const SkMatrix& viewMatrix,
- const SkIRect& clipBounds, SkScalar x, SkScalar y) {
- size_t textLen = it.glyphCount() * sizeof(uint16_t);
- const SkPoint& offset = it.offset();
-
- GrTextUtils::RunPaint runPaint(&paint, drawFilter, props);
- if (!runPaint.modifyForRun(it)) {
- return;
- }
-
- switch (it.positioning()) {
- case SkTextBlob::kDefault_Positioning:
- GrTextUtils::DrawBigText(target, clip, runPaint, viewMatrix,
- (const char*)it.glyphs(), textLen, x + offset.x(),
- y + offset.y(), clipBounds);
- break;
- case SkTextBlob::kHorizontal_Positioning:
- GrTextUtils::DrawBigPosText(target, props, clip, runPaint, viewMatrix,
- (const char*)it.glyphs(), textLen, it.pos(), 1,
- SkPoint::Make(x, y + offset.y()), clipBounds);
- break;
- case SkTextBlob::kFull_Positioning:
- GrTextUtils::DrawBigPosText(target, props, clip, runPaint, viewMatrix,
- (const char*)it.glyphs(), textLen, it.pos(), 2,
- SkPoint::Make(x, y), clipBounds);
- break;
- }
-}
-
-void GrAtlasTextBlob::flushCached(GrAtlasGlyphCache* atlasGlyphCache, GrTextUtils::Target* target,
- const SkTextBlob* blob, const SkSurfaceProps& props,
- const GrDistanceFieldAdjustTable* distanceAdjustTable,
- const GrTextUtils::Paint& paint, 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
- SkTextBlobRunIterator it(blob);
- for (int run = 0; !it.done(); it.next(), run++) {
- if (fRuns[run].fTooBigForAtlas) {
- this->flushBigRun(target, props, it, clip, paint, drawFilter, viewMatrix,
- clipBounds, x, y);
+ // then flush each subrun, if any
+ if (!run.fInitialized) {
continue;
}
- this->flushRun(target, clip, run, viewMatrix, x, y, paint, props, distanceAdjustTable,
- atlasGlyphCache);
+ int lastSubRun = SkTMin(run.fSubRunInfo.count(), 1 << 16) - 1;
+ for (int subRun = 0; subRun <= lastSubRun; subRun++) {
+ const Run::SubRunInfo& info = run.fSubRunInfo[subRun];
+ int glyphCount = info.glyphCount();
+ if (0 == glyphCount) {
+ continue;
+ }
+
+ bool skipClip = false;
+ bool submitOp = true;
+ SkIRect clipRect = SkIRect::MakeEmpty();
+ SkRect rtBounds = SkRect::MakeWH(target->width(), target->height());
+ SkRRect clipRRect;
+ GrAA aa;
+ // We can clip geometrically if we're not using SDFs,
+ // and we have an axis-aligned rectangular non-AA clip
+ if (!info.drawAsDistanceFields() && clip.isRRect(rtBounds, &clipRRect, &aa) &&
+ clipRRect.isRect() && GrAA::kNo == aa) {
+ skipClip = true;
+ // We only need to do clipping work if the subrun isn't contained by the clip
+ SkRect subRunBounds;
+ this->computeSubRunBounds(&subRunBounds, runIndex, subRun, viewMatrix, x, y);
+ if (!clipRRect.getBounds().contains(subRunBounds)) {
+ // If the subrun is completely outside, don't add an op for it
+ if (!clipRRect.getBounds().intersects(subRunBounds)) {
+ submitOp = false;
+ }
+ else {
+ clipRRect.getBounds().round(&clipRect);
+ }
+ }
+ }
+
+ if (submitOp) {
+ auto op = this->makeOp(info, glyphCount, runIndex, subRun, viewMatrix, x, y,
+ clipRect, std::move(paint), props, distanceAdjustTable,
+ atlasGlyphCache, target);
+ if (op) {
+ if (skipClip) {
+ target->addDrawOp(GrNoClip(), std::move(op));
+ }
+ else {
+ target->addDrawOp(clip, std::move(op));
+ }
+ }
+ }
+ }
+
}
-
- // Now flush big glyphs
- this->flushBigGlyphs(target, clip, paint, viewMatrix, x, y, clipBounds);
-}
-
-void GrAtlasTextBlob::flushThrowaway(GrAtlasGlyphCache* atlasGlyphCache,
- GrTextUtils::Target* target,
- const SkSurfaceProps& props,
- const GrDistanceFieldAdjustTable* distanceAdjustTable,
- const GrTextUtils::Paint& paint, const GrClip& clip,
- const SkMatrix& viewMatrix, const SkIRect& clipBounds,
- SkScalar x, SkScalar y) {
- for (int run = 0; run < fRunCount; run++) {
- this->flushRun(target, clip, run, viewMatrix, x, y, paint, props, distanceAdjustTable,
- atlasGlyphCache);
- }
-
- // Now flush big glyphs
- this->flushBigGlyphs(target, clip, paint, viewMatrix, x, y, clipBounds);
}
std::unique_ptr<GrDrawOp> GrAtlasTextBlob::test_makeOp(
@@ -462,15 +409,6 @@
SkASSERT_RELEASE(l.fStrokeInfo.fMiterLimit == r.fStrokeInfo.fMiterLimit);
SkASSERT_RELEASE(l.fStrokeInfo.fJoin == r.fStrokeInfo.fJoin);
- SkASSERT_RELEASE(l.fBigGlyphs.count() == r.fBigGlyphs.count());
- for (int i = 0; i < l.fBigGlyphs.count(); i++) {
- const BigGlyph& lBigGlyph = l.fBigGlyphs[i];
- const BigGlyph& rBigGlyph = r.fBigGlyphs[i];
-
- SkASSERT_RELEASE(lBigGlyph.fPath == rBigGlyph.fPath);
- // We can't assert that these have the same translations
- }
-
SkASSERT_RELEASE(l.fKey == r.fKey);
//SkASSERT_RELEASE(l.fPaintColor == r.fPaintColor); // Colors might not actually be identical
SkASSERT_RELEASE(l.fMaxMinScale == r.fMaxMinScale);
@@ -506,7 +444,6 @@
// color can be changed
//SkASSERT(lRun.fColor == rRun.fColor);
SkASSERT_RELEASE(lRun.fInitialized == rRun.fInitialized);
- SkASSERT_RELEASE(lRun.fTooBigForAtlas == rRun.fTooBigForAtlas);
SkASSERT_RELEASE(lRun.fSubRunInfo.count() == rRun.fSubRunInfo.count());
for(int j = 0; j < lRun.fSubRunInfo.count(); j++) {
@@ -533,6 +470,15 @@
SkASSERT_RELEASE(lSubRun.drawAsDistanceFields() == rSubRun.drawAsDistanceFields());
SkASSERT_RELEASE(lSubRun.hasUseLCDText() == rSubRun.hasUseLCDText());
}
+
+ SkASSERT_RELEASE(lRun.fPathGlyphs.count() == rRun.fPathGlyphs.count());
+ for (int i = 0; i < lRun.fPathGlyphs.count(); i++) {
+ const Run::PathGlyph& lPathGlyph = lRun.fPathGlyphs[i];
+ const Run::PathGlyph& rPathGlyph = rRun.fPathGlyphs[i];
+
+ SkASSERT_RELEASE(lPathGlyph.fPath == rPathGlyph.fPath);
+ // We can't assert that these have the same translations
+ }
}
}