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:
parent
a117e7b75b
commit
dd1de25896
@ -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
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -136,6 +136,9 @@ private:
|
||||
bool fHasBackground;
|
||||
bool fHasShadows;
|
||||
bool fHasDecorations;
|
||||
|
||||
LineMetricStyle fAscentStyle;
|
||||
LineMetricStyle fDescentStyle;
|
||||
};
|
||||
} // namespace textlayout
|
||||
} // namespace skia
|
||||
|
@ -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();)
|
||||
|
Loading…
Reference in New Issue
Block a user