SkText: refactoring in process

Change-Id: Iac594eeba2e15549a6b2cea2489cf123f7440c79
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/438700
Reviewed-by: Julia Lavrova <jlavrova@google.com>
Commit-Queue: Julia Lavrova <jlavrova@google.com>
This commit is contained in:
Julia Lavrova 2021-08-11 16:24:24 -04:00 committed by SkCQ
parent 3bce5d1397
commit c87e9516c3
7 changed files with 294 additions and 272 deletions

View File

@ -21,7 +21,7 @@ Editor::Editor(std::u16string text, SkSize size)
{
SkPaint foreground; foreground.setColor(DEFAULT_TEXT_FOREGROUND);
SkPaint background; background.setColor(DEFAULT_TEXT_BACKGROUND);
static FontBlock textBlock(text.size(), sk_make_sp<TrivialFontChain>("Roboto", 40));
static FontBlock textBlock(text.size(), sk_make_sp<TrivialFontChain>("Roboto", 40, SkFontStyle::Normal()));
static DecoratedBlock textDecor(text.size(), foreground, background);
auto textSize = SkSize::Make(size.width(), size.height() - DEFAULT_STATUS_HEIGHT);
fEditableText = std::make_unique<EditableText>(
@ -33,7 +33,7 @@ Editor::Editor(std::u16string text, SkSize size)
SkPaint foreground; foreground.setColor(DEFAULT_STATUS_FOREGROUND);
SkPaint background; background.setColor(DEFAULT_STATUS_BACKGROUND);
std::u16string status = u"This is the status line";
static FontBlock statusBlock(status.size(), sk_make_sp<TrivialFontChain>("Roboto", 20));
static FontBlock statusBlock(status.size(), sk_make_sp<TrivialFontChain>("Roboto", 20, SkFontStyle::Normal()));
static DecoratedBlock statusDecor(status.size(), foreground, background);
auto statusPoint = SkPoint::Make(0, size.height() - DEFAULT_STATUS_HEIGHT);
fStatus = std::make_unique<DynamicText>(

View File

@ -28,15 +28,16 @@ public:
// and breaks runs on them (so we can select and interpret them later)
class FormattingFontIterator final : public SkShaper::FontRunIterator {
public:
FormattingFontIterator(TextIndex textCount, SkSpan<FontBlock> fontBlocks, SkSpan<TextIndex> marks)
: fTextCount(textCount)
, fFontBlocks(fontBlocks)
, fFormattingMarks(std::move(marks))
, fCurrentBlock(fontBlocks.begin())
, fCurrentMark(marks.begin())
, fCurrentFontIndex(0)
, fCurrentFont(fCurrentBlock->createFont())
{ }
FormattingFontIterator(TextIndex textCount,
SkSpan<FontBlock> fontBlocks,
SkSpan<TextIndex> marks)
: fTextCount(textCount)
, fFontBlocks(fontBlocks)
, fFormattingMarks(marks)
, fCurrentBlock(fontBlocks.begin())
, fCurrentMark(marks.begin())
, fCurrentFontIndex(0)
, fCurrentFont(fCurrentBlock->createFont()) {}
void consume() override {
SkASSERT(fCurrentBlock < fFontBlocks.end());
@ -66,13 +67,11 @@ public:
}
}
bool atEnd() const override {
return (fCurrentBlock != fFontBlocks.end() ? fCurrentFontIndex == fTextCount : true) &&
(fCurrentMark != fFormattingMarks.end() ? *fCurrentMark == fTextCount : true);
return (fCurrentBlock == fFontBlocks.end() || fCurrentFontIndex == fTextCount) &&
(fCurrentMark == fFormattingMarks.end() || *fCurrentMark == fTextCount);
}
const SkFont& currentFont() const override {
return fCurrentFont;
}
const SkFont& currentFont() const override { return fCurrentFont; }
private:
TextIndex const fTextCount;
@ -86,14 +85,8 @@ private:
class ShapedText;
class UnicodeText : public SkShaper::RunHandler {
public:
std::unique_ptr<ShapedText> shape(SkSpan<FontBlock> blocks,
TextDirection textDirection);
bool shapeParagraph(TextRange text,
SkSpan<FontBlock> blocks,
TextDirection textDirection);
std::unique_ptr<ShapedText> shape(SkSpan<FontBlock> blocks, TextDirection textDirection);
bool hasProperty(size_t index, CodeUnitFlags flag) {
return (fCodeUnitProperties[index] & flag) == flag;
@ -108,7 +101,7 @@ public:
private:
friend class Text;
UnicodeText() : fCurrentRun(nullptr) { }
UnicodeText() : fCurrentRun(nullptr) {}
void beginLine() override {}
void runInfo(const RunInfo&) override {}
void commitRunInfo() override {}
@ -138,7 +131,8 @@ class ShapedText {
public:
std::unique_ptr<WrappedText> wrap(float width, float height, SkUnicode* unicode);
bool isClusterEdge(size_t index) const {
return (fGlyphUnitProperties[index] & GlyphUnitFlags::kGlyphClusterStart) == GlyphUnitFlags::kGlyphClusterStart;
return (fGlyphUnitProperties[index] & GlyphUnitFlags::kGlyphClusterStart) ==
GlyphUnitFlags::kGlyphClusterStart;
}
void adjustLeft(size_t* index) const {
SkASSERT(index != nullptr);
@ -182,11 +176,11 @@ public:
}
}
return true;
}
}
private:
friend class UnicodeText;
ShapedText() { }
ShapedText() {}
SkTArray<TextRun, false> fRuns;
SkTArray<GlyphUnitFlags, true> fGlyphUnitProperties;
};
@ -197,9 +191,10 @@ public:
sk_sp<FormattedText> format(TextAlign textAlign, TextDirection textDirection);
SkSize actualSize() const { return fActualSize; }
size_t countLines() const { return fLines.size(); }
private:
friend class ShapedText;
WrappedText() : fActualSize(SkSize::MakeEmpty()) { }
WrappedText() : fActualSize(SkSize::MakeEmpty()) {}
void addLine(Stretch& stretch, Stretch& spaces, SkUnicode* unicode, bool hardLineBreak);
SkTArray<TextRun, false> fRuns;
SkTArray<Line, false> fLines;
@ -209,7 +204,7 @@ private:
class FormattedText : public SkRefCnt {
public:
SkSize actualSize() const { return fActualSize; }
SkSize actualSize() const { return fActualSize; }
Position adjustedPosition(PositionType positionType, SkPoint point) const;
@ -234,8 +229,12 @@ public:
size_t lineIndex(const Line* line) const { return line - fLines.data(); }
size_t countLines() const { return fLines.size(); }
const Line* line(size_t lineIndex) const { return fLines.empty() ? nullptr : &fLines[lineIndex]; }
size_t runIndex(const TextRun* run) const { return run == nullptr ? EMPTY_INDEX : run - fRuns.data(); }
const Line* line(size_t lineIndex) const {
return fLines.empty() ? nullptr : &fLines[lineIndex];
}
size_t runIndex(const TextRun* run) const {
return run == nullptr ? EMPTY_INDEX : run - fRuns.data();
}
bool hasProperty(size_t index, GlyphUnitFlags flag) const {
return (fGlyphUnitProperties[index] & flag) == flag;
@ -244,14 +243,14 @@ public:
class Visitor {
public:
virtual ~Visitor() = default;
virtual void onBeginLine(TextRange lineText, float baselineY, float horizontalOffset) { }
virtual void onEndLine(TextRange lineText, float baselineY) { }
virtual void onGlyphRun(SkFont font,
virtual void onBeginLine(TextRange lineText) {}
virtual void onEndLine(TextRange lineText) {}
virtual void onGlyphRun(const SkFont& font,
TextRange textRange,
SkRect boundingRect,
int glyphCount,
const uint16_t glyphs[],
const SkPoint positions[],
const SkPoint positions[],
const SkPoint offsets[]) {
SkTextBlobBuilder builder;
const auto& blobBuffer = builder.allocRunPos(font, SkToInt(glyphCount));
@ -259,7 +258,7 @@ public:
sk_careful_memcpy(blobBuffer.points(), positions, glyphCount * sizeof(SkPoint));
fTextBlobs.emplace_back(builder.make());
}
virtual void onPlaceholder(TextRange, const SkRect& bounds) { }
virtual void onPlaceholder(TextRange, const SkRect& bounds) {}
void buildTextBlobs(FormattedText* formattedText) {
fTextBlobs.clear();
@ -277,7 +276,6 @@ public:
void visit(Visitor*, SkSpan<size_t> blocks) const;
void paint(SkCanvas* canvas, SkPaint& foreground, SkPoint xy, bool rebuild = true) {
if (fVisitor == nullptr) {
fVisitor = std::make_unique<Visitor>();
rebuild = true;
@ -291,11 +289,10 @@ public:
}
private:
void adjustTextRange(Position* position) const;
friend class WrappedText;
FormattedText() { }
FormattedText() = default;
SkTArray<TextRun, false> fRuns;
SkTArray<Line, false> fLines;
SkTArray<GlyphUnitFlags, true> fGlyphUnitProperties;

View File

@ -4,116 +4,114 @@
namespace skia {
namespace text {
bool Paint::drawText(std::u16string text, SkCanvas* canvas, SkScalar x, SkScalar y) {
bool Paint::drawText(std::u16string text, SkCanvas* canvas, SkScalar x, SkScalar y) {
return drawText(std::move(text), canvas, TextDirection::kLtr, TextAlign::kLeft, SK_ColorBLACK, SK_ColorWHITE, SkString("Roboto"), 14, SkFontStyle::Normal(), x, y);
}
return drawText(std::move(text), canvas, TextDirection::kLtr, TextAlign::kLeft, SK_ColorBLACK, SK_ColorWHITE, SkString("Roboto"), 14, SkFontStyle::Normal(), x, y);
}
bool Paint::drawText(std::u16string text, SkCanvas* canvas, SkScalar width) {
return drawText(std::move(text), canvas,
TextDirection::kLtr, TextAlign::kLeft, SK_ColorBLACK, SK_ColorWHITE, SkString("Roboto"), 14, SkFontStyle::Normal(),
SkSize::Make(width, SK_ScalarInfinity), 0, 0);
}
bool Paint::drawText(std::u16string text, SkCanvas* canvas, SkScalar width) {
return drawText(std::move(text), canvas,
TextDirection::kLtr, TextAlign::kLeft, SK_ColorBLACK, SK_ColorWHITE, SkString("Roboto"), 14, SkFontStyle::Normal(),
SkSize::Make(width, SK_ScalarInfinity), 0, 0);
}
bool Paint::drawText(std::u16string text, SkCanvas* canvas,
TextDirection textDirection, TextAlign textAlign,
SkColor foreground, SkColor background,
const SkString& fontFamily, SkScalar fontSize, SkFontStyle fontStyle, SkScalar x, SkScalar y) {
return drawText(std::move(text), canvas,
textDirection, textAlign, foreground, background,
fontFamily, fontSize, fontStyle, SkSize::Make(SK_ScalarInfinity, SK_ScalarInfinity), x, y);
}
bool Paint::drawText(std::u16string text, SkCanvas* canvas,
TextDirection textDirection, TextAlign textAlign,
SkColor foreground, SkColor background,
const SkString& fontFamily, SkScalar fontSize, SkFontStyle fontStyle, SkScalar x, SkScalar y) {
return drawText(std::move(text), canvas,
textDirection, textAlign, foreground, background,
fontFamily, fontSize, fontStyle, SkSize::Make(SK_ScalarInfinity, SK_ScalarInfinity), x, y);
}
bool Paint::drawText(std::u16string text, SkCanvas* canvas,
bool Paint::drawText(std::u16string text, SkCanvas* canvas,
TextDirection textDirection, TextAlign textAlign,
SkColor foreground, SkColor background,
const SkString& fontFamily, SkScalar fontSize, SkFontStyle fontStyle, SkSize reqSize, SkScalar x, SkScalar y) {
size_t textSize = text.size();
sk_sp<TrivialFontChain> fontChain = sk_make_sp<TrivialFontChain>(fontFamily.c_str(), fontSize);
FontBlock fontBlock(text.size(), fontChain);
size_t textSize = text.size();
sk_sp<TrivialFontChain> fontChain = sk_make_sp<TrivialFontChain>(fontFamily.c_str(), fontSize, fontStyle);
FontBlock fontBlock(text.size(), fontChain);
auto formattedText = Paint::layout(std::move(text), textDirection, textAlign, reqSize, SkSpan<FontBlock>(&fontBlock, 1));
if (formattedText == nullptr) {
return false;
}
SkPaint backgroundPaint; backgroundPaint.setColor(background);
SkPaint foregroundPaint; foregroundPaint.setColor(foreground);
DecoratedBlock decoratedBlock(textSize, foregroundPaint, backgroundPaint);
Paint paint;
paint.paint(canvas, SkPoint::Make(x, y), formattedText.get(), SkSpan<DecoratedBlock>(&decoratedBlock, 1));
return true;
}
void Paint::onBeginLine(TextRange lineText, float baselineY, float horizontalOffset) {
fHorizontalOffset = horizontalOffset;
fBaselineY = baselineY;
}
void Paint::onEndLine(TextRange, float baselineY) { }
void Paint::onPlaceholder(TextRange, const SkRect& bounds) { }
void Paint::onGlyphRun(SkFont font,
TextRange textRange,
SkRect boundingRect,
int glyphCount,
const uint16_t glyphs[],
const SkPoint positions[],
const SkPoint offsets[]) {
DecoratedBlock decoratedBlock = findDecoratedBlock(textRange);
SkTextBlobBuilder builder;
const auto& blobBuffer = builder.allocRunPos(font , SkToInt(glyphCount));
sk_careful_memcpy(blobBuffer.glyphs, glyphs, glyphCount * sizeof(uint16_t));
sk_careful_memcpy(blobBuffer.points(), positions, glyphCount * sizeof(SkPoint));
auto blob = builder.make();
boundingRect.offset(fXY.fX + fHorizontalOffset, fXY.fY);
fCanvas->drawRect(boundingRect, decoratedBlock.backgroundPaint);
fCanvas->drawTextBlob(blob, fXY.fX + fHorizontalOffset, fXY.fY, decoratedBlock.foregroundPaint);
}
sk_sp<FormattedText> Paint::layout(std::u16string text,
TextDirection textDirection, TextAlign textAlign,
SkSize reqSize,
SkSpan<FontBlock> fontBlocks) {
auto unicodeText = Text::parse(SkSpan<uint16_t>((uint16_t*)text.data(), text.size()));
auto shapedText = unicodeText->shape(fontBlocks, textDirection);
auto wrappedText = shapedText->wrap(reqSize.width(), reqSize.height(), unicodeText->getUnicode());
auto formattedText = wrappedText->format(textAlign, textDirection);
return formattedText;
}
DecoratedBlock Paint::findDecoratedBlock(TextRange textRange) {
TextIndex start = 0;
for (auto& block : fDecoratedBlocks) {
if (start + block.charCount <= textRange.fStart) {
start += block.charCount;
continue;
} else if (start >= textRange.fEnd) {
break;
auto formattedText = Paint::layout(std::move(text), textDirection, textAlign, reqSize, SkSpan<FontBlock>(&fontBlock, 1));
if (formattedText == nullptr) {
return false;
}
return block;
}
return DecoratedBlock(0, SkPaint(), SkPaint());
}
void Paint::paint(SkCanvas* canvas, SkPoint xy, FormattedText* formattedText, SkSpan<DecoratedBlock> decoratedBlocks) {
fCanvas = canvas;
fXY = xy;
fDecoratedBlocks = decoratedBlocks;
SkPaint backgroundPaint; backgroundPaint.setColor(background);
SkPaint foregroundPaint; foregroundPaint.setColor(foreground);
DecoratedBlock decoratedBlock(textSize, foregroundPaint, backgroundPaint);
Paint paint;
paint.paint(canvas, SkPoint::Make(x, y), formattedText.get(), SkSpan<DecoratedBlock>(&decoratedBlock, 1));
SkTArray<size_t> chunks;
chunks.resize(decoratedBlocks.size());
size_t index = 0;
for (size_t i = 0; i < decoratedBlocks.size(); ++i) {
index += decoratedBlocks[i].charCount;
chunks[i] = index;
return true;
}
formattedText->visit(this, SkSpan<size_t>(chunks.data(), chunks.size()));
}
void Paint::onBeginLine(TextRange lineText) { }
void Paint::onEndLine(TextRange lineText) { }
void Paint::onPlaceholder(TextRange lineText, const SkRect& bounds) { }
void Paint::onGlyphRun(const SkFont& font,
TextRange textRange,
SkRect boundingRect,
int glyphCount,
const uint16_t glyphs[],
const SkPoint positions[],
const SkPoint offsets[]) {
DecoratedBlock decoratedBlock = findDecoratedBlock(textRange);
SkTextBlobBuilder builder;
const auto& blobBuffer = builder.allocRunPos(font , SkToInt(glyphCount));
sk_careful_memcpy(blobBuffer.glyphs, glyphs, glyphCount * sizeof(uint16_t));
sk_careful_memcpy(blobBuffer.points(), positions, glyphCount * sizeof(SkPoint));
auto blob = builder.make();
if (!decoratedBlock.backgroundPaint.nothingToDraw()) {
boundingRect.offset(fXY.fX, fXY.fY);
fCanvas->drawRect(boundingRect, decoratedBlock.backgroundPaint);
}
fCanvas->drawTextBlob(blob, fXY.fX, fXY.fY, decoratedBlock.foregroundPaint);
}
sk_sp<FormattedText> Paint::layout(std::u16string text,
TextDirection textDirection, TextAlign textAlign,
SkSize reqSize,
SkSpan<FontBlock> fontBlocks) {
auto unicodeText = Text::parse(SkSpan<uint16_t>((uint16_t*)text.data(), text.size()));
auto shapedText = unicodeText->shape(fontBlocks, textDirection);
auto wrappedText = shapedText->wrap(reqSize.width(), reqSize.height(), unicodeText->getUnicode());
auto formattedText = wrappedText->format(textAlign, textDirection);
return formattedText;
}
DecoratedBlock Paint::findDecoratedBlock(TextRange textRange) {
TextIndex start = 0;
for (auto& block : fDecoratedBlocks) {
if (start + block.charCount <= textRange.fStart) {
start += block.charCount;
continue;
} else if (start >= textRange.fEnd) {
break;
}
return block;
}
return DecoratedBlock(0, SkPaint(), SkPaint());
}
void Paint::paint(SkCanvas* canvas, SkPoint xy, FormattedText* formattedText, SkSpan<DecoratedBlock> decoratedBlocks) {
fCanvas = canvas;
fXY = xy;
fDecoratedBlocks = decoratedBlocks;
SkTArray<size_t> chunks;
chunks.resize(decoratedBlocks.size());
size_t index = 0;
for (size_t i = 0; i < decoratedBlocks.size(); ++i) {
index += decoratedBlocks[i].charCount;
chunks[i] = index;
}
formattedText->visit(this, SkSpan<size_t>(chunks.data(), chunks.size()));
}
} // namespace text
} // namespace skia

View File

@ -5,81 +5,80 @@
#include "experimental/sktext/include/Types.h"
#include "include/core/SkCanvas.h"
namespace skia {
namespace text {
struct DecoratedBlock {
DecoratedBlock(uint32_t count, SkPaint fg, SkPaint bg)
: charCount(count)
, foregroundPaint(std::move(fg))
, backgroundPaint(std::move(bg)) { }
uint32_t charCount;
SkPaint foregroundPaint;
SkPaint backgroundPaint;
};
struct DecoratedBlock {
DecoratedBlock(uint32_t count, SkPaint fg, SkPaint bg)
: charCount(count)
, foregroundPaint(std::move(fg))
, backgroundPaint(std::move(bg)) { }
uint32_t charCount;
SkPaint foregroundPaint;
SkPaint backgroundPaint;
};
class TrivialFontChain : public FontChain {
public:
TrivialFontChain(const char* ff, SkScalar size)
: fTypeface(sk_sp<SkTypeface>(SkFontMgr::RefDefault()->matchFamilyStyle(ff, SkFontStyle::Normal()))),
fSize(size) { }
size_t count() const override { return (size_t)1; }
sk_sp<SkTypeface> operator[](size_t index) const override {
SkASSERT(index == 0);
return fTypeface;
}
float size() const override { return fSize; }
class TrivialFontChain : public FontChain {
public:
TrivialFontChain(const char* ff, SkScalar size, SkFontStyle fontStyle)
: fTypeface(sk_sp<SkTypeface>(SkFontMgr::RefDefault()->matchFamilyStyle(ff, SkFontStyle::Normal())))
, fSize(size)
, fFontStyle(fontStyle) { }
size_t count() const override { return (size_t)1; }
sk_sp<SkTypeface> operator[](size_t index) const override {
SkASSERT(index == 0);
return fTypeface;
}
float size() const override { return fSize; }
private:
sk_sp<SkTypeface> fTypeface;
SkScalar fSize;
};
private:
sk_sp<SkTypeface> fTypeface;
SkScalar fSize;
SkFontStyle fFontStyle;
};
class Paint : public FormattedText::Visitor {
public:
static sk_sp<FormattedText> layout(std::u16string text,
TextDirection textDirection, TextAlign textAlign,
SkSize reqSize,
SkSpan<FontBlock> fontBlocks);
void paint(SkCanvas* canvas, SkPoint xy, FormattedText* formattedText, SkSpan<DecoratedBlock> decoratedBlocks);
// Simplification (using default font manager, default font family and default everything possible)
static bool drawText(std::u16string text, SkCanvas* canvas, SkScalar x, SkScalar y);
static bool drawText(std::u16string text, SkCanvas* canvas, SkScalar width);
static bool drawText(std::u16string text, SkCanvas* canvas,
TextDirection textDirection, TextAlign textAlign,
SkColor foreground, SkColor background,
const SkString& fontFamily, SkScalar fontSize, SkFontStyle fontStyle,
SkScalar x, SkScalar y);
static bool drawText(std::u16string text, SkCanvas* canvas,
TextDirection textDirection, TextAlign textAlign,
SkColor foreground, SkColor background,
const SkString& fontFamily, SkScalar fontSize, SkFontStyle fontStyle,
SkSize reqSize, SkScalar x, SkScalar y);
class Paint : public FormattedText::Visitor {
public:
void paint(SkCanvas* canvas, SkPoint xy, FormattedText* formattedText, SkSpan<DecoratedBlock> decoratedBlocks);
// Simplification (using default font manager, default font family and default everything possible)
static bool drawText(std::u16string text, SkCanvas* canvas, SkScalar x, SkScalar y);
static bool drawText(std::u16string text, SkCanvas* canvas, SkScalar width);
static bool drawText(std::u16string text, SkCanvas* canvas,
TextDirection textDirection, TextAlign textAlign,
SkColor foreground, SkColor background,
const SkString& fontFamily, SkScalar fontSize, SkFontStyle fontStyle,
SkScalar x, SkScalar y);
static bool drawText(std::u16string text, SkCanvas* canvas,
TextDirection textDirection, TextAlign textAlign,
SkColor foreground, SkColor background,
const SkString& fontFamily, SkScalar fontSize, SkFontStyle fontStyle,
SkSize reqSize, SkScalar x, SkScalar y);
private:
friend class Processor;
void onBeginLine(TextRange lineText, float baselineY, float horizontalOffset) override;
void onEndLine(TextRange, float baselineY) override;
void onGlyphRun(SkFont font,
TextRange textRange,
SkRect boundingRect,
int glyphCount,
const uint16_t glyphs[],
const SkPoint positions[],
const SkPoint offsets[]) override;
void onPlaceholder(TextRange, const SkRect& bounds) override;
private:
static sk_sp<FormattedText> layout(std::u16string text,
TextDirection textDirection, TextAlign textAlign,
SkSize reqSize,
SkSpan<FontBlock> fontBlocks);
// We guarantee that the text range will be inside one of the decorated blocks
DecoratedBlock findDecoratedBlock(TextRange textRange);
void onBeginLine(TextRange lineText) override;
void onEndLine(TextRange) override;
void onGlyphRun(const SkFont& font,
TextRange textRange,
SkRect boundingRect,
int glyphCount,
const uint16_t glyphs[],
const SkPoint positions[],
const SkPoint offsets[]) override;
void onPlaceholder(TextRange, const SkRect& bounds) override;
SkCanvas* fCanvas;
SkPoint fXY;
SkScalar fHorizontalOffset;
SkScalar fBaselineY;
SkSpan<FontBlock> fFontBlocks;
SkSpan<DecoratedBlock> fDecoratedBlocks;
};
} // namespace text
// We guarantee that the text range will be inside one of the decorated blocks
DecoratedBlock findDecoratedBlock(TextRange textRange);
SkCanvas* fCanvas;
SkPoint fXY;
SkSpan<FontBlock> fFontBlocks;
SkSpan<DecoratedBlock> fDecoratedBlocks;
};
} // namespace text
} // namespace skia
#endif // Painter_DEFINED

View File

@ -335,27 +335,25 @@ void FormattedText::visit(Visitor* visitor) const {
SkPoint offset = SkPoint::Make(0 , 0);
for (auto& line : this->fLines) {
offset.fX = 0;
visitor->onBeginLine(line.text(), line.baseline(), line.horizontalOffset());
visitor->onBeginLine(line.text());
for (auto index = 0; index < line.runsNumber(); ++index) {
auto runIndex = line.visualRun(index);
auto& run = this->fRuns[runIndex];
auto startGlyph = runIndex == line.glyphStart().runIndex() ? line.glyphStart().glyphIndex() : 0;
auto endGlyph = runIndex == line.glyphEnd().runIndex() ? line.glyphEnd().glyphIndex() : run.fGlyphs.size();
TextRange textRange(run.fClusters[startGlyph], run.fClusters[endGlyph]);
auto count = endGlyph - startGlyph;
SkScalar runWidth = run.calculateWidth(Range<GlyphIndex>(startGlyph, endGlyph));
GlyphRange glyphRange(line.glyphRange(runIndex, run.size()));
SkScalar runWidth = run.calculateWidth(glyphRange);
// Update positions
SkAutoSTMalloc<256, SkPoint> positions(count + 1);
SkPoint shift = SkPoint::Make(-run.fPositions[startGlyph].fX, line.baseline());
for (size_t i = startGlyph; i <= endGlyph; ++i) {
positions[i - startGlyph] = run.fPositions[i] + shift + offset;
SkAutoSTMalloc<256, SkPoint> positions(glyphRange.width() + 1);
SkPoint shift = SkPoint::Make(-run.fPositions[glyphRange.fStart].fX, line.baseline());
for (size_t i = glyphRange.fStart; i <= glyphRange.fEnd; ++i) {
positions[i - glyphRange.fStart] = run.fPositions[i] + shift + offset;
}
SkRect boundingRect = SkRect::MakeXYWH(shift.fX + offset.fX, offset.fY, run.fPositions[endGlyph].fX , run.fTextMetrics.height());
visitor->onGlyphRun(run.fFont, textRange, boundingRect, count, run.fGlyphs.data() + startGlyph, positions.data(), run.fOffsets.data() + startGlyph);
SkRect boundingRect = SkRect::MakeXYWH(shift.fX + offset.fX, offset.fY, run.fPositions[glyphRange.fEnd].fX , run.fTextMetrics.height());
visitor->onGlyphRun(run.fFont, run.getTextRange(glyphRange), boundingRect, glyphRange.width(), run.fGlyphs.data() + glyphRange.fStart, positions.data(), run.fOffsets.data() + glyphRange.fStart);
offset.fX += runWidth;
}
visitor->onEndLine(line.text(), line.baseline());
visitor->onEndLine(line.text());
offset.fY += line.height();
}
}
@ -374,53 +372,50 @@ void FormattedText::visit(Visitor* visitor, SkSpan<size_t> chunks) const {
size_t lineIndex = 0;
for (auto& line : this->fLines) {
offset.fX = 0;
visitor->onBeginLine(line.text(), line.baseline(), line.horizontalOffset());
visitor->onBeginLine(line.text());
for (auto index = 0; index < line.runsNumber(); ++index) {
auto runIndex = line.visualRun(index);
auto& run = this->fRuns[runIndex];
if (run.size() == 0) {
continue;
}
GlyphRange runGlyphRange(line.glyphRange(runIndex, run.size()));
SkPoint shift = SkPoint::Make(-run.fPositions[runGlyphRange.fStart].fX, line.baseline());
// The run edges are good (aligned to GGC)
// "ABCdef" -> "defCBA"
// "AB": red
// "Cd": green
// "ef": blue
// green[d] blue[ef] green [C] red [BA]
auto startGlyph = runIndex == line.glyphStart().runIndex() ? line.glyphStart().glyphIndex() : 0;
auto endGlyph = runIndex == line.glyphEnd().runIndex() ? line.glyphEnd().glyphIndex() : run.fGlyphs.size();
TextRange textRange(run.fRunStart + run.fClusters[startGlyph], run.fRunStart + run.fClusters[endGlyph]);
GlyphRange glyphRange(startGlyph, endGlyph);
GlyphRange glyphRange(runGlyphRange);
SkScalar runWidth = run.calculateWidth(glyphRange);
size_t currentEnd = *currentBlock;
for (auto glyphIndex = startGlyph; glyphIndex <= endGlyph; ++glyphIndex) {
for (auto glyphIndex = runGlyphRange.fStart; glyphIndex <= runGlyphRange.fEnd; ++glyphIndex) {
SkASSERT(currentBlock < chunks.end());
auto textIndex = run.fRunStart + run.fClusters[glyphIndex];
if (glyphIndex == endGlyph) {
if (glyphIndex == runGlyphRange.fEnd) {
// last piece of the text
} else if (run.leftToRight() && textIndex < currentEnd) {
continue;
} else if (!run.leftToRight() && textIndex >= currentStart) {
continue;
}
textRange.fEnd = textIndex;
glyphRange.fEnd = glyphIndex;
// Update positions & calculate the bounding rect
SkAutoSTMalloc<256, SkPoint> positions(glyphRange.width() + 1);
SkPoint shift = SkPoint::Make(-run.fPositions[startGlyph].fX, line.baseline());
for (size_t i = glyphRange.fStart; i <= glyphRange.fEnd; ++i) {
positions[i - glyphRange.fStart] = run.fPositions[i] + shift + offset;
positions[i - glyphRange.fStart] = run.fPositions[i] + shift + offset + SkPoint::Make(line.horizontalOffset(), 0.0f);
}
SkRect boundingRect = SkRect::MakeXYWH(positions[0].fX, offset.fY, positions[glyphRange.width()].fX - positions[0].fX, run.fTextMetrics.height());
visitor->onGlyphRun(run.fFont, textRange, boundingRect, glyphRange.width(), run.fGlyphs.data() + glyphRange.fStart, positions.data(), run.fOffsets.data() + glyphRange.fStart);
SkRect boundingRect = SkRect::MakeXYWH(positions[0].fX + line.horizontalOffset(), offset.fY, positions[glyphRange.width()].fX - positions[0].fX, run.fTextMetrics.height());
visitor->onGlyphRun(run.fFont, run.getTextRange(glyphRange), boundingRect, glyphRange.width(), run.fGlyphs.data() + glyphRange.fStart, positions.data(), run.fOffsets.data() + glyphRange.fStart);
textRange.fStart = textIndex;
glyphRange.fStart = glyphIndex;
if (glyphIndex != endGlyph) {
if (glyphIndex != runGlyphRange.fEnd) {
// We are here because we reached the end of the block
++currentBlock;
currentStart = currentEnd;
@ -429,7 +424,7 @@ void FormattedText::visit(Visitor* visitor, SkSpan<size_t> chunks) const {
}
offset.fX += runWidth;
}
visitor->onEndLine(line.text(), line.baseline());
visitor->onEndLine(line.text());
offset.fY += line.height();
}
}
@ -450,9 +445,8 @@ Position FormattedText::adjustedPosition(PositionType positionType, TextIndex te
auto runIndex = line.visualRun(index);
auto& run = fRuns[runIndex];
GlyphIndex start = runIndex == line.glyphStart().runIndex() ? line.glyphStart().glyphIndex() : 0;
GlyphIndex end = runIndex == line.glyphTrailingEnd().runIndex() ? line.glyphTrailingEnd().glyphIndex() : run.fGlyphs.size();
auto runWidth = run.calculateWidth(GlyphRange(start, end));
GlyphRange runGlyphRange(line.glyphRange(runIndex, run.size()));
auto runWidth = run.calculateWidth(runGlyphRange);
if (!run.fUtf16Range.contains(textIndex)) {
shift += runWidth;
@ -460,8 +454,8 @@ Position FormattedText::adjustedPosition(PositionType positionType, TextIndex te
}
// This is the run
GlyphIndex found = start;
for (auto i = start; i <= end; ++i) {
GlyphIndex found = runGlyphRange.fStart;
for (auto i = runGlyphRange.fStart; i <= runGlyphRange.fEnd; ++i) {
if ((run.leftToRight() && run.fClusters[i] > textIndex) ||
(!run.leftToRight() && run.fClusters[i] < textIndex)) {
break;
@ -471,10 +465,11 @@ Position FormattedText::adjustedPosition(PositionType positionType, TextIndex te
position.fLineIndex = lineIndex(&line);
position.fRun = &run;
position.fGlyphRange = GlyphRange(found, found == end ? found : found + 1);
adjustTextRange(&position);
position.fBoundaries.fLeft += shift + run.calculateWidth(start, position.fGlyphRange.fStart);
position.fBoundaries.fRight += shift + run.calculateWidth(start, position.fGlyphRange.fEnd);
position.fGlyphRange = GlyphRange(found, found == runGlyphRange.fEnd ? found : found + 1);
position.fTextRange = position.fRun->getTextRange(position.fGlyphRange);
this->adjustTextRange(&position);
position.fBoundaries.fLeft += shift + run.calculateWidth(runGlyphRange.fStart, position.fGlyphRange.fStart);
position.fBoundaries.fRight += shift + run.calculateWidth(runGlyphRange.fStart, position.fGlyphRange.fEnd);
return position;
}
// The cursor is not on the text anymore; position it after the last element
@ -484,7 +479,8 @@ Position FormattedText::adjustedPosition(PositionType positionType, TextIndex te
position.fLineIndex = fLines.size() - 1;
position.fRun = this->visuallyLastRun(position.fLineIndex);
position.fGlyphRange = GlyphRange(position.fRun->size(), position.fRun->size());
adjustTextRange(&position);
position.fTextRange = position.fRun->getTextRange(position.fGlyphRange);
this->adjustTextRange(&position);
position.fBoundaries.fLeft = fLines.back().withWithTrailingSpaces();
position.fBoundaries.fRight = fLines.back().withWithTrailingSpaces();
return position;
@ -492,74 +488,101 @@ Position FormattedText::adjustedPosition(PositionType positionType, TextIndex te
Position FormattedText::adjustedPosition(PositionType positionType, SkPoint xy) const {
if (xy.fX >= this->fActualSize.fWidth) {
xy.fX = this->fActualSize.fWidth;
}
if (xy.fY >= this->fActualSize.fHeight) {
xy.fY = this->fActualSize.fHeight;
}
xy.fX = std::min(xy.fX, this->fActualSize.fWidth);
xy.fY = std::min(xy.fY, this->fActualSize.fHeight);
Position position(positionType);
SkScalar shift = 0;
position.fRun = this->visuallyLastRun(this->fLines.size() - 1);
for (auto& line : fLines) {
position.fBoundaries.fTop = position.fBoundaries.fBottom;
position.fBoundaries.fBottom = line.verticalOffset() + line.height();
position.fLineIndex = this->lineIndex(&line);
if (position.fBoundaries.fTop > xy.fY) {
// We are past the point vertically
break;
} else if (position.fBoundaries.fBottom <= xy.fY) {
// We haven't reached the point vertically yet
continue;
}
shift = 0;
// We found the line that contains the point;
// let's walk through all its runs and find the element
SkScalar runOffsetInLine = 0;
for (auto index = 0; index < line.runsNumber(); ++index) {
auto runIndex = line.visualRun(index);
auto& run = fRuns[runIndex];
//GlyphIndex start = runIndex == line.glyphStart().runIndex() ? line.glyphStart().glyphIndex() : 0;
//GlyphIndex end = runIndex == line.glyphTrailingEnd().runIndex() ? line.glyphTrailingEnd().glyphIndex() : run.fGlyphs.size();
GlyphRange runRange = line.glyphRange(runIndex, run.size());
auto runWidth = run.calculateWidth(runRange);
if (shift > xy.fX) {
GlyphRange runGlyphRangeInLine = line.glyphRange(runIndex, run.size());
SkScalar runWidthInLine = run.calculateWidth(runGlyphRangeInLine);
position.fRun = &run;
if (runOffsetInLine > xy.fX) {
// We are past the point horizontally
break;
} else if (shift + runWidth < xy.fX) {
shift += runWidth;
} else if (runOffsetInLine + runWidthInLine < xy.fX) {
// We haven't reached the point horizontally yet
runOffsetInLine += runWidthInLine;
continue;
}
SkScalar startPos = run.fPositions[runRange.fStart].fX;
GlyphIndex found = runRange.fStart;
for (auto i = runRange.fStart; i <= runRange.fEnd; ++i) {
auto currentPos = run.fPositions[i].fX - startPos;
if (currentPos > xy.fX) {
// We found the run that contains the point
// let's find the glyph
GlyphIndex found = runGlyphRangeInLine.fStart;
for (auto i = runGlyphRangeInLine.fStart; i <= runGlyphRangeInLine.fEnd; ++i) {
if (runOffsetInLine + run.calculateWidth(runGlyphRangeInLine.fStart, i) > xy.fX) {
break;
}
found = i;
}
position.fLineIndex = lineIndex(&line);
position.fRun = &run;
position.fGlyphRange = GlyphRange(found, found == runRange.fEnd ? found : found + 1);
adjustTextRange(&position);
position.fBoundaries.fLeft += shift + run.calculateWidth(runRange.fStart, position.fGlyphRange.fStart);
position.fBoundaries.fRight += shift + run.calculateWidth(runRange.fStart, position.fGlyphRange.fEnd);
position.fGlyphRange = GlyphRange(found, found == runGlyphRangeInLine.fEnd ? found : found + 1);
position.fTextRange = position.fRun->getTextRange(position.fGlyphRange);
this->adjustTextRange(&position);
position.fBoundaries.fLeft = runOffsetInLine + run.calculateWidth(runGlyphRangeInLine.fStart, position.fGlyphRange.fStart);
position.fBoundaries.fRight = runOffsetInLine + run.calculateWidth(runGlyphRangeInLine.fStart, position.fGlyphRange.fEnd);
return position;
}
// The cursor is not on the text anymore; position it after the last element
// The cursor is not on the text anymore; position it after the last element of the last visual run of the current line
break;
}
auto line = this->line(position.fLineIndex);
position.fRun = this->visuallyLastRun(position.fLineIndex);
auto line = this->line(position.fLineIndex);
position.fGlyphRange.fStart =
position.fGlyphRange.fEnd = line->glyphRange(this->runIndex(position.fRun), position.fRun->size()).fEnd;
adjustTextRange(&position);
position.fTextRange = position.fRun->getTextRange(position.fGlyphRange);
this->adjustTextRange(&position);
position.fBoundaries.fLeft =
position.fBoundaries.fRight = line->withWithTrailingSpaces();
return position;
}
// Adjust the text positions to the position type
// (assuming for now that a grapheme cannot cross run edges; it's actually not true)
void FormattedText::adjustTextRange(Position* position) const {
// TODO: Adjust the text positions to the position type
// The textRange is aligned on a glyph cluster
if (position->fPositionType == PositionType::kGraphemeCluster) {
// Move left to the beginning of the run
while (position->fTextRange.fStart > position->fRun->fUtf8Range.begin() &&
!this->hasProperty(position->fTextRange.fStart, GlyphUnitFlags::kGraphemeStart)) {
--position->fTextRange.fStart;
}
// Update glyphRange, too
while (position->fRun->fClusters[position->fGlyphRange.fStart] > position->fTextRange.fStart) {
--position->fGlyphRange.fStart;
}
// Move right to the end of the run updating glyphRange, too
while (position->fTextRange.fEnd < position->fRun->fUtf8Range.end() &&
!this->hasProperty(position->fTextRange.fEnd, GlyphUnitFlags::kGraphemeStart)) {
++position->fTextRange.fEnd;
}
// Update glyphRange, too
while (position->fRun->fClusters[position->fGlyphRange.fEnd] < position->fTextRange.fEnd) {
++position->fGlyphRange.fEnd;
}
} else {
// TODO: Implement all the other position types
SkASSERT(false);
}
position->fTextRange = TextRange(position->fRun->fClusters[position->fGlyphRange.fStart], position->fRun->fClusters[position->fGlyphRange.fEnd]);
}
@ -701,7 +724,7 @@ Position FormattedText::nextElement(Position element) const {
// Shift left visually
element.fTextRange.fEnd = clusters[++element.fGlyphRange.fEnd];
if (element.fPositionType == PositionType::kGraphemeCluster) {
if (this->hasProperty(element.fTextRange.fStart, GlyphUnitFlags::kGraphemeStart)) {
if (this->hasProperty(element.fTextRange.fEnd, GlyphUnitFlags::kGraphemeStart)) {
break;
}
}

View File

@ -54,5 +54,9 @@ GlyphIndex TextRun::findGlyph(TextIndex textIndex) const {
return glyphIndex;
}
TextRange TextRun::getTextRange(GlyphRange glyphRange) const {
return TextRange(this->fClusters[glyphRange.fStart], this->fClusters[glyphRange.fEnd]);
}
} // namespace text
} // namespace skia

View File

@ -27,6 +27,7 @@ class TextRun {
uint8_t bidiLevel() const { return fBidiLevel; }
GlyphIndex findGlyph(TextIndex textIndex) const;
size_t size() const { return fGlyphs.size(); }
TextRange getTextRange(GlyphRange glyphRange) const;
template <typename Callback>
void forEachCluster(Callback&& callback) {