From 0c1f6e5dab622f92bbd7a39f00739190f1c47580 Mon Sep 17 00:00:00 2001 From: Julia Lavrova Date: Thu, 7 Oct 2021 14:23:39 -0400 Subject: [PATCH] Implementing baseline shift for JetBrains Bug: skia:12390 Change-Id: I90d99e1ca18c1274ac53ab35a3f63b716d26cb5d Reviewed-on: https://skia-review.googlesource.com/c/skia/+/457097 Reviewed-by: Ben Wagner Commit-Queue: Julia Lavrova --- modules/skparagraph/include/TextStyle.h | 4 + .../skparagraph/samples/SampleParagraph.cpp | 146 ++++++++++++++++++ modules/skparagraph/src/OneLineShaper.cpp | 3 + modules/skparagraph/src/OneLineShaper.h | 3 + modules/skparagraph/src/ParagraphCache.cpp | 1 + modules/skparagraph/src/Run.cpp | 5 + modules/skparagraph/src/Run.h | 11 +- modules/skparagraph/src/TextLine.cpp | 18 ++- modules/skparagraph/src/TextStyle.cpp | 12 +- 9 files changed, 190 insertions(+), 13 deletions(-) diff --git a/modules/skparagraph/include/TextStyle.h b/modules/skparagraph/include/TextStyle.h index e5764145ab..00487c7d85 100644 --- a/modules/skparagraph/include/TextStyle.h +++ b/modules/skparagraph/include/TextStyle.h @@ -215,6 +215,9 @@ public: fFontFamilies = std::move(families); } + SkScalar getBaselineShift() const { return fBaselineShift; } + void setBaselineShift(SkScalar baselineShift) { fBaselineShift = baselineShift; } + void setHeight(SkScalar height) { fHeight = height; } SkScalar getHeight() const { return fHeightOverride ? fHeight : 0; } @@ -265,6 +268,7 @@ private: SkScalar fFontSize = 14.0; SkScalar fHeight = 1.0; bool fHeightOverride = false; + SkScalar fBaselineShift = 0.0f; // true: half leading. // false: scale ascent/descent with fHeight. bool fHalfLeading = false; diff --git a/modules/skparagraph/samples/SampleParagraph.cpp b/modules/skparagraph/samples/SampleParagraph.cpp index e9e6acb087..8817d5fb93 100644 --- a/modules/skparagraph/samples/SampleParagraph.cpp +++ b/modules/skparagraph/samples/SampleParagraph.cpp @@ -3611,6 +3611,151 @@ protected: private: using INHERITED = Sample; }; + +// Baseline shift +class ParagraphView63 : public ParagraphView_Base { +protected: + SkString name() override { return SkString("ParagraphView63"); } + + void onDrawContent(SkCanvas* canvas) override { + + canvas->drawColor(SK_ColorWHITE); + auto fontCollection = getFontCollection(); + fontCollection->setDefaultFontManager(SkFontMgr::RefDefault()); + fontCollection->enableFontFallback(); + TextStyle roboto; + roboto.setColor(SK_ColorBLACK); + roboto.setFontFamilies({SkString("Roboto")}); + roboto.setFontSize(20.0f); + + TextStyle assyrian; + assyrian.setColor(SK_ColorRED); + assyrian.setFontFamilies({SkString("Assyrian")}); + assyrian.setFontSize(40.0f); + + ParagraphStyle paragraph_style; + paragraph_style.setTextStyle(roboto); + ParagraphBuilderImpl builder(paragraph_style, fontCollection); + + roboto.setBaselineShift(0.0f); + builder.pushStyle(roboto); + builder.addText("Notice that the line height increased on the lines with "); + assyrian.setBaselineShift(.0f); + builder.pushStyle(assyrian); + builder.addText("baseline shifts:\n"); + + roboto.setBaselineShift(0.0f); + builder.pushStyle(roboto); + builder.addText("Zero baseline shift text "); + + assyrian.setBaselineShift(-20.0f); + builder.pushStyle(assyrian); + builder.addText("Up20"); + + roboto.setBaselineShift(-40.0f); + builder.pushStyle(roboto); + builder.addText("Up40"); + + assyrian.setBaselineShift(-60.0f); + builder.pushStyle(assyrian); + builder.addText("Up60"); + + roboto.setBaselineShift(-40.0f); + builder.pushStyle(roboto); + builder.addText("Up40"); + + assyrian.setBaselineShift(-20.0f); + builder.pushStyle(assyrian); + builder.addText("Up20"); + + roboto.setBaselineShift(-0.0f); + builder.pushStyle(roboto); + builder.addText(" Zero baseline shift text\n"); + + assyrian.addShadow(TextShadow(SK_ColorGREEN, SkPoint::Make(5, 5), 2)); + assyrian.setDecorationStyle(TextDecorationStyle::kSolid); + assyrian.setDecoration(TextDecoration::kUnderline); + assyrian.setDecorationColor(SK_ColorBLUE); + + roboto.setBaselineShift(0.0f); + builder.pushStyle(roboto); + builder.addText("Notice that shadows and decorations are shifted if there is a text with "); + assyrian.setBaselineShift(.0f); + builder.pushStyle(assyrian); + builder.addText("baseline shifts:\n"); + + assyrian.setDecoration(TextDecoration::kNoDecoration); + + roboto.setBaselineShift(0.0f); + builder.pushStyle(roboto); + builder.addText("Zero baseline shift text "); + + assyrian.setBaselineShift(20.0f); + builder.pushStyle(assyrian); + builder.addText("Down20"); + + roboto.setBaselineShift(40.0f); + builder.pushStyle(roboto); + builder.addText("Down40"); + + assyrian.setBaselineShift(60.0f); + builder.pushStyle(assyrian); + builder.addText("Down60"); + + roboto.setBaselineShift(40.0f); + builder.pushStyle(roboto); + builder.addText("Down40"); + + assyrian.setBaselineShift(20.0f); + builder.pushStyle(assyrian); + builder.addText("Down20"); + + roboto.setBaselineShift(0.0f); + builder.pushStyle(roboto); + builder.addText(" Zero baseline shift text\n"); + + assyrian.resetShadows(); + assyrian.setDecorationStyle(TextDecorationStyle::kSolid); + assyrian.setDecoration(TextDecoration::kUnderline); + assyrian.setDecorationColor(SK_ColorBLUE); + + roboto.setBaselineShift(0.0f); + builder.pushStyle(roboto); + builder.addText("Zero baseline shift text "); + + assyrian.setBaselineShift(-20.0f); + builder.pushStyle(assyrian); + builder.addText("Up20"); + + roboto.setBaselineShift(-40.0f); + builder.pushStyle(roboto); + builder.addText("Up40"); + + assyrian.setBaselineShift(-60.0f); + builder.pushStyle(assyrian); + builder.addText("Up60"); + + roboto.setBaselineShift(-40.0f); + builder.pushStyle(roboto); + builder.addText("Up40"); + + assyrian.setBaselineShift(-20.0f); + builder.pushStyle(assyrian); + builder.addText("Up20"); + + roboto.setBaselineShift(-0.0f); + builder.pushStyle(roboto); + builder.addText(" Zero baseline shift text"); + + auto paragraph = builder.Build(); + paragraph->layout(SK_ScalarInfinity); + paragraph->paint(canvas, 0, 0); + } + +private: + using INHERITED = Sample; +}; + } // namespace ////////////////////////////////////////////////////////////////////////////// @@ -3674,3 +3819,4 @@ DEF_SAMPLE(return new ParagraphView59();) DEF_SAMPLE(return new ParagraphView60();) DEF_SAMPLE(return new ParagraphView61();) DEF_SAMPLE(return new ParagraphView62();) +DEF_SAMPLE(return new ParagraphView63();) diff --git a/modules/skparagraph/src/OneLineShaper.cpp b/modules/skparagraph/src/OneLineShaper.cpp index 4066a65c35..b2a12798a2 100644 --- a/modules/skparagraph/src/OneLineShaper.cpp +++ b/modules/skparagraph/src/OneLineShaper.cpp @@ -210,6 +210,7 @@ void OneLineShaper::finish(const Block& block, SkScalar height, SkScalar& advanc run->fClusterStart, height, block.fStyle.getHalfLeading(), + block.fStyle.getBaselineShift(), this->fParagraph->fRuns.count(), advanceX ); @@ -606,6 +607,7 @@ bool OneLineShaper::iterateThroughShapingRegions(const ShapeVisitor& shape) { runInfo, placeholder.fRange.start, 0.0f, + 0.0f, false, fParagraph->fRuns.count(), advanceX); @@ -643,6 +645,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(); + fBaselineShift = block.fStyle.getBaselineShift(); fAdvance = SkVector::Make(advanceX, 0); fCurrentText = block.fRange; fUnresolvedBlocks.emplace_back(RunBlock(block.fRange)); diff --git a/modules/skparagraph/src/OneLineShaper.h b/modules/skparagraph/src/OneLineShaper.h index 929e4ad6b9..a746f95850 100644 --- a/modules/skparagraph/src/OneLineShaper.h +++ b/modules/skparagraph/src/OneLineShaper.h @@ -19,6 +19,7 @@ public: : fParagraph(paragraph) , fHeight(0.0f) , fUseHalfLeading(false) + , fBaselineShift(0.0f) , fAdvance(SkPoint::Make(0.0f, 0.0f)) , fUnresolvedGlyphs(0) , fUniqueRunId(paragraph->fRuns.size()){ } @@ -83,6 +84,7 @@ private: fCurrentText.start, fHeight, fUseHalfLeading, + fBaselineShift, ++fUniqueRunId, fAdvance.fX); return fCurrentRun->newRunBuffer(); @@ -104,6 +106,7 @@ private: TextRange fCurrentText; SkScalar fHeight; bool fUseHalfLeading; + SkScalar fBaselineShift; SkVector fAdvance; size_t fUnresolvedGlyphs; size_t fUniqueRunId; diff --git a/modules/skparagraph/src/ParagraphCache.cpp b/modules/skparagraph/src/ParagraphCache.cpp index d9065e027e..14c1977722 100644 --- a/modules/skparagraph/src/ParagraphCache.cpp +++ b/modules/skparagraph/src/ParagraphCache.cpp @@ -93,6 +93,7 @@ uint32_t ParagraphCache::KeyHash::operator()(const ParagraphCacheKey& key) const 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()))); for (auto& ff : ts.fStyle.getFontFamilies()) { hash = mix(hash, SkGoodHash()(ff)); } diff --git a/modules/skparagraph/src/Run.cpp b/modules/skparagraph/src/Run.cpp index 88f7c2961b..e50f412541 100644 --- a/modules/skparagraph/src/Run.cpp +++ b/modules/skparagraph/src/Run.cpp @@ -19,6 +19,7 @@ Run::Run(ParagraphImpl* owner, size_t firstChar, SkScalar heightMultiplier, bool useHalfLeading, + SkScalar baselineShift, size_t index, SkScalar offsetX) : fOwner(owner) @@ -28,6 +29,7 @@ Run::Run(ParagraphImpl* owner, , fClusterStart(firstChar) , fHeightMultiplier(heightMultiplier) , fUseHalfLeading(useHalfLeading) + , fBaselineShift(baselineShift) { fBidiLevel = info.fBidiLevel; fAdvance = info.fAdvance; @@ -69,6 +71,9 @@ void Run::calculateMetrics() { fCorrectAscent *= multiplier; fCorrectDescent *= multiplier; } + // If we shift the baseline we need to make sure the shifted text fits the line + fCorrectAscent += fBaselineShift; + fCorrectDescent += fBaselineShift; } SkShaper::RunHandler::Buffer Run::newRunBuffer() { diff --git a/modules/skparagraph/src/Run.h b/modules/skparagraph/src/Run.h index 2eee6d0800..517ced8a5b 100644 --- a/modules/skparagraph/src/Run.h +++ b/modules/skparagraph/src/Run.h @@ -59,6 +59,7 @@ public: size_t firstChar, SkScalar heightMultiplier, bool useHalfLeading, + SkScalar baselineShift, size_t index, SkScalar shiftX); Run(const Run&) = default; @@ -85,11 +86,11 @@ public: return SkVector::Make(fAdvance.fX, fFontMetrics.fDescent - fFontMetrics.fAscent + fFontMetrics.fLeading); } SkVector offset() const { return fOffset; } - SkScalar ascent() const { return fFontMetrics.fAscent; } - SkScalar descent() const { return fFontMetrics.fDescent; } + SkScalar ascent() const { return fFontMetrics.fAscent + fBaselineShift; } + SkScalar descent() const { return fFontMetrics.fDescent + fBaselineShift; } SkScalar leading() const { return fFontMetrics.fLeading; } - SkScalar correctAscent() const { return fCorrectAscent; } - SkScalar correctDescent() const { return fCorrectDescent; } + SkScalar correctAscent() const { return fCorrectAscent + fBaselineShift; } + SkScalar correctDescent() const { return fCorrectDescent + fBaselineShift; } SkScalar correctLeading() const { return fCorrectLeading; } const SkFont& font() const { return fFont; } bool leftToRight() const { return fBidiLevel % 2 == 0; } @@ -97,6 +98,7 @@ public: size_t index() const { return fIndex; } SkScalar heightMultiplier() const { return fHeightMultiplier; } bool useHalfLeading() const { return fUseHalfLeading; } + SkScalar baselineShift() const { return fBaselineShift; } PlaceholderStyle* placeholderStyle() const; bool isPlaceholder() const { return fPlaceholderIndex != std::numeric_limits::max(); } size_t clusterIndex(size_t pos) const { return fClusterIndexes[pos]; } @@ -193,6 +195,7 @@ private: SkFontMetrics fFontMetrics; const SkScalar fHeightMultiplier; const bool fUseHalfLeading; + const SkScalar fBaselineShift; SkScalar fCorrectAscent; SkScalar fCorrectDescent; SkScalar fCorrectLeading; diff --git a/modules/skparagraph/src/TextLine.cpp b/modules/skparagraph/src/TextLine.cpp index 752c51bdae..02b6564cb3 100644 --- a/modules/skparagraph/src/TextLine.cpp +++ b/modules/skparagraph/src/TextLine.cpp @@ -337,7 +337,8 @@ void TextLine::buildTextBlob(TextRange textRange, const TextStyle& style, const record.fClipRect = context.clip.makeOffset(this->offset()); } - SkScalar correctedBaseline = SkScalarFloorToScalar(this->baseline() + 0.5); + SkASSERT(nearlyEqual(context.run->baselineShift(), style.getBaselineShift())); + SkScalar correctedBaseline = SkScalarFloorToScalar(this->baseline() + style.getBaselineShift() + 0.5); record.fBlob = builder.make(); if (record.fBlob != nullptr) { record.fBounds.joinPossiblyEmptyRect(record.fBlob->bounds()); @@ -366,7 +367,7 @@ void TextLine::paintBackground(SkCanvas* canvas, SkScalar x, SkScalar y, TextRan SkRect TextLine::paintShadow(SkCanvas* canvas, SkScalar x, SkScalar y, TextRange textRange, const TextStyle& style, const ClipContext& context) const { - SkScalar correctedBaseline = SkScalarFloorToScalar(this->baseline() + 0.5); + SkScalar correctedBaseline = SkScalarFloorToScalar(this->baseline() + style.getBaselineShift() + 0.5); SkRect shadowBounds = SkRect::MakeEmpty(); for (TextShadow shadow : style.getShadows()) { @@ -413,9 +414,9 @@ SkRect TextLine::paintShadow(SkCanvas* canvas, SkScalar x, SkScalar y, TextRange void TextLine::paintDecorations(SkCanvas* canvas, SkScalar x, SkScalar y, TextRange textRange, const TextStyle& style, const ClipContext& context) const { SkAutoCanvasRestore acr(canvas, true); - canvas->translate(x + this->offset().fX, y + this->offset().fY); + canvas->translate(x + this->offset().fX, y + this->offset().fY + style.getBaselineShift()); Decorations decorations; - SkScalar correctedBaseline = SkScalarFloorToScalar(this->baseline() + 0.5); + SkScalar correctedBaseline = SkScalarFloorToScalar(this->baseline() + style.getBaselineShift() + 0.5); decorations.paint(canvas, style, context, correctedBaseline); } @@ -541,8 +542,8 @@ std::unique_ptr TextLine::shapeEllipsis(const SkString& ellipsis, const Run class ShapeHandler final : public SkShaper::RunHandler { public: - ShapeHandler(SkScalar lineHeight, bool useHalfLeading, const SkString& ellipsis) - : fRun(nullptr), fLineHeight(lineHeight), fUseHalfLeading(useHalfLeading), fEllipsis(ellipsis) {} + ShapeHandler(SkScalar lineHeight, bool useHalfLeading, SkScalar baselineShift, const SkString& ellipsis) + : fRun(nullptr), fLineHeight(lineHeight), fUseHalfLeading(useHalfLeading), fBaselineShift(baselineShift), fEllipsis(ellipsis) {} Run* run() & { return fRun.get(); } std::unique_ptr run() && { return std::move(fRun); } @@ -555,7 +556,7 @@ std::unique_ptr TextLine::shapeEllipsis(const SkString& ellipsis, const Run Buffer runBuffer(const RunInfo& info) override { SkASSERT(!fRun); - fRun = std::make_unique(nullptr, info, 0, fLineHeight, fUseHalfLeading, 0, 0); + fRun = std::make_unique(nullptr, info, 0, fLineHeight, fUseHalfLeading, fBaselineShift, 0, 0); return fRun->newRunBuffer(); } @@ -571,10 +572,11 @@ std::unique_ptr TextLine::shapeEllipsis(const SkString& ellipsis, const Run std::unique_ptr fRun; SkScalar fLineHeight; bool fUseHalfLeading; + SkScalar fBaselineShift; SkString fEllipsis; }; - ShapeHandler handler(run.heightMultiplier(), run.useHalfLeading(), ellipsis); + ShapeHandler handler(run.heightMultiplier(), run.useHalfLeading(), run.baselineShift(), ellipsis); std::unique_ptr shaper = SkShaper::MakeShapeDontWrapOrReorder(); SkASSERT_RELEASE(shaper != nullptr); shaper->shape(ellipsis.c_str(), ellipsis.size(), run.font(), true, diff --git a/modules/skparagraph/src/TextStyle.cpp b/modules/skparagraph/src/TextStyle.cpp index 49c2f3b117..1f56733b3f 100644 --- a/modules/skparagraph/src/TextStyle.cpp +++ b/modules/skparagraph/src/TextStyle.cpp @@ -21,6 +21,7 @@ TextStyle::TextStyle(const TextStyle& other, bool placeholder) { fIsPlaceholder = placeholder; fFontFeatures = other.fFontFeatures; fHalfLeading = other.fHalfLeading; + fBaselineShift = other.fBaselineShift; } bool TextStyle::equals(const TextStyle& other) const { @@ -53,6 +54,9 @@ bool TextStyle::equals(const TextStyle& other) const { if (fHalfLeading != other.fHalfLeading) { return false; } + if (fBaselineShift != other.fBaselineShift) { + return false; + } if (fFontSize != other.fFontSize) { return false; } @@ -94,6 +98,7 @@ bool TextStyle::equalsByFonts(const TextStyle& that) const { nearlyEqual(fLetterSpacing, that.fLetterSpacing) && nearlyEqual(fWordSpacing, that.fWordSpacing) && nearlyEqual(fHeight, that.fHeight) && + nearlyEqual(fBaselineShift, that.fBaselineShift) && nearlyEqual(fFontSize, that.fFontSize) && fLocale == that.fLocale; } @@ -139,7 +144,9 @@ bool TextStyle::matchOneAttribute(StyleType styleType, const TextStyle& other) c fFontFamilies == other.fFontFamilies && fFontSize == other.fFontSize && fHeight == other.fHeight && - fHalfLeading == other.fHalfLeading; + fHeight == other.fHeight && + fHalfLeading == other.fHalfLeading && + fBaselineShift == other.fBaselineShift; default: SkASSERT(false); return false; @@ -162,6 +169,9 @@ void TextStyle::getFontMetrics(SkFontMetrics* metrics) const { metrics->fAscent = (metrics->fAscent - metrics->fLeading / 2); metrics->fDescent = (metrics->fDescent + metrics->fLeading / 2); } + // If we shift the baseline we need to make sure the shifted text fits the line + metrics->fAscent += fBaselineShift; + metrics->fDescent += fBaselineShift; } bool PlaceholderStyle::equals(const PlaceholderStyle& other) const {