add bounds generation code to SkGlyphRun
Change-Id: I29c269bce86cb50fea57d44d7760b60326aa56a8
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/396436
Commit-Queue: Herb Derby <herb@google.com>
Reviewed-by: Ben Wagner <bungeman@google.com>
diff --git a/bench/GlyphQuadFillBench.cpp b/bench/GlyphQuadFillBench.cpp
index 70190d1..243b4cb 100644
--- a/bench/GlyphQuadFillBench.cpp
+++ b/bench/GlyphQuadFillBench.cpp
@@ -39,7 +39,7 @@
size_t len = strlen(gText);
SkGlyphRunBuilder builder;
SkPaint paint;
- auto glyphRunList = builder.textToGlyphRunList(font, gText, len, {100, 100});
+ auto glyphRunList = builder.textToGlyphRunList(font, paint, gText, len, {100, 100});
SkASSERT(!glyphRunList.empty());
SkSurfaceProps props;
if (canvas) { canvas->getProps(&props); }
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index e06094c..a8da995 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -2278,12 +2278,13 @@
void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
const SkPaint& paint) {
- const SkRect bounds = blob->bounds().makeOffset(x, y);
- if (this->internalQuickReject(bounds, paint)) {
+ const SkRect blobBounds = blob->bounds().makeOffset(x, y);
+ if (this->internalQuickReject(blobBounds, paint)) {
return;
}
auto glyphRunList = fScratchGlyphRunBuilder->blobToGlyphRunList(*blob, {x, y});
+ SkRect bounds = glyphRunList.sourceBounds();
AutoLayerForImageFilter layer(this, paint, &bounds);
this->topDevice()->drawGlyphRunList(glyphRunList, layer.paint());
}
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
index ef67573..c2fc270 100644
--- a/src/core/SkDevice.cpp
+++ b/src/core/SkDevice.cpp
@@ -448,7 +448,7 @@
const SkPaint& paint) {
for (const SkGlyphRun& run : glyphRunList) {
if (run.scaledRotations().empty()) {
- this->drawGlyphRunList(SkGlyphRunList{run}, paint);
+ this->drawGlyphRunList(SkGlyphRunList{run, run.sourceBounds(paint)}, paint);
} else {
SkPoint origin = glyphRunList.origin();
SkPoint sharedPos{0, 0}; // we're at the origin
@@ -477,7 +477,8 @@
SkPaint invertingPaint{paint};
invertingPaint.setShader(make_post_inverse_lm(paint.getShader(), glyphToLocal));
this->setLocalToDevice(originalLocalToDevice * SkM44(glyphToLocal));
- this->drawGlyphRunList(SkGlyphRunList{glyphRun}, invertingPaint);
+ this->drawGlyphRunList(
+ SkGlyphRunList{glyphRun, glyphRun.sourceBounds(paint)}, invertingPaint);
}
this->setLocalToDevice(originalLocalToDevice);
}
diff --git a/src/core/SkGlyphRun.cpp b/src/core/SkGlyphRun.cpp
index b95a0a7..a50dfdb 100644
--- a/src/core/SkGlyphRun.cpp
+++ b/src/core/SkGlyphRun.cpp
@@ -39,19 +39,87 @@
, fClusters{that.fClusters}
, fFont{font} {}
+SkRect SkGlyphRun::sourceBounds(const SkPaint& paint) const {
+ SkASSERT(this->runSize() > 0);
+ const SkRect fontBounds = SkFontPriv::GetFontBounds(fFont);
+
+ if (fontBounds.isEmpty()) {
+ // Empty font bounds are likely a font bug. TightBounds has a better chance of
+ // producing useful results in this case.
+ SkStrikeSpec strikeSpec = SkStrikeSpec::MakeCanonicalized(fFont, &paint);
+ SkBulkGlyphMetrics metrics{strikeSpec};
+ SkSpan<const SkGlyph*> glyphs = metrics.glyphs(this->glyphsIDs());
+ if (fScaledRotations.empty()) {
+ // No RSXForm data - glyphs x/y aligned.
+ auto scaleAndTranslateRect =
+ [scale = strikeSpec.strikeToSourceRatio()](const SkRect& in, const SkPoint& pos) {
+ return SkRect::MakeLTRB(in.left() * scale + pos.x(),
+ in.top() * scale + pos.y(),
+ in.right() * scale + pos.x(),
+ in.bottom() * scale + pos.y());
+ };
+
+ SkRect bounds = SkRect::MakeEmpty();
+ for (auto [pos, glyph] : SkMakeZip(this->positions(), glyphs)) {
+ if (SkRect r = glyph->rect(); !r.isEmpty()) {
+ bounds.join(scaleAndTranslateRect(r, pos));
+ }
+ }
+ return bounds;
+ } else {
+ // RSXForm - glyphs can be any scale or rotation.
+ SkScalar scale = strikeSpec.strikeToSourceRatio();
+ SkRect bounds = SkRect::MakeEmpty();
+ for (auto [pos, scaleRotate, glyph] :
+ SkMakeZip(this->positions(), fScaledRotations, glyphs)) {
+ if (!glyph->rect().isEmpty()) {
+ SkMatrix xform = SkMatrix().setRSXform(
+ SkRSXform{pos.x(), pos.y(), scaleRotate.x(), scaleRotate.y()});
+ xform.preScale(scale, scale);
+ bounds.join(xform.mapRect(glyph->rect()));
+ }
+ }
+ return bounds;
+ }
+ }
+
+ // Use conservative bounds. All glyph have a box of fontBounds size.
+ if (fScaledRotations.empty()) {
+ SkRect bounds;
+ bounds.setBounds(this->positions().data(), this->positions().count());
+ bounds.fLeft += fontBounds.left();
+ bounds.fTop += fontBounds.top();
+ bounds.fRight += fontBounds.right();
+ bounds.fBottom += fontBounds.bottom();
+ return bounds;
+ } else {
+ // RSXForm case glyphs can be any scale or rotation.
+ SkRect bounds;
+ bounds.setEmpty();
+ for (auto [pos, scaleRotate] : SkMakeZip(this->positions(), fScaledRotations)) {
+ const SkRSXform xform{pos.x(), pos.y(), scaleRotate.x(), scaleRotate.y()};
+ bounds.join(SkMatrix().setRSXform(xform).mapRect(fontBounds));
+ }
+ return bounds;
+ }
+}
+
// -- SkGlyphRunList -------------------------------------------------------------------------------
SkGlyphRunList::SkGlyphRunList() = default;
SkGlyphRunList::SkGlyphRunList(
const SkTextBlob* blob,
+ SkRect bounds,
SkPoint origin,
SkSpan<const SkGlyphRun> glyphRunList)
: fGlyphRuns{glyphRunList}
, fOriginalTextBlob{blob}
+ , fSourceBounds{bounds}
, fOrigin{origin} { }
-SkGlyphRunList::SkGlyphRunList(const SkGlyphRun& glyphRun)
+SkGlyphRunList::SkGlyphRunList(const SkGlyphRun& glyphRun, const SkRect& bounds)
: fGlyphRuns{SkSpan<const SkGlyphRun>{&glyphRun, 1}}
, fOriginalTextBlob{nullptr}
+ , fSourceBounds{bounds}
, fOrigin{SkPoint::Make(0, 0)} {}
uint64_t SkGlyphRunList::uniqueID() const {
@@ -90,9 +158,11 @@
}
const SkGlyphRunList& SkGlyphRunBuilder::textToGlyphRunList(
- const SkFont& font, const void* bytes, size_t byteLength, SkPoint origin,
+ const SkFont& font, const SkPaint& paint,
+ const void* bytes, size_t byteLength, SkPoint origin,
SkTextEncoding encoding) {
auto glyphIDs = textToGlyphIDs(font, bytes, byteLength, encoding);
+ SkRect bounds = SkRect::MakeEmpty();
if (!glyphIDs.empty()) {
this->initialize(glyphIDs.size());
SkSpan<const SkPoint> positions = draw_text_positions(font, glyphIDs, {0, 0}, fPositions);
@@ -102,9 +172,10 @@
SkSpan<const char>{},
SkSpan<const uint32_t>{},
SkSpan<const SkVector>{});
+ bounds = fGlyphRunListStorage.front().sourceBounds(paint);
}
- return this->makeGlyphRunList(nullptr, origin);
+ return this->makeGlyphRunList(nullptr, bounds.makeOffset(origin), origin);
}
const SkGlyphRunList& SkGlyphRunBuilder::blobToGlyphRunList(
@@ -163,7 +234,7 @@
scaledRotations);
}
- return this->makeGlyphRunList(&blob, origin);
+ return this->makeGlyphRunList(&blob, blob.bounds().makeOffset(origin), origin);
}
void SkGlyphRunBuilder::initialize(int totalRunSize) {
@@ -237,8 +308,8 @@
}
}
-const SkGlyphRunList& SkGlyphRunBuilder::makeGlyphRunList(const SkTextBlob* blob, SkPoint origin) {
-
+const SkGlyphRunList& SkGlyphRunBuilder::makeGlyphRunList(
+ const SkTextBlob* blob, const SkRect& bounds, SkPoint origin) {
fGlyphRunList.~SkGlyphRunList();
- return *new (&fGlyphRunList) SkGlyphRunList{blob, origin, SkSpan(fGlyphRunListStorage)};
+ return *new (&fGlyphRunList) SkGlyphRunList{blob, bounds, origin, SkSpan(fGlyphRunListStorage)};
}
diff --git a/src/core/SkGlyphRun.h b/src/core/SkGlyphRun.h
index 45a3f66..c972031 100644
--- a/src/core/SkGlyphRun.h
+++ b/src/core/SkGlyphRun.h
@@ -44,6 +44,7 @@
SkSpan<const uint32_t> clusters() const { return fClusters; }
SkSpan<const char> text() const { return fText; }
SkSpan<const SkVector> scaledRotations() const { return fScaledRotations; }
+ SkRect sourceBounds(const SkPaint& paint) const;
private:
// GlyphIDs and positions.
@@ -66,10 +67,11 @@
// Blob maybe null.
SkGlyphRunList(
const SkTextBlob* blob,
+ SkRect bounds,
SkPoint origin,
SkSpan<const SkGlyphRun> glyphRunList);
- SkGlyphRunList(const SkGlyphRun& glyphRun);
+ SkGlyphRunList(const SkGlyphRun& glyphRun, const SkRect& bounds);
uint64_t uniqueID() const;
bool anyRunsLCD() const;
@@ -93,6 +95,7 @@
}
SkPoint origin() const { return fOrigin; }
+ SkRect sourceBounds() const { return fSourceBounds; }
const SkTextBlob* blob() const { return fOriginalTextBlob; }
auto begin() -> decltype(fGlyphRuns.begin()) { return fGlyphRuns.begin(); }
@@ -106,13 +109,15 @@
private:
// The text blob is needed to hookup the call back that the SkTextBlob destructor calls. It
// should be used for nothing else
- const SkTextBlob* fOriginalTextBlob{nullptr};
- SkPoint fOrigin = {0, 0};
+ const SkTextBlob* fOriginalTextBlob{nullptr};
+ const SkRect fSourceBounds{SkRect::MakeEmpty()};
+ const SkPoint fOrigin = {0, 0};
};
class SkGlyphRunBuilder {
public:
const SkGlyphRunList& textToGlyphRunList(const SkFont& font,
+ const SkPaint& paint,
const void* bytes,
size_t byteLength,
SkPoint origin,
@@ -135,10 +140,8 @@
SkSpan<const uint32_t> clusters,
SkSpan<const SkVector> scaledRotations);
- const SkGlyphRunList& makeGlyphRunList(const SkTextBlob* blob, SkPoint origin);
-
- SkPoint* simplifyTextBlobIgnoringRSXForm(const SkTextBlobRunIterator& it,
- SkPoint* positionsCursor);
+ const SkGlyphRunList& makeGlyphRunList(
+ const SkTextBlob* blob, const SkRect& bounds, SkPoint origin);
int fMaxTotalRunSize{0};
SkAutoTMalloc<SkPoint> fPositions;
diff --git a/src/gpu/ops/GrAtlasTextOp.cpp b/src/gpu/ops/GrAtlasTextOp.cpp
index dcaf5e3..2553c95 100644
--- a/src/gpu/ops/GrAtlasTextOp.cpp
+++ b/src/gpu/ops/GrAtlasTextOp.cpp
@@ -497,7 +497,7 @@
drawMatrix.preTranslate(x, y);
auto drawOrigin = SkPoint::Make(x, y);
SkGlyphRunBuilder builder;
- auto glyphRunList = builder.textToGlyphRunList(font, text, textLen, drawOrigin);
+ auto glyphRunList = builder.textToGlyphRunList(font, skPaint, text, textLen, drawOrigin);
if (glyphRunList.empty()) {
return nullptr;
}