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 <bungeman@google.com>
Commit-Queue: Julia Lavrova <jlavrova@google.com>
diff --git a/modules/skparagraph/include/TextStyle.h b/modules/skparagraph/include/TextStyle.h
index e576414..00487c7 100644
--- a/modules/skparagraph/include/TextStyle.h
+++ b/modules/skparagraph/include/TextStyle.h
@@ -215,6 +215,9 @@
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 @@
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 e9e6acb..8817d5f 100644
--- a/modules/skparagraph/samples/SampleParagraph.cpp
+++ b/modules/skparagraph/samples/SampleParagraph.cpp
@@ -3611,6 +3611,151 @@
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 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 4066a65..b2a1279 100644
--- a/modules/skparagraph/src/OneLineShaper.cpp
+++ b/modules/skparagraph/src/OneLineShaper.cpp
@@ -210,6 +210,7 @@
run->fClusterStart,
height,
block.fStyle.getHalfLeading(),
+ block.fStyle.getBaselineShift(),
this->fParagraph->fRuns.count(),
advanceX
);
@@ -606,6 +607,7 @@
runInfo,
placeholder.fRange.start,
0.0f,
+ 0.0f,
false,
fParagraph->fRuns.count(),
advanceX);
@@ -643,6 +645,7 @@
// 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 929e4ad..a746f95 100644
--- a/modules/skparagraph/src/OneLineShaper.h
+++ b/modules/skparagraph/src/OneLineShaper.h
@@ -19,6 +19,7 @@
: 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 @@
fCurrentText.start,
fHeight,
fUseHalfLeading,
+ fBaselineShift,
++fUniqueRunId,
fAdvance.fX);
return fCurrentRun->newRunBuffer();
@@ -104,6 +106,7 @@
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 d9065e0..14c1977 100644
--- a/modules/skparagraph/src/ParagraphCache.cpp
+++ b/modules/skparagraph/src/ParagraphCache.cpp
@@ -93,6 +93,7 @@
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 88f7c29..e50f412 100644
--- a/modules/skparagraph/src/Run.cpp
+++ b/modules/skparagraph/src/Run.cpp
@@ -19,6 +19,7 @@
size_t firstChar,
SkScalar heightMultiplier,
bool useHalfLeading,
+ SkScalar baselineShift,
size_t index,
SkScalar offsetX)
: fOwner(owner)
@@ -28,6 +29,7 @@
, fClusterStart(firstChar)
, fHeightMultiplier(heightMultiplier)
, fUseHalfLeading(useHalfLeading)
+ , fBaselineShift(baselineShift)
{
fBidiLevel = info.fBidiLevel;
fAdvance = info.fAdvance;
@@ -69,6 +71,9 @@
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 2eee6d0..517ced8 100644
--- a/modules/skparagraph/src/Run.h
+++ b/modules/skparagraph/src/Run.h
@@ -59,6 +59,7 @@
size_t firstChar,
SkScalar heightMultiplier,
bool useHalfLeading,
+ SkScalar baselineShift,
size_t index,
SkScalar shiftX);
Run(const Run&) = default;
@@ -85,11 +86,11 @@
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 @@
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<size_t>::max(); }
size_t clusterIndex(size_t pos) const { return fClusterIndexes[pos]; }
@@ -193,6 +195,7 @@
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 752c51b..02b6564 100644
--- a/modules/skparagraph/src/TextLine.cpp
+++ b/modules/skparagraph/src/TextLine.cpp
@@ -337,7 +337,8 @@
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 @@
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 @@
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 @@
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> run() && { return std::move(fRun); }
@@ -555,7 +556,7 @@
Buffer runBuffer(const RunInfo& info) override {
SkASSERT(!fRun);
- fRun = std::make_unique<Run>(nullptr, info, 0, fLineHeight, fUseHalfLeading, 0, 0);
+ fRun = std::make_unique<Run>(nullptr, info, 0, fLineHeight, fUseHalfLeading, fBaselineShift, 0, 0);
return fRun->newRunBuffer();
}
@@ -571,10 +572,11 @@
std::unique_ptr<Run> 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<SkShaper> 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 49c2f3b..1f56733 100644
--- a/modules/skparagraph/src/TextStyle.cpp
+++ b/modules/skparagraph/src/TextStyle.cpp
@@ -21,6 +21,7 @@
fIsPlaceholder = placeholder;
fFontFeatures = other.fFontFeatures;
fHalfLeading = other.fHalfLeading;
+ fBaselineShift = other.fBaselineShift;
}
bool TextStyle::equals(const TextStyle& other) const {
@@ -53,6 +54,9 @@
if (fHalfLeading != other.fHalfLeading) {
return false;
}
+ if (fBaselineShift != other.fBaselineShift) {
+ return false;
+ }
if (fFontSize != other.fFontSize) {
return false;
}
@@ -94,6 +98,7 @@
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 @@
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 @@
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 {