Reland "Cache text blobs computed by TextLine::paintText"

Change-Id: If166d7a2b287774054f833ac0f7b564bbcac9fa8
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/342936
Reviewed-by: Julia Lavrova <jlavrova@google.com>
Commit-Queue: Jason Simmons <jsimmons@google.com>
This commit is contained in:
Jason Simmons 2020-12-10 06:54:38 -08:00 committed by Skia Commit-Bot
parent 7b80726d0e
commit 6d14f3841d
2 changed files with 58 additions and 33 deletions

View File

@ -105,7 +105,8 @@ TextLine::TextLine(ParagraphImpl* owner,
, fHasShadows(false)
, fHasDecorations(false)
, fAscentStyle(LineMetricStyle::CSS)
, fDescentStyle(LineMetricStyle::CSS) {
, fDescentStyle(LineMetricStyle::CSS)
, fTextBlobCachePopulated(false) {
// Reorder visual runs
auto& start = owner->cluster(fGhostClusterRange.start);
auto& end = owner->cluster(fGhostClusterRange.end - 1);
@ -192,21 +193,27 @@ SkRect TextLine::paint(SkCanvas* textCanvas, SkScalar x, SkScalar y) {
});
}
this->iterateThroughVisualRuns(false,
[textCanvas, x, y, &bounds, this]
(const Run* run, SkScalar runOffsetInLine, TextRange textRange, SkScalar* runWidthInLine) {
if (run->placeholderStyle() != nullptr) {
*runWidthInLine = run->advance().fX;
if (!fTextBlobCachePopulated) {
this->iterateThroughVisualRuns(false,
[textCanvas, x, y, this]
(const Run* run, SkScalar runOffsetInLine, TextRange textRange, SkScalar* runWidthInLine) {
if (run->placeholderStyle() != nullptr) {
*runWidthInLine = run->advance().fX;
return true;
}
*runWidthInLine = this->iterateThroughSingleRunByStyles(
run, runOffsetInLine, textRange, StyleType::kForeground,
[textCanvas, x, y, this](TextRange textRange, const TextStyle& style, const ClipContext& context) {
this->buildTextBlob(textCanvas, x, y, textRange, style, context);
});
return true;
}
*runWidthInLine = this->iterateThroughSingleRunByStyles(
run, runOffsetInLine, textRange, StyleType::kForeground,
[textCanvas, x, y, &bounds, this](TextRange textRange, const TextStyle& style, const ClipContext& context) {
auto textBounds = this->paintText(textCanvas, x, y, textRange, style, context);
bounds.joinPossiblyEmptyRect(textBounds);
});
return true;
});
fTextBlobCachePopulated = true;
}
for (auto& record : fTextBlobCache) {
record.paint(textCanvas, x, y);
bounds.joinPossiblyEmptyRect(record.fBounds);
}
if (fHasDecorations) {
this->iterateThroughVisualRuns(false,
@ -294,44 +301,49 @@ SkScalar TextLine::metricsWithoutMultiplier(TextHeightBehavior correction) {
return delta;
}
SkRect TextLine::paintText(SkCanvas* canvas, SkScalar x, SkScalar y, TextRange textRange, const TextStyle& style, const ClipContext& context) const {
void TextLine::buildTextBlob(SkCanvas* canvas, SkScalar x, SkScalar y, TextRange textRange, const TextStyle& style, const ClipContext& context) {
if (context.run->placeholderStyle() != nullptr) {
return SkRect::MakeEmpty();
return;
}
SkPaint paint;
fTextBlobCache.emplace_back();
TextBlobRecord& record = fTextBlobCache.back();
if (style.hasForeground()) {
paint = style.getForeground();
record.fPaint = style.getForeground();
} else {
paint.setColor(style.getColor());
record.fPaint.setColor(style.getColor());
}
// TODO: This is the change for flutter, must be removed later
SkTextBlobBuilder builder;
context.run->copyTo(builder, SkToU32(context.pos), context.size);
record.fClippingNeeded = context.clippingNeeded;
if (context.clippingNeeded) {
canvas->save();
canvas->clipRect(extendHeight(context).makeOffset(this->offset() + SkPoint::Make(x, y)));
record.fClipRect = extendHeight(context).makeOffset(this->offset());
}
SkRect textBounds = SkRect::MakeEmpty();
SkScalar correctedBaseline = SkScalarFloorToScalar(this->baseline() + 0.5);
auto blob = builder.make();
if (blob != nullptr) {
auto bounds = blob->bounds();
record.fBlob = builder.make();
if (record.fBlob != nullptr) {
auto bounds = record.fBlob->bounds();
bounds.offset(x + this->offset().fX, y + this->offset().fY);
textBounds.joinPossiblyEmptyRect(bounds);
record.fBounds.joinPossiblyEmptyRect(bounds);
}
canvas->drawTextBlob(blob,
x + this->offset().fX + context.fTextShift, y + this->offset().fY + correctedBaseline, paint);
record.fOffset = SkPoint::Make(this->offset().fX + context.fTextShift,
this->offset().fY + correctedBaseline);
}
if (context.clippingNeeded) {
void TextLine::TextBlobRecord::paint(SkCanvas* canvas, SkScalar x, SkScalar y) {
if (fClippingNeeded) {
canvas->save();
canvas->clipRect(fClipRect.makeOffset(x, y));
}
canvas->drawTextBlob(fBlob, x + fOffset.x(), y + fOffset.y(), fPaint);
if (fClippingNeeded) {
canvas->restore();
}
return textBounds;
}
void TextLine::paintBackground(SkCanvas* canvas, SkScalar x, SkScalar y, TextRange textRange, const TextStyle& style, const ClipContext& context) const {

View File

@ -121,7 +121,7 @@ private:
std::unique_ptr<Run> shapeEllipsis(const SkString& ellipsis, Run* run);
void justify(SkScalar maxWidth);
SkRect paintText(SkCanvas* canvas, SkScalar x, SkScalar y, TextRange textRange, const TextStyle& style, const ClipContext& context) const;
void buildTextBlob(SkCanvas* canvas, SkScalar x, SkScalar y, TextRange textRange, const TextStyle& style, const ClipContext& context);
void paintBackground(SkCanvas* canvas, SkScalar x, SkScalar y, TextRange textRange, const TextStyle& style, const ClipContext& context) const;
SkRect paintShadow(SkCanvas* canvas, SkScalar x, SkScalar y, TextRange textRange, const TextStyle& style, const ClipContext& context) const;
void paintDecorations(SkCanvas* canvas, SkScalar x, SkScalar y, TextRange textRange, const TextStyle& style, const ClipContext& context) const;
@ -149,6 +149,19 @@ private:
LineMetricStyle fAscentStyle;
LineMetricStyle fDescentStyle;
struct TextBlobRecord {
void paint(SkCanvas* canvas, SkScalar x, SkScalar y);
sk_sp<SkTextBlob> fBlob;
SkPoint fOffset = SkPoint::Make(0.0f, 0.0f);
SkPaint fPaint;
SkRect fBounds = SkRect::MakeEmpty();
bool fClippingNeeded = false;
SkRect fClipRect = SkRect::MakeEmpty();
};
bool fTextBlobCachePopulated;
std::vector<TextBlobRecord> fTextBlobCache;
};
} // namespace textlayout
} // namespace skia