blob: a80146fd9b556256e22a7ad643b4724d596c688b [file]
/*
* Copyright 2025 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <memory>
#include <utility>
#include "include/core/SkTypes.h"
#include "include/private/SkAssert.h"
#include "modules/skottie/include/TextShaper.h"
#include "modules/skunicode/include/SkUnicode.h"
namespace {
// Performs iterator "intersection", i.e. it only returns break positions matching both iterators.
class IntersectingBreakIterator final : public SkBreakIterator {
public:
IntersectingBreakIterator(std::unique_ptr<SkBreakIterator> a,
std::unique_ptr<SkBreakIterator> b)
: fA(std::move(a))
, fB(std::move(b))
{
SkASSERT(fA);
SkASSERT(fB);
}
Position first() override {
fCurrent = fA->first();
SkAssertResult(fB->first() == fCurrent);
fDone = false;
return fCurrent;
}
Position current() override {
return fCurrent;
}
bool isDone() override {
SkASSERT((fA->isDone() || fB->isDone()) == fDone);
return fDone;
}
Position next() override {
SkASSERT(fDone || (fA->current() == fB->current()));
Position pos_a = fCurrent,
pos_b = fCurrent;
while (!fDone) {
if (pos_a < pos_b) {
pos_a = fA->next();
fDone = fA->isDone();
} else {
pos_b = fB->next();
fDone = fB->isDone();
}
if (pos_a == pos_b) {
break;
}
}
// At this point the positions are either valid and equal (advanced successfully),
// or one of them is negative (for the done iterator);
SkASSERT(pos_a == pos_b || fDone);
SkASSERT((pos_a < 0 || pos_b < 0) == fDone);
return fCurrent = std::min(pos_a, pos_b);
}
bool setText(const char utftext8[], int utf8Units) override {
fDone = false;
fCurrent = 0;
return fA->setText(utftext8, utf8Units) &&
fB->setText(utftext8, utf8Units);
}
bool setText(const char16_t utftext16[], int utf16Units) override {
fDone = false;
fCurrent = 0;
return fA->setText(utftext16, utf16Units) &&
fB->setText(utftext16, utf16Units);
}
Status status() override {
SkUNREACHABLE;
}
private:
const std::unique_ptr<SkBreakIterator> fA, fB;
Position fCurrent = 0;
bool fDone = false;
};
// An SkUnicode wrapper which offers stricter line break semantics (it never breaks words).
class StrictLinebreakUnicode final : public SkUnicode {
public:
explicit StrictLinebreakUnicode(sk_sp<SkUnicode> uc) : fUnicode(std::move(uc))
{
SkASSERT(fUnicode);
}
std::unique_ptr<SkBreakIterator> makeBreakIterator(const char locale[],
BreakType breakType) override {
std::unique_ptr<SkBreakIterator> brk = fUnicode->makeBreakIterator(locale, breakType);
if (!brk || breakType != BreakType::kLines) {
return brk;
}
// When requested a line break iterator, return a composite iterator which prevents
// breaking mid-word.
std::unique_ptr<SkBreakIterator> wbrk =
fUnicode->makeBreakIterator(locale, BreakType::kWords);
return wbrk
? std::make_unique<IntersectingBreakIterator>(std::move(brk), std::move(wbrk))
: std::move(brk);
}
// Proxy everything else.
SkString toUpper(const SkString& str) override { return fUnicode->toUpper(str); }
SkString toUpper(const SkString& str, const char* locale) override {
return fUnicode->toUpper(str, locale);
}
bool isControl(SkUnichar utf8) override { return fUnicode->isControl(utf8); }
bool isWhitespace(SkUnichar utf8) override { return fUnicode->isWhitespace(utf8); }
bool isSpace(SkUnichar utf8) override { return fUnicode->isSpace(utf8); }
bool isTabulation(SkUnichar utf8) override { return fUnicode->isTabulation(utf8); }
bool isHardBreak(SkUnichar utf8) override { return fUnicode->isHardBreak(utf8); }
bool isEmoji(SkUnichar utf8) override { return fUnicode->isEmoji(utf8); }
bool isEmojiComponent(SkUnichar utf8) override { return fUnicode->isEmojiComponent(utf8); }
bool isEmojiModifierBase(SkUnichar utf8) override {
return fUnicode->isEmojiModifierBase(utf8);
}
bool isEmojiModifier(SkUnichar utf8) override { return fUnicode->isEmojiModifier(utf8); }
bool isRegionalIndicator(SkUnichar utf8) override {
return fUnicode->isRegionalIndicator(utf8);
}
bool isIdeographic(SkUnichar utf8) override { return fUnicode->isIdeographic(utf8); }
std::unique_ptr<SkBidiIterator> makeBidiIterator(const uint16_t text[],
int count,
SkBidiIterator::Direction dir) override {
return fUnicode->makeBidiIterator(text, count, dir);
}
std::unique_ptr<SkBidiIterator> makeBidiIterator(const char text[],
int count,
SkBidiIterator::Direction dir) override {
return fUnicode->makeBidiIterator(text, count, dir);
}
std::unique_ptr<SkBreakIterator> makeBreakIterator(BreakType breakType) override {
return fUnicode->makeBreakIterator(breakType);
}
bool getBidiRegions(const char utf8[],
int utf8Units,
TextDirection dir,
std::vector<BidiRegion>* results) override {
return fUnicode->getBidiRegions(utf8, utf8Units, dir, results);
}
bool getWords(const char utf8[], int utf8Units, const char* locale,
std::vector<Position>* results) override {
return fUnicode->getWords(utf8, utf8Units, locale, results);
}
bool getUtf8Words(const char utf8[],
int utf8Units,
const char* locale,
std::vector<Position>* results) override {
return fUnicode->getUtf8Words(utf8, utf8Units, locale, results);
}
bool getSentences(const char utf8[],
int utf8Units,
const char* locale,
std::vector<Position>* results) override {
return fUnicode->getSentences(utf8, utf8Units, locale, results);
}
bool computeCodeUnitFlags(char utf8[], int utf8Units, bool replaceTabs,
skia_private::TArray<SkUnicode::CodeUnitFlags, true>* results) override {
return fUnicode->computeCodeUnitFlags(utf8, utf8Units, replaceTabs, results);
}
bool computeCodeUnitFlags(char16_t utf16[], int utf16Units, bool replaceTabs,
skia_private::TArray<SkUnicode::CodeUnitFlags, true>* results) override {
return fUnicode->computeCodeUnitFlags(utf16, utf16Units, replaceTabs, results);
}
void reorderVisual(const BidiLevel runLevels[],
int levelsCount,
int32_t logicalFromVisual[]) override {
return fUnicode->reorderVisual(runLevels, levelsCount, logicalFromVisual);
}
private:
const sk_sp<SkUnicode> fUnicode;
};
} // namespace
namespace skottie {
sk_sp<SkUnicode> SK_API MakeStrictLinebreakUnicode(sk_sp<SkUnicode> uc) {
return uc
? sk_make_sp<StrictLinebreakUnicode>(std::move(uc))
: nullptr;
}
std::unique_ptr<SkBreakIterator> MakeIntersectingBreakIteratorForTesting(
std::unique_ptr<SkBreakIterator> a, std::unique_ptr<SkBreakIterator> b) {
return std::make_unique<IntersectingBreakIterator>(std::move(a), std::move(b));
}
} // namespace skottie