diff --git a/src/core/SkGlyph.h b/src/core/SkGlyph.h index 5ab53e7f2a..d52787196c 100644 --- a/src/core/SkGlyph.h +++ b/src/core/SkGlyph.h @@ -162,7 +162,7 @@ public: void initWithGlyphID(SkPackedGlyphID glyph_id); - bool isEmpty() { + bool isEmpty() const { return fWidth == 0 || fHeight == 0; } diff --git a/src/core/SkGlyphRun.cpp b/src/core/SkGlyphRun.cpp index 7bb452a449..f645c6a343 100644 --- a/src/core/SkGlyphRun.cpp +++ b/src/core/SkGlyphRun.cpp @@ -17,6 +17,7 @@ #endif #include "SkDevice.h" +#include "SkDistanceFieldGen.h" #include "SkDraw.h" #include "SkFindAndPlaceGlyph.h" #include "SkGlyphCache.h" @@ -137,6 +138,76 @@ SkGlyphRunListDrawer::SkGlyphRunListDrawer(const GrRenderTargetContext& rtc) : SkGlyphRunListDrawer{rtc.surfaceProps(), rtc.colorSpaceInfo()} {} #endif +// TODO: all this logic should move to the glyph cache. +static const SkGlyph& lookup_glyph_by_subpixel( + SkAxisAlignment axisAlignment, SkPoint position, SkGlyphID glyphID, SkGlyphCache* cache) { + SkFixed lookupX = SkScalarToFixed(SkScalarFraction(position.x())), + lookupY = SkScalarToFixed(SkScalarFraction(position.y())); + + // Snap to a given axis if alignment is requested. + if (axisAlignment == kX_SkAxisAlignment) { + lookupY = 0; + } else if (axisAlignment == kY_SkAxisAlignment) { + lookupX = 0; + } + + return cache->getGlyphIDMetrics(glyphID, lookupX, lookupY); +} + + +// forEachMappedDrawableGlyph handles positioning for mask type glyph handling for both sub-pixel +// and full pixel positioning. +template +void SkGlyphRunListDrawer::forEachMappedDrawableGlyph( + const SkGlyphRun& glyphRun, SkPoint origin, const SkMatrix& deviceMatrix, + SkGlyphCache* cache, EachGlyph eachGlyph) { + bool isSubpixel = cache->isSubpixel(); + + SkAxisAlignment axisAlignment = kNone_SkAxisAlignment; + SkMatrix mapping = deviceMatrix; + mapping.preTranslate(origin.x(), origin.y()); + // TODO: all this logic should move to the glyph cache. + if (isSubpixel) { + axisAlignment = cache->getScalerContext()->computeAxisAlignmentForHText(); + SkPoint rounding = SkFindAndPlaceGlyph::SubpixelPositionRounding(axisAlignment); + mapping.postTranslate(rounding.x(), rounding.y()); + } else { + mapping.postTranslate(SK_ScalarHalf, SK_ScalarHalf); + } + + auto runSize = glyphRun.runSize(); + if (this->ensureBitmapBuffers(runSize)) { + mapping.mapPoints(fPositions, glyphRun.positions().data(), runSize); + const SkPoint* mappedPtCursor = fPositions; + const SkPoint* ptCursor = glyphRun.positions().data(); + for (auto glyphID : glyphRun.shuntGlyphsIDs()) { + auto mappedPt = *mappedPtCursor++; + auto pt = origin + *ptCursor++; + if (SkScalarsAreFinite(mappedPt.x(), mappedPt.y())) { + // TODO: all this logic should move to the glyph cache. + const SkGlyph& glyph = + isSubpixel ? lookup_glyph_by_subpixel(axisAlignment, mappedPt, glyphID, cache) + : cache->getGlyphIDMetrics(glyphID); + if (!glyph.isEmpty()) { + // Prevent glyphs from being drawn outside of or straddling the edge + // of device space. Comparisons written a little weirdly so that NaN + // coordinates are treated safely. + auto le = [](float a, int b) { return a <= (float)b; }; + auto ge = [](float a, int b) { return a >= (float)b; }; + if (le(mappedPt.fX, INT_MAX - (INT16_MAX + SkTo(UINT16_MAX))) && + ge(mappedPt.fX, INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/)) && + le(mappedPt.fY, INT_MAX - (INT16_MAX + SkTo(UINT16_MAX))) && + ge(mappedPt.fY, INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/))) + { + eachGlyph(glyph, pt, mappedPt); + } + } + } + } + } +} + + bool SkGlyphRunListDrawer::ShouldDrawAsPath(const SkPaint& paint, const SkMatrix& matrix) { // hairline glyphs are fast enough so we don't need to cache them if (SkPaint::kStroke_Style == paint.getStyle() && 0 == paint.getStrokeWidth()) { @@ -218,6 +289,10 @@ static bool prepare_mask( return true; } +static bool glyph_too_big_for_atlas(const SkGlyph& glyph) { + return glyph.fWidth >= 256 || glyph.fHeight >= 256; +} + void SkGlyphRunListDrawer::drawGlyphRunAsSubpixelMask( SkGlyphCache* cache, const SkGlyphRun& glyphRun, SkPoint origin, const SkMatrix& deviceMatrix, @@ -256,6 +331,29 @@ void SkGlyphRunListDrawer::drawGlyphRunAsSubpixelMask( } } +void SkGlyphRunListDrawer::drawGlyphRunAsGlyphWithPathFallback( + SkGlyphCache* cache, const SkGlyphRun& glyphRun, + SkPoint origin, const SkMatrix& deviceMatrix, + PerGlyph perGlyph, PerPath perPath) { + auto eachGlyph = + [cache, perGlyph{std::move(perGlyph)}, perPath{std::move(perPath)}] + (const SkGlyph& glyph, SkPoint pt, SkPoint mappedPt) { + if (glyph_too_big_for_atlas(glyph)) { + const SkPath* glyphPath = cache->findPath(glyph); + if (glyphPath != nullptr) { + perPath(glyphPath, glyph, mappedPt); + } + } else { + const void* glyphImage = cache->findImage(glyph); + if (glyphImage != nullptr) { + perGlyph(glyph, mappedPt); + } + } + }; + + this->forEachMappedDrawableGlyph(glyphRun, origin, deviceMatrix, cache, eachGlyph); +} + void SkGlyphRunListDrawer::drawGlyphRunAsFullpixelMask( SkGlyphCache* cache, const SkGlyphRun& glyphRun, SkPoint origin, const SkMatrix& deviceMatrix, @@ -283,6 +381,7 @@ void SkGlyphRunListDrawer::drawGlyphRunAsFullpixelMask( } } + void SkGlyphRunListDrawer::drawForBitmapDevice( const SkGlyphRunList& glyphRunList, const SkMatrix& deviceMatrix, PerMaskCreator perMaskCreator, PerPathCreator perPathCreator) { @@ -322,8 +421,7 @@ void SkGlyphRunListDrawer::drawForBitmapDevice( void SkGlyphRunListDrawer::drawUsingMasks( SkGlyphCache* cache, const SkGlyphRun& glyphRun, - SkPoint origin, const SkMatrix& deviceMatrix, - SkGlyphRunListDrawer::PerMask perMask) { + SkPoint origin, const SkMatrix& deviceMatrix, PerMask perMask) { if (cache->isSubpixel()) { this->drawGlyphRunAsSubpixelMask(cache, glyphRun, origin, deviceMatrix, perMask); } else { diff --git a/src/core/SkGlyphRun.h b/src/core/SkGlyphRun.h index 7f9307292c..0c9dd39cb1 100644 --- a/src/core/SkGlyphRun.h +++ b/src/core/SkGlyphRun.h @@ -117,6 +117,7 @@ public: explicit SkGlyphRunListDrawer(const GrRenderTargetContext& renderTargetContext); #endif + using PerGlyph = std::function; using PerMask = std::function; using PerMaskCreator = std::function; using PerPath = std::function; @@ -131,17 +132,31 @@ public: void drawUsingPaths( const SkGlyphRun& glyphRun, SkPoint origin, SkGlyphCache* cache, PerPath perPath) const; + void drawGlyphRunAsGlyphWithPathFallback( + SkGlyphCache* cache, const SkGlyphRun& glyphRun, + SkPoint origin, const SkMatrix& deviceMatrix, + PerGlyph perGlyph, PerPath perPath); + private: static bool ShouldDrawAsPath(const SkPaint& paint, const SkMatrix& matrix); bool ensureBitmapBuffers(size_t runSize); + + + template + void forEachMappedDrawableGlyph( + const SkGlyphRun& glyphRun, SkPoint origin, const SkMatrix& deviceMatrix, + SkGlyphCache* cache, EachGlyph eachGlyph); + void drawGlyphRunAsSubpixelMask( SkGlyphCache* cache, const SkGlyphRun& glyphRun, SkPoint origin, const SkMatrix& deviceMatrix, PerMask perMask); + void drawGlyphRunAsFullpixelMask( SkGlyphCache* cache, const SkGlyphRun& glyphRun, SkPoint origin, const SkMatrix& deviceMatrix, PerMask perMask); + // The props as on the actual device. const SkSurfaceProps fDeviceProps; // The props for when the bitmap device can't draw LCD text. diff --git a/src/gpu/text/GrTextContext.cpp b/src/gpu/text/GrTextContext.cpp index 45145f6432..93bd1826df 100644 --- a/src/gpu/text/GrTextContext.cpp +++ b/src/gpu/text/GrTextContext.cpp @@ -238,31 +238,27 @@ void GrTextContext::regenerateGlyphRunList(GrTextBlob* cacheBlob, auto cache = cacheBlob->setupCache( runIndex, props, scalerContextFlags, runPaint, &viewMatrix); - auto drawOneGlyph = + auto perGlyph = [cacheBlob, runIndex, glyphCache, &currStrike, runPaint, cache{cache.get()}] - (const SkMask& mask, const SkGlyph& glyph, SkPoint position) { - SkScalar sx = SkScalarFloorToScalar(position.fX), - sy = SkScalarFloorToScalar(position.fY); - - if (glyph_too_big_for_atlas(glyph)) { - SkRect glyphRect = - rect_to_draw(glyph, {sx, sy}, SK_Scalar1, - GrGlyph::kCoverage_MaskStyle); - if (!glyphRect.isEmpty()) { - const SkPath* glyphPath = cache->findPath(glyph); - if (glyphPath != nullptr) { - cacheBlob->appendPathGlyph( - runIndex, *glyphPath, sx, sy, SK_Scalar1, true); - } - } - } else { - AppendGlyph(cacheBlob, runIndex, glyphCache, &currStrike, - glyph, GrGlyph::kCoverage_MaskStyle, sx, sy, - runPaint.filteredPremulColor(), cache, SK_Scalar1, false); - } + (const SkGlyph& glyph, SkPoint mappedPt) { + SkScalar sx = SkScalarFloorToScalar(mappedPt.fX), + sy = SkScalarFloorToScalar(mappedPt.fY); + AppendGlyph(cacheBlob, runIndex, glyphCache, &currStrike, + glyph, GrGlyph::kCoverage_MaskStyle, sx, sy, + runPaint.filteredPremulColor(), cache, SK_Scalar1, false); }; - glyphDrawer->drawUsingMasks(cache.get(), glyphRun, origin, viewMatrix, drawOneGlyph); + auto perPath = + [cacheBlob, runIndex] + (const SkPath* path, const SkGlyph& glyph, SkPoint position) { + SkScalar sx = SkScalarFloorToScalar(position.fX), + sy = SkScalarFloorToScalar(position.fY); + cacheBlob->appendPathGlyph( + runIndex, *path, sx, sy, SK_Scalar1, true); + }; + + glyphDrawer->drawGlyphRunAsGlyphWithPathFallback( + cache.get(), glyphRun, origin, viewMatrix, perGlyph, perPath); } runIndex += 1; }