aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/SkBitmapDevice.cpp17
-rw-r--r--src/core/SkBitmapDevice.h16
-rw-r--r--src/core/SkDraw.cpp180
-rw-r--r--src/core/SkDraw.h21
-rw-r--r--src/core/SkGlyphRun.cpp210
-rw-r--r--src/core/SkGlyphRun.h56
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