Fix TextHeightBehavior

Change-Id: I08148d2e942691c8d12070940bc2ee28855ffe59
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/288761
Reviewed-by: Ben Wagner <bungeman@google.com>
Commit-Queue: Julia Lavrova <jlavrova@google.com>
This commit is contained in:
Julia Lavrova 2020-05-08 11:53:19 -04:00 committed by Skia Commit-Bot
parent a117e7b75b
commit dd1de25896
7 changed files with 163 additions and 42 deletions

View File

@ -133,6 +133,13 @@ enum TextHeightBehavior {
kDisableAll = 0x1 | 0x2,
};
enum class LineMetricStyle : uint8_t {
// Use ascent, descent, etc from a fixed baseline.
Typographic,
// Use ascent, descent, etc like css with the leading split and with height adjustments
CSS
};
} // namespace textlayout
} // namespace skia

View File

@ -447,6 +447,7 @@ void ParagraphImpl::breakShapedTextIntoLines(SkScalar maxWidth) {
fHeight += delta;
// Shift all the lines up
for (auto& line : fLines) {
if (line.isFirstLine()) continue;
line.shiftVertically(delta);
}
}

View File

@ -168,7 +168,7 @@ void Run::iterateThroughClustersInTextOrder(const ClusterTextVisitor& visitor) {
fClusterStart + cluster,
fClusterStart + nextCluster,
this->calculateWidth(start, glyph, glyph == size()),
this->calculateHeight());
this->calculateHeight(LineMetricStyle::CSS, LineMetricStyle::CSS));
start = glyph;
cluster = nextCluster;
@ -188,7 +188,7 @@ void Run::iterateThroughClustersInTextOrder(const ClusterTextVisitor& visitor) {
fClusterStart + cluster,
fClusterStart + nextCluster,
this->calculateWidth(start, glyph, glyph == 0),
this->calculateHeight());
this->calculateHeight(LineMetricStyle::CSS, LineMetricStyle::CSS));
glyph = start;
cluster = nextCluster;

View File

@ -111,7 +111,7 @@ public:
bool leftToRight() const { return fBidiLevel % 2 == 0; }
TextDirection getTextDirection() const { return leftToRight() ? TextDirection::kLtr : TextDirection::kRtl; }
size_t index() const { return fIndex; }
SkScalar lineHeight() const { return fHeightMultiplier; }
SkScalar heightMultiplier() const { return fHeightMultiplier; }
PlaceholderStyle* placeholderStyle() const;
bool isPlaceholder() const { return fPlaceholderIndex != std::numeric_limits<size_t>::max(); }
size_t clusterIndex(size_t pos) const { return fClusterIndexes[pos]; }
@ -136,11 +136,12 @@ public:
SkScalar addSpacesEvenly(SkScalar space, Cluster* cluster);
void shift(const Cluster* cluster, SkScalar offset);
SkScalar calculateHeight() const {
if (fHeightMultiplier == 0) {
return fFontMetrics.fDescent - fFontMetrics.fAscent;
}
return fHeightMultiplier * fFont.getSize();
SkScalar calculateHeight(LineMetricStyle ascentStyle, LineMetricStyle descentStyle) const {
auto ascent = ascentStyle == LineMetricStyle::Typographic ? this->ascent()
: this->correctAscent();
auto descent = descentStyle == LineMetricStyle::Typographic ? this->descent()
: this->correctDescent();
return descent - ascent;
}
SkScalar calculateWidth(size_t start, size_t end, bool clip) const;
@ -403,8 +404,9 @@ public:
}
}
SkScalar runTop(const Run* run) const {
return fLeading / 2 - fAscent + run->ascent() + delta();
SkScalar runTop(const Run* run, LineMetricStyle ascentStyle) const {
return fLeading / 2 - fAscent +
(ascentStyle == LineMetricStyle::Typographic ? run->ascent() : run->correctAscent()) + delta();
}
SkScalar height() const {
@ -424,6 +426,7 @@ public:
private:
friend class TextWrapper;
friend class TextLine;
SkScalar fAscent;
SkScalar fDescent;

View File

@ -82,7 +82,9 @@ TextLine::TextLine(ParagraphImpl* master,
, fSizes(sizes)
, fHasBackground(false)
, fHasShadows(false)
, fHasDecorations(false) {
, fHasDecorations(false)
, fAscentStyle(LineMetricStyle::CSS)
, fDescentStyle(LineMetricStyle::CSS) {
// Reorder visual runs
auto& start = master->cluster(fGhostClusterRange.start);
auto& end = master->cluster(fGhostClusterRange.end - 1);
@ -317,9 +319,13 @@ SkScalar TextLine::metricsWithoutMultiplier(TextHeightBehavior correction) {
});
SkScalar delta = 0;
if (correction == TextHeightBehavior::kDisableFirstAscent) {
delta += (this->fSizes.ascent() - result.ascent());
delta += (this->fSizes.fAscent - result.fAscent);
this->fSizes.fAscent -= delta;
this->fAscentStyle = LineMetricStyle::Typographic;
} else if (correction == TextHeightBehavior::kDisableLastDescent) {
delta -= (this->fSizes.descent() - result.descent());
delta -= (this->fSizes.fDescent - result.fDescent);
this->fSizes.fDescent -= delta;
this->fDescentStyle = LineMetricStyle::Typographic;
}
fAdvance.fY += delta;
return delta;
@ -545,7 +551,7 @@ Run* TextLine::shapeEllipsis(const SkString& ellipsis, Run* run) {
SkString fEllipsis;
};
ShapeHandler handler(run->lineHeight(), ellipsis);
ShapeHandler handler(run->heightMultiplier(), ellipsis);
std::unique_ptr<SkShaper> shaper = SkShaper::MakeShapeDontWrapOrReorder();
SkASSERT_RELEASE(shaper != nullptr);
shaper->shape(ellipsis.c_str(), ellipsis.size(), run->font(), true,
@ -568,22 +574,21 @@ TextLine::ClipContext TextLine::measureTextInsideOneRun(TextRange textRange,
SkASSERT(textRange == run->textRange());
result.fTextShift = runOffsetInLine;
result.clip = SkRect::MakeXYWH(runOffsetInLine,
sizes().runTop(run),
sizes().runTop(run, this->fAscentStyle),
run->advance().fX,
run->calculateHeight());
run->calculateHeight(this->fAscentStyle,this->fDescentStyle));
return result;
} else if (run->isPlaceholder()) {
if (SkScalarIsFinite(run->fFontMetrics.fAscent)) {
result.clip = SkRect::MakeXYWH(runOffsetInLine,
sizes().runTop(run),
sizes().runTop(run, this->fAscentStyle),
run->advance().fX,
run->calculateHeight());
run->calculateHeight(this->fAscentStyle,this->fDescentStyle));
} else {
result.clip = SkRect::MakeXYWH(runOffsetInLine, run->fFontMetrics.fAscent, run->advance().fX, 0);
}
return result;
}
// Find [start:end] clusters for the text
bool found;
ClusterIndex startIndex;
@ -617,9 +622,9 @@ TextLine::ClipContext TextLine::measureTextInsideOneRun(TextRange textRange,
// coming from letter spacing or word spacing or justification)
result.clip =
SkRect::MakeXYWH(0,
sizes().runTop(run),
sizes().runTop(run, this->fAscentStyle),
run->calculateWidth(result.pos, result.pos + result.size, false),
run->calculateHeight());
run->calculateHeight(this->fAscentStyle,this->fDescentStyle));
// Correct the width in case the text edges don't match clusters
// TODO: This is where we get smart about selecting a part of a cluster
@ -908,27 +913,27 @@ void TextLine::getRectsForRange(TextRange textRange0,
clip.fBottom = this->height();
clip.fTop = this->sizes().delta();
break;
case RectHeightStyle::kIncludeLineSpacingTop:
if (!isFirstLine()) {
clip.fTop -= this->sizes().runTop(context.run);
}
clip.fBottom -= this->sizes().runTop(context.run);
break;
case RectHeightStyle::kIncludeLineSpacingMiddle:
if (!isFirstLine()) {
clip.fTop -= this->sizes().runTop(context.run) / 2;
case RectHeightStyle::kIncludeLineSpacingTop: {
if (isFirstLine()) {
auto verticalShift = this->sizes().runTop(context.run, LineMetricStyle::Typographic);
clip.fTop += verticalShift;
}
break;
}
case RectHeightStyle::kIncludeLineSpacingMiddle: {
auto verticalShift = this->sizes().runTop(context.run, LineMetricStyle::Typographic);
clip.fTop += isFirstLine() ? verticalShift : verticalShift / 2;
clip.fBottom += isLastLine() ? 0 : verticalShift / 2;
break;
}
case RectHeightStyle::kIncludeLineSpacingBottom: {
auto verticalShift = this->sizes().runTop(context.run, LineMetricStyle::Typographic);
clip.offset(0, verticalShift);
if (isLastLine()) {
clip.fBottom -= this->sizes().runTop(context.run);
} else {
clip.fBottom -= this->sizes().runTop(context.run) / 2;
clip.fBottom -= verticalShift;
}
break;
case RectHeightStyle::kIncludeLineSpacingBottom:
if (isLastLine()) {
clip.fBottom -= this->sizes().runTop(context.run);
}
break;
break;
}
case RectHeightStyle::kStrut: {
auto strutStyle = paragraphStyle.getStrutStyle();
if (strutStyle.getStrutEnabled()
@ -940,11 +945,15 @@ void TextLine::getRectsForRange(TextRange textRange0,
}
}
break;
case RectHeightStyle::kTight:
case RectHeightStyle::kTight: {
if (run->fHeightMultiplier > 0) {
// This is a special case when we do not need to take in account this height multiplier
clip.fBottom = clip.fTop + clip.height() / run->fHeightMultiplier;
auto correctedHeight = clip.height() / run->fHeightMultiplier;
auto verticalShift = this->sizes().runTop(context.run, LineMetricStyle::Typographic);
clip.fTop += verticalShift;
clip.fBottom = clip.fTop + correctedHeight;
}
}
break;
default:
SkASSERT(false);
@ -1003,7 +1012,8 @@ void TextLine::getRectsForRange(TextRange textRange0,
lastRun != nullptr &&
lastRun->placeholderStyle() == nullptr &&
context.run->placeholderStyle() == nullptr &&
nearlyEqual(lastRun->lineHeight(), context.run->lineHeight()) &&
nearlyEqual(lastRun->heightMultiplier(),
context.run->heightMultiplier()) &&
lastRun->font() == context.run->font())
{
auto& lastBox = boxes.back();

View File

@ -136,6 +136,9 @@ private:
bool fHasBackground;
bool fHasShadows;
bool fHasDecorations;
LineMetricStyle fAscentStyle;
LineMetricStyle fDescentStyle;
};
} // namespace textlayout
} // namespace skia

View File

@ -2636,6 +2636,101 @@ private:
typedef Sample INHERITED;
};
class ParagraphView41 : public ParagraphView_Base {
protected:
SkString name() override { return SkString("Paragraph41"); }
void onDrawContent(SkCanvas* canvas) override {
canvas->drawColor(SK_ColorWHITE);
auto fontCollection = sk_make_sp<FontCollection>();
fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
fontCollection->enableFontFallback();
SkPaint line;
line.setColor(SK_ColorRED);
line.setStyle(SkPaint::kStroke_Style);
line.setAntiAlias(true);
line.setStrokeWidth(1);
auto draw = [&](SkColor color, TextHeightBehavior thb) {
ParagraphStyle paragraph_style;
paragraph_style.setTextHeightBehavior(thb);
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setColor(SK_ColorBLACK);
SkPaint paint;
paint.setColor(color);
text_style.setBackgroundColor(paint);
text_style.setFontFamilies({SkString("Roboto")});
text_style.setFontSize(20);
text_style.setHeight(5);
text_style.setHeightOverride(true);
builder.pushStyle(text_style);
builder.addText("World domination is such an ugly phrase - I prefer to call it world optimisation");
auto paragraph = builder.Build();
paragraph->layout(width());
paragraph->paint(canvas, 0, 0);
canvas->drawLine(0, paragraph->getHeight(), paragraph->getMaxWidth(), paragraph->getHeight(), line);
canvas->translate(0, paragraph->getHeight());
};
draw(SK_ColorLTGRAY, TextHeightBehavior::kDisableFirstAscent);
draw(SK_ColorYELLOW, TextHeightBehavior::kDisableLastDescent);
draw(SK_ColorGRAY, TextHeightBehavior::kDisableAll);
}
private:
typedef Sample INHERITED;
};
class ParagraphView42 : public ParagraphView_Base {
protected:
SkString name() override { return SkString("Paragraph42"); }
void onDrawContent(SkCanvas* canvas) override {
SkString text("Atwater Peel Sherbrooke Bonaventure\nhi\nwasssup!");
canvas->drawColor(SK_ColorWHITE);
auto fontCollection = sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str(), true, true);
ParagraphStyle paragraph_style;
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
TextStyle text_style;
text_style.setColor(SK_ColorBLACK);
text_style.setFontFamilies({SkString("Ahem")});
text_style.setFontSize(16);
text_style.setHeight(4);
text_style.setHeightOverride(true);
builder.pushStyle(text_style);
builder.addText(text.c_str());
auto paragraph = builder.Build();
paragraph->layout(width());
auto boxes = paragraph->getRectsForRange(0, 7, RectHeightStyle::kIncludeLineSpacingTop, RectWidthStyle::kMax);
for (auto& box : boxes) {
SkPaint paint;
paint.setColor(SK_ColorGRAY);
canvas->drawRect(box.rect, paint);
}
auto boxes2 = paragraph->getRectsForRange(0, 7, RectHeightStyle::kTight, RectWidthStyle::kMax);
for (auto& box : boxes2) {
SkPaint paint;
paint.setColor(SK_ColorRED);
canvas->drawRect(box.rect, paint);
}
paragraph->paint(canvas, 0, 0);
}
private:
typedef Sample INHERITED;
};
//
//////////////////////////////////////////////////////////////////////////////
DEF_SAMPLE(return new ParagraphView1();)
DEF_SAMPLE(return new ParagraphView2();)
@ -2675,3 +2770,5 @@ DEF_SAMPLE(return new ParagraphView36();)
DEF_SAMPLE(return new ParagraphView37();)
DEF_SAMPLE(return new ParagraphView38();)
DEF_SAMPLE(return new ParagraphView39();)
DEF_SAMPLE(return new ParagraphView41();)
DEF_SAMPLE(return new ParagraphView42();)