Skip to content

Commit

Permalink
Merge pull request #5 from JetBrains/ivan.matkov/line-topratio
Browse files Browse the repository at this point in the history
Support top ratio based vertical alignment
  • Loading branch information
MatkovIvan authored Sep 17, 2024
2 parents 2213bff + 804f26f commit d2aaacc
Show file tree
Hide file tree
Showing 12 changed files with 90 additions and 77 deletions.
6 changes: 6 additions & 0 deletions modules/skparagraph/include/FontRastrSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ struct FontRastrSettings {

/** Whether glyphs respect sub-pixel positioning. */
bool fSubpixel = true;

bool operator==(const FontRastrSettings& rhs) const {
return this->fEdging == rhs.fEdging &&
this->fHinting == rhs.fHinting &&
this->fSubpixel == rhs.fSubpixel;
}
};


Expand Down
51 changes: 30 additions & 21 deletions modules/skparagraph/include/ParagraphStyle.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,34 +46,36 @@ struct StrutStyle {
bool getHeightOverride() const { return fHeightOverride; }
void setHeightOverride(bool v) { fHeightOverride = v; }

void setHalfLeading(bool halfLeading) { fHalfLeading = halfLeading; }
bool getHalfLeading() const { return fHalfLeading; }
void setHalfLeading(bool halfLeading) { fTopRatio = halfLeading ? 0.5f : -1.0f; }
bool getHalfLeading() const { return fTopRatio == 0.5f; }

void setTopRatio(SkScalar topRatio) { fTopRatio = topRatio; }
SkScalar getTopRatio() const { return fTopRatio; }

bool operator==(const StrutStyle& rhs) const {
return this->fEnabled == rhs.fEnabled &&
this->fHeightOverride == rhs.fHeightOverride &&
this->fForceHeight == rhs.fForceHeight &&
this->fHalfLeading == rhs.fHalfLeading &&
nearlyEqual(this->fLeading, rhs.fLeading) &&
nearlyEqual(this->fHeight, rhs.fHeight) &&
nearlyEqual(this->fFontSize, rhs.fFontSize) &&
return this->fFontFamilies == rhs.fFontFamilies &&
this->fFontStyle == rhs.fFontStyle &&
this->fFontFamilies == rhs.fFontFamilies;
nearlyEqual(this->fFontSize, rhs.fFontSize) &&
nearlyEqual(this->fHeight, rhs.fHeight) &&
nearlyEqual(this->fLeading, rhs.fLeading) &&
nearlyEqual(this->fTopRatio, rhs.fTopRatio) &&
this->fForceHeight == rhs.fForceHeight &&
this->fEnabled == rhs.fEnabled &&
this->fHeightOverride == rhs.fHeightOverride;
}

private:

std::vector<SkString> fFontFamilies;
SkFontStyle fFontStyle;
SkScalar fFontSize;
SkScalar fHeight;
SkScalar fLeading;
// [0..1]: the ratio of ascent to ascent+descent
// -1: proportional to the ascent/descent
SkScalar fTopRatio;
bool fForceHeight;
bool fEnabled;
bool fHeightOverride;
// true: half leading.
// false: scale ascent/descent with fHeight.
bool fHalfLeading;
};

struct TextIndent {
Expand All @@ -99,13 +101,20 @@ struct ParagraphStyle {
ParagraphStyle();

bool operator==(const ParagraphStyle& rhs) const {
return this->fHeight == rhs.fHeight &&
this->fEllipsis == rhs.fEllipsis &&
this->fEllipsisUtf16 == rhs.fEllipsisUtf16 &&
this->fTextDirection == rhs.fTextDirection && this->fTextAlign == rhs.fTextAlign &&
return this->fStrutStyle == rhs.fStrutStyle &&
this->fDefaultTextStyle == rhs.fDefaultTextStyle &&
this->fTextAlign == rhs.fTextAlign &&
this->fTextDirection == rhs.fTextDirection &&
this->fLinesLimit == rhs.fLinesLimit &&
this->fEllipsisUtf16 == rhs.fEllipsisUtf16 &&
this->fEllipsis == rhs.fEllipsis &&
this->fHeight == rhs.fHeight &&
this->fTextHeightBehavior == rhs.fTextHeightBehavior &&
this->fTextIndent == rhs.fTextIndent &&
this->fReplaceTabCharacters == rhs.fReplaceTabCharacters;
this->fFontRastrSettings == rhs.fFontRastrSettings &&
this->fHintingIsOn == rhs.fHintingIsOn &&
this->fReplaceTabCharacters == rhs.fReplaceTabCharacters &&
this->fApplyRoundingHack == rhs.fApplyRoundingHack;
}

const StrutStyle& getStrutStyle() const { return fStrutStyle; }
Expand Down Expand Up @@ -164,11 +173,11 @@ struct ParagraphStyle {
SkString fEllipsis;
SkScalar fHeight;
TextHeightBehavior fTextHeightBehavior;
TextIndent fTextIndent;
FontRastrSettings fFontRastrSettings;
bool fHintingIsOn;
bool fReplaceTabCharacters;
bool fApplyRoundingHack = true;
TextIndent fTextIndent;
FontRastrSettings fFontRastrSettings;
};
} // namespace textlayout
} // namespace skia
Expand Down
13 changes: 8 additions & 5 deletions modules/skparagraph/include/TextStyle.h
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,11 @@ class TextStyle {
void setHeightOverride(bool heightOverride) { fHeightOverride = heightOverride; }
bool getHeightOverride() const { return fHeightOverride; }

void setHalfLeading(bool halfLeading) { fHalfLeading = halfLeading; }
bool getHalfLeading() const { return fHalfLeading; }
void setHalfLeading(bool halfLeading) { fTopRatio = halfLeading ? 0.5f : -1.0f; }
bool getHalfLeading() const { return fTopRatio == 0.5f; }

void setTopRatio(SkScalar topRatio) { fTopRatio = topRatio; }
SkScalar getTopRatio() const { return fTopRatio; }

void setLetterSpacing(SkScalar letterSpacing) { fLetterSpacing = letterSpacing; }
SkScalar getLetterSpacing() const { return fLetterSpacing; }
Expand Down Expand Up @@ -312,9 +315,9 @@ class TextStyle {
SkScalar fHeight = 1.0;
bool fHeightOverride = false;
SkScalar fBaselineShift = 0.0f;
// true: half leading.
// false: scale ascent/descent with fHeight.
bool fHalfLeading = false;
// [0..1]: the ratio of ascent to ascent+descent
// -1: proportional to the ascent/descent
SkScalar fTopRatio = -1.0f;
SkString fLocale = {};
SkScalar fLetterSpacing = 0.0;
SkScalar fWordSpacing = 0.0;
Expand Down
4 changes: 2 additions & 2 deletions modules/skparagraph/src/OneLineShaper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ void OneLineShaper::finish(const Block& block, SkScalar height, SkScalar& advanc
info,
run->fClusterStart,
height,
block.fStyle.getHalfLeading(),
block.fStyle.getTopRatio(),
block.fStyle.getBaselineShift(),
this->fParagraph->fRuns.size(),
advanceX
Expand Down Expand Up @@ -639,7 +639,7 @@ bool OneLineShaper::shape() {

// Start from the beginning (hoping that it's a simple case one block - one run)
fHeight = block.fStyle.getHeightOverride() ? block.fStyle.getHeight() : 0;
fUseHalfLeading = block.fStyle.getHalfLeading();
fTopRatio = block.fStyle.getTopRatio();
fBaselineShift = block.fStyle.getBaselineShift();
fAdvance = SkVector::Make(advanceX, 0);
fCurrentText = block.fRange;
Expand Down
6 changes: 3 additions & 3 deletions modules/skparagraph/src/OneLineShaper.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class OneLineShaper : public SkShaper::RunHandler {
explicit OneLineShaper(ParagraphImpl* paragraph)
: fParagraph(paragraph)
, fHeight(0.0f)
, fUseHalfLeading(false)
, fTopRatio(-1.0f)
, fBaselineShift(0.0f)
, fAdvance(SkPoint::Make(0.0f, 0.0f))
, fUnresolvedGlyphs(0)
Expand Down Expand Up @@ -93,7 +93,7 @@ class OneLineShaper : public SkShaper::RunHandler {
info,
fCurrentText.start,
fHeight,
fUseHalfLeading,
fTopRatio,
fBaselineShift,
++fUniqueRunId,
fAdvance.fX);
Expand All @@ -115,7 +115,7 @@ class OneLineShaper : public SkShaper::RunHandler {
ParagraphImpl* fParagraph;
TextRange fCurrentText;
SkScalar fHeight;
bool fUseHalfLeading;
SkScalar fTopRatio;
SkScalar fBaselineShift;
SkVector fAdvance;
size_t fUnresolvedGlyphs;
Expand Down
29 changes: 11 additions & 18 deletions modules/skparagraph/src/ParagraphCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,21 +123,23 @@ uint32_t ParagraphCacheKey::computeHash() const {
if (ts.fStyle.isPlaceholder()) {
continue;
}
hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getLetterSpacing())));
hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getWordSpacing())));
hash = mix(hash, SkGoodHash()(ts.fStyle.getLocale()));
hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getHeight())));
hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getBaselineShift())));
hash = mix(hash, SkGoodHash()(ts.fStyle.getFontStyle()));
for (auto& ff : ts.fStyle.getFontFamilies()) {
hash = mix(hash, SkGoodHash()(ff));
}
hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getFontSize())));
hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getHeight())));
hash = mix(hash, SkGoodHash()(ts.fStyle.getHeightOverride() ? 1 : 0));
hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getBaselineShift())));
hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getTopRatio())));
hash = mix(hash, SkGoodHash()(ts.fStyle.getLocale()));
hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getLetterSpacing())));
hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getWordSpacing())));
for (auto& ff : ts.fStyle.getFontFeatures()) {
hash = mix(hash, SkGoodHash()(ff.fValue));
hash = mix(hash, SkGoodHash()(ff.fName));
}
hash = mix(hash, std::hash<std::optional<FontArguments>>()(ts.fStyle.getFontArguments()));
hash = mix(hash, SkGoodHash()(ts.fStyle.getFontStyle()));
hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getFontSize())));
hash = mix(hash, SkGoodHash()(ts.fRange));
}

Expand All @@ -155,6 +157,7 @@ uint32_t ParagraphCacheKey::computeHash() const {
hash = mix(hash, SkGoodHash()(relax(strutStyle.getHeight())));
hash = mix(hash, SkGoodHash()(relax(strutStyle.getLeading())));
hash = mix(hash, SkGoodHash()(relax(strutStyle.getFontSize())));
hash = mix(hash, SkGoodHash()(relax(strutStyle.getTopRatio())));
hash = mix(hash, SkGoodHash()(strutStyle.getHeightOverride()));
hash = mix(hash, SkGoodHash()(strutStyle.getFontStyle()));
hash = mix(hash, SkGoodHash()(strutStyle.getForceStrutHeight()));
Expand Down Expand Up @@ -186,18 +189,8 @@ bool ParagraphCacheKey::operator==(const ParagraphCacheKey& other) const {
}

// There is no need to compare default paragraph styles - they are included into fTextStyles
if (!exactlyEqual(fParagraphStyle.getHeight(), other.fParagraphStyle.getHeight())) {
return false;
}
if (fParagraphStyle.getTextDirection() != other.fParagraphStyle.getTextDirection()) {
return false;
}

if (!(fParagraphStyle.getStrutStyle() == other.fParagraphStyle.getStrutStyle())) {
return false;
}

if (!(fParagraphStyle.getReplaceTabCharacters() == other.fParagraphStyle.getReplaceTabCharacters())) {
if (!(fParagraphStyle == other.fParagraphStyle)) {
return false;
}

Expand Down
21 changes: 11 additions & 10 deletions modules/skparagraph/src/ParagraphImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -707,14 +707,13 @@ void ParagraphImpl::resolveStrut() {
if (strutStyle.getHeightOverride()) {
SkScalar strutAscent = 0.0f;
SkScalar strutDescent = 0.0f;
// The half leading flag doesn't take effect unless there's height override.
if (strutStyle.getHalfLeading()) {
// The top ratio doesn't take effect unless there's height override.
SkScalar topRatio = strutStyle.getTopRatio();
if (topRatio >= 0.0f && topRatio <= 1.0f) {
const auto occupiedHeight = metrics.fDescent - metrics.fAscent;
auto flexibleHeight = strutStyle.getHeight() * strutStyle.getFontSize() - occupiedHeight;
// Distribute the flexible height evenly over and under.
flexibleHeight /= 2;
strutAscent = metrics.fAscent - flexibleHeight;
strutDescent = metrics.fDescent + flexibleHeight;
strutAscent = metrics.fAscent - flexibleHeight * topRatio;
strutDescent = metrics.fDescent + flexibleHeight * (1.0f - topRatio);
} else {
const SkScalar strutMetricsHeight = metrics.fDescent - metrics.fAscent + metrics.fLeading;
const auto strutHeightMultiplier = strutMetricsHeight == 0
Expand Down Expand Up @@ -1036,11 +1035,13 @@ void ParagraphImpl::computeEmptyMetrics() {
textStyle.getHeightOverride()) {
const auto intrinsicHeight = fEmptyMetrics.height();
const auto strutHeight = textStyle.getHeight() * textStyle.getFontSize();
if (paragraphStyle().getStrutStyle().getHalfLeading()) {
SkScalar topRatio = paragraphStyle().getStrutStyle().getTopRatio();
if (topRatio >= 0.0f && topRatio <= 1.0f) {
const auto extraLeading = strutHeight - intrinsicHeight;
fEmptyMetrics.update(
fEmptyMetrics.ascent(),
fEmptyMetrics.descent(),
fEmptyMetrics.leading() + strutHeight - intrinsicHeight);
fEmptyMetrics.ascent() - extraLeading * topRatio,
fEmptyMetrics.descent() + extraLeading * (1.0f - topRatio),
fEmptyMetrics.leading() + extraLeading);
} else {
const auto multiplier = strutHeight / intrinsicHeight;
fEmptyMetrics.update(
Expand Down
2 changes: 1 addition & 1 deletion modules/skparagraph/src/ParagraphStyle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ StrutStyle::StrutStyle() {
fLeading = -1;
fForceHeight = false;
fHeightOverride = false;
fHalfLeading = false;
fTopRatio = -1.0f;
fEnabled = false;
}

Expand Down
12 changes: 6 additions & 6 deletions modules/skparagraph/src/Run.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Run::Run(ParagraphImpl* owner,
const SkShaper::RunHandler::RunInfo& info,
size_t firstChar,
SkScalar heightMultiplier,
bool useHalfLeading,
SkScalar topRatio,
SkScalar baselineShift,
size_t index,
SkScalar offsetX)
Expand All @@ -33,7 +33,7 @@ Run::Run(ParagraphImpl* owner,
, fOffsets(fGlyphData->offsets)
, fClusterIndexes(fGlyphData->clusterIndexes)
, fHeightMultiplier(heightMultiplier)
, fUseHalfLeading(useHalfLeading)
, fTopRatio(topRatio)
, fBaselineShift(baselineShift)
{
fBidiLevel = info.fBidiLevel;
Expand Down Expand Up @@ -67,10 +67,10 @@ void Run::calculateMetrics() {
}
const auto runHeight = fHeightMultiplier * fFont.getSize();
const auto fontIntrinsicHeight = fCorrectDescent - fCorrectAscent;
if (fUseHalfLeading) {
const auto extraLeading = (runHeight - fontIntrinsicHeight) / 2;
fCorrectAscent -= extraLeading;
fCorrectDescent += extraLeading;
if (fTopRatio >= 0.0f && fTopRatio <= 1.0f) {
const auto extraLeading = runHeight - fontIntrinsicHeight;
fCorrectAscent -= extraLeading * fTopRatio;
fCorrectDescent += extraLeading * (1.0f - fTopRatio);
} else {
const auto multiplier = runHeight / fontIntrinsicHeight;
fCorrectAscent *= multiplier;
Expand Down
6 changes: 3 additions & 3 deletions modules/skparagraph/src/Run.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class Run {
const SkShaper::RunHandler::RunInfo& info,
size_t firstChar,
SkScalar heightMultiplier,
bool useHalfLeading,
SkScalar topRatio,
SkScalar baselineShift,
size_t index,
SkScalar shiftX);
Expand Down Expand Up @@ -97,7 +97,7 @@ class Run {
TextDirection getTextDirection() const { return leftToRight() ? TextDirection::kLtr : TextDirection::kRtl; }
size_t index() const { return fIndex; }
SkScalar heightMultiplier() const { return fHeightMultiplier; }
bool useHalfLeading() const { return fUseHalfLeading; }
SkScalar topRatio() const { return fTopRatio; }
SkScalar baselineShift() const { return fBaselineShift; }
PlaceholderStyle* placeholderStyle() const;
bool isPlaceholder() const { return fPlaceholderIndex != std::numeric_limits<size_t>::max(); }
Expand Down Expand Up @@ -204,7 +204,7 @@ class Run {

SkFontMetrics fFontMetrics;
const SkScalar fHeightMultiplier;
const bool fUseHalfLeading;
const SkScalar fTopRatio;
const SkScalar fBaselineShift;
SkScalar fCorrectAscent;
SkScalar fCorrectDescent;
Expand Down
10 changes: 5 additions & 5 deletions modules/skparagraph/src/TextLine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -643,8 +643,8 @@ std::unique_ptr<Run> TextLine::shapeEllipsis(const SkString& ellipsis, const Clu

class ShapeHandler final : public SkShaper::RunHandler {
public:
ShapeHandler(SkScalar lineHeight, bool useHalfLeading, SkScalar baselineShift, const SkString& ellipsis)
: fRun(nullptr), fLineHeight(lineHeight), fUseHalfLeading(useHalfLeading), fBaselineShift(baselineShift), fEllipsis(ellipsis) {}
ShapeHandler(SkScalar lineHeight, SkScalar topRatio, SkScalar baselineShift, const SkString& ellipsis)
: fRun(nullptr), fLineHeight(lineHeight), fTopRatio(topRatio), fBaselineShift(baselineShift), fEllipsis(ellipsis) {}
std::unique_ptr<Run> run() & { return std::move(fRun); }

private:
Expand All @@ -656,7 +656,7 @@ std::unique_ptr<Run> TextLine::shapeEllipsis(const SkString& ellipsis, const Clu

Buffer runBuffer(const RunInfo& info) override {
SkASSERT(!fRun);
fRun = std::make_unique<Run>(nullptr, info, 0, fLineHeight, fUseHalfLeading, fBaselineShift, 0, 0);
fRun = std::make_unique<Run>(nullptr, info, 0, fLineHeight, fTopRatio, fBaselineShift, 0, 0);
return fRun->newRunBuffer();
}

Expand All @@ -671,7 +671,7 @@ std::unique_ptr<Run> TextLine::shapeEllipsis(const SkString& ellipsis, const Clu

std::unique_ptr<Run> fRun;
SkScalar fLineHeight;
bool fUseHalfLeading;
SkScalar fTopRatio;
SkScalar fBaselineShift;
SkString fEllipsis;
};
Expand All @@ -690,7 +690,7 @@ std::unique_ptr<Run> TextLine::shapeEllipsis(const SkString& ellipsis, const Clu
}

auto shaped = [&](sk_sp<SkTypeface> typeface, sk_sp<SkFontMgr> fallback) -> std::unique_ptr<Run> {
ShapeHandler handler(run.heightMultiplier(), run.useHalfLeading(), run.baselineShift(), ellipsis);
ShapeHandler handler(run.heightMultiplier(), run.topRatio(), run.baselineShift(), ellipsis);
SkFont font(std::move(typeface), textStyle.getFontSize());
font.setEdging(SkFont::Edging::kAntiAlias);
font.setHinting(SkFontHinting::kSlight);
Expand Down
Loading

0 comments on commit d2aaacc

Please sign in to comment.