One routine to rule them all!

Change-Id: I6c448f325fca21c4919c0a0854d9d5cb9d54beb0
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/198661
Commit-Queue: Herb Derby <herb@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
This commit is contained in:
Herb Derby 2019-03-06 14:47:11 -05:00 committed by Skia Commit-Bot
parent ca9b2082d1
commit 4eff3f934d
4 changed files with 242 additions and 337 deletions

View File

@ -380,134 +380,27 @@ void SkGlyphRunListPainter::processARGBFallback(SkScalar maxGlyphDimension,
}
}
// Beware! The following code will end up holding two glyph caches at the same time, but they
// will not be the same cache (which would cause two separate caches to be created).
void SkGlyphRunListPainter::drawGlyphRunAsPathWithARGBFallback(
const SkPaint& runPaint, const SkFont& runFont,
const SkGlyphRun& glyphRun, SkPoint origin, const SkMatrix& viewMatrix,
SkGlyphRunPainterInterface* process) {
fARGBGlyphsIDs.clear();
fARGBPositions.clear();
ScopedBuffers _ = this->ensureBuffers(glyphRun);
SkScalar maxFallbackDimension{-SK_ScalarInfinity};
// setup our std runPaint, in hopes of getting hits in the cache
SkPaint pathPaint{runPaint};
SkFont pathFont{runFont};
// The factor to get from the size stored in the strike to the size needed for the source.
SkScalar strikeToSourceRatio = pathFont.setupForAsPaths(&pathPaint);
SkAutoDescriptor ad;
SkScalerContextEffects effects;
SkScalerContext::CreateDescriptorAndEffectsUsingPaint(
pathFont, pathPaint, fDeviceProps, fScalerContextFlags, SkMatrix::I(), &ad, &effects);
SkScopedStrike strike =
fStrikeCache->findOrCreateScopedStrike(
*ad.getDesc(), effects,*pathFont.getTypefaceOrDefault());
int glyphCount = 0;
const SkPoint* positionCursor = glyphRun.positions().data();
for (auto glyphID : glyphRun.glyphsIDs()) {
SkPoint glyphPos = origin + *positionCursor++;
// Use outline from {0, 0} because all transforms including subpixel translation happen
// during drawing.
const SkGlyph& glyph = strike->getGlyphMetrics(glyphID, {0, 0});
if (!glyph.isEmpty()) {
if (glyph.fMaskFormat != SkMask::kARGB32_Format) {
if (strike->decideCouldDrawFromPath(glyph)) {
fGlyphPos[glyphCount++] = {&glyph, glyphPos};
}
} else {
SkScalar largestDimension = std::max(glyph.fWidth, glyph.fHeight);
maxFallbackDimension = std::max(maxFallbackDimension, largestDimension);
fARGBGlyphsIDs.push_back(glyphID);
fARGBPositions.push_back(glyphPos);
}
}
}
if (process) {
if (glyphCount > 0) {
process->processSourcePaths(SkSpan<const GlyphAndPos>{fGlyphPos, SkTo<size_t>
(glyphCount)},
strike.get(),
strikeToSourceRatio);
}
// fGlyphPos will be reused here.
if (!fARGBGlyphsIDs.empty()) {
this->processARGBFallback(
maxFallbackDimension, runPaint, glyphRun.font(), viewMatrix,
strikeToSourceRatio,
process);
}
}
}
void SkGlyphRunListPainter::drawGlyphRunAsBMPWithPathFallback(
const SkPaint& paint, const SkFont& font,
const SkGlyphRun& glyphRun, SkPoint origin, const SkMatrix& deviceMatrix,
SkGlyphRunPainterInterface* process) {
SkAutoDescriptor ad;
SkScalerContextEffects effects;
SkScalerContext::CreateDescriptorAndEffectsUsingPaint(
font, paint, fDeviceProps, fScalerContextFlags, deviceMatrix, &ad, &effects);
SkTypeface* typeface = font.getTypefaceOrDefault();
SkScopedStrike strike =
fStrikeCache->findOrCreateScopedStrike(*ad.getDesc(), effects, *typeface);
ScopedBuffers _ = this->ensureBuffers(glyphRun);
SkMatrix mapping = deviceMatrix;
mapping.preTranslate(origin.x(), origin.y());
SkVector rounding = strike->rounding();
mapping.postTranslate(rounding.x(), rounding.y());
mapping.mapPoints(fPositions, glyphRun.positions().data(), glyphRun.runSize());
int glyphCount = 0;
const SkPoint* posCursor = fPositions;
for (auto glyphID : glyphRun.glyphsIDs()) {
SkPoint mappedPt = *posCursor++;
if (SkScalarsAreFinite(mappedPt.x(), mappedPt.y())) {
const SkGlyph& glyph = strike->getGlyphMetrics(glyphID, mappedPt);
if (!glyph.isEmpty()) {
if (SkStrikeCommon::GlyphTooBigForAtlas(glyph)) {
if (strike->decideCouldDrawFromPath(glyph)) {
fPaths.push_back({&glyph, mappedPt});
}
} else {
// If the glyph is not empty, then it will have a pointer to mask data.
fGlyphPos[glyphCount++] = {&glyph, mappedPt};
}
}
}
}
if (process) {
if (glyphCount > 0) {
mapping.mapPoints(fPositions, glyphCount);
process->processDeviceMasks(
SkSpan<const GlyphAndPos>{fGlyphPos, SkTo<size_t>(glyphCount)}, strike.get());
}
if (!fPaths.empty()) {
process->processDevicePaths(SkSpan<const GlyphAndPos>{fPaths});
}
}
}
#if SK_SUPPORT_GPU
void SkGlyphRunListPainter::drawGlyphRunAsSDFWithARGBFallback(
const SkPaint& runPaint, const SkFont& runFont,
const SkGlyphRun& glyphRun, SkPoint origin, const SkMatrix& viewMatrix,
void SkGlyphRunListPainter::processGlyphRunList(const SkGlyphRunList& glyphRunList,
const SkMatrix& viewMatrix,
const SkSurfaceProps& props,
bool contextSupportsDistanceFieldText,
const GrTextContext::Options& options,
SkGlyphRunPainterInterface* process) {
SkPoint origin = glyphRunList.origin();
const SkPaint& runPaint = glyphRunList.paint();
for (const auto& glyphRun : glyphRunList) {
const SkFont& runFont = glyphRun.font();
bool useSDFT = GrTextContext::CanDrawAsDistanceFields(
runPaint, runFont, viewMatrix, props, contextSupportsDistanceFieldText, options);
if (process) {
process->startRun(glyphRun, useSDFT);
}
if (useSDFT) {
fARGBGlyphsIDs.clear();
fARGBPositions.clear();
ScopedBuffers _ = this->ensureBuffers(glyphRun);
@ -515,9 +408,9 @@ void SkGlyphRunListPainter::drawGlyphRunAsSDFWithARGBFallback(
// Setup distance field runPaint and text ratio
SkPaint dfPaint = GrTextContext::InitDistanceFieldPaint(runPaint);
SkScalar textScale;
SkScalar cacheToSourceScale;
SkFont dfFont = GrTextContext::InitDistanceFieldFont(
runFont, viewMatrix, options, &textScale);
runFont, viewMatrix, options, &cacheToSourceScale);
// Fake-gamma and subpixel antialiasing are applied in the shader, so we ignore the
// passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
SkScalerContextFlags flags = SkScalerContextFlags::kNone;
@ -567,28 +460,155 @@ void SkGlyphRunListPainter::drawGlyphRunAsSDFWithARGBFallback(
|| options.fDistanceFieldVerticesAlwaysHaveW;
process->processSourceSDFT(
SkSpan<const GlyphAndPos>{fGlyphPos, SkTo<size_t>(glyphCount)},
strike.get(), runFont, textScale, minScale, maxScale, hasWCoord);
strike.get(),
runFont,
cacheToSourceScale,
minScale,
maxScale,
hasWCoord);
}
if (!paths.empty()) {
process->processSourcePaths(
SkSpan<const GlyphAndPos>{paths}, strike.get(), textScale);
SkSpan<const GlyphAndPos>{paths}, strike.get(), cacheToSourceScale);
}
{
// fGlyphPos will be reused here.
if (!fARGBGlyphsIDs.empty()) {
this->processARGBFallback(
maxFallbackDimension, runPaint, glyphRun.font(), viewMatrix, textScale,
maxFallbackDimension, runPaint, glyphRun.font(), viewMatrix,
cacheToSourceScale,
process);
}
}
}
}
#endif
} else if (SkGlyphRunListPainter::ShouldDrawAsPath(runPaint, runFont, viewMatrix)) {
SkGlyphRunListPainter::ScopedBuffers
SkGlyphRunListPainter::ensureBuffers(const SkGlyphRunList& glyphRunList) {
fARGBGlyphsIDs.clear();
fARGBPositions.clear();
ScopedBuffers _ = this->ensureBuffers(glyphRun);
SkScalar maxFallbackDimension{-SK_ScalarInfinity};
// setup our std runPaint, in hopes of getting hits in the cache
SkPaint pathPaint{runPaint};
SkFont pathFont{runFont};
// The factor to get from the size stored in the strike to the size needed for
// the source.
SkScalar strikeToSourceRatio = pathFont.setupForAsPaths(&pathPaint);
SkAutoDescriptor ad;
SkScalerContextEffects effects;
SkScalerContext::CreateDescriptorAndEffectsUsingPaint(pathFont,
pathPaint,
fDeviceProps,
fScalerContextFlags,
SkMatrix::I(),
&ad,
&effects);
SkScopedStrike strike =
fStrikeCache->findOrCreateScopedStrike(
*ad.getDesc(), effects,*pathFont.getTypefaceOrDefault());
int glyphCount = 0;
const SkPoint* positionCursor = glyphRun.positions().data();
for (auto glyphID : glyphRun.glyphsIDs()) {
SkPoint glyphPos = origin + *positionCursor++;
// Use outline from {0, 0} because all transforms including subpixel translation
// happen during drawing.
const SkGlyph& glyph = strike->getGlyphMetrics(glyphID, {0, 0});
if (!glyph.isEmpty()) {
if (glyph.fMaskFormat != SkMask::kARGB32_Format) {
if (strike->decideCouldDrawFromPath(glyph)) {
fGlyphPos[glyphCount++] = {&glyph, glyphPos};
}
} else {
SkScalar largestDimension = std::max(glyph.fWidth, glyph.fHeight);
maxFallbackDimension = std::max(maxFallbackDimension, largestDimension);
fARGBGlyphsIDs.push_back(glyphID);
fARGBPositions.push_back(glyphPos);
}
}
}
if (process) {
if (glyphCount > 0) {
process->processSourcePaths(
SkSpan<const GlyphAndPos>{fGlyphPos, SkTo<size_t>(glyphCount)},
strike.get(),
strikeToSourceRatio);
}
// fGlyphPos will be reused here.
if (!fARGBGlyphsIDs.empty()) {
this->processARGBFallback(
maxFallbackDimension, runPaint, glyphRun.font(), viewMatrix,
strikeToSourceRatio,
process);
}
}
} else {
SkAutoDescriptor ad;
SkScalerContextEffects effects;
SkScalerContext::CreateDescriptorAndEffectsUsingPaint(
runFont, runPaint, fDeviceProps, fScalerContextFlags, viewMatrix, &ad,
&effects);
SkTypeface* typeface = runFont.getTypefaceOrDefault();
SkScopedStrike strike =
fStrikeCache->findOrCreateScopedStrike(*ad.getDesc(), effects, *typeface);
ScopedBuffers _ = this->ensureBuffers(glyphRun);
SkMatrix mapping = viewMatrix;
mapping.preTranslate(origin.x(), origin.y());
SkVector rounding = strike->rounding();
mapping.postTranslate(rounding.x(), rounding.y());
mapping.mapPoints(fPositions, glyphRun.positions().data(), glyphRun.runSize());
int glyphCount = 0;
const SkPoint* posCursor = fPositions;
for (auto glyphID : glyphRun.glyphsIDs()) {
SkPoint mappedPt = *posCursor++;
if (SkScalarsAreFinite(mappedPt.x(), mappedPt.y())) {
const SkGlyph& glyph = strike->getGlyphMetrics(glyphID, mappedPt);
if (!glyph.isEmpty()) {
if (SkStrikeCommon::GlyphTooBigForAtlas(glyph)) {
if (strike->decideCouldDrawFromPath(glyph)) {
fPaths.push_back({&glyph, mappedPt});
}
} else {
// If the glyph is not empty, then it will have a pointer to mask data.
fGlyphPos[glyphCount++] = {&glyph, mappedPt};
}
}
}
}
if (process) {
if (glyphCount > 0) {
mapping.mapPoints(fPositions, glyphCount);
process->processDeviceMasks(
SkSpan<const GlyphAndPos>{fGlyphPos, SkTo<size_t>(glyphCount)},
strike.get());
}
if (!fPaths.empty()) {
process->processDevicePaths(SkSpan<const GlyphAndPos>{fPaths});
}
}
}
}
}
#endif // SK_SUPPORT_GPU
auto SkGlyphRunListPainter::ensureBuffers(const SkGlyphRunList& glyphRunList) -> ScopedBuffers {
size_t size = 0;
for (const SkGlyphRun& run : glyphRunList) {
size = std::max(run.runSize(), size);
@ -811,38 +831,12 @@ void GrTextBlob::generateFromGlyphRunList(const GrShaderCaps& shaderCaps,
this->initReusableBlob(SkPaintPriv::ComputeLuminanceColor(runPaint), viewMatrix,
origin.x(), origin.y());
for (const auto& glyphRun : glyphRunList) {
const SkFont& runFont = glyphRun.font();
bool useSDFT = GrTextContext::CanDrawAsDistanceFields(
runPaint, runFont, viewMatrix, props, shaderCaps.supportsDistanceFieldText(),
options);
this->startRun(glyphRun, useSDFT);
if (useSDFT) {
glyphPainter->drawGlyphRunAsSDFWithARGBFallback(
runPaint, glyphRun.font(),
glyphRun, origin, viewMatrix,
glyphPainter->processGlyphRunList(glyphRunList,
viewMatrix,
props,
shaderCaps.supportsDistanceFieldText(),
options,
this);
} else if (SkGlyphRunListPainter::ShouldDrawAsPath(runPaint, runFont, viewMatrix)) {
// The glyphs are big, so use paths to draw them.
glyphPainter->drawGlyphRunAsPathWithARGBFallback(
runPaint, runFont,
glyphRun, origin, viewMatrix,
this);
} else {
glyphPainter->drawGlyphRunAsBMPWithPathFallback(
runPaint, runFont,
glyphRun, origin, viewMatrix,
this);
}
}
}
GrTextBlob::Run* GrTextBlob::currentRun() {
@ -1002,75 +996,6 @@ std::unique_ptr<GrDrawOp> GrTextContext::createOp_TestingOnly(GrRecordingContext
#endif // GR_TEST_UTILS
#endif // SK_SUPPORT_GPU
// -- SkTextBlobCacheDiffCanvas::TrackLayerDevice --------------------------------------------------
void SkTextBlobCacheDiffCanvas::TrackLayerDevice::processGlyphRun(
const SkPoint& origin, const SkGlyphRun& glyphRun, const SkPaint& runPaint) {
TRACE_EVENT0("skia", "SkTextBlobCacheDiffCanvas::processGlyphRun");
const SkMatrix& runMatrix = this->ctm();
// If the matrix has perspective, we fall back to using distance field text or paths.
#if SK_SUPPORT_GPU
if (this->maybeProcessGlyphRunForDFT(glyphRun, runMatrix, origin, runPaint)) {
return;
} else
#endif
if (SkGlyphRunListPainter::ShouldDrawAsPath(runPaint, glyphRun.font(), runMatrix)) {
this->processGlyphRunForPaths(glyphRun, runMatrix, origin, runPaint);
} else {
this->processGlyphRunForMask(glyphRun, runMatrix, origin, runPaint);
}
}
void SkTextBlobCacheDiffCanvas::TrackLayerDevice::processGlyphRunForMask(
const SkGlyphRun& glyphRun, const SkMatrix& runMatrix,
SkPoint origin, const SkPaint& runPaint) {
TRACE_EVENT0("skia", "SkTextBlobCacheDiffCanvas::processGlyphRunForMask");
fPainter.drawGlyphRunAsBMPWithPathFallback(
runPaint, glyphRun.font(), glyphRun, origin, runMatrix, nullptr);
}
SkScalar SkTextBlobCacheDiffCanvas::SetupForPath(SkPaint* paint, SkFont* font) {
return font->setupForAsPaths(paint);
}
void SkTextBlobCacheDiffCanvas::TrackLayerDevice::processGlyphRunForPaths(
const SkGlyphRun& glyphRun, const SkMatrix& runMatrix,
SkPoint origin, const SkPaint& runPaint) {
TRACE_EVENT0("skia", "SkTextBlobCacheDiffCanvas::processGlyphRunForPaths");
fPainter.drawGlyphRunAsPathWithARGBFallback(
runPaint, glyphRun.font(), glyphRun, origin, runMatrix, nullptr);
}
#if SK_SUPPORT_GPU
bool SkTextBlobCacheDiffCanvas::TrackLayerDevice::maybeProcessGlyphRunForDFT(
const SkGlyphRun& glyphRun, const SkMatrix& runMatrix,
SkPoint origin, const SkPaint& runPaint) {
TRACE_EVENT0("skia", "SkTextBlobCacheDiffCanvas::maybeProcessGlyphRunForDFT");
const SkFont& runFont = glyphRun.font();
GrTextContext::Options options;
options.fMinDistanceFieldFontSize = fSettings.fMinDistanceFieldFontSize;
options.fMaxDistanceFieldFontSize = fSettings.fMaxDistanceFieldFontSize;
GrTextContext::SanitizeOptions(&options);
if (!GrTextContext::CanDrawAsDistanceFields(runPaint, runFont,
runMatrix, this->surfaceProps(),
fSettings.fContextSupportsDistanceFieldText,
options)) {
return false;
}
fPainter.drawGlyphRunAsSDFWithARGBFallback(
runPaint, glyphRun.font(), glyphRun, origin, runMatrix, options, nullptr);
return true;
}
#endif
SkGlyphRunListPainter::ScopedBuffers::ScopedBuffers(SkGlyphRunListPainter* painter, int size)
: fPainter{painter} {
SkASSERT(size >= 0);

View File

@ -76,31 +76,16 @@ public:
const SkGlyphRunList& glyphRunList, const SkMatrix& deviceMatrix,
const BitmapDevicePainter* bitmapDevice);
// In the drawGlyphRunAs... routines a nullptr for process means that the calls to the cache
// will be performed, but none of the callbacks will be called.
void drawGlyphRunAsBMPWithPathFallback(
const SkPaint& paint, const SkFont& font,
const SkGlyphRun& glyphRun, SkPoint origin, const SkMatrix& deviceMatrix,
SkGlyphRunPainterInterface* process);
// Draw glyphs as paths with fallback to scaled ARGB glyphs if color is needed.
// PerPath - perPath(const SkGlyph&, SkPoint position)
// FallbackARGB - fallbackARGB(SkSpan<const SkGlyphID>, SkSpan<const SkPoint>)
// For each glyph that is not ARGB call perPath. If the glyph is ARGB then store the glyphID
// and the position in fallback vectors. After all the glyphs are processed, pass the
// fallback glyphIDs and positions to fallbackARGB.
void drawGlyphRunAsPathWithARGBFallback(
const SkPaint& runPaint, const SkFont& runFont,
const SkGlyphRun& glyphRun, SkPoint origin, const SkMatrix& viewMatrix,
SkGlyphRunPainterInterface* process);
#if SK_SUPPORT_GPU
void drawGlyphRunAsSDFWithARGBFallback(
const SkPaint& runPaint, const SkFont& runFont,
const SkGlyphRun& glyphRun, SkPoint origin, const SkMatrix& viewMatrix,
// A nullptr for process means that the calls to the cache will be performed, but none of the
// callbacks will be called.
void processGlyphRunList(const SkGlyphRunList& glyphRunList,
const SkMatrix& viewMatrix,
const SkSurfaceProps& props,
bool contextSupportsDistanceFieldText,
const GrTextContext::Options& options,
SkGlyphRunPainterInterface* process);
#endif
#endif // SK_SUPPORT_GPU
// TODO: Make this the canonical check for Skia.
static bool ShouldDrawAsPath(const SkPaint& paint, const SkFont& font, const SkMatrix& matrix);

View File

@ -210,9 +210,21 @@ SkBaseDevice* SkTextBlobCacheDiffCanvas::TrackLayerDevice::onCreateDevice(
void SkTextBlobCacheDiffCanvas::TrackLayerDevice::drawGlyphRunList(
const SkGlyphRunList& glyphRunList) {
for (auto& glyphRun : glyphRunList) {
this->processGlyphRun(glyphRunList.origin(), glyphRun, glyphRunList.paint());
}
#if SK_SUPPORT_GPU
GrTextContext::Options options;
options.fMinDistanceFieldFontSize = fSettings.fMinDistanceFieldFontSize;
options.fMaxDistanceFieldFontSize = fSettings.fMaxDistanceFieldFontSize;
GrTextContext::SanitizeOptions(&options);
fPainter.processGlyphRunList(glyphRunList,
this->ctm(),
this->surfaceProps(),
fSettings.fContextSupportsDistanceFieldText,
options,
nullptr);
#endif // SK_SUPPORT_GPU
}
// -- SkTextBlobCacheDiffCanvas -------------------------------------------------------------------

View File

@ -110,23 +110,6 @@ protected:
void drawGlyphRunList(const SkGlyphRunList& glyphRunList) override;
private:
void processGlyphRun(
const SkPoint& origin, const SkGlyphRun& glyphRun, const SkPaint& runPaint);
void processGlyphRunForMask(
const SkGlyphRun& glyphRun, const SkMatrix& runMatrix,
SkPoint origin, const SkPaint& paint);
void processGlyphRunForPaths(
const SkGlyphRun& glyphRun, const SkMatrix& runMatrix,
SkPoint origin, const SkPaint& paint);
#if SK_SUPPORT_GPU
bool maybeProcessGlyphRunForDFT(
const SkGlyphRun& glyphRun, const SkMatrix& runMatrix,
SkPoint origin, const SkPaint& paint);
#endif
SkStrikeServer* const fStrikeServer;
const SkTextBlobCacheDiffCanvas::Settings fSettings;
SkGlyphRunListPainter fPainter;