aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/SkGlyphRun.cpp
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/core/SkGlyphRun.cpp
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/core/SkGlyphRun.cpp')
-rw-r--r--src/core/SkGlyphRun.cpp210
1 files changed, 209 insertions, 1 deletions
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(