Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit dd1de25

Browse files
RusinoSkia Commit-Bot
authored andcommitted
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>
1 parent a117e7b commit dd1de25

File tree

7 files changed

+163
-42
lines changed

7 files changed

+163
-42
lines changed

modules/skparagraph/include/DartTypes.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,13 @@ enum TextHeightBehavior {
133133
kDisableAll = 0x1 | 0x2,
134134
};
135135

136+
enum class LineMetricStyle : uint8_t {
137+
// Use ascent, descent, etc from a fixed baseline.
138+
Typographic,
139+
// Use ascent, descent, etc like css with the leading split and with height adjustments
140+
CSS
141+
};
142+
136143
} // namespace textlayout
137144
} // namespace skia
138145

modules/skparagraph/src/ParagraphImpl.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,7 @@ void ParagraphImpl::breakShapedTextIntoLines(SkScalar maxWidth) {
447447
fHeight += delta;
448448
// Shift all the lines up
449449
for (auto& line : fLines) {
450+
if (line.isFirstLine()) continue;
450451
line.shiftVertically(delta);
451452
}
452453
}

modules/skparagraph/src/Run.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ void Run::iterateThroughClustersInTextOrder(const ClusterTextVisitor& visitor) {
168168
fClusterStart + cluster,
169169
fClusterStart + nextCluster,
170170
this->calculateWidth(start, glyph, glyph == size()),
171-
this->calculateHeight());
171+
this->calculateHeight(LineMetricStyle::CSS, LineMetricStyle::CSS));
172172

173173
start = glyph;
174174
cluster = nextCluster;
@@ -188,7 +188,7 @@ void Run::iterateThroughClustersInTextOrder(const ClusterTextVisitor& visitor) {
188188
fClusterStart + cluster,
189189
fClusterStart + nextCluster,
190190
this->calculateWidth(start, glyph, glyph == 0),
191-
this->calculateHeight());
191+
this->calculateHeight(LineMetricStyle::CSS, LineMetricStyle::CSS));
192192

193193
glyph = start;
194194
cluster = nextCluster;

modules/skparagraph/src/Run.h

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ class Run {
111111
bool leftToRight() const { return fBidiLevel % 2 == 0; }
112112
TextDirection getTextDirection() const { return leftToRight() ? TextDirection::kLtr : TextDirection::kRtl; }
113113
size_t index() const { return fIndex; }
114-
SkScalar lineHeight() const { return fHeightMultiplier; }
114+
SkScalar heightMultiplier() const { return fHeightMultiplier; }
115115
PlaceholderStyle* placeholderStyle() const;
116116
bool isPlaceholder() const { return fPlaceholderIndex != std::numeric_limits<size_t>::max(); }
117117
size_t clusterIndex(size_t pos) const { return fClusterIndexes[pos]; }
@@ -136,11 +136,12 @@ class Run {
136136
SkScalar addSpacesEvenly(SkScalar space, Cluster* cluster);
137137
void shift(const Cluster* cluster, SkScalar offset);
138138

139-
SkScalar calculateHeight() const {
140-
if (fHeightMultiplier == 0) {
141-
return fFontMetrics.fDescent - fFontMetrics.fAscent;
142-
}
143-
return fHeightMultiplier * fFont.getSize();
139+
SkScalar calculateHeight(LineMetricStyle ascentStyle, LineMetricStyle descentStyle) const {
140+
auto ascent = ascentStyle == LineMetricStyle::Typographic ? this->ascent()
141+
: this->correctAscent();
142+
auto descent = descentStyle == LineMetricStyle::Typographic ? this->descent()
143+
: this->correctDescent();
144+
return descent - ascent;
144145
}
145146
SkScalar calculateWidth(size_t start, size_t end, bool clip) const;
146147

@@ -403,8 +404,9 @@ class InternalLineMetrics {
403404
}
404405
}
405406

406-
SkScalar runTop(const Run* run) const {
407-
return fLeading / 2 - fAscent + run->ascent() + delta();
407+
SkScalar runTop(const Run* run, LineMetricStyle ascentStyle) const {
408+
return fLeading / 2 - fAscent +
409+
(ascentStyle == LineMetricStyle::Typographic ? run->ascent() : run->correctAscent()) + delta();
408410
}
409411

410412
SkScalar height() const {
@@ -424,6 +426,7 @@ class InternalLineMetrics {
424426
private:
425427

426428
friend class TextWrapper;
429+
friend class TextLine;
427430

428431
SkScalar fAscent;
429432
SkScalar fDescent;

modules/skparagraph/src/TextLine.cpp

Lines changed: 42 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@ TextLine::TextLine(ParagraphImpl* master,
8282
, fSizes(sizes)
8383
, fHasBackground(false)
8484
, fHasShadows(false)
85-
, fHasDecorations(false) {
85+
, fHasDecorations(false)
86+
, fAscentStyle(LineMetricStyle::CSS)
87+
, fDescentStyle(LineMetricStyle::CSS) {
8688
// Reorder visual runs
8789
auto& start = master->cluster(fGhostClusterRange.start);
8890
auto& end = master->cluster(fGhostClusterRange.end - 1);
@@ -317,9 +319,13 @@ SkScalar TextLine::metricsWithoutMultiplier(TextHeightBehavior correction) {
317319
});
318320
SkScalar delta = 0;
319321
if (correction == TextHeightBehavior::kDisableFirstAscent) {
320-
delta += (this->fSizes.ascent() - result.ascent());
322+
delta += (this->fSizes.fAscent - result.fAscent);
323+
this->fSizes.fAscent -= delta;
324+
this->fAscentStyle = LineMetricStyle::Typographic;
321325
} else if (correction == TextHeightBehavior::kDisableLastDescent) {
322-
delta -= (this->fSizes.descent() - result.descent());
326+
delta -= (this->fSizes.fDescent - result.fDescent);
327+
this->fSizes.fDescent -= delta;
328+
this->fDescentStyle = LineMetricStyle::Typographic;
323329
}
324330
fAdvance.fY += delta;
325331
return delta;
@@ -545,7 +551,7 @@ Run* TextLine::shapeEllipsis(const SkString& ellipsis, Run* run) {
545551
SkString fEllipsis;
546552
};
547553

548-
ShapeHandler handler(run->lineHeight(), ellipsis);
554+
ShapeHandler handler(run->heightMultiplier(), ellipsis);
549555
std::unique_ptr<SkShaper> shaper = SkShaper::MakeShapeDontWrapOrReorder();
550556
SkASSERT_RELEASE(shaper != nullptr);
551557
shaper->shape(ellipsis.c_str(), ellipsis.size(), run->font(), true,
@@ -568,22 +574,21 @@ TextLine::ClipContext TextLine::measureTextInsideOneRun(TextRange textRange,
568574
SkASSERT(textRange == run->textRange());
569575
result.fTextShift = runOffsetInLine;
570576
result.clip = SkRect::MakeXYWH(runOffsetInLine,
571-
sizes().runTop(run),
577+
sizes().runTop(run, this->fAscentStyle),
572578
run->advance().fX,
573-
run->calculateHeight());
579+
run->calculateHeight(this->fAscentStyle,this->fDescentStyle));
574580
return result;
575581
} else if (run->isPlaceholder()) {
576582
if (SkScalarIsFinite(run->fFontMetrics.fAscent)) {
577583
result.clip = SkRect::MakeXYWH(runOffsetInLine,
578-
sizes().runTop(run),
584+
sizes().runTop(run, this->fAscentStyle),
579585
run->advance().fX,
580-
run->calculateHeight());
586+
run->calculateHeight(this->fAscentStyle,this->fDescentStyle));
581587
} else {
582588
result.clip = SkRect::MakeXYWH(runOffsetInLine, run->fFontMetrics.fAscent, run->advance().fX, 0);
583589
}
584590
return result;
585591
}
586-
587592
// Find [start:end] clusters for the text
588593
bool found;
589594
ClusterIndex startIndex;
@@ -617,9 +622,9 @@ TextLine::ClipContext TextLine::measureTextInsideOneRun(TextRange textRange,
617622
// coming from letter spacing or word spacing or justification)
618623
result.clip =
619624
SkRect::MakeXYWH(0,
620-
sizes().runTop(run),
625+
sizes().runTop(run, this->fAscentStyle),
621626
run->calculateWidth(result.pos, result.pos + result.size, false),
622-
run->calculateHeight());
627+
run->calculateHeight(this->fAscentStyle,this->fDescentStyle));
623628

624629
// Correct the width in case the text edges don't match clusters
625630
// TODO: This is where we get smart about selecting a part of a cluster
@@ -908,27 +913,27 @@ void TextLine::getRectsForRange(TextRange textRange0,
908913
clip.fBottom = this->height();
909914
clip.fTop = this->sizes().delta();
910915
break;
911-
case RectHeightStyle::kIncludeLineSpacingTop:
912-
if (!isFirstLine()) {
913-
clip.fTop -= this->sizes().runTop(context.run);
914-
}
915-
clip.fBottom -= this->sizes().runTop(context.run);
916-
break;
917-
case RectHeightStyle::kIncludeLineSpacingMiddle:
918-
if (!isFirstLine()) {
919-
clip.fTop -= this->sizes().runTop(context.run) / 2;
916+
case RectHeightStyle::kIncludeLineSpacingTop: {
917+
if (isFirstLine()) {
918+
auto verticalShift = this->sizes().runTop(context.run, LineMetricStyle::Typographic);
919+
clip.fTop += verticalShift;
920920
}
921+
break;
922+
}
923+
case RectHeightStyle::kIncludeLineSpacingMiddle: {
924+
auto verticalShift = this->sizes().runTop(context.run, LineMetricStyle::Typographic);
925+
clip.fTop += isFirstLine() ? verticalShift : verticalShift / 2;
926+
clip.fBottom += isLastLine() ? 0 : verticalShift / 2;
927+
break;
928+
}
929+
case RectHeightStyle::kIncludeLineSpacingBottom: {
930+
auto verticalShift = this->sizes().runTop(context.run, LineMetricStyle::Typographic);
931+
clip.offset(0, verticalShift);
921932
if (isLastLine()) {
922-
clip.fBottom -= this->sizes().runTop(context.run);
923-
} else {
924-
clip.fBottom -= this->sizes().runTop(context.run) / 2;
925-
}
926-
break;
927-
case RectHeightStyle::kIncludeLineSpacingBottom:
928-
if (isLastLine()) {
929-
clip.fBottom -= this->sizes().runTop(context.run);
933+
clip.fBottom -= verticalShift;
930934
}
931-
break;
935+
break;
936+
}
932937
case RectHeightStyle::kStrut: {
933938
auto strutStyle = paragraphStyle.getStrutStyle();
934939
if (strutStyle.getStrutEnabled()
@@ -940,11 +945,15 @@ void TextLine::getRectsForRange(TextRange textRange0,
940945
}
941946
}
942947
break;
943-
case RectHeightStyle::kTight:
948+
case RectHeightStyle::kTight: {
944949
if (run->fHeightMultiplier > 0) {
945950
// This is a special case when we do not need to take in account this height multiplier
946-
clip.fBottom = clip.fTop + clip.height() / run->fHeightMultiplier;
951+
auto correctedHeight = clip.height() / run->fHeightMultiplier;
952+
auto verticalShift = this->sizes().runTop(context.run, LineMetricStyle::Typographic);
953+
clip.fTop += verticalShift;
954+
clip.fBottom = clip.fTop + correctedHeight;
947955
}
956+
}
948957
break;
949958
default:
950959
SkASSERT(false);
@@ -1003,7 +1012,8 @@ void TextLine::getRectsForRange(TextRange textRange0,
10031012
lastRun != nullptr &&
10041013
lastRun->placeholderStyle() == nullptr &&
10051014
context.run->placeholderStyle() == nullptr &&
1006-
nearlyEqual(lastRun->lineHeight(), context.run->lineHeight()) &&
1015+
nearlyEqual(lastRun->heightMultiplier(),
1016+
context.run->heightMultiplier()) &&
10071017
lastRun->font() == context.run->font())
10081018
{
10091019
auto& lastBox = boxes.back();

modules/skparagraph/src/TextLine.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,9 @@ class TextLine {
136136
bool fHasBackground;
137137
bool fHasShadows;
138138
bool fHasDecorations;
139+
140+
LineMetricStyle fAscentStyle;
141+
LineMetricStyle fDescentStyle;
139142
};
140143
} // namespace textlayout
141144
} // namespace skia

samplecode/SampleParagraph.cpp

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2636,6 +2636,101 @@ class ParagraphView39 : public ParagraphView_Base {
26362636
typedef Sample INHERITED;
26372637
};
26382638

2639+
class ParagraphView41 : public ParagraphView_Base {
2640+
protected:
2641+
SkString name() override { return SkString("Paragraph41"); }
2642+
2643+
void onDrawContent(SkCanvas* canvas) override {
2644+
2645+
canvas->drawColor(SK_ColorWHITE);
2646+
2647+
auto fontCollection = sk_make_sp<FontCollection>();
2648+
fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
2649+
fontCollection->enableFontFallback();
2650+
2651+
SkPaint line;
2652+
line.setColor(SK_ColorRED);
2653+
line.setStyle(SkPaint::kStroke_Style);
2654+
line.setAntiAlias(true);
2655+
line.setStrokeWidth(1);
2656+
2657+
auto draw = [&](SkColor color, TextHeightBehavior thb) {
2658+
ParagraphStyle paragraph_style;
2659+
paragraph_style.setTextHeightBehavior(thb);
2660+
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
2661+
TextStyle text_style;
2662+
text_style.setColor(SK_ColorBLACK);
2663+
SkPaint paint;
2664+
paint.setColor(color);
2665+
text_style.setBackgroundColor(paint);
2666+
text_style.setFontFamilies({SkString("Roboto")});
2667+
text_style.setFontSize(20);
2668+
text_style.setHeight(5);
2669+
text_style.setHeightOverride(true);
2670+
builder.pushStyle(text_style);
2671+
builder.addText("World domination is such an ugly phrase - I prefer to call it world optimisation");
2672+
auto paragraph = builder.Build();
2673+
paragraph->layout(width());
2674+
paragraph->paint(canvas, 0, 0);
2675+
canvas->drawLine(0, paragraph->getHeight(), paragraph->getMaxWidth(), paragraph->getHeight(), line);
2676+
canvas->translate(0, paragraph->getHeight());
2677+
};
2678+
2679+
draw(SK_ColorLTGRAY, TextHeightBehavior::kDisableFirstAscent);
2680+
draw(SK_ColorYELLOW, TextHeightBehavior::kDisableLastDescent);
2681+
draw(SK_ColorGRAY, TextHeightBehavior::kDisableAll);
2682+
2683+
}
2684+
2685+
private:
2686+
typedef Sample INHERITED;
2687+
};
2688+
2689+
class ParagraphView42 : public ParagraphView_Base {
2690+
protected:
2691+
SkString name() override { return SkString("Paragraph42"); }
2692+
2693+
void onDrawContent(SkCanvas* canvas) override {
2694+
2695+
SkString text("Atwater Peel Sherbrooke Bonaventure\nhi\nwasssup!");
2696+
canvas->drawColor(SK_ColorWHITE);
2697+
2698+
auto fontCollection = sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str(), true, true);
2699+
2700+
ParagraphStyle paragraph_style;
2701+
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
2702+
TextStyle text_style;
2703+
text_style.setColor(SK_ColorBLACK);
2704+
text_style.setFontFamilies({SkString("Ahem")});
2705+
text_style.setFontSize(16);
2706+
text_style.setHeight(4);
2707+
text_style.setHeightOverride(true);
2708+
builder.pushStyle(text_style);
2709+
builder.addText(text.c_str());
2710+
auto paragraph = builder.Build();
2711+
paragraph->layout(width());
2712+
2713+
auto boxes = paragraph->getRectsForRange(0, 7, RectHeightStyle::kIncludeLineSpacingTop, RectWidthStyle::kMax);
2714+
for (auto& box : boxes) {
2715+
SkPaint paint;
2716+
paint.setColor(SK_ColorGRAY);
2717+
canvas->drawRect(box.rect, paint);
2718+
}
2719+
2720+
auto boxes2 = paragraph->getRectsForRange(0, 7, RectHeightStyle::kTight, RectWidthStyle::kMax);
2721+
for (auto& box : boxes2) {
2722+
SkPaint paint;
2723+
paint.setColor(SK_ColorRED);
2724+
canvas->drawRect(box.rect, paint);
2725+
}
2726+
2727+
paragraph->paint(canvas, 0, 0);
2728+
}
2729+
2730+
private:
2731+
typedef Sample INHERITED;
2732+
};
2733+
//
26392734
//////////////////////////////////////////////////////////////////////////////
26402735
DEF_SAMPLE(return new ParagraphView1();)
26412736
DEF_SAMPLE(return new ParagraphView2();)
@@ -2675,3 +2770,5 @@ DEF_SAMPLE(return new ParagraphView36();)
26752770
DEF_SAMPLE(return new ParagraphView37();)
26762771
DEF_SAMPLE(return new ParagraphView38();)
26772772
DEF_SAMPLE(return new ParagraphView39();)
2773+
DEF_SAMPLE(return new ParagraphView41();)
2774+
DEF_SAMPLE(return new ParagraphView42();)

0 commit comments

Comments
 (0)