Reland "ICU: SkShaper (bidi iterator only)"
This reverts commit73f003acfd
. Reason for revert: Fixed the build Original change's description: > Revert "ICU: SkShaper (bidi iterator only)" > > This reverts commit64e3d040e9
. > > Reason for revert: Breaking google3 > > Original change's description: > > ICU: SkShaper (bidi iterator only) > > > > Change-Id: I845cc0a962790ce37600f943473f21f619ee029b > > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/308508 > > Reviewed-by: Ben Wagner <bungeman@google.com> > > Commit-Queue: Julia Lavrova <jlavrova@google.com> > > TBR=djsollen@google.com,bungeman@google.com,reed@google.com,jlavrova@google.com > > Change-Id: Ib081d97f61e57f74bb9414b3973cca640f3b3929 > No-Presubmit: true > No-Tree-Checks: true > No-Try: true > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/309442 > Reviewed-by: Julia Lavrova <jlavrova@google.com> > Commit-Queue: Julia Lavrova <jlavrova@google.com> TBR=djsollen@google.com,bungeman@google.com,reed@google.com,jlavrova@google.com Change-Id: I2d9c79deaaac97d3e0eb9b39ec9d53815fdb1f59 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/310757 Reviewed-by: Ben Wagner <bungeman@google.com> Commit-Queue: Julia Lavrova <jlavrova@google.com>
This commit is contained in:
parent
b2b4cc899d
commit
7bc5542a9e
@ -477,7 +477,7 @@ bool OneLineShaper::iterateThroughShapingRegions(const ShapeVisitor& shape) {
|
||||
if (placeholder.fTextBefore.width() > 0) {
|
||||
// Shape the text by bidi regions
|
||||
while (bidiIndex < fParagraph->fBidiRegions.size()) {
|
||||
BidiRegion& bidiRegion = fParagraph->fBidiRegions[bidiIndex];
|
||||
SkUnicode::BidiRegion& bidiRegion = fParagraph->fBidiRegions[bidiIndex];
|
||||
auto start = std::max(bidiRegion.start, placeholder.fTextBefore.start);
|
||||
auto end = std::min(bidiRegion.end, placeholder.fTextBefore.end);
|
||||
|
||||
|
@ -52,7 +52,7 @@ public:
|
||||
// ICU results
|
||||
SkTArray<CodeUnitFlags> fCodeUnitProperties;
|
||||
std::vector<size_t> fWords;
|
||||
std::vector<BidiRegion> fBidiRegions;
|
||||
std::vector<SkUnicode::BidiRegion> fBidiRegions;
|
||||
SkTArray<TextIndex, true> fUTF8IndexForUTF16Index;
|
||||
SkTArray<size_t, true> fUTF16IndexForUTF8Index;
|
||||
};
|
||||
|
@ -82,7 +82,7 @@ ParagraphImpl::ParagraphImpl(const SkString& text,
|
||||
, fStrutMetrics(false)
|
||||
, fOldWidth(0)
|
||||
, fOldHeight(0) {
|
||||
fICU = ::skia::SkUnicode::Make();
|
||||
fICU = SkUnicode::Make();
|
||||
}
|
||||
|
||||
ParagraphImpl::ParagraphImpl(const std::u16string& utf16text,
|
||||
@ -255,15 +255,15 @@ bool ParagraphImpl::computeCodeUnitProperties() {
|
||||
#endif
|
||||
|
||||
// Get bidi regions
|
||||
Direction textDirection = fParagraphStyle.getTextDirection() == TextDirection::kLtr
|
||||
? Direction::kLTR
|
||||
: Direction::kRTL;
|
||||
auto textDirection = fParagraphStyle.getTextDirection() == TextDirection::kLtr
|
||||
? SkUnicode::TextDirection::kLTR
|
||||
: SkUnicode::TextDirection::kRTL;
|
||||
if (!fICU->getBidiRegions(fText.c_str(), fText.size(), textDirection, &fBidiRegions)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get white spaces
|
||||
std::vector<Position> whitespaces;
|
||||
std::vector<SkUnicode::Position> whitespaces;
|
||||
if (!fICU->getWhitespaces(fText.c_str(), fText.size(), &whitespaces)) {
|
||||
return false;
|
||||
}
|
||||
@ -272,18 +272,18 @@ bool ParagraphImpl::computeCodeUnitProperties() {
|
||||
}
|
||||
|
||||
// Get line breaks
|
||||
std::vector<LineBreakBefore> lineBreaks;
|
||||
std::vector<SkUnicode::LineBreakBefore> lineBreaks;
|
||||
if (!fICU->getLineBreaks(fText.c_str(), fText.size(), &lineBreaks)) {
|
||||
return false;
|
||||
}
|
||||
for (auto& lineBreak : lineBreaks) {
|
||||
fCodeUnitProperties[lineBreak.pos] |= lineBreak.breakType == LineBreakType::kHardLineBreak
|
||||
fCodeUnitProperties[lineBreak.pos] |= lineBreak.breakType == SkUnicode::LineBreakType::kHardLineBreak
|
||||
? CodeUnitFlags::kHardLineBreakBefore
|
||||
: CodeUnitFlags::kSoftLineBreakBefore;
|
||||
}
|
||||
|
||||
// Get graphemes
|
||||
std::vector<Position> graphemes;
|
||||
std::vector<SkUnicode::Position> graphemes;
|
||||
if (!fICU->getGraphemes(fText.c_str(), fText.size(), &graphemes)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -249,7 +249,7 @@ private:
|
||||
SkTArray<CodeUnitFlags> fCodeUnitProperties;
|
||||
SkTArray<size_t> fClustersIndexFromCodeUnit;
|
||||
std::vector<size_t> fWords;
|
||||
std::vector<BidiRegion> fBidiRegions;
|
||||
std::vector<SkUnicode::BidiRegion> fBidiRegions;
|
||||
// These two arrays are used in measuring methods (getRectsForRange, getGlyphPositionAtCoordinate)
|
||||
// They are filled lazily whenever they need and cached
|
||||
SkTArray<TextIndex, true> fUTF8IndexForUTF16Index;
|
||||
|
@ -131,7 +131,7 @@ TextLine::TextLine(ParagraphImpl* owner,
|
||||
|
||||
// This is just chosen to catch the common/fast cases. Feel free to tweak.
|
||||
constexpr int kPreallocCount = 4;
|
||||
SkAutoSTArray<kPreallocCount, BidiLevel> runLevels(numRuns);
|
||||
SkAutoSTArray<kPreallocCount, SkUnicode::BidiLevel> runLevels(numRuns);
|
||||
size_t runLevelsIndex = 0;
|
||||
for (auto runIndex = start.runIndex(); runIndex <= end.runIndex(); ++runIndex) {
|
||||
auto& run = fOwner->run(runIndex);
|
||||
|
@ -39,6 +39,7 @@
|
||||
|
||||
class SkFont;
|
||||
class SkFontMgr;
|
||||
class SkUnicode;
|
||||
|
||||
/**
|
||||
Shapes text using HarfBuzz and places the shaped text into a
|
||||
@ -133,7 +134,9 @@ public:
|
||||
|
||||
static std::unique_ptr<BiDiRunIterator>
|
||||
MakeBiDiRunIterator(const char* utf8, size_t utf8Bytes, uint8_t bidiLevel);
|
||||
#ifdef SK_SHAPER_HARFBUZZ_AVAILABLE
|
||||
#ifdef SK_UNICODE_AVAILABLE
|
||||
static std::unique_ptr<BiDiRunIterator>
|
||||
MakeSkUnicodeBidiRunIterator(SkUnicode* unicode, const char* utf8, size_t utf8Bytes, uint8_t bidiLevel);
|
||||
static std::unique_ptr<BiDiRunIterator>
|
||||
MakeIcuBiDiRunIterator(const char* utf8, size_t utf8Bytes, uint8_t bidiLevel);
|
||||
#endif
|
||||
|
@ -38,9 +38,13 @@ std::unique_ptr<SkShaper> SkShaper::Make(sk_sp<SkFontMgr> fontmgr) {
|
||||
|
||||
std::unique_ptr<SkShaper::BiDiRunIterator>
|
||||
SkShaper::MakeBiDiRunIterator(const char* utf8, size_t utf8Bytes, uint8_t bidiLevel) {
|
||||
#ifdef SK_SHAPER_HARFBUZZ_AVAILABLE
|
||||
#ifdef SK_UNICODE_AVAILABLE
|
||||
auto unicode = SkUnicode::Make();
|
||||
std::unique_ptr<SkShaper::BiDiRunIterator> bidi =
|
||||
SkShaper::MakeIcuBiDiRunIterator(utf8, utf8Bytes, bidiLevel);
|
||||
SkShaper::MakeSkUnicodeBidiRunIterator(unicode.get(),
|
||||
utf8,
|
||||
utf8Bytes,
|
||||
bidiLevel);
|
||||
if (bidi) {
|
||||
return bidi;
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "include/private/SkTemplates.h"
|
||||
#include "include/private/SkTo.h"
|
||||
#include "modules/skshaper/include/SkShaper.h"
|
||||
#include "modules/skshaper/src/SkUnicode.h"
|
||||
#include "src/core/SkLRUCache.h"
|
||||
#include "src/core/SkSpan.h"
|
||||
#include "src/core/SkTDPQueue.h"
|
||||
@ -34,7 +35,6 @@
|
||||
#include <hb.h>
|
||||
#include <hb-icu.h>
|
||||
#include <hb-ot.h>
|
||||
#include <unicode/ubidi.h>
|
||||
#include <unicode/ubrk.h>
|
||||
#include <unicode/umachine.h>
|
||||
#include <unicode/urename.h>
|
||||
@ -71,10 +71,11 @@ using HBBlob = resource<hb_blob_t , decltype(hb_blob_destroy) , hb_blob_d
|
||||
using HBFace = resource<hb_face_t , decltype(hb_face_destroy) , hb_face_destroy >;
|
||||
using HBFont = resource<hb_font_t , decltype(hb_font_destroy) , hb_font_destroy >;
|
||||
using HBBuffer = resource<hb_buffer_t , decltype(hb_buffer_destroy), hb_buffer_destroy>;
|
||||
using ICUBiDi = resource<UBiDi , decltype(ubidi_close) , ubidi_close >;
|
||||
using ICUBrk = resource<UBreakIterator, decltype(ubrk_close) , ubrk_close >;
|
||||
using ICUUText = resource<UText , decltype(utext_close) , utext_close >;
|
||||
|
||||
using SkUnicodeBidi = std::unique_ptr<SkBidiIterator>;
|
||||
|
||||
hb_position_t skhb_position(SkScalar value) {
|
||||
// Treat HarfBuzz hb_position_t as 16.16 fixed-point.
|
||||
constexpr int kHbPosition1 = 1 << 16;
|
||||
@ -343,25 +344,26 @@ static inline SkUnichar utf8_next(const char** ptr, const char* end) {
|
||||
return val < 0 ? 0xFFFD : val;
|
||||
}
|
||||
|
||||
class IcuBiDiRunIterator final : public SkShaper::BiDiRunIterator {
|
||||
class SkUnicodeBidiRunIterator final : public SkShaper::BiDiRunIterator {
|
||||
public:
|
||||
IcuBiDiRunIterator(const char* utf8, const char* end, ICUBiDi bidi)
|
||||
SkUnicodeBidiRunIterator(const char* utf8, const char* end, SkUnicodeBidi bidi)
|
||||
: fBidi(std::move(bidi))
|
||||
, fEndOfCurrentRun(utf8)
|
||||
, fBegin(utf8)
|
||||
, fEnd(end)
|
||||
, fUTF16LogicalPosition(0)
|
||||
, fLevel(UBIDI_DEFAULT_LTR)
|
||||
, fLevel(SkBidiIterator::kLTR)
|
||||
{}
|
||||
|
||||
void consume() override {
|
||||
SkASSERT(fUTF16LogicalPosition < ubidi_getLength(fBidi.get()));
|
||||
int32_t endPosition = ubidi_getLength(fBidi.get());
|
||||
fLevel = ubidi_getLevelAt(fBidi.get(), fUTF16LogicalPosition);
|
||||
SkASSERT(fUTF16LogicalPosition < fBidi->getLength());
|
||||
int32_t endPosition = fBidi->getLength();
|
||||
fLevel = fBidi->getLevelAt(fUTF16LogicalPosition);
|
||||
SkUnichar u = utf8_next(&fEndOfCurrentRun, fEnd);
|
||||
fUTF16LogicalPosition += SkUTF::ToUTF16(u);
|
||||
UBiDiLevel level;
|
||||
SkBidiIterator::Level level;
|
||||
while (fUTF16LogicalPosition < endPosition) {
|
||||
level = ubidi_getLevelAt(fBidi.get(), fUTF16LogicalPosition);
|
||||
level = fBidi->getLevelAt(fUTF16LogicalPosition);
|
||||
if (level != fLevel) {
|
||||
break;
|
||||
}
|
||||
@ -374,19 +376,18 @@ public:
|
||||
return fEndOfCurrentRun - fBegin;
|
||||
}
|
||||
bool atEnd() const override {
|
||||
return fUTF16LogicalPosition == ubidi_getLength(fBidi.get());
|
||||
return fUTF16LogicalPosition == fBidi->getLength();
|
||||
}
|
||||
|
||||
UBiDiLevel currentLevel() const override {
|
||||
SkBidiIterator::Level currentLevel() const override {
|
||||
return fLevel;
|
||||
}
|
||||
private:
|
||||
ICUBiDi fBidi;
|
||||
SkUnicodeBidi fBidi;
|
||||
char const * fEndOfCurrentRun;
|
||||
char const * const fBegin;
|
||||
char const * const fEnd;
|
||||
int32_t fUTF16LogicalPosition;
|
||||
UBiDiLevel fLevel;
|
||||
SkBidiIterator::Level fLevel;
|
||||
};
|
||||
|
||||
class HbIcuScriptRunIterator final : public SkShaper::ScriptRunIterator {
|
||||
@ -509,7 +510,7 @@ struct ShapedGlyph {
|
||||
bool fUnsafeToBreak;
|
||||
};
|
||||
struct ShapedRun {
|
||||
ShapedRun(SkShaper::RunHandler::Range utf8Range, const SkFont& font, UBiDiLevel level,
|
||||
ShapedRun(SkShaper::RunHandler::Range utf8Range, const SkFont& font, SkBidiIterator::Level level,
|
||||
std::unique_ptr<ShapedGlyph[]> glyphs, size_t numGlyphs, SkVector advance = {0, 0})
|
||||
: fUtf8Range(utf8Range), fFont(font), fLevel(level)
|
||||
, fGlyphs(std::move(glyphs)), fNumGlyphs(numGlyphs), fAdvance(advance)
|
||||
@ -517,7 +518,7 @@ struct ShapedRun {
|
||||
|
||||
SkShaper::RunHandler::Range fUtf8Range;
|
||||
SkFont fFont;
|
||||
UBiDiLevel fLevel;
|
||||
SkBidiIterator::Level fLevel;
|
||||
std::unique_ptr<ShapedGlyph[]> fGlyphs;
|
||||
size_t fNumGlyphs;
|
||||
SkVector fAdvance;
|
||||
@ -527,7 +528,7 @@ struct ShapedLine {
|
||||
SkVector fAdvance = { 0, 0 };
|
||||
};
|
||||
|
||||
constexpr bool is_LTR(UBiDiLevel level) {
|
||||
constexpr bool is_LTR(SkBidiIterator::Level level) {
|
||||
return (level & 1) == 0;
|
||||
}
|
||||
|
||||
@ -565,12 +566,12 @@ void emit(const ShapedLine& line, SkShaper::RunHandler* handler) {
|
||||
handler->beginLine();
|
||||
|
||||
int numRuns = line.runs.size();
|
||||
SkAutoSTMalloc<4, UBiDiLevel> runLevels(numRuns);
|
||||
SkAutoSTMalloc<4, SkBidiIterator::Level> runLevels(numRuns);
|
||||
for (int i = 0; i < numRuns; ++i) {
|
||||
runLevels[i] = line.runs[i].fLevel;
|
||||
}
|
||||
SkAutoSTMalloc<4, int32_t> logicalFromVisual(numRuns);
|
||||
ubidi_reorderVisual(runLevels, numRuns, logicalFromVisual);
|
||||
SkBidiIterator::ReorderVisual(runLevels, numRuns, logicalFromVisual);
|
||||
|
||||
for (int i = 0; i < numRuns; ++i) {
|
||||
int logicalIndex = logicalFromVisual[i];
|
||||
@ -667,6 +668,7 @@ protected:
|
||||
const FontRunIterator&,
|
||||
const Feature*, size_t featuresSize) const;
|
||||
private:
|
||||
std::unique_ptr<SkUnicode> fUnicode = SkUnicode::Make();
|
||||
const sk_sp<SkFontMgr> fFontMgr;
|
||||
HBBuffer fBuffer;
|
||||
hb_language_t fUndefinedLanguage;
|
||||
@ -804,9 +806,12 @@ void ShaperHarfBuzz::shape(const char* utf8, size_t utf8Bytes,
|
||||
SkScalar width,
|
||||
RunHandler* handler) const
|
||||
{
|
||||
UBiDiLevel defaultLevel = leftToRight ? UBIDI_DEFAULT_LTR : UBIDI_DEFAULT_RTL;
|
||||
SkBidiIterator::Level defaultLevel = leftToRight ? SkBidiIterator::kLTR : SkBidiIterator::kRTL;
|
||||
std::unique_ptr<BiDiRunIterator> bidi(MakeSkUnicodeBidiRunIterator(fUnicode.get(),
|
||||
utf8,
|
||||
utf8Bytes,
|
||||
defaultLevel));
|
||||
|
||||
std::unique_ptr<BiDiRunIterator> bidi(MakeIcuBiDiRunIterator(utf8, utf8Bytes, defaultLevel));
|
||||
if (!bidi) {
|
||||
return;
|
||||
}
|
||||
@ -1182,12 +1187,12 @@ void ShapeThenWrap::wrap(char const * const utf8, size_t utf8Bytes,
|
||||
}
|
||||
|
||||
int numRuns = current.fRunIndex - previousBreak.fRunIndex + 1;
|
||||
SkAutoSTMalloc<4, UBiDiLevel> runLevels(numRuns);
|
||||
SkAutoSTMalloc<4, SkBidiIterator::Level> runLevels(numRuns);
|
||||
for (int i = 0; i < numRuns; ++i) {
|
||||
runLevels[i] = runs[previousBreak.fRunIndex + i].fLevel;
|
||||
}
|
||||
SkAutoSTMalloc<4, int32_t> logicalFromVisual(numRuns);
|
||||
ubidi_reorderVisual(runLevels, numRuns, logicalFromVisual);
|
||||
SkBidiIterator::ReorderVisual(runLevels, numRuns, logicalFromVisual);
|
||||
|
||||
// step through the runs in reverse visual order and the glyphs in reverse logical order
|
||||
// until a visible glyph is found and force them to the end of the visual line.
|
||||
@ -1438,6 +1443,17 @@ ShapedRun ShaperHarfBuzz::shape(char const * const utf8,
|
||||
|
||||
std::unique_ptr<SkShaper::BiDiRunIterator>
|
||||
SkShaper::MakeIcuBiDiRunIterator(const char* utf8, size_t utf8Bytes, uint8_t bidiLevel) {
|
||||
auto unicode = SkUnicode::Make();
|
||||
std::unique_ptr<SkShaper::BiDiRunIterator> bidi =
|
||||
SkShaper::MakeSkUnicodeBidiRunIterator(unicode.get(),
|
||||
utf8,
|
||||
utf8Bytes,
|
||||
bidiLevel);
|
||||
return bidi;
|
||||
}
|
||||
|
||||
std::unique_ptr<SkShaper::BiDiRunIterator>
|
||||
SkShaper::MakeSkUnicodeBidiRunIterator(SkUnicode* unicode, const char* utf8, size_t utf8Bytes, uint8_t bidiLevel) {
|
||||
// ubidi only accepts utf16 (though internally it basically works on utf32 chars).
|
||||
// We want an ubidi_setPara(UBiDi*, UText*, UBiDiLevel, UBiDiLevel*, UErrorCode*);
|
||||
if (!SkTFitsIn<int32_t>(utf8Bytes)) {
|
||||
@ -1445,35 +1461,23 @@ SkShaper::MakeIcuBiDiRunIterator(const char* utf8, size_t utf8Bytes, uint8_t bid
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
|
||||
// Getting the length like this seems to always set U_BUFFER_OVERFLOW_ERROR
|
||||
int32_t utf16Units;
|
||||
u_strFromUTF8(nullptr, 0, &utf16Units, utf8, utf8Bytes, &status);
|
||||
status = U_ZERO_ERROR;
|
||||
std::unique_ptr<UChar[]> utf16(new UChar[utf16Units]);
|
||||
u_strFromUTF8(utf16.get(), utf16Units, nullptr, utf8, utf8Bytes, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
SkDEBUGF("Invalid utf8 input: %s", u_errorName(status));
|
||||
int32_t utf16Units = SkUTF::UTF8ToUTF16(nullptr, 0, utf8, utf8Bytes);
|
||||
if (utf16Units < 0) {
|
||||
SkDEBUGF("Invalid utf8 input\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ICUBiDi bidi(ubidi_openSized(utf16Units, 0, &status));
|
||||
if (U_FAILURE(status)) {
|
||||
SkDEBUGF("Bidi error: %s", u_errorName(status));
|
||||
return nullptr;
|
||||
}
|
||||
SkASSERT(bidi);
|
||||
std::unique_ptr<uint16_t[]> utf16(new uint16_t[utf16Units]);
|
||||
(void)SkUTF::UTF8ToUTF16(utf16.get(), utf16Units, utf8, utf8Bytes);
|
||||
|
||||
// The required lifetime of utf16 isn't well documented.
|
||||
// It appears it isn't used after ubidi_setPara except through ubidi_getText.
|
||||
ubidi_setPara(bidi.get(), utf16.get(), utf16Units, bidiLevel, nullptr, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
SkDEBUGF("Bidi error: %s", u_errorName(status));
|
||||
auto bidiDir = (bidiLevel % 2 == 0) ? SkBidiIterator::kLTR : SkBidiIterator::kRTL;
|
||||
SkUnicodeBidi bidi = unicode->makeBidiIterator(utf16.get(), utf16Units, bidiDir);
|
||||
if (!bidi) {
|
||||
SkDEBUGF("Bidi error\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_unique<IcuBiDiRunIterator>(utf8, utf8 + utf8Bytes, std::move(bidi));
|
||||
return std::make_unique<SkUnicodeBidiRunIterator>(utf8, utf8 + utf8Bytes, std::move(bidi));
|
||||
}
|
||||
|
||||
std::unique_ptr<SkShaper::ScriptRunIterator>
|
||||
|
@ -31,46 +31,25 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace skia {
|
||||
|
||||
enum class UtfFormat {
|
||||
kUTF8,
|
||||
kUTF16
|
||||
};
|
||||
// Bidi
|
||||
typedef size_t Position;
|
||||
typedef uint8_t BidiLevel;
|
||||
enum class Direction {
|
||||
kLTR,
|
||||
kRTL,
|
||||
};
|
||||
struct BidiRegion {
|
||||
BidiRegion(Position start, Position end, BidiLevel level)
|
||||
: start(start), end(end), level(level) { }
|
||||
Position start;
|
||||
Position end;
|
||||
BidiLevel level;
|
||||
};
|
||||
// LineBreaks
|
||||
enum class LineBreakType {
|
||||
kSoftLineBreak,
|
||||
kHardLineBreak
|
||||
};
|
||||
struct LineBreakBefore {
|
||||
LineBreakBefore(Position pos, LineBreakType breakType)
|
||||
: pos(pos), breakType(breakType) { }
|
||||
Position pos;
|
||||
LineBreakType breakType;
|
||||
};
|
||||
// Other breaks
|
||||
enum class UBreakType {
|
||||
kWords,
|
||||
kGraphemes,
|
||||
kLines
|
||||
};
|
||||
struct Range {
|
||||
Position start;
|
||||
Position end;
|
||||
class SKUNICODE_API SkBidiIterator {
|
||||
public:
|
||||
typedef int32_t Position;
|
||||
typedef uint8_t Level;
|
||||
struct Region {
|
||||
Region(Position start, Position end, Level level)
|
||||
: start(start), end(end), level(level) { }
|
||||
Position start;
|
||||
Position end;
|
||||
Level level;
|
||||
};
|
||||
enum Direction {
|
||||
kLTR,
|
||||
kRTL,
|
||||
};
|
||||
virtual ~SkBidiIterator() {}
|
||||
virtual Position getLength() = 0;
|
||||
virtual Level getLevelAt(Position) = 0;
|
||||
static void ReorderVisual(const Level runLevels[], int levelsCount, int32_t logicalFromVisual[]);
|
||||
};
|
||||
|
||||
class SKUNICODE_API SkUnicode {
|
||||
@ -78,10 +57,47 @@ class SKUNICODE_API SkUnicode {
|
||||
typedef uint32_t ScriptID;
|
||||
typedef uint32_t CombiningClass;
|
||||
typedef uint32_t GeneralCategory;
|
||||
enum class TextDirection {
|
||||
kLTR,
|
||||
kRTL,
|
||||
};
|
||||
typedef size_t Position;
|
||||
typedef uint8_t BidiLevel;
|
||||
struct BidiRegion {
|
||||
BidiRegion(Position start, Position end, BidiLevel level)
|
||||
: start(start), end(end), level(level) { }
|
||||
Position start;
|
||||
Position end;
|
||||
BidiLevel level;
|
||||
};
|
||||
enum class LineBreakType {
|
||||
kSoftLineBreak,
|
||||
kHardLineBreak
|
||||
};
|
||||
|
||||
enum class UBreakType {
|
||||
kWords,
|
||||
kGraphemes,
|
||||
kLines
|
||||
};
|
||||
struct LineBreakBefore {
|
||||
LineBreakBefore(Position pos, LineBreakType breakType)
|
||||
: pos(pos), breakType(breakType) { }
|
||||
Position pos;
|
||||
LineBreakType breakType;
|
||||
};
|
||||
|
||||
virtual ~SkUnicode() = default;
|
||||
|
||||
// Iterators (used in SkShaper)
|
||||
virtual std::unique_ptr<SkBidiIterator> makeBidiIterator
|
||||
(const uint16_t text[], int count, SkBidiIterator::Direction) = 0;
|
||||
virtual std::unique_ptr<SkBidiIterator> makeBidiIterator
|
||||
(const char text[], int count, SkBidiIterator::Direction) = 0;
|
||||
|
||||
// High level methods (that we actually use somewhere=SkParagraph)
|
||||
virtual bool getBidiRegions
|
||||
(const char utf8[], int utf8Units, Direction dir, std::vector<BidiRegion>* results) = 0;
|
||||
(const char utf8[], int utf8Units, TextDirection dir, std::vector<BidiRegion>* results) = 0;
|
||||
virtual bool getLineBreaks
|
||||
(const char utf8[], int utf8Units, std::vector<LineBreakBefore>* results) = 0;
|
||||
virtual bool getWords
|
||||
@ -96,6 +112,4 @@ class SKUNICODE_API SkUnicode {
|
||||
static std::unique_ptr<SkUnicode> Make();
|
||||
};
|
||||
|
||||
} // namespace skia
|
||||
|
||||
#endif // SkUnicode_DEFINED
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
using ICUBiDi = std::unique_ptr<UBiDi, SkFunctionWrapper<decltype(ubidi_close), ubidi_close>>;
|
||||
using SkUnicodeBidi = std::unique_ptr<UBiDi, SkFunctionWrapper<decltype(ubidi_close), ubidi_close>>;
|
||||
using ICUUText = std::unique_ptr<UText, SkFunctionWrapper<decltype(utext_close), utext_close>>;
|
||||
using ICUBreakIterator = std::unique_ptr<UBreakIterator, SkFunctionWrapper<decltype(ubrk_close), ubrk_close>>;
|
||||
|
||||
@ -25,7 +25,92 @@ static inline SkUnichar utf8_next(const char** ptr, const char* end) {
|
||||
return val < 0 ? 0xFFFD : val;
|
||||
}
|
||||
|
||||
namespace skia {
|
||||
class SkBidiIterator_icu : public SkBidiIterator {
|
||||
SkUnicodeBidi fBidi;
|
||||
public:
|
||||
explicit SkBidiIterator_icu(SkUnicodeBidi bidi) : fBidi(std::move(bidi)) {}
|
||||
Position getLength() override { return ubidi_getLength(fBidi.get()); }
|
||||
Level getLevelAt(Position pos) override { return ubidi_getLevelAt(fBidi.get(), pos); }
|
||||
|
||||
static std::unique_ptr<SkBidiIterator> makeBidiIterator(const uint16_t utf16[], int utf16Units, Direction dir) {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
SkUnicodeBidi bidi(ubidi_openSized(utf16Units, 0, &status));
|
||||
if (U_FAILURE(status)) {
|
||||
SkDEBUGF("Bidi error: %s", u_errorName(status));
|
||||
return nullptr;
|
||||
}
|
||||
SkASSERT(bidi);
|
||||
uint8_t bidiLevel = (dir == SkBidiIterator::kLTR) ? UBIDI_LTR : UBIDI_RTL;
|
||||
// The required lifetime of utf16 isn't well documented.
|
||||
// It appears it isn't used after ubidi_setPara except through ubidi_getText.
|
||||
ubidi_setPara(bidi.get(), (const UChar*)utf16, utf16Units, bidiLevel, nullptr, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
SkDEBUGF("Bidi error: %s", u_errorName(status));
|
||||
return nullptr;
|
||||
}
|
||||
return std::unique_ptr<SkBidiIterator>(new SkBidiIterator_icu(std::move(bidi)));
|
||||
}
|
||||
|
||||
// ICU bidi iterator works with utf16 but clients (Flutter for instance) may work with utf8
|
||||
// This method allows the clients not to think about all these details
|
||||
static std::unique_ptr<SkBidiIterator> makeBidiIterator(const char utf8[], int utf8Units, Direction dir) {
|
||||
// Convert utf8 into utf16 since ubidi only accepts utf16
|
||||
if (!SkTFitsIn<int32_t>(utf8Units)) {
|
||||
SkDEBUGF("Bidi error: text too long");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Getting the length like this seems to always set U_BUFFER_OVERFLOW_ERROR
|
||||
int utf16Units = SkUTF::UTF8ToUTF16(nullptr, 0, utf8, utf8Units);
|
||||
if (utf16Units < 0) {
|
||||
SkDEBUGF("Bidi error: Invalid utf8 input");
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<uint16_t[]> utf16(new uint16_t[utf16Units]);
|
||||
SkDEBUGCODE(int dstLen =) SkUTF::UTF8ToUTF16(utf16.get(), utf16Units, utf8, utf8Units);
|
||||
SkASSERT(dstLen == utf16Units);
|
||||
|
||||
return makeBidiIterator(utf16.get(), utf16Units, dir);
|
||||
}
|
||||
|
||||
// This method returns the final results only: a list of bidi regions
|
||||
// (this is all SkParagraph really needs; SkShaper however uses the iterator itself)
|
||||
static std::vector<Region> getBidiRegions(const char utf8[], int utf8Units, Direction dir) {
|
||||
|
||||
auto bidiIterator = makeBidiIterator(utf8, utf8Units, dir);
|
||||
std::vector<Region> bidiRegions;
|
||||
const char* start8 = utf8;
|
||||
const char* end8 = utf8 + utf8Units;
|
||||
SkBidiIterator::Level currentLevel = 0;
|
||||
|
||||
Position pos8 = 0;
|
||||
Position pos16 = 0;
|
||||
Position end16 = bidiIterator->getLength();
|
||||
while (pos16 < end16) {
|
||||
auto level = bidiIterator->getLevelAt(pos16);
|
||||
if (pos16 == 0) {
|
||||
currentLevel = level;
|
||||
} else if (level != currentLevel) {
|
||||
auto end = start8 - utf8;
|
||||
bidiRegions.emplace_back(pos8, end, currentLevel);
|
||||
currentLevel = level;
|
||||
pos8 = end;
|
||||
}
|
||||
SkUnichar u = utf8_next(&start8, end8);
|
||||
pos16 += SkUTF::ToUTF16(u);
|
||||
}
|
||||
auto end = start8 - utf8;
|
||||
if (end != pos8) {
|
||||
bidiRegions.emplace_back(pos8, end, currentLevel);
|
||||
}
|
||||
return bidiRegions;
|
||||
}
|
||||
};
|
||||
|
||||
void SkBidiIterator::ReorderVisual(const Level runLevels[], int levelsCount,
|
||||
int32_t logicalFromVisual[]) {
|
||||
ubidi_reorderVisual(runLevels, levelsCount, logicalFromVisual);
|
||||
}
|
||||
|
||||
class SkUnicode_icu : public SkUnicode {
|
||||
|
||||
@ -52,7 +137,7 @@ class SkUnicode_icu : public SkUnicode {
|
||||
return utf16Units;
|
||||
}
|
||||
|
||||
static bool extractBidi(const char utf8[], int utf8Units, Direction dir, std::vector<BidiRegion>* bidiRegions) {
|
||||
static bool extractBidi(const char utf8[], int utf8Units, TextDirection dir, std::vector<BidiRegion>* bidiRegions) {
|
||||
|
||||
// Convert to UTF16 since for now bidi iterator only operates on utf16
|
||||
std::unique_ptr<uint16_t[]> utf16;
|
||||
@ -63,13 +148,13 @@ class SkUnicode_icu : public SkUnicode {
|
||||
|
||||
// Create bidi iterator
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
ICUBiDi bidi(ubidi_openSized(utf16Units, 0, &status));
|
||||
SkUnicodeBidi bidi(ubidi_openSized(utf16Units, 0, &status));
|
||||
if (U_FAILURE(status)) {
|
||||
SkDEBUGF("Bidi error: %s", u_errorName(status));
|
||||
return false;
|
||||
}
|
||||
SkASSERT(bidi);
|
||||
uint8_t bidiLevel = (dir == Direction::kLTR) ? UBIDI_LTR : UBIDI_RTL;
|
||||
uint8_t bidiLevel = (dir == TextDirection::kLTR) ? UBIDI_LTR : UBIDI_RTL;
|
||||
// The required lifetime of utf16 isn't well documented.
|
||||
// It appears it isn't used after ubidi_setPara except through ubidi_getText.
|
||||
ubidi_setPara(bidi.get(), (const UChar*)utf16.get(), utf16Units, bidiLevel, nullptr, &status);
|
||||
@ -193,8 +278,16 @@ class SkUnicode_icu : public SkUnicode {
|
||||
|
||||
public:
|
||||
~SkUnicode_icu() override { }
|
||||
std::unique_ptr<SkBidiIterator> makeBidiIterator(const uint16_t text[], int count,
|
||||
SkBidiIterator::Direction dir) override {
|
||||
return SkBidiIterator_icu::makeBidiIterator(text, count, dir);
|
||||
}
|
||||
std::unique_ptr<SkBidiIterator> makeBidiIterator(const char text[], int count,
|
||||
SkBidiIterator::Direction dir) override {
|
||||
return SkBidiIterator_icu::makeBidiIterator(text, count, dir);
|
||||
}
|
||||
|
||||
bool getBidiRegions(const char utf8[], int utf8Units, Direction dir, std::vector<BidiRegion>* results) override {
|
||||
bool getBidiRegions(const char utf8[], int utf8Units, TextDirection dir, std::vector<BidiRegion>* results) override {
|
||||
return extractBidi(utf8, utf8Units, dir, results);
|
||||
}
|
||||
|
||||
@ -238,6 +331,3 @@ public:
|
||||
};
|
||||
|
||||
std::unique_ptr<SkUnicode> SkUnicode::Make() { return std::make_unique<SkUnicode_icu>(); }
|
||||
|
||||
} // namespace skia
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user