skia2/experimental/sktext/include/Text.h
Julia Lavrova ecc8e3bc04 CPP Text Editor
The very first version:
1. Moves cursort up, down, left, right by grapheme/glyph clusters
2. Breaks lines by grapheme/glyph cluster
3. Just started!

Change-Id: Ib2881794ff33af9e428828f3a9e2d3b54946fa8f
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/417476
Commit-Queue: Julia Lavrova <jlavrova@google.com>
Reviewed-by: Mike Reed <reed@google.com>
2021-06-10 19:15:04 +00:00

179 lines
5.8 KiB
C++

// Copyright 2021 Google LLC.
#ifndef Processor_DEFINED
#define Processor_DEFINED
#include "experimental/sktext/src/Line.h"
#include <string>
#include "experimental/sktext/include/Types.h"
#include "experimental/sktext/src/TextRun.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkFontMgr.h"
#include "include/core/SkFontStyle.h"
#include "include/core/SkPaint.h"
#include "include/core/SkSize.h"
#include "include/core/SkString.h"
#include "include/core/SkTextBlob.h"
#include "modules/skshaper/include/SkShaper.h"
#include "modules/skshaper/src/SkUnicode.h"
namespace skia {
namespace text {
class UnicodeText;
class Text {
public:
static std::unique_ptr<UnicodeText> parse(SkSpan<uint16_t> utf16);
};
class ShapedText;
class UnicodeText : public SkShaper::RunHandler {
public:
std::unique_ptr<ShapedText> shape(SkSpan<Block> blocks,
TextDirection textDirection);
bool hasProperty(size_t index, CodeUnitFlags flag) {
return (fCodeUnitProperties[index] & flag) == flag;
}
bool isHardLineBreak(size_t index) {
return this->hasProperty(index, CodeUnitFlags::kHardLineBreakBefore);
}
bool isSoftLineBreak(size_t index) {
return index != 0 && this->hasProperty(index, CodeUnitFlags::kSoftLineBreakBefore);
}
SkUnicode* getUnicode() const { return fUnicode.get(); }
private:
friend class Text;
UnicodeText() : fCurrentRun(nullptr) { }
void beginLine() override {}
void runInfo(const RunInfo&) override {}
void commitRunInfo() override {}
void commitLine() override {}
void commitRunBuffer(const RunInfo&) override;
Buffer runBuffer(const RunInfo& info) override {
fCurrentRun = std::make_unique<TextRun>(info);
return fCurrentRun->newRunBuffer();
}
SkFont createFont(const Block& block);
SkTArray<size_t, true> fUTF16FromUTF8;
SkTArray<CodeUnitFlags, true> fCodeUnitProperties;
SkString fText8;
std::unique_ptr<SkUnicode> fUnicode;
std::unique_ptr<TextRun> fCurrentRun;
std::unique_ptr<ShapedText> fShapedText;
};
class WrappedText;
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;
}
void adjustLeft(size_t* index) const {
SkASSERT(index != nullptr);
while (*index != 0) {
if (isClusterEdge(*index)) {
return;
}
--index;
}
}
bool hasProperty(size_t index, GlyphUnitFlags flag) {
return (fGlyphUnitProperties[index] & flag) == flag;
}
bool isHardLineBreak(size_t index) {
return this->hasProperty(index, GlyphUnitFlags::kHardLineBreakBefore);
}
bool isSoftLineBreak(size_t index) {
return index != 0 && this->hasProperty(index, GlyphUnitFlags::kSoftLineBreakBefore);
}
bool isWhitespaces(TextRange range) {
if (range.leftToRight()) {
for (auto i = range.fStart; i < range.fEnd; ++i) {
if (!this->hasProperty(i, GlyphUnitFlags::kPartOfWhiteSpace)) {
return false;
}
}
} else {
for (auto i = range.fStart; i > range.fEnd; --i) {
if (!this->hasProperty(i, GlyphUnitFlags::kPartOfWhiteSpace)) {
return false;
}
}
}
return true;
}
private:
friend class UnicodeText;
ShapedText() { }
SkTArray<TextRun, false> fRuns;
SkTArray<GlyphUnitFlags, true> fGlyphUnitProperties;
};
class FormattedText;
class WrappedText {
public:
sk_sp<FormattedText> format(TextAlign, TextDirection);
SkSize size() const { return fSize; }
size_t countLines() const { return fLines.size(); }
private:
friend class ShapedText;
WrappedText() : fSize(SkSize::MakeEmpty()) { }
void addLine(Stretch& stretch, Stretch& spaces, SkUnicode* unicode);
SkTArray<TextRun, false> fRuns;
SkTArray<Line, false> fLines;
SkTArray<GlyphUnitFlags, true> fGlyphUnitProperties;
SkSize fSize;
};
class FormattedText : public SkRefCnt {
public:
SkSize size() const { return fSize; }
std::tuple<const Line*, const TextRun*, GlyphIndex, SkRect> indexToAdjustedGraphemePosition(TextIndex textIndex) const;
TextIndex positionToAdjustedGraphemeIndex(SkPoint xy) const;
size_t lineIndex(const Line* line) const {
return line - fLines.data();
}
size_t countLines() const { return fLines.size(); }
// This one does not make sense; how would we get glyphRange in the first place?
// It has to point to the same run or we cannot even index it
std::vector<TextRange> adjustIndicesAndComputeTextRanges(GlyphRange glyphRange) const;
class Visitor {
public:
virtual ~Visitor() = default;
virtual void onBeginLine(TextRange, float baselineY) = 0;
virtual void onEndLine(TextRange, float baselineY) = 0;
virtual void onGlyphRun(SkFont font,
TextRange textRange,
int glyphCount,
const uint16_t glyphs[],
const SkPoint positions[],
const SkPoint offsets[]) = 0;
virtual void onPlaceholder(TextRange, const SkRect& bounds) = 0;
};
// Visit runs as is by lines
void visit(Visitor*) const;
// Visit chunked runs
void visit(Visitor*, SkSpan<size_t> blocks) const;
private:
friend class WrappedText;
FormattedText() { }
SkTArray<TextRun, false> fRuns;
SkTArray<Line, false> fLines;
SkTArray<GlyphUnitFlags, true> fGlyphUnitProperties;
SkSize fSize;
};
} // namespace text
} // namespace skia
#endif // Processor_DEFINED