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:
parent
3bce5d1397
commit
c87e9516c3
@ -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>(
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user