Fixing incorrect boundaries calculations + 3 ways of drawing a paragraph
Bug: skia:10620 Change-Id: I70086013130b23435d4e7c5ba375731760deb174 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/311447 Reviewed-by: Ben Wagner <bungeman@google.com> Commit-Queue: Julia Lavrova <jlavrova@google.com>
This commit is contained in:
parent
6e0fa409e6
commit
d279cee2f2
@ -144,6 +144,12 @@ enum class LineMetricStyle : uint8_t {
|
||||
CSS
|
||||
};
|
||||
|
||||
enum class DrawOptions {
|
||||
kRecord,
|
||||
kReplay,
|
||||
kDirect
|
||||
};
|
||||
|
||||
} // namespace textlayout
|
||||
} // namespace skia
|
||||
|
||||
|
@ -99,7 +99,6 @@ struct ParagraphStyle {
|
||||
SkScalar getHeight() const { return fHeight; }
|
||||
void setHeight(SkScalar height) { fHeight = height; }
|
||||
|
||||
|
||||
TextHeightBehavior getTextHeightBehavior() const { return fTextHeightBehavior; }
|
||||
void setTextHeightBehavior(TextHeightBehavior v) { fTextHeightBehavior = v; }
|
||||
|
||||
@ -110,6 +109,8 @@ struct ParagraphStyle {
|
||||
TextAlign effective_align() const;
|
||||
bool hintingIsOn() const { return fHintingIsOn; }
|
||||
void turnHintingOff() { fHintingIsOn = false; }
|
||||
DrawOptions getDrawOptions() { return fDrawingOptions; }
|
||||
void setDrawOptions(DrawOptions value) { fDrawingOptions = value; }
|
||||
|
||||
private:
|
||||
StrutStyle fStrutStyle;
|
||||
@ -121,6 +122,7 @@ private:
|
||||
SkScalar fHeight;
|
||||
TextHeightBehavior fTextHeightBehavior;
|
||||
bool fHintingIsOn;
|
||||
DrawOptions fDrawingOptions = DrawOptions::kReplay;
|
||||
};
|
||||
} // namespace textlayout
|
||||
} // namespace skia
|
||||
|
@ -2898,6 +2898,46 @@ private:
|
||||
typedef Sample INHERITED;
|
||||
};
|
||||
|
||||
class ParagraphView46 : public ParagraphView_Base {
|
||||
protected:
|
||||
SkString name() override { return SkString("Paragraph44"); }
|
||||
|
||||
void onDrawContent(SkCanvas* canvas) override {
|
||||
|
||||
SkString text;
|
||||
for (auto i = 0; i < 150; ++i) text.append("XXXXXXXXXX");
|
||||
canvas->drawColor(SK_ColorWHITE);
|
||||
|
||||
auto fontCollection = sk_make_sp<FontCollection>();
|
||||
fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
|
||||
fontCollection->enableFontFallback();
|
||||
|
||||
ParagraphStyle paragraph_style;
|
||||
|
||||
auto draw = [&](DrawOptions options) {
|
||||
paragraph_style.setDrawOptions(options);
|
||||
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
|
||||
TextStyle text_style;
|
||||
text_style.setColor(SK_ColorBLACK);
|
||||
text_style.setFontFamilies({SkString("Roboto")});
|
||||
text_style.setFontSize(20);
|
||||
builder.pushStyle(text_style);
|
||||
builder.addText(text.c_str());
|
||||
auto paragraph = builder.Build();
|
||||
paragraph->layout(width() / 3);
|
||||
paragraph->paint(canvas, 0, 0);
|
||||
canvas->translate(width() / 3, 0);
|
||||
};
|
||||
|
||||
draw(DrawOptions::kReplay);
|
||||
draw(DrawOptions::kRecord);
|
||||
draw(DrawOptions::kDirect);
|
||||
}
|
||||
|
||||
private:
|
||||
typedef Sample INHERITED;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
@ -2944,3 +2984,4 @@ DEF_SAMPLE(return new ParagraphView42();)
|
||||
DEF_SAMPLE(return new ParagraphView43();)
|
||||
DEF_SAMPLE(return new ParagraphView44();)
|
||||
DEF_SAMPLE(return new ParagraphView45();)
|
||||
DEF_SAMPLE(return new ParagraphView46();)
|
||||
|
@ -81,8 +81,7 @@ ParagraphImpl::ParagraphImpl(const SkString& text,
|
||||
, fPicture(nullptr)
|
||||
, fStrutMetrics(false)
|
||||
, fOldWidth(0)
|
||||
, fOldHeight(0)
|
||||
, fOrigin(SkRect::MakeEmpty()) {
|
||||
, fOldHeight(0) {
|
||||
fICU = ::skia::SkUnicode::Make();
|
||||
}
|
||||
|
||||
@ -184,8 +183,6 @@ void ParagraphImpl::layout(SkScalar rawWidth) {
|
||||
if (fState < kFormatted) {
|
||||
// Build the picture lazily not until we actually have to paint (or never)
|
||||
this->formatLines(fWidth);
|
||||
// We have to calculate the paragraph boundaries only after we format the lines
|
||||
this->calculateBoundaries();
|
||||
fState = kFormatted;
|
||||
}
|
||||
|
||||
@ -207,14 +204,32 @@ void ParagraphImpl::layout(SkScalar rawWidth) {
|
||||
|
||||
void ParagraphImpl::paint(SkCanvas* canvas, SkScalar x, SkScalar y) {
|
||||
|
||||
if (fParagraphStyle.getDrawOptions() == DrawOptions::kDirect) {
|
||||
// Paint the text without recording it
|
||||
canvas->save();
|
||||
canvas->translate(x, y);
|
||||
this->paintLines(canvas);
|
||||
canvas->restore();
|
||||
return;
|
||||
}
|
||||
|
||||
if (fState < kDrawn) {
|
||||
// Record the picture anyway (but if we have some pieces in the cache they will be used)
|
||||
this->paintLinesIntoPicture();
|
||||
fState = kDrawn;
|
||||
}
|
||||
|
||||
SkMatrix matrix = SkMatrix::Translate(x + fOrigin.fLeft, y + fOrigin.fTop);
|
||||
canvas->drawPicture(fPicture, &matrix, nullptr);
|
||||
if (fParagraphStyle.getDrawOptions() == DrawOptions::kReplay) {
|
||||
// Replay the recorded picture
|
||||
canvas->save();
|
||||
canvas->translate(x, y);
|
||||
fPicture->playback(canvas);
|
||||
canvas->restore();
|
||||
} else {
|
||||
// Draw the picture
|
||||
SkMatrix matrix = SkMatrix::Translate(x, y);
|
||||
canvas->drawPicture(fPicture, &matrix, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void ParagraphImpl::resetContext() {
|
||||
@ -417,13 +432,6 @@ void ParagraphImpl::breakShapedTextIntoLines(SkScalar maxWidth) {
|
||||
auto& line = this->addLine(offset, advance, text, textWithSpaces, clusters, clustersWithGhosts, widthWithSpaces, metrics);
|
||||
if (addEllipsis) {
|
||||
line.createEllipsis(maxWidth, fParagraphStyle.getEllipsis(), true);
|
||||
if (line.ellipsis() != nullptr) {
|
||||
// Make sure the paragraph boundaries include its ellipsis
|
||||
auto size = line.ellipsis()->advance();
|
||||
auto offset = line.ellipsis()->offset();
|
||||
SkRect boundaries = SkRect::MakeXYWH(offset.fX, offset.fY, size.fX, size.fY);
|
||||
fOrigin.joinPossiblyEmptyRect(boundaries);
|
||||
}
|
||||
}
|
||||
|
||||
fLongestLine = std::max(fLongestLine, nearlyZero(advance.fX) ? widthWithSpaces : advance.fX);
|
||||
@ -476,14 +484,21 @@ void ParagraphImpl::formatLines(SkScalar maxWidth) {
|
||||
|
||||
void ParagraphImpl::paintLinesIntoPicture() {
|
||||
SkPictureRecorder recorder;
|
||||
SkCanvas* textCanvas = recorder.beginRecording(fOrigin.width(), fOrigin.height(), nullptr, 0);
|
||||
textCanvas->translate(-fOrigin.fLeft, -fOrigin.fTop);
|
||||
SkCanvas* textCanvas = recorder.beginRecording(this->getMaxWidth(), this->getHeight(), nullptr, 0);
|
||||
|
||||
auto bounds = SkRect::MakeEmpty();
|
||||
for (auto& line : fLines) {
|
||||
line.paint(textCanvas);
|
||||
auto boundaries = line.paint(textCanvas);
|
||||
bounds.joinPossiblyEmptyRect(boundaries);
|
||||
}
|
||||
|
||||
fPicture = recorder.finishRecordingAsPicture();
|
||||
fPicture = recorder.finishRecordingAsPictureWithCull(bounds);
|
||||
}
|
||||
|
||||
void ParagraphImpl::paintLines(SkCanvas* canvas) {
|
||||
for (auto& line : fLines) {
|
||||
line.paint(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
void ParagraphImpl::resolveStrut() {
|
||||
@ -539,12 +554,6 @@ BlockRange ParagraphImpl::findAllBlocks(TextRange textRange) {
|
||||
return { begin, end + 1 };
|
||||
}
|
||||
|
||||
void ParagraphImpl::calculateBoundaries() {
|
||||
for (auto& line : fLines) {
|
||||
fOrigin.joinPossiblyEmptyRect(line.calculateBoundaries());
|
||||
}
|
||||
}
|
||||
|
||||
TextLine& ParagraphImpl::addLine(SkVector offset,
|
||||
SkVector advance,
|
||||
TextRange text,
|
||||
|
@ -179,7 +179,6 @@ public:
|
||||
|
||||
void setState(InternalState state);
|
||||
sk_sp<SkPicture> getPicture() { return fPicture; }
|
||||
SkRect getBoundaries() const { return fOrigin; }
|
||||
|
||||
SkScalar widthWithTrailingSpaces() { return fMaxWidthWithTrailingSpaces; }
|
||||
|
||||
@ -193,6 +192,7 @@ public:
|
||||
bool shapeTextIntoEndlessLine();
|
||||
void breakShapedTextIntoLines(SkScalar maxWidth);
|
||||
void paintLinesIntoPicture();
|
||||
void paintLines(SkCanvas* canvas);
|
||||
|
||||
void updateTextAlign(TextAlign textAlign) override;
|
||||
void updateText(size_t from, SkString text) override;
|
||||
@ -229,8 +229,6 @@ private:
|
||||
friend class TextWrapper;
|
||||
friend class OneLineShaper;
|
||||
|
||||
void calculateBoundaries();
|
||||
|
||||
void computeEmptyMetrics();
|
||||
|
||||
// Input
|
||||
@ -269,7 +267,6 @@ private:
|
||||
SkScalar fOldWidth;
|
||||
SkScalar fOldHeight;
|
||||
SkScalar fMaxWidthWithTrailingSpaces;
|
||||
SkRect fOrigin;
|
||||
|
||||
std::unique_ptr<SkUnicode> fICU;
|
||||
};
|
||||
|
@ -159,78 +159,10 @@ TextLine::TextLine(ParagraphImpl* owner,
|
||||
}
|
||||
}
|
||||
|
||||
SkRect TextLine::calculateBoundaries() {
|
||||
|
||||
// For flutter: height and/or width and/or baseline! can be Inf
|
||||
// (coming from placeholders - we should ignore it)
|
||||
auto boundaries = SkRect::MakeWH(
|
||||
SkScalarIsFinite(fAdvance.fX) ? fAdvance.fX : 0,
|
||||
SkScalarIsFinite(fAdvance.fY) ? fAdvance.fY : 0);
|
||||
auto baseline = SkScalarIsFinite(this->baseline()) ? this->baseline() : 0;
|
||||
auto clusters = fOwner->clusters(fClusterRange);
|
||||
Run* run = nullptr;
|
||||
auto runShift = 0.0f;
|
||||
auto clusterShift = 0.0f;
|
||||
for (auto cluster = clusters.begin(); cluster != clusters.end(); ++cluster) {
|
||||
if (run == nullptr || cluster->runIndex() != run->index()) {
|
||||
run = &fOwner->run(cluster->runIndex());
|
||||
runShift += clusterShift;
|
||||
clusterShift = 0;
|
||||
}
|
||||
clusterShift += cluster->width();
|
||||
for (auto i = cluster->startPos(); i < cluster->endPos(); ++i) {
|
||||
auto posX = run->positionX(i);
|
||||
auto posY = run->posY(i);
|
||||
auto bounds = run->getBounds(i);
|
||||
bounds.offset(posX + runShift, posY);
|
||||
boundaries.joinPossiblyEmptyRect(bounds);
|
||||
}
|
||||
}
|
||||
|
||||
// We need to take in account all the shadows when we calculate the boundaries
|
||||
// TODO: Need to find a better solution
|
||||
if (fHasShadows) {
|
||||
SkRect shadowRect = SkRect::MakeEmpty();
|
||||
this->iterateThroughVisualRuns(false,
|
||||
[this, &shadowRect, boundaries]
|
||||
(const Run* run, SkScalar runOffsetInLine, TextRange textRange, SkScalar* runWidthInLine) {
|
||||
*runWidthInLine = this->iterateThroughSingleRunByStyles(
|
||||
run, runOffsetInLine, textRange, StyleType::kShadow,
|
||||
[&shadowRect, boundaries](TextRange textRange, const TextStyle& style, const ClipContext& context) {
|
||||
|
||||
for (TextShadow shadow : style.getShadows()) {
|
||||
if (!shadow.hasShadow()) continue;
|
||||
SkPaint paint;
|
||||
paint.setColor(shadow.fColor);
|
||||
if (shadow.fBlurRadius != 0.0) {
|
||||
auto filter = SkMaskFilter::MakeBlur(
|
||||
kNormal_SkBlurStyle,
|
||||
SkDoubleToScalar(shadow.fBlurRadius),
|
||||
false);
|
||||
paint.setMaskFilter(filter);
|
||||
SkRect bound;
|
||||
paint.doComputeFastBounds(boundaries, &bound, SkPaint::Style::kFill_Style);
|
||||
shadowRect.joinPossiblyEmptyRect(bound);
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
});
|
||||
boundaries.fLeft += shadowRect.fLeft;
|
||||
boundaries.fTop += shadowRect.fTop;
|
||||
boundaries.fRight += shadowRect.fRight;
|
||||
boundaries.fBottom += shadowRect.fBottom;
|
||||
}
|
||||
|
||||
boundaries.offset(this->offset()); // Line offset from the beginning of the para
|
||||
boundaries.offset(0, baseline); // Down by baseline
|
||||
|
||||
return boundaries;
|
||||
}
|
||||
|
||||
void TextLine::paint(SkCanvas* textCanvas) {
|
||||
SkRect TextLine::paint(SkCanvas* textCanvas) {
|
||||
auto bounds = SkRect::MakeEmpty();
|
||||
if (this->empty()) {
|
||||
return;
|
||||
return bounds;
|
||||
}
|
||||
|
||||
if (fHasBackground) {
|
||||
@ -248,19 +180,20 @@ void TextLine::paint(SkCanvas* textCanvas) {
|
||||
|
||||
if (fHasShadows) {
|
||||
this->iterateThroughVisualRuns(false,
|
||||
[textCanvas, this]
|
||||
[textCanvas, &bounds, this]
|
||||
(const Run* run, SkScalar runOffsetInLine, TextRange textRange, SkScalar* runWidthInLine) {
|
||||
*runWidthInLine = this->iterateThroughSingleRunByStyles(
|
||||
run, runOffsetInLine, textRange, StyleType::kShadow,
|
||||
[textCanvas, this](TextRange textRange, const TextStyle& style, const ClipContext& context) {
|
||||
this->paintShadow(textCanvas, textRange, style, context);
|
||||
[textCanvas, &bounds, this](TextRange textRange, const TextStyle& style, const ClipContext& context) {
|
||||
auto shadowBounds = this->paintShadow(textCanvas, textRange, style, context);
|
||||
bounds.joinPossiblyEmptyRect(shadowBounds);
|
||||
});
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
this->iterateThroughVisualRuns(false,
|
||||
[textCanvas, this]
|
||||
[textCanvas, &bounds, this]
|
||||
(const Run* run, SkScalar runOffsetInLine, TextRange textRange, SkScalar* runWidthInLine) {
|
||||
if (run->placeholderStyle() != nullptr) {
|
||||
*runWidthInLine = run->advance().fX;
|
||||
@ -268,8 +201,9 @@ void TextLine::paint(SkCanvas* textCanvas) {
|
||||
}
|
||||
*runWidthInLine = this->iterateThroughSingleRunByStyles(
|
||||
run, runOffsetInLine, textRange, StyleType::kForeground,
|
||||
[textCanvas, this](TextRange textRange, const TextStyle& style, const ClipContext& context) {
|
||||
this->paintText(textCanvas, textRange, style, context);
|
||||
[textCanvas, &bounds, this](TextRange textRange, const TextStyle& style, const ClipContext& context) {
|
||||
auto textBounds = this->paintText(textCanvas, textRange, style, context);
|
||||
bounds.joinPossiblyEmptyRect(textBounds);
|
||||
});
|
||||
return true;
|
||||
});
|
||||
@ -286,6 +220,8 @@ void TextLine::paint(SkCanvas* textCanvas) {
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
void TextLine::format(TextAlign align, SkScalar maxWidth) {
|
||||
@ -358,10 +294,10 @@ SkScalar TextLine::metricsWithoutMultiplier(TextHeightBehavior correction) {
|
||||
return delta;
|
||||
}
|
||||
|
||||
void TextLine::paintText(SkCanvas* canvas, TextRange textRange, const TextStyle& style, const ClipContext& context) const {
|
||||
SkRect TextLine::paintText(SkCanvas* canvas, TextRange textRange, const TextStyle& style, const ClipContext& context) const {
|
||||
|
||||
if (context.run->placeholderStyle() != nullptr) {
|
||||
return;
|
||||
return SkRect::MakeEmpty();
|
||||
}
|
||||
|
||||
SkPaint paint;
|
||||
@ -379,13 +315,23 @@ void TextLine::paintText(SkCanvas* canvas, TextRange textRange, const TextStyle&
|
||||
canvas->clipRect(extendHeight(context).makeOffset(this->offset()));
|
||||
}
|
||||
|
||||
SkRect textBounds = SkRect::MakeEmpty();
|
||||
SkScalar correctedBaseline = SkScalarFloorToScalar(this->baseline() + 0.5);
|
||||
canvas->drawTextBlob(builder.make(),
|
||||
auto blob = builder.make();
|
||||
if (blob != nullptr) {
|
||||
auto bounds = blob->bounds();
|
||||
bounds.offset(this->offset().fX, this->offset().fY);
|
||||
textBounds.joinPossiblyEmptyRect(bounds);
|
||||
}
|
||||
|
||||
canvas->drawTextBlob(blob,
|
||||
this->offset().fX + context.fTextShift, this->offset().fY + correctedBaseline, paint);
|
||||
|
||||
if (context.clippingNeeded) {
|
||||
canvas->restore();
|
||||
}
|
||||
|
||||
return textBounds;
|
||||
}
|
||||
|
||||
void TextLine::paintBackground(SkCanvas* canvas, TextRange textRange, const TextStyle& style, const ClipContext& context) const {
|
||||
@ -394,8 +340,11 @@ void TextLine::paintBackground(SkCanvas* canvas, TextRange textRange, const Text
|
||||
}
|
||||
}
|
||||
|
||||
void TextLine::paintShadow(SkCanvas* canvas, TextRange textRange, const TextStyle& style, const ClipContext& context) const {
|
||||
auto shiftDown = this->baseline();
|
||||
SkRect TextLine::paintShadow(SkCanvas* canvas, TextRange textRange, const TextStyle& style, const ClipContext& context) const {
|
||||
|
||||
SkScalar correctedBaseline = SkScalarFloorToScalar(this->baseline() + 0.5);
|
||||
SkRect shadowBounds = SkRect::MakeEmpty();
|
||||
|
||||
for (TextShadow shadow : style.getShadows()) {
|
||||
if (!shadow.hasShadow()) continue;
|
||||
|
||||
@ -416,15 +365,25 @@ void TextLine::paintShadow(SkCanvas* canvas, TextRange textRange, const TextStyl
|
||||
clip.offset(this->offset());
|
||||
canvas->clipRect(clip);
|
||||
}
|
||||
canvas->drawTextBlob(builder.make(),
|
||||
auto blob = builder.make();
|
||||
if (blob != nullptr) {
|
||||
auto bounds = blob->bounds();
|
||||
bounds.offset(
|
||||
this->offset().fX + shadow.fOffset.x(),
|
||||
this->offset().fY + shadow.fOffset.y()
|
||||
);
|
||||
shadowBounds.joinPossiblyEmptyRect(bounds);
|
||||
}
|
||||
canvas->drawTextBlob(blob,
|
||||
this->offset().fX + shadow.fOffset.x() + context.fTextShift,
|
||||
this->offset().fY + shadow.fOffset.y() + shiftDown,
|
||||
this->offset().fY + shadow.fOffset.y() + correctedBaseline,
|
||||
paint);
|
||||
|
||||
if (context.clippingNeeded) {
|
||||
canvas->restore();
|
||||
}
|
||||
}
|
||||
|
||||
return shadowBounds;
|
||||
}
|
||||
|
||||
void TextLine::paintDecorations(SkCanvas* canvas, TextRange textRange, const TextStyle& style, const ClipContext& context) const {
|
||||
|
@ -84,7 +84,7 @@ public:
|
||||
void iterateThroughClustersInGlyphsOrder(bool reverse, bool includeGhosts, const ClustersVisitor& visitor) const;
|
||||
|
||||
void format(TextAlign align, SkScalar maxWidth);
|
||||
void paint(SkCanvas* canvas);
|
||||
SkRect paint(SkCanvas* canvas);
|
||||
|
||||
void createEllipsis(SkScalar maxWidth, const SkString& ellipsis, bool ltr);
|
||||
|
||||
@ -109,8 +109,6 @@ public:
|
||||
|
||||
LineMetrics getMetrics() const;
|
||||
|
||||
SkRect calculateBoundaries();
|
||||
|
||||
SkRect extendHeight(const ClipContext& context) const;
|
||||
|
||||
SkScalar metricsWithoutMultiplier(TextHeightBehavior correction);
|
||||
@ -123,9 +121,9 @@ private:
|
||||
std::unique_ptr<Run> shapeEllipsis(const SkString& ellipsis, Run* run);
|
||||
void justify(SkScalar maxWidth);
|
||||
|
||||
void paintText(SkCanvas* canvas, TextRange textRange, const TextStyle& style, const ClipContext& context) const;
|
||||
SkRect paintText(SkCanvas* canvas, TextRange textRange, const TextStyle& style, const ClipContext& context) const;
|
||||
void paintBackground(SkCanvas* canvas, TextRange textRange, const TextStyle& style, const ClipContext& context) const;
|
||||
void paintShadow(SkCanvas* canvas, TextRange textRange, const TextStyle& style, const ClipContext& context) const;
|
||||
SkRect paintShadow(SkCanvas* canvas, TextRange textRange, const TextStyle& style, const ClipContext& context) const;
|
||||
void paintDecorations(SkCanvas* canvas, TextRange textRange, const TextStyle& style, const ClipContext& context) const;
|
||||
|
||||
void shiftCluster(const Cluster* cluster, SkScalar shift, SkScalar prevShift);
|
||||
|
@ -5400,19 +5400,18 @@ DEF_TEST(SkParagraph_PlaceholderHeightInf, reporter) {
|
||||
placeholder_style.fBaselineOffset = SK_ScalarInfinity;
|
||||
|
||||
ParagraphStyle paragraph_style;
|
||||
|
||||
paragraph_style.setDrawOptions(DrawOptions::kRecord);
|
||||
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
|
||||
builder.pushStyle(text_style);
|
||||
builder.addText("Limited by budget");
|
||||
builder.addPlaceholder(placeholder_style);
|
||||
auto paragraph = builder.Build();
|
||||
paragraph->layout(SK_ScalarInfinity);
|
||||
paragraph->paint(canvas.get(), 0, 0);
|
||||
|
||||
auto impl = static_cast<ParagraphImpl*>(paragraph.get());
|
||||
REPORTER_ASSERT(reporter, SkScalarIsFinite(impl->getBoundaries().height()));
|
||||
REPORTER_ASSERT(reporter, SkScalarIsFinite(impl->getBoundaries().width()));
|
||||
|
||||
paragraph->paint(canvas.get(), 0, 0);
|
||||
REPORTER_ASSERT(reporter, SkScalarIsFinite(impl->getPicture()->cullRect().height()));
|
||||
REPORTER_ASSERT(reporter, SkScalarIsFinite(impl->getPicture()->cullRect().width()));
|
||||
}
|
||||
|
||||
DEF_TEST(SkParagraph_LineMetricsTextAlign, reporter) {
|
||||
|
Loading…
Reference in New Issue
Block a user