blob: 7f5f0e0f40ef4a44af48612ec4b33a660dd7168a [file] [log] [blame]
// Copyright 2019 Google LLC.
#ifndef ParagraphImpl_DEFINED
#define ParagraphImpl_DEFINED
#include <unicode/brkiter.h>
#include <unicode/ubidi.h>
#include <unicode/unistr.h>
#include <unicode/urename.h>
#include "include/core/SkPicture.h"
#include "include/private/SkMutex.h"
#include "include/private/SkTHash.h"
#include "modules/skparagraph/include/Paragraph.h"
#include "modules/skparagraph/include/ParagraphStyle.h"
#include "modules/skparagraph/include/TextStyle.h"
#include "modules/skparagraph/src/Run.h"
#include "modules/skparagraph/src/TextLine.h"
class SkCanvas;
namespace skia {
namespace textlayout {
template <typename T> bool operator==(const SkSpan<T>& a, const SkSpan<T>& b) {
return a.size() == b.size() && a.begin() == b.begin();
}
template <typename T> bool operator<=(const SkSpan<T>& a, const SkSpan<T>& b) {
return a.begin() >= b.begin() && a.end() <= b.end();
}
template <typename TStyle>
struct StyleBlock {
StyleBlock() : fRange(EMPTY_RANGE), fStyle() { }
StyleBlock(size_t start, size_t end, const TStyle& style) : fRange(start, end), fStyle(style) {}
StyleBlock(TextRange textRange, const TStyle& style) : fRange(textRange), fStyle(style) {}
void add(TextRange tail) {
SkASSERT(fRange.end == tail.start);
fRange = TextRange(fRange.start, fRange.start + fRange.width() + tail.width());
}
TextRange fRange;
TStyle fStyle;
};
struct ResolvedFontDescriptor {
ResolvedFontDescriptor(TextIndex index, SkFont font)
: fFont(font), fTextStart(index) { }
SkFont fFont;
TextIndex fTextStart;
};
class TextBreaker {
public:
TextBreaker() : fInitialized(false), fPos(-1) {}
bool initialize(SkSpan<const char> text, UBreakIteratorType type);
bool initialized() const { return fInitialized; }
size_t first() {
fPos = ubrk_first(fIterator.get());
return eof() ? fSize : fPos;
}
size_t next() {
fPos = ubrk_next(fIterator.get());
return eof() ? fSize : fPos;
}
size_t preceding(size_t offset) {
auto pos = ubrk_preceding(fIterator.get(), offset);
return pos == icu::BreakIterator::DONE ? 0 : pos;
}
size_t following(size_t offset) {
auto pos = ubrk_following(fIterator.get(), offset);
return pos == icu::BreakIterator::DONE ? fSize : pos;
}
int32_t status() { return ubrk_getRuleStatus(fIterator.get()); }
bool eof() { return fPos == icu::BreakIterator::DONE; }
private:
std::unique_ptr<UBreakIterator, SkFunctionWrapper<decltype(ubrk_close), ubrk_close>> fIterator;
bool fInitialized;
int32_t fPos;
size_t fSize;
};
class ParagraphImpl final : public Paragraph {
public:
ParagraphImpl(const SkString& text,
ParagraphStyle style,
SkTArray<Block, true> blocks,
SkTArray<Placeholder, true> placeholders,
sk_sp<FontCollection> fonts);
ParagraphImpl(const std::u16string& utf16text,
ParagraphStyle style,
SkTArray<Block, true> blocks,
SkTArray<Placeholder, true> placeholders,
sk_sp<FontCollection> fonts);
~ParagraphImpl() override;
void layout(SkScalar width) override;
void paint(SkCanvas* canvas, SkScalar x, SkScalar y) override;
std::vector<TextBox> getRectsForRange(unsigned start,
unsigned end,
RectHeightStyle rectHeightStyle,
RectWidthStyle rectWidthStyle) override;
std::vector<TextBox> getRectsForPlaceholders() override;
void getLineMetrics(std::vector<LineMetrics>&) override;
PositionWithAffinity getGlyphPositionAtCoordinate(SkScalar dx, SkScalar dy) override;
SkRange<size_t> getWordBoundary(unsigned offset) override;
size_t lineNumber() override { return fLines.size(); }
TextLine& addLine(SkVector offset, SkVector advance, TextRange text, TextRange textWithSpaces,
ClusterRange clusters, ClusterRange clustersWithGhosts, SkScalar AddLineToParagraph,
InternalLineMetrics sizes);
SkSpan<const char> text() const { return SkSpan<const char>(fText.c_str(), fText.size()); }
InternalState state() const { return fState; }
SkSpan<Run> runs() { return SkSpan<Run>(fRuns.data(), fRuns.size()); }
SkSpan<Block> styles() {
return SkSpan<Block>(fTextStyles.data(), fTextStyles.size());
}
SkSpan<TextLine> lines() { return SkSpan<TextLine>(fLines.data(), fLines.size()); }
const ParagraphStyle& paragraphStyle() const { return fParagraphStyle; }
SkSpan<Cluster> clusters() { return SkSpan<Cluster>(fClusters.begin(), fClusters.size()); }
sk_sp<FontCollection> fontCollection() const { return fFontCollection; }
void formatLines(SkScalar maxWidth);
void shiftCluster(ClusterIndex index, SkScalar shift, SkScalar lastShift) {
auto& cluster = fClusters[index];
auto& runShift = fRunShifts[cluster.runIndex()];
auto& run = fRuns[cluster.runIndex()];
auto start = cluster.startPos();
auto end = cluster.endPos();
if (!run.leftToRight()) {
runShift.fShifts[start] = lastShift;
++start;
++end;
}
for (size_t pos = start; pos < end; ++pos) {
runShift.fShifts[pos] = shift;
}
}
SkScalar posShift(RunIndex index, size_t pos) const {
if (fRunShifts.count() == 0) return 0.0;
return fRunShifts[index].fShifts[pos];
}
bool strutEnabled() const { return paragraphStyle().getStrutStyle().getStrutEnabled(); }
bool strutForceHeight() const {
return paragraphStyle().getStrutStyle().getForceStrutHeight();
}
bool strutHeightOverride() const {
return paragraphStyle().getStrutStyle().getHeightOverride();
}
InternalLineMetrics strutMetrics() const { return fStrutMetrics; }
SkSpan<const char> text(TextRange textRange);
SkSpan<Cluster> clusters(ClusterRange clusterRange);
Cluster& cluster(ClusterIndex clusterIndex);
Run& run(RunIndex runIndex);
Run& runByCluster(ClusterIndex clusterIndex);
SkSpan<Block> blocks(BlockRange blockRange);
Block& block(BlockIndex blockIndex);
SkTArray<ResolvedFontDescriptor> resolvedFonts() const { return fFontSwitches; }
void markDirty() override { fState = kUnknown; }
void setState(InternalState state);
sk_sp<SkPicture> getPicture() { return fPicture; }
SkRect getBoundaries() const { return fOrigin; }
void resetContext();
void resolveStrut();
void resetRunShifts();
void buildClusterTable();
void markLineBreaks();
bool shapeTextIntoEndlessLine();
void breakShapedTextIntoLines(SkScalar maxWidth);
void paintLinesIntoPicture();
void updateTextAlign(TextAlign textAlign) override;
void updateText(size_t from, SkString text) override;
void updateFontSize(size_t from, size_t to, SkScalar fontSize) override;
void updateForegroundPaint(size_t from, size_t to, SkPaint paint) override;
void updateBackgroundPaint(size_t from, size_t to, SkPaint paint) override;
InternalLineMetrics computeEmptyMetrics();
InternalLineMetrics getStrutMetrics() const { return fStrutMetrics; }
private:
friend class ParagraphBuilder;
friend class ParagraphCacheKey;
friend class ParagraphCacheValue;
friend class ParagraphCache;
friend class TextWrapper;
friend class OneLineShaper;
void calculateBoundaries(ClusterRange clusters, SkVector offset, SkVector advance);
BlockRange findAllBlocks(TextRange textRange);
void extractStyles();
void markGraphemes16();
void markGraphemes();
// Input
SkTArray<StyleBlock<SkScalar>> fLetterSpaceStyles;
SkTArray<StyleBlock<SkScalar>> fWordSpaceStyles;
SkTArray<StyleBlock<SkPaint>> fBackgroundStyles;
SkTArray<StyleBlock<SkPaint>> fForegroundStyles;
SkTArray<StyleBlock<std::vector<TextShadow>>> fShadowStyles;
SkTArray<StyleBlock<Decoration>> fDecorationStyles;
SkTArray<Block, true> fTextStyles; // TODO: take out only the font stuff
SkTArray<Placeholder, true> fPlaceholders;
SkString fText;
// Internal structures
InternalState fState;
SkTArray<Run, false> fRuns; // kShaped
SkTArray<Cluster, true> fClusters; // kClusterized (cached: text, word spacing, letter spacing, resolved fonts)
SkTArray<Grapheme, true> fGraphemes16;
SkTArray<Codepoint, true> fCodePoints;
SkTHashSet<size_t> fGraphemes;
SkTArray<RunShifts, false> fRunShifts;
SkTArray<TextLine, true> fLines; // kFormatted (cached: width, max lines, ellipsis, text align)
sk_sp<SkPicture> fPicture; // kRecorded (cached: text styles)
SkTArray<ResolvedFontDescriptor> fFontSwitches;
InternalLineMetrics fStrutMetrics;
SkScalar fOldWidth;
SkScalar fOldHeight;
SkScalar fMaxWidthWithTrailingSpaces;
SkRect fOrigin;
std::vector<size_t> fWords;
};
} // namespace textlayout
} // namespace skia
#endif // ParagraphImpl_DEFINED