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>
This commit is contained in:
parent
c9f160b8dd
commit
0c1f6e5dab
@ -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;
|
||||
|
@ -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();)
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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<size_t>::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;
|
||||
|
@ -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<Run> 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> run() && { return std::move(fRun); }
|
||||
|
||||
@ -555,7 +556,7 @@ std::unique_ptr<Run> TextLine::shapeEllipsis(const SkString& ellipsis, const Run
|
||||
|
||||
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> TextLine::shapeEllipsis(const SkString& ellipsis, const Run
|
||||
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,
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user