blob: b01ddebc21dcbcf4c6c4b2c6d9e2f3151a527907 [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 Processor;
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;
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;
}
private:
SkScalar fAscent;
SkScalar fDescent;
SkScalar fLeading;
};
class GlyphPos {
public:
GlyphPos() { }
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 fRunIndex;
size_t fGlyphIndex;
};
class Stretch {
public:
Stretch() : fGlyphStart(), fGlyphEnd(), fWidth(0), fTextRange(0, 0), fTextMetrics(), fEmpty(true) { }
Stretch(GlyphPos glyphStart, size_t textIndex, const TextMetrics& metrics)
: fGlyphStart(glyphStart)
, fGlyphEnd(glyphStart)
, fWidth(0.0)
, fTextRange(textIndex, textIndex)
, fTextMetrics(metrics)
, fEmpty(false) { }
Stretch(Stretch&&) = default;
Stretch& operator=(Stretch&&) = default;
bool isEmpty() const {
return this->fEmpty/* ||
(this->fGlyphStart.fRunIndex == this->fGlyphEnd.fRunIndex &&
this->fGlyphStart.fGlyphIndex == this->fGlyphEnd.fGlyphIndex)*/;
}
void clean() {
this->fEmpty = true;
}
void moveTo(Stretch& tail) {
if (tail.fEmpty) {
return;
}
if (this->fEmpty) {
if (!tail.fEmpty) {
this->fGlyphStart = tail.fGlyphStart;
this->fGlyphEnd = tail.fGlyphEnd;
this->fWidth = tail.fWidth;
this->fTextRange = tail.fTextRange;
this->fTextMetrics = tail.fTextMetrics;
this->fEmpty = tail.fEmpty;
}
tail.clean();
return;
}
SkASSERT(this->fGlyphEnd.fRunIndex != tail.fGlyphStart.fRunIndex ||
this->fGlyphEnd.fGlyphIndex == tail.fGlyphStart.fGlyphIndex);
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.fGlyphIndex = glyphIndex;
this->fWidth = width;
}
GlyphPos fGlyphStart;
GlyphPos fGlyphEnd;
SkScalar fWidth;
Range fTextRange;
TextMetrics fTextMetrics;
bool fEmpty;
};
class Line {
public:
Line(Processor* processor, const Stretch& stretch, const Stretch& spaces);
~Line() = default;
private:
friend class Processor;
GlyphPos fTextStart;
GlyphPos fTextEnd ;
GlyphPos fWhitespacesEnd;
Range fText;
Range fWhitespaces;
SkScalar fTextWidth;
SkScalar fSpacesWidth;
TextMetrics fTextMetrics;
SkSTArray<1, size_t, true> fRunsInVisualOrder;
Processor* fProcessor;
};
} // namespace text
} // namespace skia
#endif