aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar Herb Derby <herb@google.com>2018-07-23 16:20:53 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2018-07-26 18:16:46 +0000
commit1347144e3ef790c9431d18a8005dcf8914899b1d (patch)
tree212383343d996fcc2a9249760f93bff317ec4490 /src
parent97e858f359d165b35a304a840d43c2b6412fc241 (diff)
Centralize bitmap glyph positioning
Centralize all text drawing for bitmap devices into SkGlyphRunListDrawer. All drawing decisions are encapsulated in this class, and its behavior only depends on contant properties of the device. The method drawForBitmap will probably have to be converted to a template based in preliminary performance numbers. Change-Id: Id21567c1511eee9d2e9e20c7ae93544530cfdb81 Reviewed-on: https://skia-review.googlesource.com/143106 Reviewed-by: Mike Klein <mtklein@google.com> Reviewed-by: Herb Derby <herb@google.com> Commit-Queue: Herb Derby <herb@google.com>
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