Skip to content

Commit 7924d9a

Browse files
herbderbySkia Commit-Bot
authored andcommitted
Simplify intercepts
Introduce textBlobToGlyphRunListWithoutRSX to convert text blob into glyph runs. Convert the core of the code from working over text blobs to working over glyph runs. + Misc cleanups Change-Id: I33c1fc5e948dd7270031496325a96409f2cfeeb6 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/222277 Reviewed-by: Ben Wagner <bungeman@google.com> Commit-Queue: Herb Derby <herb@google.com>
1 parent d7b321a commit 7924d9a

File tree

3 files changed

+127
-195
lines changed

3 files changed

+127
-195
lines changed

src/core/SkGlyphRun.cpp

Lines changed: 68 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -186,46 +186,22 @@ void SkGlyphRunBuilder::drawTextBlob(const SkPaint& paint, const SkTextBlob& blo
186186
SkPoint* positions = fPositions;
187187

188188
for (SkTextBlobRunIterator it(&blob); !it.done(); it.next()) {
189-
// applyFontToPaint() always overwrites the exact same attributes,
190-
// so it is safe to not re-seed the paint for this reason.
191-
size_t runSize = it.glyphCount();
192-
193-
auto text = SkSpan<const char>(it.text(), it.textSize());
194-
auto clusters = SkSpan<const uint32_t>(it.clusters(), runSize);
195-
const SkPoint& offset = it.offset();
196-
auto glyphIDs = SkSpan<const SkGlyphID>{it.glyphs(), runSize};
197-
198-
switch (it.positioning()) {
199-
case SkTextBlobRunIterator::kDefault_Positioning: {
200-
this->simplifyDrawText(
201-
it.font(), glyphIDs, offset, positions, text, clusters);
202-
}
203-
break;
204-
case SkTextBlobRunIterator::kHorizontal_Positioning: {
205-
auto constY = offset.y();
206-
this->simplifyDrawPosTextH(
207-
it.font(), glyphIDs, it.pos(), constY, positions, text, clusters);
189+
if (it.positioning() != SkTextBlobRunIterator::kRSXform_Positioning) {
190+
simplifyTextBlobIgnoringRSXForm(paint, it, positions);
191+
} else {
192+
// Handle kRSXform_Positioning
193+
if (!this->empty()) {
194+
this->makeGlyphRunList(paint, &blob, origin);
195+
device->drawGlyphRunList(this->useGlyphRunList());
208196
}
209-
break;
210-
case SkTextBlobRunIterator::kFull_Positioning:
211-
this->simplifyDrawPosText(
212-
it.font(), glyphIDs, (const SkPoint*)it.pos(), text, clusters);
213-
break;
214-
case SkTextBlobRunIterator::kRSXform_Positioning: {
215-
if (!this->empty()) {
216-
this->makeGlyphRunList(paint, &blob, origin);
217-
device->drawGlyphRunList(this->useGlyphRunList());
218-
}
219-
220-
device->drawGlyphRunRSXform(it.font(), it.glyphs(), (const SkRSXform*)it.pos(),
221-
runSize, origin, paint);
222-
223-
// re-init in case we keep looping and need the builder again
224-
this->initialize(totalGlyphs);
225-
} break;
226-
}
227197

228-
positions += runSize;
198+
device->drawGlyphRunRSXform(it.font(), it.glyphs(), (const SkRSXform*)it.pos(),
199+
it.glyphCount(), origin, paint);
200+
201+
// re-init in case we keep looping and need the builder again
202+
this->initialize(totalGlyphs);
203+
}
204+
positions += it.glyphCount();
229205
}
230206

231207
if (!this->empty()) {
@@ -234,6 +210,60 @@ void SkGlyphRunBuilder::drawTextBlob(const SkPaint& paint, const SkTextBlob& blo
234210
}
235211
}
236212

213+
void SkGlyphRunBuilder::textBlobToGlyphRunListIgnoringRSXForm(
214+
const SkPaint& paint, const SkTextBlob& blob, SkPoint origin) {
215+
// Figure out all the storage needed to pre-size everything below.
216+
size_t totalGlyphs = 0;
217+
for (SkTextBlobRunIterator it(&blob); !it.done(); it.next()) {
218+
totalGlyphs += it.glyphCount();
219+
}
220+
221+
// Pre-size all the buffers so they don't move during processing.
222+
this->initialize(totalGlyphs);
223+
224+
SkPoint* positions = fPositions;
225+
226+
for (SkTextBlobRunIterator it(&blob); !it.done(); it.next()) {
227+
simplifyTextBlobIgnoringRSXForm(paint, it, positions);
228+
positions += it.glyphCount();
229+
}
230+
231+
if (!this->empty()) {
232+
this->makeGlyphRunList(paint, &blob, origin);
233+
}
234+
}
235+
236+
void SkGlyphRunBuilder::simplifyTextBlobIgnoringRSXForm(const SkPaint& paint,
237+
const SkTextBlobRunIterator& it,
238+
SkPoint* positions) {
239+
size_t runSize = it.glyphCount();
240+
241+
auto text = SkSpan<const char>(it.text(), it.textSize());
242+
auto clusters = SkSpan<const uint32_t>(it.clusters(), runSize);
243+
const SkPoint& offset = it.offset();
244+
auto glyphIDs = SkSpan<const SkGlyphID>{it.glyphs(), runSize};
245+
246+
switch (it.positioning()) {
247+
case SkTextBlobRunIterator::kDefault_Positioning: {
248+
this->simplifyDrawText(
249+
it.font(), glyphIDs, offset, positions, text, clusters);
250+
break;
251+
}
252+
case SkTextBlobRunIterator::kHorizontal_Positioning: {
253+
auto constY = offset.y();
254+
this->simplifyDrawPosTextH(
255+
it.font(), glyphIDs, it.pos(), constY, positions, text, clusters);
256+
break;
257+
}
258+
case SkTextBlobRunIterator::kFull_Positioning: {
259+
this->simplifyDrawPosText(
260+
it.font(), glyphIDs, (const SkPoint*) it.pos(), text, clusters);
261+
break;
262+
}
263+
case SkTextBlobRunIterator::kRSXform_Positioning: break;
264+
}
265+
}
266+
237267
void SkGlyphRunBuilder::drawGlyphsWithPositions(const SkPaint& paint, const SkFont& font,
238268
SkSpan<const SkGlyphID> glyphIDs, const SkPoint* pos) {
239269
if (!glyphIDs.empty()) {

src/core/SkGlyphRun.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
class SkBaseDevice;
2222
class SkGlyph;
2323
class SkTextBlob;
24+
class SkTextBlobRunIterator;
2425

2526
class SkGlyphRun {
2627
public:
@@ -120,13 +121,13 @@ class SkGlyphRunBuilder {
120121
const SkPaint&, const SkFont&, SkSpan<const SkGlyphID> glyphIDs, const SkPoint* pos);
121122
void drawTextBlob(const SkPaint& paint, const SkTextBlob& blob, SkPoint origin, SkBaseDevice*);
122123

124+
void textBlobToGlyphRunListIgnoringRSXForm(
125+
const SkPaint& paint, const SkTextBlob& blob, SkPoint origin);
126+
123127
const SkGlyphRunList& useGlyphRunList();
124128

125129
bool empty() const { return fGlyphRunListStorage.size() == 0; }
126130

127-
static void DispatchBlob(SkGlyphRunBuilder* builder, const SkPaint& paint,
128-
const SkTextBlob& blob, SkPoint origin, SkBaseDevice* device);
129-
130131
private:
131132
void initialize(size_t totalRunSize);
132133
SkSpan<const SkGlyphID> textToGlyphIDs(
@@ -156,6 +157,10 @@ class SkGlyphRunBuilder {
156157
const SkPoint* pos,
157158
SkSpan<const char> text = SkSpan<const char>{},
158159
SkSpan<const uint32_t> clusters = SkSpan<const uint32_t>{});
160+
void simplifyTextBlobIgnoringRSXForm(
161+
const SkPaint& paint,
162+
const SkTextBlobRunIterator& it,
163+
SkPoint* positions);
159164

160165
size_t fMaxTotalRunSize{0};
161166
SkAutoTMalloc<SkPoint> fPositions;

src/core/SkTextBlob.cpp

Lines changed: 51 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -843,183 +843,80 @@ size_t SkTextBlob::serialize(const SkSerialProcs& procs, void* memory, size_t me
843843
///////////////////////////////////////////////////////////////////////////////////////////////////
844844

845845
namespace {
846-
847-
class TextInterceptsIter {
848-
public:
849-
TextInterceptsIter(const SkGlyphID glyphs[], int count, const SkFont& font,
850-
const SkPaint* paint, const SkScalar bounds[2], SkScalar x, SkScalar y);
851-
852-
/**
853-
* Returns false when all of the text has been consumed
854-
*/
855-
bool next(SkScalar* array, int* count);
856-
857-
void setPosition(SkScalar x, SkScalar y) {
858-
SkScalar xOffset = 0;
859-
for (int i = 0; i < (int) SK_ARRAY_COUNT(fBounds); ++i) {
860-
SkScalar bound = fBoundsBase[i] - y;
861-
fBounds[i] = bound / fScale;
862-
}
863-
864-
fXPos = xOffset + x;
865-
fPrevAdvance = 0;
866-
}
867-
868-
private:
869-
SkExclusiveStrikePtr fCache;
870-
SkFont fFont;
871-
SkPaint fPaint;
872-
SkScalar fScale;
873-
SkScalar fPrevAdvance;
874-
const SkGlyphID* fGlyphs;
875-
const SkGlyphID* fStop;
876-
877-
SkScalar fXPos; // accumulated xpos, returned in next
878-
879-
SkScalar fBounds[2];
880-
SkScalar fBoundsBase[2];
881-
};
882-
883-
TextInterceptsIter::TextInterceptsIter(const SkGlyphID glyphs[],
884-
int count,
885-
const SkFont& font,
886-
const SkPaint* paint,
887-
const SkScalar bounds[2],
888-
SkScalar x, SkScalar y)
889-
: fFont(font) {
890-
SkAssertResult(count >= 0);
891-
892-
if (paint) {
893-
fPaint = *paint;
894-
}
895-
fPaint.setMaskFilter(nullptr); // don't want this affecting our path-cache lookup
896-
897-
// can't use our canonical size if we need to apply patheffects
898-
if (fPaint.getPathEffect() == nullptr) {
846+
int get_glyph_run_intercepts(const SkGlyphRun& glyphRun,
847+
const SkPaint& paint,
848+
const SkScalar bounds[2],
849+
SkScalar intervals[],
850+
int* intervalCount) {
851+
SkScalar scale = SK_Scalar1;
852+
SkPaint interceptPaint{paint};
853+
SkFont interceptFont{glyphRun.font()};
854+
855+
interceptPaint.setMaskFilter(nullptr); // don't want this affecting our path-cache lookup
856+
857+
// can't use our canonical size if we need to apply path effects
858+
if (interceptPaint.getPathEffect() == nullptr) {
899859
// If the wrong size is going to be used, don't hint anything.
900-
fFont.setHinting(SkFontHinting::kNone);
901-
fFont.setSubpixel(true);
902-
fScale = fFont.getSize() / SkFontPriv::kCanonicalTextSizeForPaths;
903-
fFont.setSize(SkIntToScalar(SkFontPriv::kCanonicalTextSizeForPaths));
860+
interceptFont.setHinting(SkFontHinting::kNone);
861+
interceptFont.setSubpixel(true);
862+
scale = interceptFont.getSize() / SkFontPriv::kCanonicalTextSizeForPaths;
863+
interceptFont.setSize(SkIntToScalar(SkFontPriv::kCanonicalTextSizeForPaths));
904864
// Note: fScale can be zero here (even if it wasn't before the divide). It can also
905865
// be very very small. We call sk_ieee_float_divide below to ensure IEEE divide behavior,
906866
// since downstream we will check for the resulting coordinates being non-finite anyway.
907867
// Thus we don't need to check for zero here.
908-
if (fPaint.getStrokeWidth() > 0 && fPaint.getStyle() != SkPaint::kFill_Style) {
909-
fPaint.setStrokeWidth(sk_ieee_float_divide(fPaint.getStrokeWidth(), fScale));
868+
if (interceptPaint.getStrokeWidth() > 0
869+
&& interceptPaint.getStyle() != SkPaint::kFill_Style) {
870+
interceptPaint.setStrokeWidth(
871+
sk_ieee_float_divide(interceptPaint.getStrokeWidth(), scale));
910872
}
911-
} else {
912-
fScale = SK_Scalar1;
913873
}
914874

915-
SkPaint::Style prevStyle = fPaint.getStyle();
916-
auto prevPE = fPaint.refPathEffect();
917-
auto prevMF = fPaint.refMaskFilter();
918-
fPaint.setStyle(SkPaint::kFill_Style);
919-
fPaint.setPathEffect(nullptr);
875+
interceptPaint.setStyle(SkPaint::kFill_Style);
876+
interceptPaint.setPathEffect(nullptr);
920877

921-
SkStrikeSpec strikeSpec = SkStrikeSpec::MakeWithNoDevice(fFont, &fPaint);
922-
fCache = strikeSpec.findOrCreateExclusiveStrike();
923-
924-
fPaint.setStyle(prevStyle);
925-
fPaint.setPathEffect(std::move(prevPE));
926-
fPaint.setMaskFilter(std::move(prevMF));
927-
928-
// now compute fXOffset if needed
878+
SkStrikeSpec strikeSpec = SkStrikeSpec::MakeWithNoDevice(interceptFont, &interceptPaint);
879+
auto cache = strikeSpec.findOrCreateExclusiveStrike();
929880

930881
SkScalar xOffset = 0;
931-
fXPos = xOffset;
932-
fPrevAdvance = 0;
882+
SkScalar xPos = xOffset;
883+
SkScalar prevAdvance = 0;
933884

934-
fGlyphs = glyphs;
935-
fStop = glyphs + count;
885+
// The typeface is scaled, so un-scale the bounds to be in the space of the typeface.
886+
SkScalar scaledBounds[2] = {bounds[0] / scale, bounds[1] / scale};
936887

937-
fBoundsBase[0] = bounds[0];
938-
fBoundsBase[1] = bounds[1];
939-
this->setPosition(x, y);
940-
}
888+
const SkPoint* posCursor = glyphRun.positions().begin();
889+
for (auto glyphID : glyphRun.glyphsIDs()) {
890+
SkPoint pos = *posCursor++;
941891

942-
bool TextInterceptsIter::next(SkScalar* array, int* count) {
943-
SkASSERT(fGlyphs < fStop);
944-
SkGlyph* glyph = fCache->glyph(*fGlyphs++);
945-
fXPos += fPrevAdvance * fScale;
946-
fPrevAdvance = glyph->advanceX();
947-
if (fCache->preparePath(glyph) != nullptr) {
948-
fCache->findIntercepts(fBounds, fScale, fXPos, glyph, array, count);
949-
}
950-
return fGlyphs < fStop;
951-
}
952-
953-
enum class TextType {
954-
kText,
955-
kPosText
956-
};
957-
958-
template<TextType TextType, typename Func>
959-
static int get_text_intercepts(
960-
const SkFont& font, const SkPaint* paint, const SkGlyphID* glyphs,
961-
int glyphCount, const SkScalar* bounds, SkScalar* array, Func posMaker) {
962-
SkASSERT(glyphCount == 0 || glyphs != nullptr);
963-
964-
const SkPoint pos0 = posMaker(0);
965-
TextInterceptsIter iter(glyphs, glyphCount, font, paint, bounds, pos0.x(), pos0.y());
966-
967-
int i = 0;
968-
int count = 0;
969-
while (iter.next(array, &count)) {
970-
if (TextType == TextType::kPosText) {
971-
const SkPoint pos = posMaker(++i);
972-
iter.setPosition(pos.x(), pos.y());
892+
SkGlyph* glyph = cache->glyph(glyphID);
893+
xPos += prevAdvance * scale;
894+
prevAdvance = glyph->advanceX();
895+
if (cache->preparePath(glyph) != nullptr) {
896+
cache->findIntercepts(scaledBounds, scale, pos.x(), glyph, intervals, intervalCount);
973897
}
974898
}
975-
976-
return count;
899+
return *intervalCount;
977900
}
978-
979901
} // namespace
980902

981903
int SkTextBlob::getIntercepts(const SkScalar bounds[2], SkScalar intervals[],
982904
const SkPaint* paint) const {
983-
int count = 0;
984-
SkTextBlobRunIterator it(this);
985905

986-
while (!it.done()) {
987-
SkScalar* runIntervals = intervals ? intervals + count : nullptr;
988-
const SkFont& font = it.font();
989-
const SkGlyphID* glyphs = it.glyphs();
990-
const int glyphCount = it.glyphCount();
991-
992-
switch (it.positioning()) {
993-
case SkTextBlobRunIterator::kDefault_Positioning: {
994-
SkPoint loc = it.offset();
995-
count += get_text_intercepts<TextType::kText>(
996-
font, paint, glyphs, glyphCount, bounds, runIntervals, [loc](int) {
997-
return loc;
998-
});
999-
} break;
1000-
case SkTextBlobRunIterator::kHorizontal_Positioning: {
1001-
const SkScalar* xpos = it.pos();
1002-
const SkScalar constY = it.offset().fY;
1003-
count += get_text_intercepts<TextType::kPosText>(
1004-
font, paint, glyphs, glyphCount, bounds, runIntervals,
1005-
[xpos, constY](int i) {
1006-
return SkPoint::Make(xpos[i], constY);
1007-
});
1008-
} break;
1009-
case SkTextBlobRunIterator::kFull_Positioning: {
1010-
const SkPoint* pos = reinterpret_cast<const SkPoint*>(it.pos());
1011-
count += get_text_intercepts<TextType::kPosText>(
1012-
font, paint, glyphs, glyphCount, bounds, runIntervals, [pos](int i) {
1013-
return pos[i];
1014-
});
1015-
} break;
1016-
case SkTextBlobRunIterator::kRSXform_Positioning:
1017-
// Unimplemented for now -- can/should we try to make this work?
1018-
break;
1019-
}
906+
SkTLazy<SkPaint> defaultPaint;
907+
if (paint == nullptr) {
908+
defaultPaint.init();
909+
paint = defaultPaint.get();
910+
}
1020911

1021-
it.next();
912+
SkGlyphRunBuilder builder;
913+
builder.textBlobToGlyphRunListIgnoringRSXForm(*paint, *this, SkPoint{0, 0});
914+
auto glyphRunList = builder.useGlyphRunList();
915+
916+
int intervalCount = 0;
917+
for (const SkGlyphRun& glyphRun : glyphRunList) {
918+
intervalCount = get_glyph_run_intercepts(glyphRun, *paint, bounds, intervals, &intervalCount);
1022919
}
1023920

1024-
return count;
921+
return intervalCount;
1025922
}

0 commit comments

Comments
 (0)