diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/core/SkBitmapDevice.cpp | 17 | ||||
-rw-r--r-- | src/core/SkBitmapDevice.h | 16 | ||||
-rw-r--r-- | src/core/SkDraw.cpp | 180 | ||||
-rw-r--r-- | src/core/SkDraw.h | 21 | ||||
-rw-r--r-- | src/core/SkGlyphRun.cpp | 210 | ||||
-rw-r--r-- | src/core/SkGlyphRun.h | 56 |
6 files changed, 356 insertions, 144 deletions
diff --git a/src/core/SkBitmapDevice.cpp b/src/core/SkBitmapDevice.cpp index 94a4ddcd0d..c789496557 100644 --- a/src/core/SkBitmapDevice.cpp +++ b/src/core/SkBitmapDevice.cpp @@ -229,10 +229,23 @@ static bool valid_for_bitmap_device(const SkImageInfo& info, return true; } +// TODO: unify this with the same functionality on SkDraw. +static SkScalerContextFlags scaler_context_flags(const SkBitmap& bitmap) { + // If we're doing linear blending, then we can disable the gamma hacks. + // Otherwise, leave them on. In either case, we still want the contrast boost: + // TODO: Can we be even smarter about mask gamma based on the dst transfer function? + if (bitmap.colorSpace() && bitmap.colorSpace()->gammaIsLinear()) { + return SkScalerContextFlags::kBoostContrast; + } else { + return SkScalerContextFlags::kFakeGammaAndBoostContrast; + } +} + SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap) : INHERITED(bitmap.info(), SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType)) , fBitmap(bitmap) , fRCStack(bitmap.width(), bitmap.height()) + , fGlyphDraw(this->surfaceProps(), bitmap.colorType(), scaler_context_flags(bitmap)) { SkASSERT(valid_for_bitmap_device(bitmap.info(), nullptr)); } @@ -247,6 +260,7 @@ SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap, const SkSurfaceProps& sur , fBitmap(bitmap) , fRasterHandle(hndl) , fRCStack(bitmap.width(), bitmap.height()) + , fGlyphDraw(this->surfaceProps(), bitmap.colorType(), scaler_context_flags(bitmap)) { SkASSERT(valid_for_bitmap_device(bitmap.info(), nullptr)); @@ -583,8 +597,7 @@ void SkBitmapDevice::drawGlyphRunList(SkGlyphRunList* glyphRunList) { this->drawTextBlob(blob, origin.x(), origin.y(), paint); } #else - SkBitmapDeviceFilteredSurfaceProps props(fBitmap, glyphRunList->paint(), fSurfaceProps); - LOOP_TILER( drawGlyphRunList(glyphRunList, &props()), nullptr ) + LOOP_TILER( drawGlyphRunList(glyphRunList, &fGlyphDraw), nullptr ) #endif } diff --git a/src/core/SkBitmapDevice.h b/src/core/SkBitmapDevice.h index 8abc677f99..127c09b83b 100644 --- a/src/core/SkBitmapDevice.h +++ b/src/core/SkBitmapDevice.h @@ -12,6 +12,7 @@ #include "SkCanvas.h" #include "SkColor.h" #include "SkDevice.h" +#include "SkGlyphRun.h" #include "SkImageInfo.h" #include "SkPixelRef.h" #include "SkRasterClip.h" @@ -106,12 +107,6 @@ protected: void drawBitmapRect(const SkBitmap&, const SkRect*, const SkRect&, const SkPaint&, SkCanvas::SrcRectConstraint) override; - /** - * Does not handle text decoration. - * Decorations (underline and stike-thru) will be handled by SkCanvas. - */ - void drawPosText(const void* text, size_t len, const SkScalar pos[], - int scalarsPerPos, const SkPoint& offset, const SkPaint& paint) override; void drawGlyphRunList(SkGlyphRunList* glyphRunList) override; void drawVertices(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode, const SkPaint& paint) override; @@ -158,6 +153,13 @@ private: class BDDraw; + /** + * Does not handle text decoration. + * Decorations (underline and stike-thru) will be handled by SkCanvas. + */ + void drawPosText(const void* text, size_t len, const SkScalar pos[], + int scalarsPerPos, const SkPoint& offset, const SkPaint& paint) override; + // used to change the backend's pixels (and possibly config/rowbytes) // but cannot change the width/height, so there should be no change to // any clip information. @@ -173,6 +175,8 @@ private: void* fRasterHandle = nullptr; SkRasterClipStack fRCStack; std::unique_ptr<SkBitmap> fCoverage; // if non-null, will have the same dimensions as fBitmap + SkGlyphRunListDrawer fGlyphDraw; + typedef SkBaseDevice INHERITED; }; diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp index bd607553cd..c9f2e0ed2c 100644 --- a/src/core/SkDraw.cpp +++ b/src/core/SkDraw.cpp @@ -51,9 +51,7 @@ static SkPaint make_paint_with_image( /////////////////////////////////////////////////////////////////////////////// -SkDraw::SkDraw() { - sk_bzero(this, sizeof(*this)); -} +SkDraw::SkDraw() {} bool SkDraw::computeConservativeLocalClipBounds(SkRect* localBounds) const { if (fRC->isEmpty()) { @@ -1407,8 +1405,9 @@ public: // this extra test is worth it, assuming that most of the time it succeeds // since we can avoid writing to storage if (!fClipBounds.containsNoEmptyCheck(mask.fBounds)) { - if (!storage.intersectNoEmptyCheck(mask.fBounds, fClipBounds)) + if (!storage.intersectNoEmptyCheck(mask.fBounds, fClipBounds)) { return; + } bounds = &storage; } @@ -1556,116 +1555,72 @@ void SkDraw::drawPosText(const char text[], size_t byteLength, const SkScalar po offset, *fMatrix, pos, scalarsPerPosition, cache.get(), drawOneGlyph); } -void SkDraw::drawGlyphRunAsPaths( - SkGlyphRun* glyphRun, SkPoint origin, const SkSurfaceProps* props) const { - // setup our std paint, in hopes of getting hits in the cache - const SkPaint& origPaint = glyphRun->paint(); - SkPaint paint(glyphRun->paint()); - SkScalar matrixScale = paint.setupForAsPaths(); - - SkMatrix matrix; - matrix.setScale(matrixScale, matrixScale); - - // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache. - paint.setStyle(SkPaint::kFill_Style); - paint.setPathEffect(nullptr); - - auto cache = SkStrikeCache::FindOrCreateStrikeExclusive( - paint, props, this->scalerContextFlags(), nullptr); - - // Now restore the original settings, so we "draw" with whatever style/stroking. - paint.setStyle(origPaint.getStyle()); - paint.setPathEffect(origPaint.refPathEffect()); +void SkDraw::blitARGB32Mask(const SkMask& mask, const SkPaint& paint) const { + SkASSERT(SkMask::kARGB32_Format == mask.fFormat); + SkBitmap bm; + bm.installPixels( + SkImageInfo::MakeN32Premul(mask.fBounds.width(), mask.fBounds.height()), + (SkPMColor*)mask.fImage, mask.fRowBytes); - auto eachGlyph = [this, origin, &cache, &matrix, &paint](SkGlyphID glyphID, SkPoint position) { - const SkGlyph& glyph = cache->getGlyphIDMetrics(glyphID); - if (glyph.fWidth > 0) { - const SkPath* path = cache->findPath(glyph); - if (path != nullptr) { - SkPoint loc = position + origin; - matrix[SkMatrix::kMTransX] = loc.fX; - matrix[SkMatrix::kMTransY] = loc.fY; - this->drawPath(*path, paint, &matrix, false); - } - } - }; - glyphRun->forEachGlyphAndPosition(eachGlyph); + this->drawSprite(bm, mask.fBounds.x(), mask.fBounds.y(), paint); } -void SkDraw::drawGlyphRunAsSubpixelMask( - SkGlyphCache* cache, SkGlyphRun* glyphRun, SkPoint origin) const { +SkGlyphRunListDrawer::PerMask SkDraw::drawOneMaskCreator( + const SkPaint& paint, SkArenaAlloc* alloc) const { + SkBlitter* blitter = SkBlitter::Choose(fDst, *fMatrix, paint, alloc, false); + if (fCoverage != nullptr) { + auto coverageBlitter = SkBlitter::Choose(*fCoverage, *fMatrix, SkPaint(), alloc, true); + blitter = alloc->make<SkPairBlitter>(blitter, coverageBlitter); + } - SkMatrix matrix = *fMatrix; - // Add rounding and origin. - SkAxisAlignment axisAlignment = cache->getScalerContext()->computeAxisAlignmentForHText(); - SkPoint rounding = SkFindAndPlaceGlyph::SubpixelPositionRounding(axisAlignment); - matrix.preTranslate(origin.x(), origin.y()); - matrix.postTranslate(rounding.x(), rounding.y()); + auto wrapaper = alloc->make<SkAAClipBlitterWrapper>(*fRC, blitter); + blitter = wrapaper->getBlitter(); - glyphRun->mapPositions(matrix); + auto useRegion = fRC->isBW() && !fRC->isRect(); - auto paint = glyphRun->paint(); - // The Blitter Choose needs to be live while using the blitter below. - SkAutoBlitterChoose blitterChooser(*this, nullptr, paint); - SkAAClipBlitterWrapper wrapper(*fRC, blitterChooser.get()); - DrawOneGlyph drawOneGlyph(*this, paint, cache, wrapper.getBlitter()); + if (useRegion) { + return [this, blitter, &paint](const SkMask& mask) { + SkRegion::Cliperator clipper(fRC->bwRgn(), mask.fBounds); - auto eachGlyph = [cache, &drawOneGlyph, axisAlignment](SkGlyphID glyphID, SkPoint position) { - auto subpixelAlignment = [](SkAxisAlignment axisAlignment, SkPoint position) -> SkIPoint { + if (!clipper.done()) { + if (SkMask::kARGB32_Format == mask.fFormat) { + this->blitARGB32Mask(mask, paint); + } else { + const SkIRect& cr = clipper.rect(); + do { + blitter->blitMask(mask, cr); + clipper.next(); + } while (!clipper.done()); + } + } + }; + } else { + SkIRect clipBounds = fRC->isBW() ? fRC->bwRgn().getBounds() + : fRC->aaRgn().getBounds(); + return [this, blitter, clipBounds, &paint](const SkMask& mask) { + SkIRect storage; + const SkIRect* bounds = &mask.fBounds; - if (!SkScalarsAreFinite(position.fX, position.fY)) { - return {0, 0}; + // this extra test is worth it, assuming that most of the time it succeeds + // since we can avoid writing to storage + if (!clipBounds.containsNoEmptyCheck(mask.fBounds)) { + if (!storage.intersectNoEmptyCheck(mask.fBounds, clipBounds)) { + return; + } + bounds = &storage; } - // Only the fractional part of position.fX and position.fY matter, because the result of - // this function will just be passed to FixedToSub. - switch (axisAlignment) { - case kX_SkAxisAlignment: - return {SkScalarToFixed(SkScalarFraction(position.fX)), 0}; - case kY_SkAxisAlignment: - return {0, SkScalarToFixed(SkScalarFraction(position.fY))}; - case kNone_SkAxisAlignment: - return {SkScalarToFixed(SkScalarFraction(position.fX)), - SkScalarToFixed(SkScalarFraction(position.fY))}; + if (SkMask::kARGB32_Format == mask.fFormat) { + this->blitARGB32Mask(mask, paint); + } else { + blitter->blitMask(mask, *bounds); } - SK_ABORT("Should not get here."); - return {0, 0}; }; - - SkIPoint lookupPosition = subpixelAlignment(axisAlignment, position); - const SkGlyph& glyph = cache->getGlyphIDMetrics( - glyphID, lookupPosition.x(), lookupPosition.y()); - if (glyph.fWidth > 0) { - drawOneGlyph(glyph, position, SkPoint::Make(0, 0)); - } - }; - glyphRun->forEachGlyphAndPosition(eachGlyph); -} - -void SkDraw::drawGlyphRunAsFullpixelMask( - SkGlyphCache* cache, SkGlyphRun* glyphRun, SkPoint origin) const { - SkMatrix matrix = *fMatrix; - // Add rounding and origin. - matrix.preTranslate(origin.x(), origin.y()); - matrix.postTranslate(SK_ScalarHalf, SK_ScalarHalf); - glyphRun->mapPositions(matrix); - - auto paint = glyphRun->paint(); - // The Blitter Choose needs to be live while using the blitter below. - SkAutoBlitterChoose blitterChooser(*this, nullptr, paint); - SkAAClipBlitterWrapper wrapper(*fRC, blitterChooser.get()); - DrawOneGlyph drawOneGlyph(*this, paint, cache, wrapper.getBlitter()); - - auto eachGlyph = [cache, &drawOneGlyph](SkGlyphID glyphID, SkPoint position) { - const SkGlyph& glyph = cache->getGlyphIDMetrics(glyphID); - if (glyph.fWidth > 0) { - drawOneGlyph(glyph, position, SkPoint::Make(0, 0)); - } - }; - glyphRun->forEachGlyphAndPosition(eachGlyph); + } } -void SkDraw::drawGlyphRunList(SkGlyphRunList* glyphRunList, const SkSurfaceProps* props) const { +void SkDraw::drawGlyphRunList( + SkGlyphRunList* glyphRunList, SkGlyphRunListDrawer* glyphDraw) const { SkDEBUGCODE(this->validate();) @@ -1673,21 +1628,18 @@ void SkDraw::drawGlyphRunList(SkGlyphRunList* glyphRunList, const SkSurfaceProps return; } - SkPoint origin = glyphRunList->origin(); - for (auto& glyphRun : *glyphRunList) { - if (ShouldDrawTextAsPaths(glyphRun.paint(), *fMatrix)) { - this->drawGlyphRunAsPaths(&glyphRun, origin, props); - } else { - auto cache = SkStrikeCache::FindOrCreateStrikeExclusive( - glyphRun.paint(), props, this->scalerContextFlags(), fMatrix); - if (cache->isSubpixel()) { - this->drawGlyphRunAsSubpixelMask(cache.get(), &glyphRun, origin); - } else { - this->drawGlyphRunAsFullpixelMask(cache.get(), &glyphRun, origin); - } - } - } + auto perPathBuilder = [this](const SkPaint& paint, SkArenaAlloc*) { + auto perPath = [this, &paint](const SkPath& path, const SkMatrix& matrix) { + this->drawPath(path, paint, &matrix, false); + }; + return perPath; + }; + + auto perMaskBuilder = [this](const SkPaint& paint, SkArenaAlloc* alloc) { + return this->drawOneMaskCreator(paint, alloc); + }; + glyphDraw->drawForBitmap(glyphRunList, *fMatrix, perMaskBuilder, perPathBuilder); } #if defined _WIN32 diff --git a/src/core/SkDraw.h b/src/core/SkDraw.h index a64abe74ff..a199215878 100644 --- a/src/core/SkDraw.h +++ b/src/core/SkDraw.h @@ -11,6 +11,7 @@ #define SkDraw_DEFINED #include "SkCanvas.h" +#include "SkGlyphRun.h" #include "SkMask.h" #include "SkPaint.h" #include "SkPixmap.h" @@ -66,16 +67,7 @@ public: void drawPosText(const char text[], size_t byteLength, const SkScalar pos[], int scalarsPerPosition, const SkPoint& offset, const SkPaint&, const SkSurfaceProps*) const; - - void drawGlyphRunAsPaths( - SkGlyphRun* glyphRun, SkPoint origin, const SkSurfaceProps* props) const; - - void drawGlyphRunAsSubpixelMask( - SkGlyphCache* cache, SkGlyphRun* glyphRun, SkPoint origin) const; - - void drawGlyphRunAsFullpixelMask( - SkGlyphCache* cache, SkGlyphRun* glyphRun, SkPoint origin) const; - void drawGlyphRunList(SkGlyphRunList* glyphRunList, const SkSurfaceProps*) const; + void drawGlyphRunList(SkGlyphRunList* glyphRunList, SkGlyphRunListDrawer* glyphDraw) const; void drawVertices(SkVertices::VertexMode mode, int vertexCount, const SkPoint vertices[], const SkPoint textures[], const SkColor colors[], const SkVertices::BoneIndices boneIndices[], @@ -132,6 +124,9 @@ public: const SkPaint&, const SkSurfaceProps*) const; static SkScalar ComputeResScaleForStroking(const SkMatrix& ); private: + void blitARGB32Mask(const SkMask& mask, const SkPaint& paint) const; + SkGlyphRunListDrawer::PerMask drawOneMaskCreator( + const SkPaint& paint, SkArenaAlloc* alloc) const; void drawBitmapAsMask(const SkBitmap&, const SkPaint&) const; void drawPath(const SkPath&, const SkPaint&, const SkMatrix* preMatrix, @@ -157,11 +152,11 @@ private: public: SkPixmap fDst; - const SkMatrix* fMatrix; // required - const SkRasterClip* fRC; // required + const SkMatrix* fMatrix{nullptr}; // required + const SkRasterClip* fRC{nullptr}; // required // optional, will be same dimensions as fDst if present - const SkPixmap* fCoverage = nullptr; + const SkPixmap* fCoverage{nullptr}; #ifdef SK_DEBUG void validate() const; diff --git a/src/core/SkGlyphRun.cpp b/src/core/SkGlyphRun.cpp index cfe63a87b1..4889482432 100644 --- a/src/core/SkGlyphRun.cpp +++ b/src/core/SkGlyphRun.cpp @@ -13,12 +13,15 @@ #include "SkDevice.h" #include "SkDraw.h" +#include "SkFindAndPlaceGlyph.h" #include "SkGlyphCache.h" #include "SkMSAN.h" #include "SkMakeUnique.h" #include "SkMatrix.h" #include "SkPaint.h" #include "SkPaintPriv.h" +#include "SkPathEffect.h" +#include "SkRasterClip.h" #include "SkStrikeCache.h" #include "SkTextBlob.h" #include "SkTextBlobRunIterator.h" @@ -74,7 +77,6 @@ void SkGlyphRun::eachGlyphToGlyphRun(SkGlyphRun::PerGlyph perGlyph) { point = fPositions[i]; perGlyph(&run, runPaint); } - } void SkGlyphRun::mapPositions(const SkMatrix& matrix) { @@ -104,6 +106,212 @@ void SkGlyphRun::filloutGlyphsAndPositions(SkGlyphID* glyphIDs, SkPoint* positio memcpy(positions, fPositions.data(), fPositions.size_bytes()); } +// -- SkGlyphRunListDrawer ------------------------------------------------------------------------- +SkGlyphRunListDrawer::SkGlyphRunListDrawer( + const SkSurfaceProps& props, SkColorType colorType, SkScalerContextFlags flags) + : fDeviceProps{props} + , fBitmapFallbackProps{SkSurfaceProps{props.flags(), kUnknown_SkPixelGeometry}} + , fColorType{colorType} + , fScalerContextFlags{flags} {} + +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()) { + return true; + } + + // we don't cache perspective + if (matrix.hasPerspective()) { + return true; + } + + SkMatrix textM; + SkPaintPriv::MakeTextMatrix(&textM, paint); + return SkPaint::TooBigToUseCache(matrix, textM, 1024); +} + +bool SkGlyphRunListDrawer::ensureBitmapBuffers(size_t runSize) { + if (runSize > fMaxRunSize) { + fPositions.reset(runSize); + fMaxRunSize = runSize; + } + + return true; +} + +void SkGlyphRunListDrawer::drawGlyphRunAsPaths( + SkGlyphRun* glyphRun, SkPoint origin, const SkSurfaceProps& props, PerPath perPath) const { + // setup our std paint, in hopes of getting hits in the cache + const SkPaint& origPaint = glyphRun->paint(); + SkPaint paint(glyphRun->paint()); + SkScalar matrixScale = paint.setupForAsPaths(); + + SkMatrix matrix; + matrix.setScale(matrixScale, matrixScale); + + // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache. + paint.setStyle(SkPaint::kFill_Style); + paint.setPathEffect(nullptr); + + auto cache = SkStrikeCache::FindOrCreateStrikeExclusive( + paint, &props, fScalerContextFlags, nullptr); + + // Now restore the original settings, so we "draw" with whatever style/stroking. + paint.setStyle(origPaint.getStyle()); + paint.setPathEffect(origPaint.refPathEffect()); + + auto eachGlyph = [perPath{std::move(perPath)}, origin, &cache, &matrix] + (SkGlyphID glyphID, SkPoint position) { + const SkGlyph& glyph = cache->getGlyphIDMetrics(glyphID); + if (glyph.fWidth > 0) { + const SkPath* path = cache->findPath(glyph); + if (path != nullptr) { + SkPoint loc = position + origin; + matrix[SkMatrix::kMTransX] = loc.fX; + matrix[SkMatrix::kMTransY] = loc.fY; + perPath(*path, matrix); + } + } + }; + + glyphRun->forEachGlyphAndPosition(eachGlyph); +} + +static bool prepare_mask( + SkGlyphCache* cache, const SkGlyph& glyph, SkPoint position, SkMask* mask) { + if (glyph.fWidth == 0) { return false; } + + // 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 gt = [](float a, int b) { return !(a <= (float)b); }; + auto lt = [](float a, int b) { return !(a >= (float)b); }; + if (gt(position.fX, INT_MAX - (INT16_MAX + SkTo<int>(UINT16_MAX))) || + lt(position.fX, INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/)) || + gt(position.fY, INT_MAX - (INT16_MAX + SkTo<int>(UINT16_MAX))) || + lt(position.fY, INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/))) { + return false; + } + + int left = SkScalarFloorToInt(position.fX); + int top = SkScalarFloorToInt(position.fY); + + left += glyph.fLeft; + top += glyph.fTop; + + int right = left + glyph.fWidth; + int bottom = top + glyph.fHeight; + + mask->fBounds.set(left, top, right, bottom); + SkASSERT(!mask->fBounds.isEmpty()); + + uint8_t* bits = (uint8_t*)(cache->findImage(glyph)); + if (nullptr == bits) { + return false; // can't rasterize glyph + } + + mask->fImage = bits; + mask->fRowBytes = glyph.rowBytes(); + mask->fFormat = static_cast<SkMask::Format>(glyph.fMaskFormat); + + return true; +} + +void SkGlyphRunListDrawer::drawGlyphRunAsSubpixelMask( + SkGlyphCache* cache, SkGlyphRun* glyphRun, + SkPoint origin, const SkMatrix& deviceMatrix, + PerMask perMask) { + auto runSize = glyphRun->runSize(); + if (this->ensureBitmapBuffers(glyphRun->runSize())) { + // Add rounding and origin. + SkMatrix matrix = deviceMatrix; + SkAxisAlignment axisAlignment = cache->getScalerContext()->computeAxisAlignmentForHText(); + SkPoint rounding = SkFindAndPlaceGlyph::SubpixelPositionRounding(axisAlignment); + matrix.preTranslate(origin.x(), origin.y()); + matrix.postTranslate(rounding.x(), rounding.y()); + matrix.mapPoints(fPositions, glyphRun->positions().data(), runSize); + + const SkPoint* positionCursor = fPositions; + for (auto glyphID : glyphRun->shuntGlyphsIDs()) { + auto position = *positionCursor++; + if (SkScalarsAreFinite(position.fX, position.fY)) { + SkFixed lookupX = SkScalarToFixed(SkScalarFraction(position.fX)), + lookupY = SkScalarToFixed(SkScalarFraction(position.fY)); + + // Snap to a given axis if alignment is requested. + if (axisAlignment == kX_SkAxisAlignment ) { + lookupY = 0; + } else if (axisAlignment == kY_SkAxisAlignment) { + lookupX = 0; + } + + const SkGlyph& glyph = cache->getGlyphIDMetrics(glyphID, lookupX, lookupY); + SkMask mask; + if (prepare_mask(cache, glyph, position, &mask)) { + perMask(mask); + } + } + } + } +} + +void SkGlyphRunListDrawer::drawGlyphRunAsFullpixelMask( + SkGlyphCache* cache, SkGlyphRun* glyphRun, + SkPoint origin, const SkMatrix& deviceMatrix, + PerMask perMask) { + auto runSize = glyphRun->runSize(); + if (this->ensureBitmapBuffers(glyphRun->runSize())) { + + // Add rounding and origin. + SkMatrix matrix = deviceMatrix; + matrix.preTranslate(origin.x(), origin.y()); + matrix.postTranslate(SK_ScalarHalf, SK_ScalarHalf); + matrix.mapPoints(fPositions, glyphRun->positions().data(), runSize); + + const SkPoint* positionCursor = fPositions; + for (auto glyphID : glyphRun->shuntGlyphsIDs()) { + auto position = *positionCursor++; + if (SkScalarsAreFinite(position.fX, position.fY)) { + const SkGlyph& glyph = cache->getGlyphIDMetrics(glyphID); + SkMask mask; + if (prepare_mask(cache, glyph, position, &mask)) { + perMask(mask); + } + } + } + } +} + +void SkGlyphRunListDrawer::drawForBitmap( + SkGlyphRunList* glyphRunList, const SkMatrix& deviceMatrix, + PerMaskCreator perMaskCreator, PerPathCreator perPathCreator) { + + SkPoint origin = glyphRunList->origin(); + for (auto& glyphRun : *glyphRunList) { + SkSTArenaAlloc<3332> alloc; + // The bitmap blitters can only draw lcd text to a N32 bitmap in srcOver. Otherwise, + // convert the lcd text into A8 text. The props communicates this to the scaler. + auto& props = (kN32_SkColorType == fColorType && glyphRun.paint().isSrcOver()) + ? fDeviceProps + : fBitmapFallbackProps; + auto paint = glyphRun.paint(); + if (ShouldDrawAsPath(glyphRun.paint(), deviceMatrix)) { + auto perPath = perPathCreator(paint, &alloc); + this->drawGlyphRunAsPaths(&glyphRun, origin, props, perPath); + } else { + auto cache = SkStrikeCache::FindOrCreateStrikeExclusive( + paint, &props, fScalerContextFlags, &deviceMatrix); + auto perMask = perMaskCreator(paint, &alloc); + if (cache->isSubpixel()) { + this->drawGlyphRunAsSubpixelMask( + cache.get(), &glyphRun, origin, deviceMatrix, perMask); + } else { + this->drawGlyphRunAsFullpixelMask( + cache.get(), &glyphRun, origin, deviceMatrix, perMask); + } + } + } +} + // -- SkGlyphRunList ------------------------------------------------------------------------------- SkGlyphRunList::SkGlyphRunList() = default; SkGlyphRunList::SkGlyphRunList( diff --git a/src/core/SkGlyphRun.h b/src/core/SkGlyphRun.h index f3b4ff34e3..8348fe9013 100644 --- a/src/core/SkGlyphRun.h +++ b/src/core/SkGlyphRun.h @@ -12,16 +12,19 @@ #include <memory> #include <vector> +#include "SkArenaAlloc.h" #include "SkDescriptor.h" #include "SkMask.h" #include "SkPath.h" #include "SkPoint.h" +#include "SkSurfaceProps.h" #include "SkTemplates.h" #include "SkTextBlob.h" #include "SkTypes.h" class SkBaseDevice; class SkGlyphRunList; +class SkRasterClip; template <typename T> class SkSpan { @@ -69,7 +72,6 @@ public: template <typename PerGlyphPos> void forEachGlyphAndPosition(PerGlyphPos perGlyph) const; - // The temporaryShunt calls are to allow inter-operating with existing code while glyph runs // are developed. void temporaryShuntToDrawPosText(SkBaseDevice* device, SkPoint origin); @@ -102,13 +104,43 @@ private: SkPaint fRunPaint; }; -template <typename PerGlyphPos> -inline void SkGlyphRun::forEachGlyphAndPosition(PerGlyphPos perGlyph) const { - SkPoint* ptCursor = fPositions.data(); - for (auto glyphID : fGlyphIDs) { - perGlyph(glyphID, *ptCursor++); - } -} +class SkGlyphRunListDrawer { +public: + // Constructor for SkBitmpapDevice. + SkGlyphRunListDrawer( + const SkSurfaceProps& props, SkColorType colorType, SkScalerContextFlags flags); + + using PerMask = std::function<void(const SkMask&)>; + using PerMaskCreator = std::function<PerMask(const SkPaint&, SkArenaAlloc* alloc)>; + using PerPath = std::function<void(const SkPath&, const SkMatrix&)>; + using PerPathCreator = std::function<PerPath(const SkPaint&, SkArenaAlloc* alloc)>; + void drawForBitmap( + SkGlyphRunList* glyphRunList, const SkMatrix& deviceMatrix, + PerMaskCreator perMaskCreator, PerPathCreator perPathCreator); + +private: + static bool ShouldDrawAsPath(const SkPaint& paint, const SkMatrix& matrix); + bool ensureBitmapBuffers(size_t runSize); + void drawGlyphRunAsPaths( + SkGlyphRun* glyphRun, SkPoint origin, + const SkSurfaceProps& props, PerPath perPath) const; + void drawGlyphRunAsSubpixelMask( + SkGlyphCache* cache, SkGlyphRun* glyphRun, + SkPoint origin, const SkMatrix& deviceMatrix, + PerMask perMask); + void drawGlyphRunAsFullpixelMask( + SkGlyphCache* cache, 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. + const SkSurfaceProps fBitmapFallbackProps; + const SkColorType fColorType; + const SkScalerContextFlags fScalerContextFlags; + size_t fMaxRunSize{0}; + SkAutoTMalloc<SkPoint> fPositions; +}; class SkGlyphRunList { const SkPaint* fOriginalPaint{nullptr}; // This should be deleted soon. @@ -269,4 +301,12 @@ private: SkGlyphIDSet fGlyphIDSet; }; +template <typename PerGlyphPos> +inline void SkGlyphRun::forEachGlyphAndPosition(PerGlyphPos perGlyph) const { + SkPoint* ptCursor = fPositions.data(); + for (auto glyphID : fGlyphIDs) { + perGlyph(glyphID, *ptCursor++); + } +} + #endif // SkGlyphRunInfo_DEFINED |