platform/cc/shaper/TextLineRunHandler.hh (110 lines of code) (raw):
#include <iostream>
#include <jni.h>
#include "../interop.hh"
#include "../TextLine.hh"
#include "SkShaper.h"
#include "SkTextBlob.h"
class TextLineRunHandler: public SkShaper::RunHandler {
public:
TextLineRunHandler(const SkString& text,
std::shared_ptr<UBreakIterator> graphemeIter):
fLine(new TextLine()),
conv(text),
fGraphemeIter(graphemeIter)
{
}
void beginLine() override {
}
void runInfo(const RunInfo& info) override {
fLine->fGlyphCount += info.glyphCount;
SkFontMetrics metrics;
info.fFont.getMetrics(&metrics);
fLine->fAscent = std::min(fLine->fAscent, metrics.fAscent);
fLine->fCapHeight = std::max(fLine->fCapHeight, metrics.fCapHeight);
fLine->fXHeight = std::max(fLine->fXHeight, metrics.fXHeight);
fLine->fDescent = std::max(fLine->fDescent, metrics.fDescent);
fLine->fLeading = std::max(fLine->fLeading, metrics.fLeading);
fLine->fWidth += info.fAdvance.fX;
}
void commitRunInfo() override {
SkDEBUGCODE(++fLines;)
}
Buffer runBuffer(const RunInfo& info) override {
const SkTextBlobBuilder::RunBuffer& buffer = fBuilder.allocRunPos(info.fFont, info.glyphCount);
fLine->fRuns.emplace_back(
info.fFont,
info.fBidiLevel,
fPosition,
info.fAdvance.fX,
info.glyphCount,
buffer.points());
TextLine::Run& run = fLine->fRuns.back();
if (fGlyphOffsets.capacity() < info.glyphCount)
fGlyphOffsets.resize(info.glyphCount);
return {
buffer.glyphs,
buffer.points(),
nullptr,
fGlyphOffsets.data(),
{fPosition, 0}
};
}
void commitRunBuffer(const RunInfo& info) override {
TextLine::Run& run = fLine->fRuns.back();
int32_t glyph = 0;
int32_t graphemesInGlyph = 1;
SkScalar glyphLeft = run.fPos[glyph].fX;
run.fBreakOffsets.reserve(info.utf8Range.size() + 1);
run.fBreakPositions.reserve(info.utf8Range.size() + 1);
// Only record grapheme clusters boundaries
for (int32_t offset = fGlyphOffsets[0]; offset <= info.utf8Range.end(); offset = ubrk_following(fGraphemeIter.get(), offset)) {
run.fBreakOffsets.push_back(conv.from8To16(offset));
// if grapheme clusters includes multiple glyphs, skip over them
while (glyph < info.glyphCount && fGlyphOffsets[glyph] < offset)
++glyph;
// if one glyph includes multiple grapheme clusters (ligature, e.g. <->), accumulate
if ((glyph < info.glyphCount ? fGlyphOffsets[glyph] : info.utf8Range.end()) > offset)
++graphemesInGlyph;
// when boundaries meet, distribute break positions evenly inside glyph
else {
SkScalar glyphRight = glyph < info.glyphCount ? run.fPos[glyph].fX : fPosition + info.fAdvance.fX;
SkScalar step = (glyphRight - glyphLeft) / graphemesInGlyph;
for (int i = 0; i < graphemesInGlyph; ++i)
run.fBreakPositions.push_back(glyphLeft + step * (i + 1));
graphemesInGlyph = 1;
glyphLeft = glyphRight;
}
}
fPosition += info.fAdvance.fX;
}
void commitLine() override {
}
sk_sp<TextLine> makeLine() {
SkASSERTF(fLines == 1, "TextLineRunHandler: Expected single line, got %d", fLines);
sk_sp<SkTextBlob> blob = fBuilder.make();
if (nullptr == blob.get())
return fLine;
SkTextBlob::Iter iter(*blob);
SkTextBlob::Iter::Run blobRun;
int runIdx = 0;
while (iter.next(&blobRun)) {
// from SkTextBlobPriv.h
// -----------------------------------------------------------------------------
// | SkTextBlob | RunRecord | Glyphs[] | Pos[] | RunRecord | Glyphs[] | Pos[] | ...
// -----------------------------------------------------------------------------
int glyphCount = blobRun.fGlyphCount;
const uint16_t* glyphIndices = blobRun.fGlyphIndices;
const SkPoint* positions = reinterpret_cast<const SkPoint*>(reinterpret_cast<const uint8_t*>(blobRun.fGlyphIndices) +
SkAlign4(glyphCount * sizeof(uint16_t)));
for (int consumed = 0; consumed < glyphCount;) {
auto& run = fLine->fRuns[runIdx];
run.fGlyphs = glyphIndices + consumed;
run.fPos = positions + consumed;
runIdx += 1;
consumed += run.fGlyphCount;
}
}
fLine->fBlob = blob;
return fLine;
}
private:
sk_sp<TextLine> fLine;
SkTextBlobBuilder fBuilder;
skija::UtfIndicesConverter conv;
std::shared_ptr<UBreakIterator> fGraphemeIter;
std::vector<uint32_t> fGlyphOffsets;
SkScalar fPosition = 0;
SkDEBUGCODE(int fLines = 0;)
};