blob: d205b2bb92d739a1fbba6704230800c55398fae8 [file] [log] [blame]
// Copyright 2021 Google LLC.
#ifndef TextLine_DEFINED
#define TextLine_DEFINED
#include "experimental/sktext/include/Types.h"
#include "include/core/SkFont.h"
#include "include/core/SkFontMetrics.h"
namespace skia {
namespace text {
class TextMetrics {
public:
TextMetrics() {
clean();
}
TextMetrics(const SkFont& font) {
SkFontMetrics metrics;
font.getMetrics(&metrics);
fAscent = metrics.fAscent;
fDescent = metrics.fDescent;
fLeading = metrics.fLeading;
}
TextMetrics(const TextMetrics&) = default;
TextMetrics& operator=(const TextMetrics&) = default;
void merge(TextMetrics tail) {
this->fAscent = std::min(this->fAscent, tail.fAscent);
this->fDescent = std::max(this->fDescent, tail.fDescent);
this->fLeading = std::max(this->fLeading, tail.fLeading);
}
void clean() {
this->fAscent = 0;
this->fDescent = 0;
this->fLeading = 0;
}
SkScalar height() const {
return this->fDescent - this->fAscent + this->fLeading;
}
SkScalar baseline() const {
return - this->fAscent + this->fLeading / 2;
}
SkScalar above() const { return - this->fAscent + this->fLeading / 2; }
SkScalar below() const { return this->fDescent + this->fLeading / 2; }
private:
SkScalar fAscent;
SkScalar fDescent;
SkScalar fLeading;
};
class GlyphPos {
public:
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;
};
class Stretch {
public:
Stretch() : fGlyphStart(), fGlyphEnd(), fWidth(0), fTextRange(EMPTY_RANGE), fTextMetrics() { }
Stretch(GlyphPos glyphStart, size_t textIndex, const TextMetrics& metrics)
: fGlyphStart(glyphStart)
, fGlyphEnd(glyphStart)
, fWidth(0.0)
, fTextRange(textIndex, textIndex)
, fTextMetrics(metrics) { }
Stretch(RunIndex runIndex, GlyphRange glyphRange, TextRange textRange, SkScalar width, const TextMetrics& metrics)
: fGlyphStart(runIndex, glyphRange.fStart)
, fGlyphEnd(runIndex, glyphRange.fEnd)
, fWidth(width)
, fTextRange(textRange)
, fTextMetrics(metrics) { }
Stretch(const Stretch&) = default;
Stretch(Stretch&&) = default;
Stretch& operator=(Stretch&&) = default;
Stretch& operator=(const Stretch&) = default;
bool isEmpty() const {
if (fGlyphStart.isEmpty() || fGlyphEnd.isEmpty()) {
return true;
} else {
return fGlyphStart == fGlyphEnd;
}
}
void clean() {
fGlyphStart = fGlyphEnd;
fTextRange.fStart = fTextRange.fEnd;
fWidth = 0.0f;
fTextMetrics.clean();
}
void moveTo(Stretch& tail) {
if (tail.isEmpty()) {
return;
}
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;
}
tail.clean();
return;
}
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);
this->fTextMetrics.merge(tail.fTextMetrics);
tail.clean();
}
void finish(size_t glyphIndex, size_t textIndex, SkScalar width) {
this->fTextRange.fEnd = textIndex;
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;
TextRange fTextRange;
TextMetrics fTextMetrics;
};
class LogicalLine {
public:
LogicalLine(const Stretch& stretch, const Stretch& spaces, SkScalar verticalOffset, bool hardLineBreak);
~LogicalLine() = default;
TextMetrics getMetrics() const { return fTextMetrics; }
GlyphPos glyphStart() const { return fTextStart; }
GlyphPos glyphEnd() const { return fTextEnd; }
GlyphPos glyphTrailingEnd() const { return fWhitespacesEnd; }
SkScalar width() const { return fTextWidth; }
SkScalar withWithTrailingSpaces() const { return fTextWidth + fSpacesWidth; }
SkScalar horizontalOffset() const { return fHorizontalOffset; }
SkScalar verticalOffset() const { return fVerticalOffset; }
SkScalar height() const { return fTextMetrics.height(); }
SkScalar baseline() const { return fTextMetrics.baseline(); }
TextRange text() const { return fText; }
TextRange whitespaces() const { return fWhitespaces; }
bool isHardLineBreak() const { return fHardLineBreak; }
private:
friend class WrappedText;
GlyphPos fTextStart;
GlyphPos fTextEnd;
GlyphPos fWhitespacesEnd;
TextRange fText;
TextRange fWhitespaces;
SkScalar fTextWidth;
SkScalar fSpacesWidth;
SkScalar fHorizontalOffset;
SkScalar fVerticalOffset;
TextMetrics fTextMetrics;
bool fHardLineBreak;
};
} // namespace text
} // namespace skia
#endif