Multi line + LTR/RTL
Change-Id: I0859773c00b6a4bd19047d25b75ee7c6727bc470
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/403837
Reviewed-by: Julia Lavrova <jlavrova@google.com>
Commit-Queue: Julia Lavrova <jlavrova@google.com>
diff --git a/experimental/sktext/include/Processor.h b/experimental/sktext/include/Processor.h
index 4c31c70..521f9a7 100644
--- a/experimental/sktext/include/Processor.h
+++ b/experimental/sktext/include/Processor.h
@@ -25,16 +25,16 @@
kDecor,
kFormat,
};
- Block(BlockType type, Range range)
+ Block(BlockType type, TextRange range)
: fType(type)
, fRange(range) { }
BlockType fType;
- Range fRange;
+ TextRange fRange;
};
class FontBlock : public Block {
public:
- FontBlock(const SkString& family, SkScalar size, SkFontStyle style, Range range)
+ FontBlock(const SkString& family, SkScalar size, SkFontStyle style, TextRange range)
: Block(Block::kFont, range)
, fFontFamily(family)
, fFontSize(size)
@@ -48,12 +48,12 @@
class DecorBlock : public Block {
public:
- DecorBlock(const SkPaint* foreground, const SkPaint* background, Range range)
+ DecorBlock(const SkPaint* foreground, const SkPaint* background, TextRange range)
: Block(Block::kDecor, range)
, fForegroundColor(foreground)
, fBackgroundColor(background) { }
- DecorBlock(Range range)
+ DecorBlock(TextRange range)
: DecorBlock(nullptr, nullptr, range) { }
const SkPaint* fForegroundColor;
@@ -119,10 +119,10 @@
}
bool isSoftLineBreak(size_t index) {
- return this->hasProperty(index, CodeUnitFlags::kSoftLineBreakBefore);
+ return index != 0 && this->hasProperty(index, CodeUnitFlags::kSoftLineBreakBefore);
}
- bool isWhitespaces(Range range) {
+ bool isWhitespaces(TextRange range) {
if (range.leftToRight()) {
for (auto i = range.fStart; i < range.fEnd; ++i) {
if (!this->hasProperty(i, CodeUnitFlags::kPartOfWhiteSpace)) {
@@ -158,8 +158,11 @@
// Simplification (using default font manager, default font family and default everything possible)
static bool drawText(const char* text, SkCanvas* canvas, SkScalar x, SkScalar y);
- //static bool drawText(const char* text, SkCanvas* canvas, TextFormatStyle textFormat, const SkString& fontFamily, SkScalar fontSize, SkFontStyle fontStyle, SkScalar x, SkScalar y);
+ static bool drawText(const char* text, SkCanvas* canvas, SkScalar width);
static bool drawText(const char* text, SkCanvas* canvas, TextFormatStyle textFormat, SkColor foreground, SkColor background, const SkString& fontFamily, SkScalar fontSize, SkFontStyle fontStyle, SkScalar x, SkScalar y);
+ static bool drawText(const char* text, SkCanvas* canvas,
+ TextFormatStyle textFormat, SkColor foreground, SkColor background, const SkString& fontFamily, SkScalar fontSize, SkFontStyle fontStyle,
+ SkSize reqSize, SkScalar x, SkScalar y);
void sortDecorBlocks(SkTArray<DecorBlock, true>& decorBlocks);
@@ -167,10 +170,6 @@
void markGlyphs();
- // Iterating through the input code units and breaking the runs by units flag (no breaking if units == CodeUnitFlags::kNonExistingFlag)
- template<typename Visitor>
- void iterateByLogicalOrder(CodeUnitFlags units, Visitor visitor);
-
// Iterating through the output glyphs and breaking the runs by units flag (no breaking if units == CodeUnitFlags::kNonExistingFlag)
template<typename Visitor>
void iterateByVisualOrder(CodeUnitFlags units, Visitor visitor);
diff --git a/experimental/sktext/include/Types.h b/experimental/sktext/include/Types.h
index e46a387..594e64b 100644
--- a/experimental/sktext/include/Types.h
+++ b/experimental/sktext/include/Types.h
@@ -28,22 +28,27 @@
kHardLineBreakBefore,
};
-enum CodeUnitFlags : uint64_t {
- kNoCodeUnitFlag = 0x0,
- kPartOfWhiteSpace = 0x1,
- kGraphemeStart = 0x2,
- kSoftLineBreakBefore = 0x4,
- kHardLineBreakBefore = 0x8,
+enum CodeUnitFlags : uint32_t {
+ kNoCodeUnitFlag = 0x000,
+ kPartOfWhiteSpace = 0x001,
+ kGraphemeStart = 0x002,
+ kSoftLineBreakBefore = 0x004,
+ kHardLineBreakBefore = 0x008,
// This information we get from SkShaper
- kGlyphStart = 0x16,
- kGlyphClusterStart = 0x32,
- kNonExistingFlag = 0x64,
+ kGlyphStart = 0x010,
+ kGlyphClusterStart = 0x020,
+ kNonExistingFlag = 0x040,
};
+typedef size_t TextIndex;
+typedef size_t GlyphIndex;
+const size_t EMPTY_INDEX = std::numeric_limits<size_t>::max();
+
+template <typename T>
class Range {
public:
Range() : fStart(0), fEnd(0) { }
- Range(size_t start, size_t end) : fStart(start) , fEnd(end) { }
+ Range(T start, T end) : fStart(start) , fEnd(end) { }
bool leftToRight() const {
return fEnd >= fStart;
@@ -70,15 +75,17 @@
}
void merge(Range tail) {
- auto ltr = this->leftToRight();
-
+ auto ltr1 = this->leftToRight();
+ auto ltr2 = tail.leftToRight();
this->normalize();
tail.normalize();
SkASSERT(this->fEnd == tail.fStart || this->fStart == tail.fEnd);
this->fStart = std::min(this->fStart, tail.fStart);
this->fEnd = std::max(this->fEnd, tail.fEnd);
- if (!ltr) {
+ // TODO: Merging 2 different directions
+ if (!ltr1 || !ltr2) {
std::swap(this->fStart, this->fEnd);
+ std::swap(tail.fStart, tail.fEnd);
}
}
@@ -110,11 +117,12 @@
}
}
- size_t fStart;
- size_t fEnd;
+ T fStart;
+ T fEnd;
};
-const size_t EMPTY_INDEX = std::numeric_limits<size_t>::max();
+typedef Range<TextIndex> TextRange;
+typedef Range<GlyphIndex> GlyphRange;
const Range EMPTY_RANGE = Range(EMPTY_INDEX, EMPTY_INDEX);
} // namespace text
diff --git a/experimental/sktext/samples/Text.cpp b/experimental/sktext/samples/Text.cpp
index c72cdf9..af59cd63a 100644
--- a/experimental/sktext/samples/Text.cpp
+++ b/experimental/sktext/samples/Text.cpp
@@ -116,7 +116,54 @@
using INHERITED = Sample;
std::unique_ptr<SkUnicode> fUnicode;
};
+
+class TextSample_LongLTR : public Sample {
+protected:
+ SkString name() override { return SkString("TextSample_LongLTR"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ canvas->drawColor(SK_ColorWHITE);
+ Processor::drawText("A very_very_very_very_very_very_very_very_very_very "
+ "very_very_very_very_very_very_very_very_very_very very very very very very very "
+ "very very very very very very very very very very very very very very very very "
+ "very very very very very very very very very very very very very long text", canvas, this->width());
+
+ }
+
+private:
+ using INHERITED = Sample;
+ std::unique_ptr<SkUnicode> fUnicode;
+};
+
+class TextSample_LongRTL : public Sample {
+protected:
+ SkString name() override { return SkString("TextSample_LongRTL"); }
+
+ SkString mirror(const std::string& text) {
+ std::u16string result;
+ result += u"\u202E";
+ for (auto i = text.size(); i > 0; --i) {
+ result += text[i - 1];
+ }
+ for (auto ch : text) {
+ result += ch;
+ }
+ result += u"\u202C";
+ return fUnicode->convertUtf16ToUtf8(result);
+ }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ canvas->drawColor(SK_ColorWHITE);
+ Processor::drawText("LONG MIRRORED TEXT SHOULD SHOW RIGHT TO LEFT (AS NORMAL)", canvas, 0, 0);
+ }
+
+private:
+ using INHERITED = Sample;
+ std::unique_ptr<SkUnicode> fUnicode;
+};
} // namespace
DEF_SAMPLE(return new TextSample_HelloWorld();)
DEF_SAMPLE(return new TextSample_Align_Dir();)
+DEF_SAMPLE(return new TextSample_LongLTR();)
+DEF_SAMPLE(return new TextSample_LongRTL();)
diff --git a/experimental/sktext/src/Line.cpp b/experimental/sktext/src/Line.cpp
index d13008f..8c289a1 100644
--- a/experimental/sktext/src/Line.cpp
+++ b/experimental/sktext/src/Line.cpp
@@ -8,29 +8,29 @@
namespace skia {
namespace text {
Line::Line(Processor* processor, const Stretch& stretch, const Stretch& spaces)
- : fTextStart(stretch.fGlyphStart)
- , fTextEnd(stretch.fGlyphEnd)
- , fWhitespacesEnd (spaces.fGlyphEnd)
- , fText(stretch.fTextRange)
- , fWhitespaces(spaces.fTextRange)
- , fTextWidth(stretch.fWidth)
- , fSpacesWidth(spaces.fWidth) {
+ : fTextStart(stretch.glyphStart())
+ , fTextEnd(stretch.glyphEnd())
+ , fWhitespacesEnd (spaces.glyphEnd())
+ , fText(stretch.textRange())
+ , fWhitespaces(spaces.textRange())
+ , fTextWidth(stretch.width())
+ , fSpacesWidth(spaces.width()) {
SkASSERT(stretch.isEmpty() ||
spaces.isEmpty() ||
- (stretch.fGlyphEnd == spaces.fGlyphStart));
+ (stretch.glyphEnd() == spaces.glyphStart()));
if (!stretch.isEmpty()) {
- this->fTextMetrics.merge(stretch.fTextMetrics);
+ this->fTextMetrics.merge(stretch.textMetrics());
}
if (!spaces.isEmpty()) {
- this->fTextMetrics.merge(spaces.fTextMetrics);
+ this->fTextMetrics.merge(spaces.textMetrics());
}
// This is just chosen to catch the common/fast cases. Feel free to tweak.
constexpr int kPreallocCount = 4;
- auto start = stretch.fGlyphStart.fRunIndex;
- auto end = spaces.fGlyphEnd.fRunIndex;
+ auto start = stretch.glyphStart().runIndex();
+ auto end = spaces.glyphEnd().runIndex();
auto numRuns = end - start + 1;
SkAutoSTArray<kPreallocCount, SkUnicode::BidiLevel> runLevels(numRuns);
size_t runLevelsIndex = 0;
diff --git a/experimental/sktext/src/Line.h b/experimental/sktext/src/Line.h
index b01ddeb..9951c93 100644
--- a/experimental/sktext/src/Line.h
+++ b/experimental/sktext/src/Line.h
@@ -58,13 +58,20 @@
class GlyphPos {
public:
- GlyphPos() { }
+ GlyphPos() : fRunIndex(EMPTY_INDEX), fGlyphIndex(EMPTY_INDEX) { }
GlyphPos(size_t runIndex, size_t glyphIndex) : fRunIndex(runIndex), fGlyphIndex(glyphIndex) { }
bool operator==(const GlyphPos& other) const {
return this->fRunIndex == other.fRunIndex && this->fGlyphIndex == other.fGlyphIndex;
}
+ size_t runIndex() const { return fRunIndex; }
+ size_t glyphIndex() const { return fGlyphIndex; }
+ void setGlyphIndex(size_t index) { fGlyphIndex = index; }
+
+ bool isEmpty() const { return fRunIndex == EMPTY_INDEX; }
+
+private:
size_t fRunIndex;
size_t fGlyphIndex;
};
@@ -72,50 +79,59 @@
class Stretch {
public:
- Stretch() : fGlyphStart(), fGlyphEnd(), fWidth(0), fTextRange(0, 0), fTextMetrics(), fEmpty(true) { }
+ Stretch() : fGlyphStart(), fGlyphEnd(), fWidth(0), fTextRange(0, 0), fTextMetrics() { }
Stretch(GlyphPos glyphStart, size_t textIndex, const TextMetrics& metrics)
: fGlyphStart(glyphStart)
, fGlyphEnd(glyphStart)
, fWidth(0.0)
, fTextRange(textIndex, textIndex)
- , fTextMetrics(metrics)
- , fEmpty(false) { }
+ , fTextMetrics(metrics) { }
+ Stretch(const Stretch&) = default;
Stretch(Stretch&&) = default;
Stretch& operator=(Stretch&&) = default;
+ Stretch& operator=(const Stretch&) = default;
bool isEmpty() const {
- return this->fEmpty/* ||
- (this->fGlyphStart.fRunIndex == this->fGlyphEnd.fRunIndex &&
- this->fGlyphStart.fGlyphIndex == this->fGlyphEnd.fGlyphIndex)*/;
+ if (this->fGlyphStart.isEmpty()) {
+ SkASSERT(this->fGlyphEnd.isEmpty());
+ return true;
+ } else {
+ SkASSERT(!this->fGlyphEnd.isEmpty());
+ return false;
+ //return (this->fGlyphStart.runIndex() == this->fGlyphEnd.runIndex() &&
+ // this->fGlyphStart.glyphIndex() == this->fGlyphEnd.glyphIndex());
+ }
}
void clean() {
- this->fEmpty = true;
+ fGlyphStart = fGlyphEnd;
+ fTextRange.fStart = fTextRange.fEnd;
+ fWidth = 0.0f;
+ fTextMetrics.clean();
}
void moveTo(Stretch& tail) {
- if (tail.fEmpty) {
+ if (tail.isEmpty()) {
return;
}
- if (this->fEmpty) {
- if (!tail.fEmpty) {
+ if (this->isEmpty()) {
+ if (!tail.isEmpty()) {
this->fGlyphStart = tail.fGlyphStart;
this->fGlyphEnd = tail.fGlyphEnd;
this->fWidth = tail.fWidth;
this->fTextRange = tail.fTextRange;
this->fTextMetrics = tail.fTextMetrics;
- this->fEmpty = tail.fEmpty;
}
tail.clean();
return;
}
- SkASSERT(this->fGlyphEnd.fRunIndex != tail.fGlyphStart.fRunIndex ||
- this->fGlyphEnd.fGlyphIndex == tail.fGlyphStart.fGlyphIndex);
+ SkASSERT(this->fGlyphEnd.runIndex() != tail.fGlyphStart.runIndex() ||
+ this->fGlyphEnd.glyphIndex() == tail.fGlyphStart.glyphIndex());
this->fGlyphEnd = tail.fGlyphEnd;
this->fWidth += tail.fWidth;
this->fTextRange.merge(tail.fTextRange);
@@ -125,16 +141,26 @@
void finish(size_t glyphIndex, size_t textIndex, SkScalar width) {
this->fTextRange.fEnd = textIndex;
- this->fGlyphEnd.fGlyphIndex = glyphIndex;
+ this->fGlyphEnd.setGlyphIndex(glyphIndex);
this->fWidth = width;
}
+ SkScalar width() const { return fWidth; }
+ TextRange textRange() const { return fTextRange; }
+ void setTextRange(TextRange range) { fTextRange = range; }
+
+ const TextMetrics& textMetrics() const { return fTextMetrics; }
+ GlyphPos glyphStart() const { return fGlyphStart; }
+ GlyphPos glyphEnd() const { return fGlyphEnd; }
+ size_t glyphStartIndex() const { return fGlyphStart.glyphIndex(); }
+ size_t textStart() const { return fTextRange.fStart; }
+
+private:
GlyphPos fGlyphStart;
GlyphPos fGlyphEnd;
SkScalar fWidth;
- Range fTextRange;
+ TextRange fTextRange;
TextMetrics fTextMetrics;
- bool fEmpty;
};
class Line {
@@ -146,10 +172,10 @@
friend class Processor;
GlyphPos fTextStart;
- GlyphPos fTextEnd ;
+ GlyphPos fTextEnd;
GlyphPos fWhitespacesEnd;
- Range fText;
- Range fWhitespaces;
+ TextRange fText;
+ TextRange fWhitespaces;
SkScalar fTextWidth;
SkScalar fSpacesWidth;
TextMetrics fTextMetrics;
diff --git a/experimental/sktext/src/Processor.cpp b/experimental/sktext/src/Processor.cpp
index f92a2c4..676a612 100644
--- a/experimental/sktext/src/Processor.cpp
+++ b/experimental/sktext/src/Processor.cpp
@@ -55,7 +55,7 @@
bool Processor::decorate(SkTArray<DecorBlock, true> decorBlocks) {
this->iterateByVisualOrder(decorBlocks,
- [&](SkSize offset, SkScalar baseline, const TextRun* run, Range textRange, Range glyphRange, const DecorBlock& block) {
+ [&](SkSize offset, SkScalar baseline, const TextRun* run, TextRange textRange, GlyphRange glyphRange, const DecorBlock& block) {
SkTextBlobBuilder builder;
const auto& blobBuffer = builder.allocRunPos(run->fFont, SkToInt(glyphRange.width()));
sk_careful_memcpy(blobBuffer.glyphs, run->fGlyphs.data() + glyphRange.fStart, glyphRange.width() * sizeof(SkGlyphID));
@@ -74,10 +74,22 @@
return drawText(text, canvas, TextFormatStyle(TextAlign::kLeft, TextDirection::kLtr), SK_ColorBLACK, SK_ColorWHITE, SkString("Roboto"), 14, SkFontStyle::Normal(), x, y);
}
+bool Processor::drawText(const char* text, SkCanvas* canvas, SkScalar width) {
+ return drawText(text, canvas,
+ TextFormatStyle(TextAlign::kLeft, TextDirection::kLtr), SK_ColorBLACK, SK_ColorWHITE, SkString("Roboto"), 14, SkFontStyle::Normal(),
+ SkSize::Make(width, SK_ScalarInfinity), 0, 0);
+}
+
bool Processor::drawText(const char* text, SkCanvas* canvas, TextFormatStyle textFormat, SkColor foreground, SkColor background, const SkString& fontFamily, SkScalar fontSize, SkFontStyle fontStyle, SkScalar x, SkScalar y) {
+ return drawText(text, canvas, textFormat, foreground, background, fontFamily, fontSize, fontStyle, SkSize::Make(SK_ScalarInfinity, SK_ScalarInfinity), x, y);
+}
+
+bool Processor::drawText(const char* text, SkCanvas* canvas,
+ TextFormatStyle textFormat, SkColor foreground, SkColor background, const SkString& fontFamily, SkScalar fontSize, SkFontStyle fontStyle,
+ SkSize reqSize, SkScalar x, SkScalar y) {
SkString str(text);
- Range textRange(0, str.size());
+ TextRange textRange(0, str.size());
Processor processor(str);
if (!processor.computeCodeUnitProperties()) {
return false;
@@ -85,7 +97,7 @@
if (!processor.shape({ textFormat.fDefaultTextDirection, SkFontMgr::RefDefault()}, {{{ fontFamily, fontSize, fontStyle, textRange }}})) {
return false;
}
- if (!processor.wrap(SK_ScalarInfinity, SK_ScalarInfinity)) {
+ if (!processor.wrap(reqSize.fWidth, reqSize.fHeight)) {
return false;
}
if (!processor.format(textFormat)) {
@@ -117,7 +129,7 @@
SkPaint* foreground = new SkPaint();
foreground->setColor(SK_ColorBLACK);
std::stack<DecorBlock> defaultBlocks;
- defaultBlocks.emplace(foreground, nullptr, Range(0, fText.size()));
+ defaultBlocks.emplace(foreground, nullptr, TextRange(0, fText.size()));
size_t start = 0;
for (auto& block : decorBlocks) {
this->adjustLeft(&block.fRange.fStart);
@@ -193,18 +205,6 @@
}
template<typename Visitor>
-void Processor::iterateByLogicalOrder(CodeUnitFlags units, Visitor visitor) {
- Range range(0, 0);
- for (size_t index = 0; index < fCodeUnitProperties.size(); ++index) {
- if (this->hasProperty(index, units)) {
- range.fEnd = index;
- visitor(range, this->fCodeUnitProperties[index]);
- range.fStart = index;
- }
- }
-}
-
-template<typename Visitor>
void Processor::iterateByVisualOrder(CodeUnitFlags units, Visitor visitor) {
SkSize offset = SkSize::MakeEmpty();
for (auto& line : fLines) {
@@ -212,8 +212,8 @@
for (auto& runIndex : line.fRunsInVisualOrder) {
auto& run = fRuns[runIndex];
- auto startGlyph = runIndex == line.fTextStart.fRunIndex == runIndex ? line.fTextStart.fGlyphIndex : 0;
- auto endGlyph = runIndex == line.fTextEnd.fRunIndex ? line.fTextEnd.fGlyphIndex : run.fGlyphs.size();
+ auto startGlyph = runIndex == line.fTextStart.runIndex() ? line.fTextStart.glyphIndex() : 0;
+ auto endGlyph = runIndex == line.fTextEnd.runIndex() ? line.fTextEnd.glyphIndex() : run.fGlyphs.size();
Range textRange(run.fUtf8Range.begin(), run.fUtf8Range.end());
Range glyphRange(startGlyph, endGlyph);
@@ -257,11 +257,11 @@
// "Cd": green
// "ef": blue
// green[d] blue[ef] green [C] red [BA]
- auto startGlyph = runIndex == line.fTextStart.fRunIndex == runIndex ? line.fTextStart.fGlyphIndex : 0;
- auto endGlyph = runIndex == line.fTextEnd.fRunIndex ? line.fTextEnd.fGlyphIndex : run.fGlyphs.size();
+ auto startGlyph = runIndex == line.fTextStart.runIndex() ? line.fTextStart.glyphIndex() : 0;
+ auto endGlyph = runIndex == line.fTextEnd.runIndex() ? line.fTextEnd.glyphIndex() : run.fGlyphs.size();
- Range textRange(run.fUtf8Range.begin(), run.fUtf8Range.end());
- Range glyphRange(startGlyph, endGlyph);
+ TextRange textRange(run.fClusters[startGlyph], run.fClusters[endGlyph]);
+ GlyphRange glyphRange(startGlyph, endGlyph);
SkASSERT(currentBlock->fRange.fStart <= textRange.fStart);
for (auto glyphIndex = startGlyph; glyphIndex <= endGlyph; ++glyphIndex) {
@@ -278,7 +278,8 @@
textRange.fStart = textIndex;
}
glyphRange.fEnd = glyphIndex;
- visitor(offset, line.fTextMetrics.baseline(), &run, textRange, glyphRange, *currentBlock);
+ SkSize shift = SkSize::Make(offset.fWidth - run.fPositions[startGlyph].fX, offset.fHeight);
+ visitor(shift, line.fTextMetrics.baseline(), &run, textRange, glyphRange, *currentBlock);
if (run.leftToRight()) {
textRange.fStart = textIndex;
} else {
@@ -287,6 +288,16 @@
glyphRange.fStart = glyphIndex;
offset.fWidth += run.calculateWidth(glyphRange);
}
+
+ // The last line
+ if (run.leftToRight()) {
+ textRange.fEnd = run.fClusters[endGlyph];
+ } else {
+ textRange.fStart = run.fClusters[endGlyph];
+ }
+ glyphRange.fEnd = endGlyph;
+ SkSize shift = SkSize::Make(offset.fWidth - run.fPositions[startGlyph].fX, offset.fHeight);
+ visitor(shift, line.fTextMetrics.baseline(), &run, textRange, glyphRange, *currentBlock);
}
offset.fHeight += line.fTextMetrics.height();
}
diff --git a/experimental/sktext/src/TextRun.cpp b/experimental/sktext/src/TextRun.cpp
index e1b0952..8e2e450 100644
--- a/experimental/sktext/src/TextRun.cpp
+++ b/experimental/sktext/src/TextRun.cpp
@@ -34,7 +34,7 @@
return {fGlyphs.data(), fPositions.data(), nullptr, fClusters.data(), {0.0f, 0.0f} };
}
-SkScalar TextRun::calculateWidth(Range glyphRange) const {
+SkScalar TextRun::calculateWidth(GlyphRange glyphRange) const {
SkASSERT(glyphRange.fStart <= glyphRange.fEnd && glyphRange.fEnd < fPositions.size());
return fPositions[glyphRange.fEnd].fX - fPositions[glyphRange.fStart].fX;
}
diff --git a/experimental/sktext/src/TextRun.h b/experimental/sktext/src/TextRun.h
index ec7a890..6bc0e12 100644
--- a/experimental/sktext/src/TextRun.h
+++ b/experimental/sktext/src/TextRun.h
@@ -18,7 +18,7 @@
SkShaper::RunHandler::Buffer newRunBuffer();
void commit();
- SkScalar calculateWidth(Range glyphRange) const;
+ SkScalar calculateWidth(GlyphRange glyphRange) const;
bool leftToRight() const { return fBidiLevel % 2 == 0; }
uint8_t bidiLevel() const { return fBidiLevel; }
diff --git a/experimental/sktext/src/Wrapper.cpp b/experimental/sktext/src/Wrapper.cpp
index e0a4f31..13e4acc 100644
--- a/experimental/sktext/src/Wrapper.cpp
+++ b/experimental/sktext/src/Wrapper.cpp
@@ -11,7 +11,7 @@
return this->breakTextIntoLines(this->fWidth);
}
-Range Wrapper::glyphRange(const TextRun* run, const Range& textRange) {
+GlyphRange Wrapper::glyphRange(const TextRun* run, const TextRange& textRange) {
Range glyphRange = EMPTY_RANGE;
for (size_t i = 0; i < run->fClusters.size(); ++i) {
auto cluster = run->fClusters[i];
@@ -26,7 +26,7 @@
return glyphRange;
}
-Range Wrapper::textRange(const TextRun* run, const Range& glyphRange) {
+TextRange Wrapper::textRange(const TextRun* run, const GlyphRange& glyphRange) {
Range textRange = EMPTY_RANGE;
for (size_t i = 0; i < run->fClusters.size(); ++i) {
auto cluster = run->fClusters[i];
@@ -54,7 +54,7 @@
TextMetrics runMetrics(run.fFont);
Stretch cluster;
if (!run.leftToRight()) {
- cluster.fTextRange = { run.fUtf8Range.end(), run.fUtf8Range.end()};
+ cluster.setTextRange({ run.fUtf8Range.end(), run.fUtf8Range.end()});
}
for (size_t glyphIndex = 0; glyphIndex < run.fPositions.size(); ++glyphIndex) {
@@ -63,66 +63,77 @@
if (cluster.isEmpty()) {
cluster = Stretch(GlyphPos(runIndex, glyphIndex), textIndex, runMetrics);
continue;
- /*
- } else if (cluster.fTextRange.fStart == textIndex) {
- break;
- } else if (cluster.fTextRange.fEnd == textIndex) {
- break;
- */
}
// The entire cluster belongs to a single run
- SkASSERT(cluster.fGlyphStart.fRunIndex == runIndex);
+ SkASSERT(cluster.glyphStart().runIndex() == runIndex);
- auto clusterWidth = run.fPositions[glyphIndex].fX - run.fPositions[cluster.fGlyphStart.fGlyphIndex].fX;
+ auto clusterWidth = run.fPositions[glyphIndex].fX - run.fPositions[cluster.glyphStartIndex()].fX;
cluster.finish(glyphIndex, textIndex, clusterWidth);
- auto isHardLineBreak = fProcessor->isHardLineBreak(cluster.fTextRange.fStart);
- auto isSoftLineBreak = fProcessor->isSoftLineBreak(cluster.fTextRange.fStart);
- auto isWhitespaces = fProcessor->isWhitespaces(cluster.fTextRange);
+ auto isHardLineBreak = fProcessor->isHardLineBreak(cluster.textStart());
+ auto isSoftLineBreak = fProcessor->isSoftLineBreak(cluster.textStart());
+ auto isWhitespaces = fProcessor->isWhitespaces(cluster.textRange());
auto isEndOfText = run.leftToRight() ? textIndex == run.fUtf8Range.end() : textIndex == run.fUtf8Range.begin();
- if (isHardLineBreak || isEndOfText || isSoftLineBreak || isWhitespaces) {
+ if (isSoftLineBreak || isWhitespaces || isHardLineBreak) {
+ // This is the end of the word
if (!clusters.isEmpty()) {
line.moveTo(spaces);
line.moveTo(clusters);
- }
- if (isWhitespaces) {
- spaces.moveTo(cluster);
- }
- if (isHardLineBreak) {
- this->addLine(line, spaces);
- break;
- }
- if (isEndOfText) {
- line.moveTo(cluster);
- if (!line.isEmpty()) {
- this->addLine(line, spaces);
- }
- break;
+ spaces = clusters;
}
}
- if (!SkScalarIsFinite(width)) {
+ // line + spaces + clusters + cluster
+ if (isWhitespaces) {
+ // Whitespaces do not extend the line width
+ spaces.moveTo(cluster);
+ clusters = cluster;
+ continue;
+ } else if (isHardLineBreak) {
+ // Hard line break ends the line but does not extend the width
+ // Same goes for the end of the text
+ this->addLine(line, spaces);
+ break;
+ } else if (!SkScalarIsFinite(width)) {
clusters.moveTo(cluster);
- } else if ((line.fWidth + spaces.fWidth + clusters.fWidth + cluster.fWidth) <= width) {
+ continue;
+ }
+
+ // Now let's find out if we can add the cluster to the line
+ if ((line.width() + spaces.width() + clusters.width() + cluster.width()) <= width) {
clusters.moveTo(cluster);
} else {
- // Wrapping the text by whitespaces
if (line.isEmpty()) {
- if (clusters.isEmpty()) {
+ if (spaces.isEmpty() && clusters.isEmpty()) {
+ // There is only this cluster and it's too long;
+ // we are drawing it anyway
line.moveTo(cluster);
} else {
+ // We break the only one word on the line by this cluster
line.moveTo(clusters);
}
+ } else {
+ // We move clusters + cluster on the next line
+ // TODO: Parametrise possible ways of breaking too long word
+ // (start it from a new line or squeeze the part of it on this line)
}
this->addLine(line, spaces);
clusters.moveTo(cluster);
}
+
cluster = Stretch(GlyphPos(runIndex, glyphIndex), textIndex, runMetrics);
}
}
+ if (!clusters.isEmpty()) {
+ line.moveTo(spaces);
+ line.moveTo(clusters);
+ spaces = clusters;
+ }
+ this->addLine(line, spaces);
+
return true;
}
diff --git a/experimental/sktext/src/Wrapper.h b/experimental/sktext/src/Wrapper.h
index 90d5ffa..c54f177 100644
--- a/experimental/sktext/src/Wrapper.h
+++ b/experimental/sktext/src/Wrapper.h
@@ -21,17 +21,18 @@
spaces.clean();
}
- SkScalar glyphRangeWidth(const TextRun* run, const Range& glyphRange) {
+ SkScalar glyphRangeWidth(const TextRun* run, const GlyphRange& glyphRange) {
return run->fPositions[glyphRange.fEnd].fX - run->fPositions[glyphRange.fStart].fX;
}
- static Range glyphRange(const TextRun* run, const Range& textRange);
- static Range textRange(const TextRun* run, const Range& glyphRange);
+ static GlyphRange glyphRange(const TextRun* run, const TextRange& textRange);
+ static TextRange textRange(const TextRun* run, const GlyphRange& glyphRange);
bool breakTextIntoLines(SkScalar width);
private:
Processor* fProcessor;
SkScalar fWidth;
+ // TODO: Implement
SkScalar fHeight;
};