Simplify SubRun structure in GrTextBlob

* Remove Run.
* Have SubRun handle both mask and path data.
* Collect up runs of glyph with the same format to
  simplify building SubRuns.

The nexus of this change is GrTextBlob::makeSubRun. All the
invariants for a SubRun are set in this code, and those
invariants stem from the SubRunType. Much of the code has been
simplified by not having to maintain complicated invariant
state between adding glyphs.

The change that reduces the most number of problems with the
old code is having SubRun own its SkStrikeSpec. Having bulk
glyph insertion facilitated this.

Change-Id: I2439392a9ee3485e7b07a1f3fdaf1351128c4d47
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/255309
Commit-Queue: Herb Derby <herb@google.com>
Reviewed-by: Jim Van Verth <jvanverth@google.com>
This commit is contained in:
Herb Derby 2019-11-15 15:21:15 -05:00 committed by Skia Commit-Bot
parent 61169e9c47
commit 1b8dcd112c
7 changed files with 379 additions and 555 deletions

View File

@ -149,10 +149,6 @@ void SkGlyphRunListPainter::processGlyphRunList(const SkGlyphRunList& glyphRunLi
bool usePaths =
useSDFT ? false : SkStrikeSpec::ShouldDrawAsPath(runPaint, runFont, viewMatrix);
if (process) {
process->startRun(glyphRun, useSDFT);
}
if (!useSDFT && !usePaths) {
// Process masks - this should be the 99.99% case.
@ -184,13 +180,10 @@ void SkGlyphRunListPainter::processGlyphRunList(const SkGlyphRunList& glyphRunLi
fRejects.flipRejectsToSource();
if (process) {
bool hasWCoord =
viewMatrix.hasPerspective() || options.fDistanceFieldVerticesAlwaysHaveW;
// processSourceSDFT must be called even if there are no glyphs to make sure runs
// are set correctly.
process->processSourceSDFT(
fDrawable.drawable(), strikeSpec, runFont, minScale, maxScale, hasWCoord);
fDrawable.drawable(), strikeSpec, runFont, minScale, maxScale);
}
}
@ -214,7 +207,7 @@ void SkGlyphRunListPainter::processGlyphRunList(const SkGlyphRunList& glyphRunLi
if (process) {
// processSourcePaths must be called even if there are no glyphs to make sure runs
// are set correctly.
process->processSourcePaths(fDrawable.drawable(), strikeSpec);
process->processSourcePaths(fDrawable.drawable(), runFont, strikeSpec);
}
}
@ -276,7 +269,7 @@ void SkGlyphRunListPainter::processGlyphRunList(const SkGlyphRunList& glyphRunLi
SkASSERT(fRejects.source().empty());
if (process) {
process->processDeviceFallback(fDrawable.drawable(), strikeSpec);
process->processDeviceMasks(fDrawable.drawable(), strikeSpec);
}
} else {
// If the matrix is complicated or if scaling is used to fit the glyphs in the
@ -294,10 +287,7 @@ void SkGlyphRunListPainter::processGlyphRunList(const SkGlyphRunList& glyphRunLi
SkASSERT(fRejects.source().empty());
if (process) {
process->processSourceFallback(
fDrawable.drawable(),
strikeSpec,
viewMatrix.hasPerspective());
process->processSourceMasks(fDrawable.drawable(), strikeSpec);
}
}
}
@ -384,6 +374,7 @@ void GrTextContext::drawGlyphRunList(
cacheBlob = textBlobCache->find(key);
}
bool forceW = fOptions.fDistanceFieldVerticesAlwaysHaveW;
if (cacheBlob) {
if (cacheBlob->mustRegenerate(listPaint, glyphRunList.anyRunsSubpixelPositioned(),
blurRec, viewMatrix, origin.x(),origin.y())) {
@ -392,21 +383,21 @@ void GrTextContext::drawGlyphRunList(
// but we'd have to clear the subrun information
textBlobCache->remove(cacheBlob.get());
cacheBlob = textBlobCache->makeCachedBlob(
glyphRunList, key, blurRec, listPaint, color, grStrikeCache);
glyphRunList, key, blurRec, listPaint, forceW, color, grStrikeCache);
cacheBlob->generateFromGlyphRunList(
*context->priv().caps()->shaderCaps(), fOptions,
listPaint, scalerContextFlags, viewMatrix, props,
listPaint, viewMatrix, props,
glyphRunList, target->glyphPainter());
} else {
textBlobCache->makeMRU(cacheBlob.get());
if (CACHE_SANITY_CHECK) {
sk_sp<GrTextBlob> sanityBlob(textBlobCache->makeBlob(
glyphRunList, color, grStrikeCache));
glyphRunList, forceW, color, grStrikeCache));
sanityBlob->setupKey(key, blurRec, listPaint);
cacheBlob->generateFromGlyphRunList(
*context->priv().caps()->shaderCaps(), fOptions,
listPaint, scalerContextFlags, viewMatrix, props, glyphRunList,
listPaint, viewMatrix, props, glyphRunList,
target->glyphPainter());
GrTextBlob::AssertEqual(*sanityBlob, *cacheBlob);
}
@ -414,13 +405,13 @@ void GrTextContext::drawGlyphRunList(
} else {
if (canCache) {
cacheBlob = textBlobCache->makeCachedBlob(
glyphRunList, key, blurRec, listPaint, color, grStrikeCache);
glyphRunList, key, blurRec, listPaint, forceW, color, grStrikeCache);
} else {
cacheBlob = textBlobCache->makeBlob(glyphRunList, color, grStrikeCache);
cacheBlob = textBlobCache->makeBlob(glyphRunList, forceW, color, grStrikeCache);
}
cacheBlob->generateFromGlyphRunList(
*context->priv().caps()->shaderCaps(), fOptions, listPaint,
scalerContextFlags, viewMatrix, props, glyphRunList,
viewMatrix, props, glyphRunList,
target->glyphPainter());
}
@ -428,97 +419,9 @@ void GrTextContext::drawGlyphRunList(
clip, viewMatrix, origin.x(), origin.y());
}
void GrTextBlob::SubRun::appendGlyph(GrGlyph* glyph, SkRect dstRect) {
this->joinGlyphBounds(dstRect);
GrTextBlob* blob = fRun->fBlob;
bool hasW = this->hasWCoord();
// glyphs drawn in perspective must always have a w coord.
SkASSERT(hasW || !blob->fInitialViewMatrix.hasPerspective());
auto maskFormat = this->maskFormat();
size_t vertexStride = GetVertexStride(maskFormat, hasW);
intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices + fVertexEndIndex);
// We always write the third position component used by SDFs. If it is unused it gets
// overwritten. Similarly, we always write the color and the blob will later overwrite it
// with texture coords if it is unused.
size_t colorOffset = hasW ? sizeof(SkPoint3) : sizeof(SkPoint);
// V0
*reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fLeft, dstRect.fTop, 1.f};
*reinterpret_cast<GrColor*>(vertex + colorOffset) = fColor;
vertex += vertexStride;
// V1
*reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fLeft, dstRect.fBottom, 1.f};
*reinterpret_cast<GrColor*>(vertex + colorOffset) = fColor;
vertex += vertexStride;
// V2
*reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fRight, dstRect.fTop, 1.f};
*reinterpret_cast<GrColor*>(vertex + colorOffset) = fColor;
vertex += vertexStride;
// V3
*reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fRight, dstRect.fBottom, 1.f};
*reinterpret_cast<GrColor*>(vertex + colorOffset) = fColor;
fVertexEndIndex += vertexStride * kVerticesPerGlyph;
blob->fGlyphs[fGlyphEndIndex++] = glyph;
}
void GrTextBlob::Run::switchSubRunIfNeededAndAppendGlyph(GrGlyph* glyph,
const sk_sp<GrTextStrike>& strike,
const SkRect& destRect,
bool needsTransform) {
GrMaskFormat format = glyph->fMaskFormat;
SubRun* subRun = &fSubRunInfo.back();
if (fInitialized && subRun->maskFormat() != format) {
subRun = pushBackSubRun(fStrikeSpec, fColor);
subRun->setStrike(strike);
} else if (!fInitialized) {
subRun->setStrike(strike);
}
fInitialized = true;
subRun->setMaskFormat(format);
subRun->setNeedsTransform(needsTransform);
subRun->appendGlyph(glyph, destRect);
}
void GrTextBlob::Run::appendDeviceSpaceGlyph(const sk_sp<GrTextStrike>& strike,
const SkGlyph& skGlyph, SkPoint origin) {
if (GrGlyph* glyph = strike->getGlyph(skGlyph)) {
SkRect glyphRect = glyph->destRect(origin);
if (!glyphRect.isEmpty()) {
this->switchSubRunIfNeededAndAppendGlyph(glyph, strike, glyphRect, false);
}
}
}
void GrTextBlob::Run::appendSourceSpaceGlyph(const sk_sp<GrTextStrike>& strike,
const SkGlyph& skGlyph,
SkPoint origin,
SkScalar textScale) {
if (GrGlyph* glyph = strike->getGlyph(skGlyph)) {
SkRect glyphRect = glyph->destRect(origin, textScale);
if (!glyphRect.isEmpty()) {
this->switchSubRunIfNeededAndAppendGlyph(glyph, strike, glyphRect, true);
}
}
}
void GrTextBlob::generateFromGlyphRunList(const GrShaderCaps& shaderCaps,
const GrTextContext::Options& options,
const SkPaint& paint,
SkScalerContextFlags scalerContextFlags,
const SkMatrix& viewMatrix,
const SkSurfaceProps& props,
const SkGlyphRunList& glyphRunList,
@ -536,43 +439,181 @@ void GrTextBlob::generateFromGlyphRunList(const GrShaderCaps& shaderCaps,
this);
}
GrTextBlob::Run* GrTextBlob::currentRun() {
return &fRuns[fRunCount - 1];
GrTextBlob::SubRun::SubRun(SubRunType type, GrTextBlob* textBlob, const SkStrikeSpec& strikeSpec,
GrMaskFormat format, const GrTextBlob::SubRunBufferSpec& bufferSpec,
sk_sp<GrTextStrike>&& grStrike)
: fType{type}
, fBlob{textBlob}
, fMaskFormat{format}
, fGlyphStartIndex{std::get<0>(bufferSpec)}
, fGlyphEndIndex{std::get<1>(bufferSpec)}
, fVertexStartIndex{std::get<2>(bufferSpec)}
, fVertexEndIndex{std::get<3>(bufferSpec)}
, fStrikeSpec{strikeSpec}
, fStrike{grStrike}
, fColor{textBlob->fColor}
, fX{textBlob->fInitialX}
, fY{textBlob->fInitialY}
, fCurrentViewMatrix{textBlob->fInitialViewMatrix} {
SkASSERT(type != kTransformedPath);
}
void GrTextBlob::startRun(const SkGlyphRun& glyphRun, bool useSDFT) {
if (useSDFT) {
this->setHasDistanceField();
GrTextBlob::SubRun::SubRun(GrTextBlob* textBlob, const SkStrikeSpec& strikeSpec)
: fType{kTransformedPath}
, fBlob{textBlob}
, fMaskFormat{kA8_GrMaskFormat}
, fGlyphStartIndex{0}
, fGlyphEndIndex{0}
, fVertexStartIndex{0}
, fVertexEndIndex{0}
, fStrikeSpec{strikeSpec}
, fStrike{nullptr}
, fColor{textBlob->fColor}
, fPaths{} { }
class GrTextBlob::SubRun* GrTextBlob::makeSubRun(SubRunType type,
const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkStrikeSpec& strikeSpec,
GrMaskFormat format) {
bool hasW = this->hasW(type);
uint32_t glyphsStart = fGlyphsCursor;
fGlyphsCursor += drawables.size();
uint32_t glyphsEnd = fGlyphsCursor;
size_t verticesStart = fVerticesCursor;
fVerticesCursor += drawables.size() * GetVertexStride(format, hasW) * kVerticesPerGlyph;
size_t verticesEnd = fVerticesCursor;
SubRunBufferSpec bufferSpec = {glyphsStart, glyphsEnd, verticesStart, verticesEnd};
sk_sp<GrTextStrike> grStrike = strikeSpec.findOrCreateGrStrike(fStrikeCache);
SubRun& subRun = fSubRuns.emplace_back(
type, this, strikeSpec, format, bufferSpec, std::move(grStrike));
subRun.appendGlyphs(drawables);
return &subRun;
}
void GrTextBlob::SubRun::appendGlyphs(const SkZip<SkGlyphVariant, SkPoint>& drawables) {
GrTextStrike* grStrike = fStrike.get();
SkScalar strikeToSource = fStrikeSpec.strikeToSourceRatio();
uint32_t glyphCursor = fGlyphStartIndex;
size_t vertexCursor = fVertexStartIndex;
bool hasW = this->hasW();
GrColor color = this->color();
// glyphs drawn in perspective must always have a w coord.
SkASSERT(hasW || !fBlob->fInitialViewMatrix.hasPerspective());
size_t vertexStride = GetVertexStride(fMaskFormat, hasW);
// We always write the third position component used by SDFs. If it is unused it gets
// overwritten. Similarly, we always write the color and the blob will later overwrite it
// with texture coords if it is unused.
size_t colorOffset = hasW ? sizeof(SkPoint3) : sizeof(SkPoint);
for (auto t : drawables) {
SkGlyph* skGlyph; SkPoint pos;
std::tie(skGlyph, pos) = t;
GrGlyph* grGlyph = grStrike->getGlyph(*skGlyph);
// Only floor the device coordinates.
SkRect dstRect;
if (!this->needsTransform()) {
pos = {SkScalarFloorToScalar(pos.x()), SkScalarFloorToScalar(pos.y())};
dstRect = grGlyph->destRect(pos);
} else {
dstRect = grGlyph->destRect(pos, strikeToSource);
}
this->joinGlyphBounds(dstRect);
intptr_t vertex = reinterpret_cast<intptr_t>(fBlob->fVertices + vertexCursor);
// V0
*reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fLeft, dstRect.fTop, 1.f};
*reinterpret_cast<GrColor*>(vertex + colorOffset) = color;
vertex += vertexStride;
// V1
*reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fLeft, dstRect.fBottom, 1.f};
*reinterpret_cast<GrColor*>(vertex + colorOffset) = color;
vertex += vertexStride;
// V2
*reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fRight, dstRect.fTop, 1.f};
*reinterpret_cast<GrColor*>(vertex + colorOffset) = color;
vertex += vertexStride;
// V3
*reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fRight, dstRect.fBottom, 1.f};
*reinterpret_cast<GrColor*>(vertex + colorOffset) = color;
vertexCursor += vertexStride * kVerticesPerGlyph;
fBlob->fGlyphs[glyphCursor++] = grGlyph;
}
Run* run = this->pushBackRun();
run->setRunFontAntiAlias(glyphRun.font().hasSomeAntiAliasing());
SkASSERT(glyphCursor == fGlyphEndIndex);
SkASSERT(vertexCursor == fVertexEndIndex);
}
void GrTextBlob::addSingleMaskFormat(
SubRunType type,
const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkStrikeSpec& strikeSpec,
GrMaskFormat format) {
this->makeSubRun(type, drawables, strikeSpec, format);
}
void GrTextBlob::addMultiMaskFormat(
SubRunType type,
const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkStrikeSpec& strikeSpec) {
this->setHasBitmap();
if (drawables.empty()) { return; }
SkGlyph* glyph;
std::tie(glyph, std::ignore) = drawables[0];
GrMaskFormat format = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
size_t startIndex = 0;
for (size_t i = 1; i < drawables.size(); i++) {
std::tie(glyph, std::ignore) = drawables[i];
GrMaskFormat nextFormat = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
if (format != nextFormat) {
auto sameFormat = drawables.subspan(startIndex, i - startIndex);
this->addSingleMaskFormat(type, sameFormat, strikeSpec, format);
format = nextFormat;
startIndex = i;
}
}
auto sameFormat = drawables.last(drawables.size() - startIndex);
this->addSingleMaskFormat(type, sameFormat, strikeSpec, format);
}
void GrTextBlob::addSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkStrikeSpec& strikeSpec,
const SkFont& runFont,
SkScalar minScale,
SkScalar maxScale) {
this->setHasDistanceField();
this->setMinAndMaxScale(minScale, maxScale);
SubRun* subRun = this->makeSubRun(kTransformedSDFT, drawables, strikeSpec, kA8_GrMaskFormat);
subRun->setUseLCDText(runFont.getEdging() == SkFont::Edging::kSubpixelAntiAlias);
subRun->setAntiAliased(runFont.hasSomeAntiAliasing());
}
void GrTextBlob::processDeviceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkStrikeSpec& strikeSpec) {
Run* run = this->currentRun();
this->setHasBitmap();
run->setupFont(strikeSpec);
sk_sp<GrTextStrike> currStrike = strikeSpec.findOrCreateGrStrike(fStrikeCache);
for (auto t : drawables) {
SkGlyph* glyph; SkPoint pos;
std::tie(glyph, pos) = t;
SkPoint pt{SkScalarFloorToScalar(pos.fX),
SkScalarFloorToScalar(pos.fY)};
run->appendDeviceSpaceGlyph(currStrike, *glyph, pt);
}
this->addMultiMaskFormat(kDirectMask, drawables, strikeSpec);
}
void GrTextBlob::processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkFont& runFont,
const SkStrikeSpec& strikeSpec) {
Run* run = this->currentRun();
this->setHasBitmap();
// FIXME: why was this ever called?
//run->setupFont(strikeSpec);
SubRun& subRun = fSubRuns.emplace_back(this, strikeSpec);
subRun.setAntiAliased(runFont.hasSomeAntiAliasing());
for (auto t : drawables) {
const SkPath* path; SkPoint pos;
std::tie(path, pos) = t;
run->appendPathGlyph(*path, pos, strikeSpec.strikeToSourceRatio());
subRun.fPaths.emplace_back(*path, pos);
}
}
@ -580,58 +621,13 @@ void GrTextBlob::processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawabl
const SkStrikeSpec& strikeSpec,
const SkFont& runFont,
SkScalar minScale,
SkScalar maxScale,
bool hasWCoord) {
Run* run = this->currentRun();
run->setSubRunHasDistanceFields(
runFont.getEdging() == SkFont::Edging::kSubpixelAntiAlias,
runFont.hasSomeAntiAliasing(),
hasWCoord);
this->setMinAndMaxScale(minScale, maxScale);
run->setupFont(strikeSpec);
sk_sp<GrTextStrike> currStrike = strikeSpec.findOrCreateGrStrike(fStrikeCache);
for (auto t : drawables) {
SkGlyph* glyph; SkPoint pos;
std::tie(glyph, pos) = t;
run->appendSourceSpaceGlyph(currStrike, *glyph, pos, strikeSpec.strikeToSourceRatio());
}
SkScalar maxScale) {
this->addSDFT(drawables, strikeSpec, runFont, minScale, maxScale);
}
void GrTextBlob::processSourceFallback(const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkStrikeSpec& strikeSpec,
bool hasW) {
Run* run = this->currentRun();
auto subRun = run->initARGBFallback();
sk_sp<GrTextStrike> grStrike = strikeSpec.findOrCreateGrStrike(fStrikeCache);
subRun->setStrike(grStrike);
subRun->setHasWCoord(hasW);
this->setHasBitmap();
run->setupFont(strikeSpec);
for (auto t : drawables) {
SkGlyph* glyph; SkPoint pos;
std::tie(glyph, pos) = t;
run->appendSourceSpaceGlyph(grStrike, *glyph, pos, strikeSpec.strikeToSourceRatio());
}
}
void GrTextBlob::processDeviceFallback(const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkStrikeSpec& strikeSpec) {
Run* run = this->currentRun();
this->setHasBitmap();
sk_sp<GrTextStrike> grStrike = strikeSpec.findOrCreateGrStrike(fStrikeCache);
auto subRun = run->initARGBFallback();
run->setupFont(strikeSpec);
subRun->setStrike(grStrike);
for (auto t : drawables) {
SkGlyph* glyph; SkPoint pos;
std::tie(glyph, pos) = t;
SkPoint pt{SkScalarFloorToScalar(pos.fX),
SkScalarFloorToScalar(pos.fY)};
run->appendDeviceSpaceGlyph(grStrike, *glyph, pt);
}
void GrTextBlob::processSourceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkStrikeSpec& strikeSpec) {
this->addMultiMaskFormat(kTransformedMask, drawables, strikeSpec);
}
#if GR_TEST_UTILS
@ -669,12 +665,10 @@ std::unique_ptr<GrDrawOp> GrTextContext::createOp_TestingOnly(GrRecordingContext
auto glyphRunList = builder.useGlyphRunList();
sk_sp<GrTextBlob> blob;
if (!glyphRunList.empty()) {
blob = direct->priv().getTextBlobCache()->makeBlob(glyphRunList, color, strikeCache);
// Use the text and textLen below, because we don't want to mess with the paint.
SkScalerContextFlags scalerContextFlags = ComputeScalerContextFlags(rtc->colorInfo());
blob = direct->priv().getTextBlobCache()->makeBlob(glyphRunList, false, color, strikeCache);
blob->generateFromGlyphRunList(
*context->priv().caps()->shaderCaps(), textContext->fOptions,
skPaint, scalerContextFlags, viewMatrix, surfaceProps,
skPaint, viewMatrix, surfaceProps,
glyphRunList, rtc->textTarget()->glyphPainter());
}

View File

@ -134,28 +134,21 @@ class SkGlyphRunPainterInterface {
public:
virtual ~SkGlyphRunPainterInterface() = default;
virtual void startRun(const SkGlyphRun& glyphRun, bool useSDFT) = 0;
virtual void processDeviceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkStrikeSpec& strikeSpec) = 0;
virtual void processSourceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkStrikeSpec& strikeSpec) = 0;
virtual void processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkFont& runFont,
const SkStrikeSpec& strikeSpec) = 0;
virtual void processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkStrikeSpec& strikeSpec,
const SkFont& runFont,
SkScalar minScale,
SkScalar maxScale,
bool hasWCoord) = 0;
virtual void processSourceFallback(const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkStrikeSpec& strikeSpec,
bool hasW) = 0;
virtual void processDeviceFallback(const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkStrikeSpec& strikeSpec) = 0;
SkScalar maxScale) = 0;
};
#endif // SkGlyphRunPainter_DEFINED

View File

@ -122,12 +122,11 @@ SkString GrAtlasTextOp::dumpInfo() const {
SkString str;
for (int i = 0; i < fGeoCount; ++i) {
str.appendf("%d: Color: 0x%08x Trans: %.2f,%.2f Runs: %d\n",
str.appendf("%d: Color: 0x%08x Trans: %.2f,%.2f\n",
i,
fGeoData[i].fColor.toBytes_RGBA(),
fGeoData[i].fX,
fGeoData[i].fY,
fGeoData[i].fBlob->runCountLimit());
fGeoData[i].fY);
}
str += fProcessors.dumpProcessors();

View File

@ -24,7 +24,7 @@ template <size_t N> static size_t sk_align(size_t s) {
}
sk_sp<GrTextBlob> GrTextBlob::Make(int glyphCount,
int runCount,
bool forceWForDistanceFields,
GrColor color,
GrStrikeCache* strikeCache) {
// We allocate size for the GrTextBlob itself, plus size for the vertices array,
@ -32,10 +32,9 @@ sk_sp<GrTextBlob> GrTextBlob::Make(int glyphCount,
size_t verticesCount = glyphCount * kVerticesPerGlyph * kMaxVASize;
size_t blobStart = 0;
size_t vertex = sk_align<alignof(char)> (blobStart + sizeof(GrTextBlob) * 1);
size_t glyphs = sk_align<alignof(GrGlyph*)> (vertex + sizeof(char) * verticesCount);
size_t runs = sk_align<alignof(GrTextBlob::Run)>(glyphs + sizeof(GrGlyph*) * glyphCount);
size_t size = (runs + sizeof(GrTextBlob::Run) * runCount);
size_t vertex = sk_align<alignof(char)> (blobStart + sizeof(GrTextBlob) * 1);
size_t glyphs = sk_align<alignof(GrGlyph*)> (vertex + sizeof(char) * verticesCount);
size_t size = (glyphs + sizeof(GrGlyph*) * glyphCount);
void* allocation = ::operator new (size);
@ -43,35 +42,16 @@ sk_sp<GrTextBlob> GrTextBlob::Make(int glyphCount,
sk_bzero(allocation, size);
}
sk_sp<GrTextBlob> blob{new (allocation) GrTextBlob{strikeCache}};
sk_sp<GrTextBlob> blob{new (allocation) GrTextBlob{strikeCache, color, forceWForDistanceFields}};
blob->fSize = size;
// setup offsets for vertices / glyphs
blob->fVertices = SkTAddOffset<char>(blob.get(), vertex);
blob->fGlyphs = SkTAddOffset<GrGlyph*>(blob.get(), glyphs);
blob->fRuns = SkTAddOffset<GrTextBlob::Run>(blob.get(), runs);
blob->fGlyphs = SkTAddOffset<GrGlyph*>(blob.get(), glyphs);
// Initialize runs
for (int i = 0; i < runCount; i++) {
new (&blob->fRuns[i]) GrTextBlob::Run{blob.get(), color};
}
blob->fRunCountLimit = runCount;
return blob;
}
void GrTextBlob::Run::setupFont(const SkStrikeSpec& strikeSpec) {
if (fFallbackStrikeSpec != nullptr) {
*fFallbackStrikeSpec = strikeSpec;
} else {
fStrikeSpec = strikeSpec;
}
}
void GrTextBlob::Run::appendPathGlyph(const SkPath& path, SkPoint position, SkScalar scale) {
fPathGlyphs.emplace_back(path, position.x(), position.y(), scale);
}
bool GrTextBlob::mustRegenerate(const SkPaint& paint, bool anyRunHasSubpixelPosition,
const SkMaskFilterBase::BlurRec& blurRec,
const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
@ -218,52 +198,35 @@ void GrTextBlob::flush(GrTextTarget* target, const SkSurfaceProps& props,
const SkPaint& paint, const SkPMColor4f& filteredColor, const GrClip& clip,
const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
// GrTextBlob::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(fRunCountLimit, (1 << 16)) - 1;
// For each run in the GrTextBlob we're going to churn through all the glyphs.
// Each run is broken into a path part and a Mask / DFT / ARGB part.
for (int runIndex = 0; runIndex <= lastRun; runIndex++) {
Run& run = fRuns[runIndex];
// first flush any path glyphs
if (run.fPathGlyphs.count()) {
for (auto& subRun : fSubRuns) {
if (subRun.drawAsPaths()) {
SkPaint runPaint{paint};
runPaint.setAntiAlias(run.fAntiAlias);
runPaint.setAntiAlias(subRun.isAntiAliased());
// If there are shaders, blurs or styles, the path must be scaled into source
// space independently of the CTM. This allows the CTM to be correct for the
// different effects.
GrStyle style(runPaint);
for (int i = 0; i < run.fPathGlyphs.count(); i++) {
GrTextBlob::Run::PathGlyph& pathGlyph = run.fPathGlyphs[i];
bool scalePath = runPaint.getShader()
|| style.applies()
|| runPaint.getMaskFilter();
SkMatrix ctm;
const SkPath* path = &pathGlyph.fPath;
// The origin for the blob may have changed, so figure out the delta.
SkVector originShift = SkPoint{x, y} - SkPoint{fInitialX, fInitialY};
for (const auto& pathGlyph : subRun.fPaths) {
SkMatrix ctm{viewMatrix};
SkMatrix pathMatrix = SkMatrix::MakeScale(subRun.fStrikeSpec.strikeToSourceRatio());
// Shift the original glyph location in source space to the position of the new
// blob.
pathMatrix.postTranslate(originShift.x() + pathGlyph.fOrigin.x(),
originShift.y() + pathGlyph.fOrigin.y());
// TmpPath must be in the same scope as GrShape shape below.
SkTLazy<SkPath> tmpPath;
// Positions and outlines are in source space.
ctm = viewMatrix;
SkMatrix pathMatrix = SkMatrix::MakeScale(pathGlyph.fScale, pathGlyph.fScale);
// The origin for the blob may have changed, so figure out the delta.
SkVector originShift = SkPoint{x, y} - SkPoint{fInitialX, fInitialY};
// Shift the original glyph location in source space to the position of the new
// blob.
pathMatrix.postTranslate(originShift.x() + pathGlyph.fX,
originShift.y() + pathGlyph.fY);
// If there are shaders, blurs or styles, the path must be scaled into source
// space independently of the CTM. This allows the CTM to be correct for the
// different effects.
GrStyle style(runPaint);
bool scalePath = runPaint.getShader()
|| style.applies()
|| runPaint.getMaskFilter();
const SkPath* path = &pathGlyph.fPath;
if (!scalePath) {
// Scale can be applied to CTM -- no effects.
ctm.preConcat(pathMatrix);
} else {
// Scale the outline into source space.
@ -278,20 +241,10 @@ void GrTextBlob::flush(GrTextTarget* target, const SkSurfaceProps& props,
// TODO: we are losing the mutability of the path here
GrShape shape(*path, paint);
target->drawShape(clip, runPaint, ctm, shape);
}
}
// then flush each subrun, if any
if (!run.fInitialized) {
continue;
}
int lastSubRun = SkTMin(run.fSubRunInfo.count(), 1 << 16) - 1;
for (int subRun = 0; subRun <= lastSubRun; subRun++) {
SubRun& info = run.fSubRunInfo[subRun];
int glyphCount = info.glyphCount();
} else {
int glyphCount = subRun.glyphCount();
if (0 == glyphCount) {
continue;
}
@ -304,13 +257,13 @@ void GrTextBlob::flush(GrTextTarget* target, const SkSurfaceProps& props,
GrAA aa;
// We can clip geometrically if we're not using SDFs or transformed glyphs,
// and we have an axis-aligned rectangular non-AA clip
if (!info.drawAsDistanceFields() && !info.needsTransform() &&
if (!subRun.drawAsDistanceFields() && !subRun.needsTransform() &&
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, info, viewMatrix, x, y, false);
this->computeSubRunBounds(&subRunBounds, subRun, viewMatrix, x, y, false);
if (!clipRRect.getBounds().contains(subRunBounds)) {
// If the subrun is completely outside, don't add an op for it
if (!clipRRect.getBounds().intersects(subRunBounds)) {
@ -323,7 +276,7 @@ void GrTextBlob::flush(GrTextTarget* target, const SkSurfaceProps& props,
}
if (submitOp) {
auto op = this->makeOp(info, glyphCount, viewMatrix, x, y,
auto op = this->makeOp(subRun, glyphCount, viewMatrix, x, y,
clipRect, paint, filteredColor, props, distanceAdjustTable,
target);
if (op) {
@ -336,7 +289,6 @@ void GrTextBlob::flush(GrTextTarget* target, const SkSurfaceProps& props,
}
}
}
}
}
@ -345,7 +297,7 @@ std::unique_ptr<GrDrawOp> GrTextBlob::test_makeOp(
SkScalar x, SkScalar y, const SkPaint& paint, const SkPMColor4f& filteredColor,
const SkSurfaceProps& props, const GrDistanceFieldAdjustTable* distanceAdjustTable,
GrTextTarget* target) {
GrTextBlob::SubRun& info = fRuns[0].fSubRunInfo[0];
GrTextBlob::SubRun& info = fSubRuns[0];
SkIRect emptyRect = SkIRect::MakeEmpty();
return this->makeOp(info, glyphCount, viewMatrix, x, y, emptyRect,
paint, filteredColor, props, distanceAdjustTable, target);
@ -367,21 +319,11 @@ void GrTextBlob::AssertEqual(const GrTextBlob& l, const GrTextBlob& r) {
SkASSERT_RELEASE(l.fMinMaxScale == r.fMinMaxScale);
SkASSERT_RELEASE(l.fTextType == r.fTextType);
SkASSERT_RELEASE(l.fRunCountLimit == r.fRunCountLimit);
for (int i = 0; i < l.fRunCountLimit; i++) {
const Run& lRun = l.fRuns[i];
const Run& rRun = r.fRuns[i];
SkASSERT_RELEASE(lRun.fStrikeSpec.descriptor() == rRun.fStrikeSpec.descriptor());
// color can be changed
//SkASSERT(lRun.fColor == rRun.fColor);
SkASSERT_RELEASE(lRun.fInitialized == rRun.fInitialized);
SkASSERT_RELEASE(lRun.fSubRunInfo.count() == rRun.fSubRunInfo.count());
for(int j = 0; j < lRun.fSubRunInfo.count(); j++) {
const SubRun& lSubRun = lRun.fSubRunInfo[j];
const SubRun& rSubRun = rRun.fSubRunInfo[j];
for(auto t : SkMakeZip(l.fSubRuns, r.fSubRuns)) {
const SubRun& lSubRun = std::get<0>(t);
const SubRun& rSubRun = std::get<1>(t);
SkASSERT(lSubRun.drawAsPaths() == rSubRun.drawAsPaths());
if (!lSubRun.drawAsPaths()) {
// TODO we can do this check, but we have to apply the VM to the old vertex bounds
//SkASSERT_RELEASE(lSubRun.vertexBounds() == rSubRun.vertexBounds());
@ -396,21 +338,18 @@ void GrTextBlob::AssertEqual(const GrTextBlob& l, const GrTextBlob& r) {
}
SkASSERT_RELEASE(lSubRun.vertexStartIndex() == rSubRun.vertexStartIndex());
SkASSERT_RELEASE(lSubRun.vertexEndIndex() == rSubRun.vertexEndIndex());
SkASSERT_RELEASE(lSubRun.glyphStartIndex() == rSubRun.glyphStartIndex());
SkASSERT_RELEASE(lSubRun.glyphEndIndex() == rSubRun.glyphEndIndex());
SkASSERT_RELEASE(lSubRun.maskFormat() == rSubRun.maskFormat());
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
} else {
SkASSERT_RELEASE(lSubRun.fPaths.size() == rSubRun.fPaths.size());
for(auto p : SkMakeZip(lSubRun.fPaths, rSubRun.fPaths)) {
const PathGlyph& lPath = std::get<0>(p);
const PathGlyph& rPath = std::get<1>(p);
SkASSERT_RELEASE(lPath.fPath == rPath.fPath);
// We can't assert that these have the same translations
}
}
}
}

View File

@ -32,9 +32,10 @@ struct GrGlyph;
class SkTextBlob;
class SkTextBlobRunIterator;
// With this flag enabled, the GrTextContext will, as a sanity check, regenerate every blob
// that comes in to verify the integrity of its cache
// This is of dubious value, and maybe should be removed. I checked it on 11/21/2019, and many
// tests failed.
#define CACHE_SANITY_CHECK 0
/*
@ -47,39 +48,32 @@ class SkTextBlobRunIterator;
* The only thing(aside from a memcopy) required to flush a GrTextBlob is to ensure that
* the GrAtlas will not evict anything the Blob needs.
*
* Note: This struct should really be named GrCachedAtasTextBlob, but that is too verbose.
*
* *WARNING* If you add new fields to this struct, then you may need to to update AssertEqual
*/
class GrTextBlob : public SkNVRefCnt<GrTextBlob>, public SkGlyphRunPainterInterface {
public:
struct Run;
class SubRun;
SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrTextBlob);
using SubRunBufferSpec = std::tuple<uint32_t, uint32_t, size_t, size_t>;
class VertexRegenerator;
void generateFromGlyphRunList(const GrShaderCaps& shaderCaps,
const GrTextContext::Options& options,
const SkPaint& paint,
SkScalerContextFlags scalerContextFlags,
const SkMatrix& viewMatrix,
const SkSurfaceProps& props,
const SkGlyphRunList& glyphRunList,
SkGlyphRunListPainter* glyphPainter);
// Make an empty GrTextBlob, with all the invariants set to make the right decisions when
// adding SubRuns.
static sk_sp<GrTextBlob> Make(
int glyphCount,
int runCount,
bool forceWForDistanceFields,
GrColor color,
GrStrikeCache* strikeCache);
/**
* We currently force regeneration of a blob if old or new matrix differ in having perspective.
* If we ever change that then the key must contain the perspectiveness when there are distance
* fields as perspective distance field use 3 component vertex positions and non-perspective
* uses 2.
*/
struct Key {
Key() {
sk_bzero(this, sizeof(Key));
@ -137,28 +131,16 @@ public:
void setHasDistanceField() { fTextType |= kHasDistanceField_TextType; }
void setHasBitmap() { fTextType |= kHasBitmap_TextType; }
int runCountLimit() const { return fRunCountLimit; }
Run* pushBackRun() {
SkASSERT(fRunCount < fRunCountLimit);
// If there is more run, then connect up the subruns.
if (fRunCount > 0) {
SubRun& newRun = fRuns[fRunCount].fSubRunInfo.back();
SubRun& lastRun = fRuns[fRunCount - 1].fSubRunInfo.back();
newRun.setAsSuccessor(lastRun);
}
fRunCount++;
return this->currentRun();
}
void setMinAndMaxScale(SkScalar scaledMin, SkScalar scaledMax) {
// we init fMaxMinScale and fMinMaxScale in the constructor
fMaxMinScale = SkMaxScalar(scaledMin, fMaxMinScale);
fMinMaxScale = SkMinScalar(scaledMax, fMinMaxScale);
}
bool hasPerspective() const {
return fInitialViewMatrix.hasPerspective();
}
static size_t GetVertexStride(GrMaskFormat maskFormat, bool hasWCoord) {
switch (maskFormat) {
case kA8_GrMaskFormat:
@ -239,11 +221,7 @@ public:
size_t size() const { return fSize; }
~GrTextBlob() override {
for (int i = 0; i < fRunCountLimit; i++) {
fRuns[i].~Run();
}
}
~GrTextBlob() override { }
////////////////////////////////////////////////////////////////////////////////////////////////
// Internal test methods
@ -254,7 +232,10 @@ public:
GrTextTarget*);
private:
GrTextBlob(GrStrikeCache* strikeCache) : fStrikeCache{strikeCache} { }
GrTextBlob(GrStrikeCache* strikeCache, GrColor color, bool forceWForDistanceFields)
: fColor{color}
, fStrikeCache{strikeCache}
, fForceWForDistanceFields{forceWForDistanceFields} { }
// This function will only be called when we are generating a blob from scratch. We record the
// initial view matrix and initial offsets(x,y), because we record vertex bounds relative to
@ -267,26 +248,54 @@ private:
}
fInitialX = x;
fInitialY = y;
// make sure all initial subruns have the correct VM and X/Y applied
for (int i = 0; i < fRunCountLimit; i++) {
fRuns[i].fSubRunInfo[0].init(fInitialViewMatrix, x, y);
}
}
public:
// Any glyphs that can't be rendered with the base or override descriptor
// are rendered as paths
struct PathGlyph {
PathGlyph(const SkPath& path, SkPoint origin)
: fPath(path)
, fOrigin(origin) {}
SkPath fPath;
SkPoint fOrigin;
};
enum SubRunType {
kDirectMask,
kTransformedMask,
kTransformedPath,
kTransformedSDFT
};
bool hasW(SubRunType type) const {
if (type == kTransformedSDFT) {
return this->hasPerspective() || fForceWForDistanceFields;
} else if (type == kTransformedMask || type == kTransformedPath) {
return this->hasPerspective();
}
// The viewMatrix is implicitly SkMatrix::I when drawing kDirectMask, because it is not
// used.
return false;
}
// Hold data to draw the different types of sub run. SubRuns are produced knowing all the
// glyphs that are included in them.
class SubRun {
public:
SubRun(Run* run, const SkStrikeSpec& strikeSpec, GrColor color)
: fColor{color}
, fRun{run}
, fStrikeSpec{strikeSpec} {}
// SubRun for masks
SubRun(SubRunType type,
GrTextBlob* textBlob,
const SkStrikeSpec& strikeSpec,
GrMaskFormat format,
const SubRunBufferSpec& bufferSpec,
sk_sp<GrTextStrike>&& grStrike);
// When used with emplace_back, this constructs a SubRun from the last SubRun in an array.
//SubRun(SkSTArray<1, SubRun>* subRunList)
// : fColor{subRunList->fromBack(1).fColor} { }
// SubRun for paths
SubRun(GrTextBlob* textBlob, const SkStrikeSpec& strikeSpec);
void appendGlyph(GrGlyph* glyph, SkRect dstRect);
void appendGlyphs(const SkZip<SkGlyphVariant, SkPoint>& drawables);
// TODO when this object is more internal, drop the privacy
void resetBulkUseToken() { fBulkUseToken.reset(); }
@ -298,29 +307,15 @@ public:
void setAtlasGeneration(uint64_t atlasGeneration) { fAtlasGeneration = atlasGeneration;}
uint64_t atlasGeneration() const { return fAtlasGeneration; }
size_t byteCount() const { return fVertexEndIndex - fVertexStartIndex; }
size_t vertexStartIndex() const { return fVertexStartIndex; }
size_t vertexEndIndex() const { return fVertexEndIndex; }
uint32_t glyphCount() const { return fGlyphEndIndex - fGlyphStartIndex; }
uint32_t glyphStartIndex() const { return fGlyphStartIndex; }
uint32_t glyphEndIndex() const { return fGlyphEndIndex; }
void setColor(GrColor color) { fColor = color; }
GrColor color() const { return fColor; }
void setMaskFormat(GrMaskFormat format) { fMaskFormat = format; }
GrMaskFormat maskFormat() const { return fMaskFormat; }
void setAsSuccessor(const SubRun& prev) {
fGlyphStartIndex = prev.glyphEndIndex();
fGlyphEndIndex = fGlyphStartIndex;
fVertexStartIndex = prev.vertexEndIndex();
fVertexEndIndex = fVertexStartIndex;
// copy over viewmatrix settings
this->init(prev.fCurrentViewMatrix, prev.fX, prev.fY);
}
const SkRect& vertexBounds() const { return fVertexBounds; }
void joinGlyphBounds(const SkRect& glyphBounds) {
fVertexBounds.joinNonEmptyArg(glyphBounds);
@ -336,169 +331,69 @@ public:
void computeTranslation(const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
SkScalar* transX, SkScalar* transY);
bool drawAsDistanceFields() const { return fType == kTransformedSDFT; }
bool drawAsPaths() const { return fType == kTransformedPath; }
bool needsTransform() const {
return fType == kTransformedPath
|| fType == kTransformedMask
|| fType == kTransformedSDFT;
}
bool hasW() const {
return fBlob->hasW(fType);
}
// df properties
void setDrawAsDistanceFields() { fFlags.drawAsSdf = true; }
bool drawAsDistanceFields() const { return fFlags.drawAsSdf; }
void setUseLCDText(bool useLCDText) { fFlags.useLCDText = useLCDText; }
bool hasUseLCDText() const { return fFlags.useLCDText; }
void setAntiAliased(bool antiAliased) { fFlags.antiAliased = antiAliased; }
bool isAntiAliased() const { return fFlags.antiAliased; }
void setHasWCoord(bool hasW) { fFlags.hasWCoord = hasW; }
bool hasWCoord() const { return fFlags.hasWCoord; }
void setNeedsTransform(bool needsTransform) { fFlags.needsTransform = needsTransform; }
bool needsTransform() const { return fFlags.needsTransform; }
const SkStrikeSpec& strikeSpec() const { return fStrikeSpec; }
private:
GrDrawOpAtlas::BulkUseTokenUpdater fBulkUseToken;
const SubRunType fType;
GrTextBlob* const fBlob;
const GrMaskFormat fMaskFormat;
const uint32_t fGlyphStartIndex;
const uint32_t fGlyphEndIndex;
const size_t fVertexStartIndex;
const size_t fVertexEndIndex;
const SkStrikeSpec fStrikeSpec;
sk_sp<GrTextStrike> fStrike;
SkMatrix fCurrentViewMatrix;
SkRect fVertexBounds = SkRectPriv::MakeLargestInverted();
uint64_t fAtlasGeneration{GrDrawOpAtlas::kInvalidAtlasGeneration};
size_t fVertexStartIndex{0};
size_t fVertexEndIndex{0};
uint32_t fGlyphStartIndex{0};
uint32_t fGlyphEndIndex{0};
SkScalar fX;
SkScalar fY;
GrColor fColor{GrColor_ILLEGAL};
GrMaskFormat fMaskFormat{kA8_GrMaskFormat};
struct {
bool drawAsSdf:1;
bool useLCDText:1;
bool antiAliased:1;
bool hasWCoord:1;
bool needsTransform:1;
} fFlags{false, false, false, false, false};
Run* const fRun;
const SkStrikeSpec& fStrikeSpec;
}; // SubRunInfo
/*
* 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 GrTextContext to
* handle, we have a bit to mark the run as flushable via rendering as paths or as scaled
* glyphs. 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, it's not clear if this is a win because we'd still have to either go to 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 {
explicit Run(GrTextBlob* blob, GrColor color)
: fBlob{blob}, fColor{color} {
// To ensure we always have one subrun, we push back a fresh run here
fSubRunInfo.emplace_back(this, fStrikeSpec, color);
}
// sets the last subrun of runIndex to use w values
void setSubRunHasW(bool hasWCoord) {
SubRun& subRun = this->fSubRunInfo.back();
subRun.setHasWCoord(hasWCoord);
}
// inits the override descriptor on the current run. All following subruns must use this
// descriptor
SubRun* initARGBFallback() {
fFallbackStrikeSpec.reset(new SkStrikeSpec{});
// Push back a new subrun to fill and set the override descriptor
SubRun* subRun = this->pushBackSubRun(*fFallbackStrikeSpec, fColor);
subRun->setMaskFormat(kARGB_GrMaskFormat);
return subRun;
}
// Appends a glyph to the blob as a path only.
void appendPathGlyph(const SkPath& path, SkPoint position, SkScalar scale);
// Append a glyph to the sub run taking care to switch the glyph if needed.
void switchSubRunIfNeededAndAppendGlyph(GrGlyph* glyph,
const sk_sp<GrTextStrike>& strike,
const SkRect& destRect,
bool needsTransform);
// Used when the glyph in the cache has the CTM already applied, therefore no transform
// is needed during rendering.
void appendDeviceSpaceGlyph(const sk_sp<GrTextStrike>& strike,
const SkGlyph& skGlyph,
SkPoint origin);
// The glyph is oriented upright in the cache and needs to be transformed onto the screen.
void appendSourceSpaceGlyph(const sk_sp<GrTextStrike>& strike,
const SkGlyph& skGlyph,
SkPoint origin,
SkScalar textScale);
void setupFont(const SkStrikeSpec& strikeSpec);
void setRunFontAntiAlias(bool aa) {
fAntiAlias = aa;
}
// sets the last subrun of runIndex to use distance field text
void setSubRunHasDistanceFields(bool hasLCD, bool isAntiAlias, bool hasWCoord) {
SubRun& subRun = fSubRunInfo.back();
subRun.setUseLCDText(hasLCD);
subRun.setAntiAliased(isAntiAlias);
subRun.setDrawAsDistanceFields();
subRun.setHasWCoord(hasWCoord);
}
SubRun* pushBackSubRun(const SkStrikeSpec& desc, GrColor color) {
// Forward glyph / vertex information to seed the new sub run
SubRun& newSubRun = fSubRunInfo.emplace_back(this, desc, color);
const SubRun& prevSubRun = fSubRunInfo.fromBack(1);
// Forward glyph / vertex information to seed the new sub run
newSubRun.setAsSuccessor(prevSubRun);
return &newSubRun;
}
// Any glyphs that can't be rendered with the base or override descriptor
// are rendered as paths
struct PathGlyph {
PathGlyph(const SkPath& path, SkScalar x, SkScalar y, SkScalar scale)
: fPath(path)
, fX(x)
, fY(y)
, fScale(scale) { }
SkPath fPath;
SkScalar fX;
SkScalar fY;
SkScalar fScale;
};
SkSTArray<1, SubRun> fSubRunInfo;
SkStrikeSpec fStrikeSpec;
// Distance field text cannot draw coloremoji, and so has to fall back. However,
// though the distance field text and the coloremoji may share the same run, they
// will have different descriptors. If fFallbackStrikeSpec is non-nullptr, then it
// will be used in place of the run's descriptor to regen texture coords
std::unique_ptr<SkStrikeSpec> fFallbackStrikeSpec;
SkTArray<PathGlyph> fPathGlyphs;
bool fAntiAlias{false}; // needed mainly for rendering paths
bool fInitialized{false};
GrTextBlob* const fBlob;
} fFlags{false, false};
GrColor fColor;
}; // Run
GrDrawOpAtlas::BulkUseTokenUpdater fBulkUseToken;
SkRect fVertexBounds = SkRectPriv::MakeLargestInverted();
uint64_t fAtlasGeneration{GrDrawOpAtlas::kInvalidAtlasGeneration};
SkScalar fX;
SkScalar fY;
SkMatrix fCurrentViewMatrix;
std::vector<PathGlyph> fPaths;
}; // SubRun
SubRun* makeSubRun(SubRunType type,
const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkStrikeSpec& strikeSpec,
GrMaskFormat format);
void addSingleMaskFormat(
SubRunType type,
const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkStrikeSpec& strikeSpec,
GrMaskFormat format);
void addMultiMaskFormat(
SubRunType type,
const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkStrikeSpec& strikeSpec);
void addSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkStrikeSpec& strikeSpec,
const SkFont& runFont,
SkScalar minScale,
SkScalar maxScale);
private:
std::unique_ptr<GrAtlasTextOp> makeOp(
@ -507,52 +402,49 @@ private:
const SkPaint& paint, const SkPMColor4f& filteredColor, const SkSurfaceProps&,
const GrDistanceFieldAdjustTable*, GrTextTarget*);
// currentRun, startRun, and the process* calls are all used by the SkGlyphRunPainter, and
// live in SkGlyphRunPainter.cpp file.
Run* currentRun();
void startRun(const SkGlyphRun& glyphRun, bool useSDFT) override;
void processDeviceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkStrikeSpec& strikeSpec) override;
void processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkFont& runFont,
const SkStrikeSpec& strikeSpec) override;
void processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkStrikeSpec& strikeSpec,
const SkFont& runFont,
SkScalar minScale,
SkScalar maxScale,
bool hasWCoord) override;
SkScalar maxScale) override;
void processSourceFallback(const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkStrikeSpec& strikeSpec,
bool hasW) override;
void processSourceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkStrikeSpec& strikeSpec) override;
void processDeviceFallback(const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkStrikeSpec& strikeSpec) override;
// Pool of bytes for vertex data.
char* fVertices;
// How much (in bytes) of the vertex data is used while accumulating SubRuns.
size_t fVerticesCursor{0};
// Pointers to every glyph that will be drawn.
GrGlyph** fGlyphs;
// Number of glyphs stored in fGlyphs while accumulating SubRuns.
uint32_t fGlyphsCursor{0};
// Assume one run per text blob.
SkSTArray<1, SubRun> fSubRuns;
// The color of the text to draw for solid colors.
const GrColor fColor;
// Lifetime: The GrStrikeCache is owned by and has the same lifetime as the GrRecordingContext.
// The GrRecordingContext also owns the GrTextBlob cache which owns this GrTextBlob.
GrStrikeCache* const fStrikeCache;
SkMaskFilterBase::BlurRec fBlurRec;
struct StrokeInfo {
SkScalar fFrameWidth;
SkScalar fMiterLimit;
SkPaint::Join fJoin;
};
enum TextType {
kHasDistanceField_TextType = 0x1,
kHasBitmap_TextType = 0x2,
};
// all glyph / vertex offsets are into these pools.
char* fVertices;
GrGlyph** fGlyphs;
Run* fRuns;
// Lifetime: The GrStrikeCache is owned by and has the same lifetime as the GrRecordingContext.
// The GrRecordingContext also owns the GrTextBlob cache which owns this GrTextBlob.
GrStrikeCache* const fStrikeCache;
SkMaskFilterBase::BlurRec fBlurRec;
StrokeInfo fStrokeInfo;
Key fKey;
SkMatrix fInitialViewMatrix;
@ -567,8 +459,14 @@ private:
// maximum minimum scale, and minimum maximum scale, we can support before we need to regen
SkScalar fMaxMinScale{-SK_ScalarMax};
SkScalar fMinMaxScale{SK_ScalarMax};
int fRunCount{0};
int fRunCountLimit;
// From the distance field options to force distance fields to have a W coordinate.
const bool fForceWForDistanceFields;
enum TextType {
kHasDistanceField_TextType = 0x1,
kHasBitmap_TextType = 0x2,
};
uint8_t fTextType{0};
};

View File

@ -34,19 +34,20 @@ public:
~GrTextBlobCache();
sk_sp<GrTextBlob> makeBlob(const SkGlyphRunList& glyphRunList,
bool forceW,
GrColor color,
GrStrikeCache* strikeCache) {
return GrTextBlob::Make(
glyphRunList.totalGlyphCount(), glyphRunList.size(), color, strikeCache);
return GrTextBlob::Make(glyphRunList.totalGlyphCount(), forceW, color, strikeCache);
}
sk_sp<GrTextBlob> makeCachedBlob(const SkGlyphRunList& glyphRunList,
const GrTextBlob::Key& key,
const SkMaskFilterBase::BlurRec& blurRec,
const SkPaint& paint,
bool forceW,
GrColor color,
GrStrikeCache* strikeCache) {
sk_sp<GrTextBlob> cacheBlob(makeBlob(glyphRunList, color, strikeCache));
sk_sp<GrTextBlob> cacheBlob(makeBlob(glyphRunList, forceW, color, strikeCache));
cacheBlob->setupKey(key, blurRec, paint);
this->add(cacheBlob);
glyphRunList.temporaryShuntBlobNotifyAddedToCache(fUniqueID);

View File

@ -190,7 +190,7 @@ bool GrTextBlob::VertexRegenerator::doRegen(GrTextBlob::VertexRegenerator::Resul
}
sk_sp<GrTextStrike> grStrike = fSubRun->refStrike();
bool hasW = fSubRun->hasWCoord();
bool hasW = fSubRun->hasW();
auto vertexStride = GetVertexStride(fSubRun->maskFormat(), hasW);
char* currVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
fCurrGlyph * kVerticesPerGlyph * vertexStride;
@ -269,7 +269,7 @@ bool GrTextBlob::VertexRegenerator::regenerate(GrTextBlob::VertexRegenerator::Re
fRegenFlags & kRegenTex,
fRegenFlags & kRegenGlyph);
} else {
bool hasW = fSubRun->hasWCoord();
bool hasW = fSubRun->hasW();
auto vertexStride = GetVertexStride(fSubRun->maskFormat(), hasW);
result->fFinished = true;
result->fGlyphsRegenerated = fSubRun->glyphCount() - fCurrGlyph;