blob: d348e13d9743d69b81472e3c4537e8f47c1da1a8 [file] [log] [blame]
// Copyright 2021 Google LLC.
#ifndef VisualRun_DEFINED
#define VisualRun_DEFINED
#include "experimental/sktext/include/Types.h"
#include "experimental/sktext/src/Line.h"
#include "modules/skshaper/include/SkShaper.h"
namespace skia {
namespace text {
class VisualRun {
public:
VisualRun(TextRange textRange, GlyphIndex trailingSpacesStart, const SkFont& font, SkScalar lineBaseLine,
SkPoint runOffset,
bool leftToRight,
SkSpan<SkPoint> positions, SkSpan<SkGlyphID> glyphs, SkSpan<uint32_t> clusters)
: fFont(font)
, fTextMetrics(TextMetrics(fFont))
, fLineBaseLine(lineBaseLine)
, fDirTextRange(textRange, leftToRight)
, fTrailingSpacesStart(trailingSpacesStart) {
if (positions.size() == 0) {
SkASSERT(false);
return;
}
fPositions.reserve_back(positions.size());
runOffset -= SkPoint::Make(positions[0].fX, - fLineBaseLine);
for (auto& pos : positions) {
fPositions.emplace_back(pos + runOffset);
}
fGlyphs.reserve_back(glyphs.size());
for (auto glyph : glyphs) {
fGlyphs.emplace_back(glyph);
}
fClusters.reserve_back(clusters.size());
for (auto cluster : clusters) {
fClusters.emplace_back(SkToU16(cluster));
}
fAdvance= SkVector::Make(this->calculateWidth(0, glyphs.size()), fTextMetrics.height());
}
SkScalar calculateWidth(GlyphRange glyphRange) const {
SkASSERT(glyphRange.fStart <= glyphRange.fEnd && glyphRange.fEnd < fPositions.size());
return fPositions[glyphRange.fEnd].fX - fPositions[glyphRange.fStart].fX;
}
SkScalar calculateWidth(GlyphIndex start, GlyphIndex end) const {
return calculateWidth(GlyphRange(start, end));
}
SkScalar width() const { return fAdvance.fX; }
SkScalar height() const { return fAdvance.fY; }
SkScalar firstGlyphPosition() const { return fPositions[0].fX; }
TextMetrics textMetrics() const { return fTextMetrics; }
bool leftToRight() const { return fDirTextRange.fLeftToRight; }
size_t size() const { return fGlyphs.size(); }
SkScalar baseLine() const { return fLineBaseLine; }
GlyphIndex trailingSpacesStart() const { return fTrailingSpacesStart; }
DirTextRange dirTextRange() const { return fDirTextRange; }
template <typename Callback>
void forEachTextBlockInGlyphRange(SkSpan<TextIndex> textBlock, Callback&& callback) const {
if (this->leftToRight()) {
DirTextRange dirTextRange(fDirTextRange.fStart, fDirTextRange.fStart, fDirTextRange.fLeftToRight);
for (auto currentIndex : textBlock) {
if (currentIndex >= fDirTextRange.fEnd) {
break;
}
if (currentIndex < fDirTextRange.fStart) {
continue;
}
dirTextRange.fStart = dirTextRange.fEnd;
dirTextRange.fEnd = currentIndex;
dirTextRange.fEnd = std::min(fDirTextRange.fEnd, dirTextRange.fEnd);
callback(dirTextRange);
}
} else {
// Revert chunks
std::vector<TextIndex> revertedChunks;
}
}
private:
friend class WrappedText;
SkFont fFont;
TextMetrics fTextMetrics;
SkScalar fLineBaseLine;
SkVector fAdvance;
DirTextRange fDirTextRange;
SkSTArray<128, SkGlyphID, true> fGlyphs;
SkSTArray<128, SkPoint, true> fPositions;
SkSTArray<128, TextIndex, true> fClusters;
GlyphIndex fTrailingSpacesStart;
};
class VisualLine {
public:
VisualLine(TextRange text, bool hardLineBreak, SkScalar verticalOffset, SkSpan<VisualRun> runs)
: fText(text)
, fRuns(runs)
, fTrailingSpaces(0, 0)
, fOffset(SkPoint::Make(0, verticalOffset))
, fActualWidth(0.0f)
, fTextMetrics()
, fIsHardBreak(hardLineBreak)
, fGlyphCount(0ul) {
// Calculate all the info
for (auto& run : fRuns) {
fTextMetrics.merge(run.textMetrics());
fActualWidth += run.width(); // What about trailing spaces?
if (run.trailingSpacesStart() == 0) {
// The entire run is trailing spaces, do not move the counter
} else {
// We can reset the trailing spaces counter
fTrailingSpaces.fStart = fTrailingSpaces.fEnd + run.trailingSpacesStart();
}
fTrailingSpaces.fEnd += run.size();
}
}
SkScalar baseline() const { return fTextMetrics.baseline(); }
TextRange text() const { return fText; }
GlyphRange trailingSpaces() const { return fTrailingSpaces; }
bool isHardBreak() const { return fIsHardBreak; }
size_t glyphCount() const { return fGlyphCount; }
bool isFirst(VisualRun* run) { return &fRuns.front() == run; }
bool isLast(VisualRun* run) { return &fRuns.back() == run; }
private:
friend class WrappedText;
friend class VisualRun;
TextRange fText;
SkSpan<VisualRun> fRuns;
GlyphRange fTrailingSpaces; // This is a zero-based index across the line
SkPoint fOffset; // For left/right/center formatting and for height
SkScalar fActualWidth;
TextMetrics fTextMetrics;
bool fIsHardBreak;
size_t fGlyphCount;
};
}; // namespace text
} // namespace skia
#endif