Mixed languages text crashes
Extending text to grapheme edges should correct glyph range Bug: skia:10087 Change-Id: I254901aaaa40c2782d1afbd5d5390599bdd7c922 Change-Id: I1d51076656d09e4d2e35e3ddad28bfd60fc87081 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/281756 Reviewed-by: Ben Wagner <bungeman@google.com> Commit-Queue: Julia Lavrova <jlavrova@google.com>
This commit is contained in:
parent
7d08f4b797
commit
cd2d4e4835
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 Google LLC.
|
||||
// Copyright 2019 Google LLC.
|
||||
#ifndef ParagraphCache_DEFINED
|
||||
#define ParagraphCache_DEFINED
|
||||
|
||||
|
@ -277,7 +277,6 @@ typedef size_t TextIndex;
|
||||
typedef SkRange<size_t> TextRange;
|
||||
const SkRange<size_t> EMPTY_TEXT = EMPTY_RANGE;
|
||||
|
||||
|
||||
struct Block {
|
||||
Block() : fRange(EMPTY_RANGE), fStyle() { }
|
||||
Block(size_t start, size_t end, const TextStyle& style) : fRange(start, end), fStyle(style) {}
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include "include/core/SkFontMgr.h"
|
||||
#include "include/core/SkStream.h"
|
||||
#include "include/core/SkString.h"
|
||||
#include "src/core/SkFontDescriptor.h"
|
||||
|
||||
namespace skia {
|
||||
namespace textlayout {
|
||||
|
@ -101,14 +101,15 @@ void OneLineShaper::fillGaps(size_t startingCount) {
|
||||
|
||||
auto count = fUnresolvedBlocks.size();
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
auto unresolved = fUnresolvedBlocks.front();
|
||||
auto front = fUnresolvedBlocks.front();
|
||||
fUnresolvedBlocks.pop();
|
||||
fUnresolvedBlocks.push(unresolved);
|
||||
fUnresolvedBlocks.push(front);
|
||||
if (i < startingCount) {
|
||||
// Skip the first ones
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& unresolved = fUnresolvedBlocks.back();
|
||||
TextRange resolvedText(resolvedTextStart, fCurrentRun->leftToRight() ? unresolved.fText.start : unresolved.fText.end);
|
||||
if (resolvedText.width() > 0) {
|
||||
if (!fCurrentRun->leftToRight()) {
|
||||
@ -118,7 +119,17 @@ void OneLineShaper::fillGaps(size_t startingCount) {
|
||||
GlyphRange resolvedGlyphs(resolvedGlyphsStart, unresolved.fGlyphs.start);
|
||||
RunBlock resolved(fCurrentRun, resolvedText, resolvedGlyphs, resolvedGlyphs.width());
|
||||
|
||||
fResolvedBlocks.emplace_back(resolved);
|
||||
if (resolvedGlyphs.width() == 0) {
|
||||
// Extend the unresolved block with an empty resolved
|
||||
if (unresolved.fText.end <= resolved.fText.start) {
|
||||
unresolved.fText.end = resolved.fText.end;
|
||||
}
|
||||
if (unresolved.fText.start >= resolved.fText.end) {
|
||||
unresolved.fText.start = resolved.fText.start;
|
||||
}
|
||||
} else {
|
||||
fResolvedBlocks.emplace_back(resolved);
|
||||
}
|
||||
}
|
||||
resolvedGlyphsStart = unresolved.fGlyphs.end;
|
||||
resolvedTextStart = fCurrentRun->leftToRight()
|
||||
@ -126,7 +137,7 @@ void OneLineShaper::fillGaps(size_t startingCount) {
|
||||
: unresolved.fText.start;
|
||||
}
|
||||
|
||||
TextRange resolvedText(resolvedTextStart, resolvedTextLimits.end);
|
||||
TextRange resolvedText(resolvedTextStart,resolvedTextLimits.end);
|
||||
if (resolvedText.width() > 0) {
|
||||
if (!fCurrentRun->leftToRight()) {
|
||||
std::swap(resolvedText.start, resolvedText.end);
|
||||
@ -134,7 +145,6 @@ void OneLineShaper::fillGaps(size_t startingCount) {
|
||||
|
||||
GlyphRange resolvedGlyphs(resolvedGlyphsStart, fCurrentRun->size());
|
||||
RunBlock resolved(fCurrentRun, resolvedText, resolvedGlyphs, resolvedGlyphs.width());
|
||||
|
||||
fResolvedBlocks.emplace_back(resolved);
|
||||
}
|
||||
}
|
||||
@ -191,8 +201,12 @@ void OneLineShaper::finish(TextRange blockText, SkScalar height, SkScalar& advan
|
||||
|
||||
auto runAdvance = SkVector::Make(run->posX(glyphs.end) - run->posX(glyphs.start), run->fAdvance.fY);
|
||||
const SkShaper::RunHandler::RunInfo info = {
|
||||
run->fFont, run->fBidiLevel, runAdvance, glyphs.width(),
|
||||
SkShaper::RunHandler::Range(text.start - run->fClusterStart, text.width())};
|
||||
run->fFont,
|
||||
run->fBidiLevel,
|
||||
runAdvance,
|
||||
glyphs.width(),
|
||||
SkShaper::RunHandler::Range(text.start - run->fClusterStart, text.width())
|
||||
};
|
||||
this->fParagraph->fRuns.emplace_back(
|
||||
this->fParagraph,
|
||||
info,
|
||||
@ -249,7 +263,7 @@ TextRange OneLineShaper::normalizeTextRange(GlyphRange glyphRange) {
|
||||
return TextRange(clusterIndex(glyphRange.end - 1),
|
||||
glyphRange.start > 0
|
||||
? clusterIndex(glyphRange.start - 1)
|
||||
: fCurrentRun->fClusterStart + fCurrentRun->fTextRange.width());
|
||||
: fCurrentRun->fTextRange.end);
|
||||
}
|
||||
}
|
||||
|
||||
@ -268,20 +282,25 @@ void OneLineShaper::addUnresolvedWithRun(GlyphRange glyphRange) {
|
||||
RunBlock unresolved(fCurrentRun, clusteredText(glyphRange), glyphRange, 0);
|
||||
if (unresolved.fGlyphs.width() == fCurrentRun->size()) {
|
||||
SkASSERT(unresolved.fText.width() == fCurrentRun->fTextRange.width());
|
||||
} else if (fUnresolvedBlocks.size() > 1) {
|
||||
} else if (fUnresolvedBlocks.size() > 0) {
|
||||
auto& lastUnresolved = fUnresolvedBlocks.back();
|
||||
if (lastUnresolved.fRun != nullptr &&
|
||||
lastUnresolved.fRun->fIndex == fCurrentRun->fIndex) {
|
||||
|
||||
if (lastUnresolved.fText.end == unresolved.fText.start) {
|
||||
// Two pieces next to each other - can join them
|
||||
lastUnresolved.fText.end = unresolved.fText.end;
|
||||
lastUnresolved.fGlyphs.end = glyphRange.end;
|
||||
// Two pieces next to each other - can join them
|
||||
lastUnresolved.fText.end = unresolved.fText.end;
|
||||
lastUnresolved.fGlyphs.end = glyphRange.end;
|
||||
return;
|
||||
} else if(lastUnresolved.fText == unresolved.fText) {
|
||||
// Nothing was resolved; ignore it
|
||||
return;
|
||||
} else if (lastUnresolved.fText.contains(unresolved.fText)) {
|
||||
// We get here for the very first unresolved piece
|
||||
return;
|
||||
} else if (lastUnresolved.fText.intersects(unresolved.fText)) {
|
||||
// Few pieces of the same unresolved text block can ignore the second one
|
||||
lastUnresolved.fGlyphs.start =
|
||||
std::min(lastUnresolved.fGlyphs.start, glyphRange.start);
|
||||
lastUnresolved.fGlyphs.start = std::min(lastUnresolved.fGlyphs.start, glyphRange.start);
|
||||
lastUnresolved.fGlyphs.end = std::max(lastUnresolved.fGlyphs.end, glyphRange.end);
|
||||
lastUnresolved.fText = clusteredText(lastUnresolved.fGlyphs);
|
||||
return;
|
||||
@ -630,7 +649,8 @@ bool OneLineShaper::shape() {
|
||||
return result;
|
||||
}
|
||||
|
||||
TextRange OneLineShaper::clusteredText(GlyphRange glyphs) {
|
||||
// When we extend TextRange to the grapheme edges, we also extend glyphs range
|
||||
TextRange OneLineShaper::clusteredText(GlyphRange& glyphs) {
|
||||
|
||||
enum class Dir { left, right };
|
||||
enum class Pos { inclusive, exclusive };
|
||||
@ -661,6 +681,24 @@ TextRange OneLineShaper::clusteredText(GlyphRange glyphs) {
|
||||
textRange.start = findBaseChar(textRange.start, Dir::left);
|
||||
textRange.end = findBaseChar(textRange.end, Dir::right);
|
||||
|
||||
// Correct the glyphRange in case we extended the text to the grapheme edges
|
||||
// TODO: code it without if (as a part of LTR/RTL refactoring)
|
||||
if (fCurrentRun->leftToRight()) {
|
||||
while (glyphs.start > 0 && clusterIndex(glyphs.start) > textRange.start) {
|
||||
glyphs.start--;
|
||||
}
|
||||
while (glyphs.end < fCurrentRun->size() && clusterIndex(glyphs.end) < textRange.end) {
|
||||
glyphs.end++;
|
||||
}
|
||||
} else {
|
||||
while (glyphs.start > 0 && clusterIndex(glyphs.start - 1) < textRange.end) {
|
||||
glyphs.start--;
|
||||
}
|
||||
while (glyphs.end < fCurrentRun->size() && clusterIndex(glyphs.end) > textRange.start) {
|
||||
glyphs.end++;
|
||||
}
|
||||
}
|
||||
|
||||
return { textRange.start, textRange.end };
|
||||
}
|
||||
|
||||
|
@ -12,9 +12,6 @@
|
||||
namespace skia {
|
||||
namespace textlayout {
|
||||
|
||||
typedef size_t GlyphIndex;
|
||||
typedef SkRange<GlyphIndex> GlyphRange;
|
||||
|
||||
class ParagraphImpl;
|
||||
class OneLineShaper : public SkShaper::RunHandler {
|
||||
public:
|
||||
@ -22,7 +19,8 @@ public:
|
||||
: fParagraph(paragraph)
|
||||
, fHeight(0.0f)
|
||||
, fAdvance(SkPoint::Make(0.0f, 0.0f))
|
||||
, fUnresolvedGlyphs(0) { }
|
||||
, fUnresolvedGlyphs(0)
|
||||
, fUniqueRunId(paragraph->fRuns.size()){ }
|
||||
|
||||
bool shape();
|
||||
|
||||
@ -80,19 +78,18 @@ private:
|
||||
void commitLine() override {}
|
||||
|
||||
Buffer runBuffer(const RunInfo& info) override {
|
||||
auto index = fUnresolvedBlocks.size() + fResolvedBlocks.size();
|
||||
fCurrentRun = std::make_shared<Run>(fParagraph,
|
||||
info,
|
||||
fCurrentText.start,
|
||||
fHeight,
|
||||
index,
|
||||
++fUniqueRunId,
|
||||
fAdvance.fX);
|
||||
return fCurrentRun->newRunBuffer();
|
||||
}
|
||||
|
||||
void commitRunBuffer(const RunInfo&) override;
|
||||
|
||||
TextRange clusteredText(GlyphRange glyphs);
|
||||
TextRange clusteredText(GlyphRange& glyphs);
|
||||
ClusterIndex clusterIndex(GlyphIndex glyph) {
|
||||
return fCurrentText.start + fCurrentRun->fClusterIndexes[glyph];
|
||||
}
|
||||
@ -108,6 +105,7 @@ private:
|
||||
SkScalar fHeight;
|
||||
SkVector fAdvance;
|
||||
size_t fUnresolvedGlyphs;
|
||||
size_t fUniqueRunId;
|
||||
|
||||
// TODO: Something that is not thead-safe since we don't need it
|
||||
std::shared_ptr<Run> fCurrentRun;
|
||||
|
@ -724,8 +724,13 @@ std::vector<TextBox> ParagraphImpl::getRectsForRange(unsigned start,
|
||||
{
|
||||
// TODO: this is just a patch. Make it right later (when it's clear what and how)
|
||||
trailingSpaces = clip;
|
||||
trailingSpaces.fLeft = line.width();
|
||||
clip.fRight = line.width();
|
||||
if(run->leftToRight()) {
|
||||
trailingSpaces.fLeft = line.width();
|
||||
clip.fRight = line.width();
|
||||
} else {
|
||||
trailingSpaces.fRight = 0;
|
||||
clip.fLeft = 0;
|
||||
}
|
||||
} else if (this->fParagraphStyle.getTextDirection() == TextDirection::kRtl &&
|
||||
!run->leftToRight())
|
||||
{
|
||||
|
@ -124,7 +124,7 @@ std::tuple<bool, ClusterIndex, ClusterIndex> Run::findLimitingClusters(TextRange
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// TODO: Do we need to use this branch?..
|
||||
// We need this branch when we draw styles for the part of a cluster
|
||||
for (auto i = fClusterRange.start; i != fClusterRange.end; ++i) {
|
||||
auto& cluster = fMaster->cluster(i);
|
||||
if (cluster.textRange().end > text.start && start == nullptr) {
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include "modules/skparagraph/include/TextStyle.h"
|
||||
#include "modules/skshaper/include/SkShaper.h"
|
||||
#include "src/core/SkSpan.h"
|
||||
#include "src/core/SkTraceEvent.h"
|
||||
#include <functional> // std::function
|
||||
|
||||
namespace skia {
|
||||
@ -33,6 +32,27 @@ typedef SkRange<GraphemeIndex> GraphemeRange;
|
||||
typedef size_t CodepointIndex;
|
||||
typedef SkRange<CodepointIndex> CodepointRange;
|
||||
|
||||
typedef size_t GlyphIndex;
|
||||
typedef SkRange<GlyphIndex> GlyphRange;
|
||||
|
||||
/* This is a part of future LTR/RTL refactoring
|
||||
// This is a part of a shaped text
|
||||
// Text range (a, b) can be:
|
||||
// LTR: [a:b) where a < b
|
||||
// RTL: (b:a] where a > b
|
||||
class ShapedSpan {
|
||||
public:
|
||||
ShapedSpan(Run* run, TextRange textRange, GlyphRange glyphRange)
|
||||
: fRun(run)
|
||||
, fText(textRange)
|
||||
, fGlyphs(glyphRange) { }
|
||||
|
||||
private:
|
||||
Run* fRun;
|
||||
TextRange fText;
|
||||
GlyphRange fGlyphs;
|
||||
};
|
||||
*/
|
||||
struct RunShifts {
|
||||
RunShifts() { }
|
||||
RunShifts(size_t count) { fShifts.push_back_n(count, 0.0); }
|
||||
@ -227,13 +247,9 @@ class Cluster {
|
||||
public:
|
||||
enum BreakType {
|
||||
None,
|
||||
CharacterBoundary, // not yet in use (UBRK_CHARACTER)
|
||||
WordBoundary, // calculated for all clusters (UBRK_WORD)
|
||||
WordBreakWithoutHyphen, // calculated only for hyphenated words
|
||||
WordBreakWithHyphen,
|
||||
SoftLineBreak, // calculated for all clusters (UBRK_LINE)
|
||||
GraphemeBreak, // calculated for all clusters (UBRK_CHARACTER)
|
||||
SoftLineBreak, // calculated for all clusters (UBRK_LINE & UBRK_CHARACTER)
|
||||
HardLineBreak, // calculated for all clusters (UBRK_LINE)
|
||||
GraphemeBreak,
|
||||
};
|
||||
|
||||
Cluster()
|
||||
|
@ -1,8 +1,10 @@
|
||||
// Copyright 2019 Google LLC.
|
||||
#include "modules/skparagraph/include/TypefaceFontProvider.h"
|
||||
#include <algorithm>
|
||||
#include "include/core/SkFontMgr.h"
|
||||
#include "include/core/SkString.h"
|
||||
#include "include/core/SkTypeface.h"
|
||||
#include "src/core/SkFontDescriptor.h"
|
||||
|
||||
namespace skia {
|
||||
namespace textlayout {
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Copyright 2019 Google LLC.
|
||||
#include "modules/skparagraph/include/FontCollection.h"
|
||||
#include "modules/skparagraph/include/TypefaceFontProvider.h"
|
||||
#include "src/core/SkFontDescriptor.h"
|
||||
|
||||
namespace skia {
|
||||
namespace textlayout {
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,6 +1,7 @@
|
||||
// Copyright 2019 Google LLC.
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include "include/core/SkFontMgr.h"
|
||||
#include "modules/skparagraph/include/TypefaceFontProvider.h"
|
||||
#include "modules/skparagraph/src/ParagraphBuilderImpl.h"
|
||||
#include "modules/skparagraph/src/ParagraphImpl.h"
|
||||
|
Loading…
Reference in New Issue
Block a user