Change-Id: I3d45c6c8cef0109377812de0a3aab5d457a29b86
Bugs: skia:9849, skia:9850

9849 is related to font resolution (we may have to try different fallback
fonts to resolve all codepoints)
9850 is related to finding a position inside a glyph ("ffi" is an example)

Change-Id: I3d45c6c8cef0109377812de0a3aab5d457a29b86
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/271745
Reviewed-by: Ben Wagner <bungeman@google.com>
Commit-Queue: Julia Lavrova <jlavrova@google.com>
This commit is contained in:
Julia Lavrova 2020-02-19 15:12:48 -05:00 committed by Skia Commit-Bot
parent 0de01c05b7
commit 62076977a0
4 changed files with 84 additions and 94 deletions

View File

@ -44,7 +44,7 @@ void OneLineShaper::commitRunBuffer(const RunInfo&) {
auto& front = fUnresolvedBlocks.front(); // The one we need to resolve
auto& back = fUnresolvedBlocks.back(); // The one we have from shaper
if (fUnresolvedBlocks.size() == oldUnresolvedCount + 1 &&
front.fText.width() == back.fText.width()) {
front.fText == back.fText) {
// The entire block remains unresolved!
if (front.fRun != nullptr) {
back.fRun = front.fRun;
@ -394,25 +394,38 @@ void OneLineShaper::matchResolvedFonts(const TextStyle& textStyle,
std::vector<sk_sp<SkTypeface>> typefaces = fParagraph->fFontCollection->findTypefaces(textStyle.getFontFamilies(), textStyle.getFontStyle());
for (const auto& typeface : typefaces) {
if (!visitor(typeface))
if (!visitor(typeface)) {
// Resolved everything
return;
}
}
if (fParagraph->fFontCollection->fontFallbackEnabled()) {
// Give fallback a clue
SkASSERT(!fUnresolvedBlocks.empty());
auto unresolvedRange = fUnresolvedBlocks.front().fText;
auto unresolvedText = fParagraph->text(unresolvedRange);
const char* ch = unresolvedText.begin();
SkUnichar unicode = utf8_next(&ch, unresolvedText.end());
// Some unresolved subblocks might be resolved with different fallback fonts
while (!fUnresolvedBlocks.empty()) {
auto unresolvedRange = fUnresolvedBlocks.front().fText;
auto unresolvedText = fParagraph->text(unresolvedRange);
const char* ch = unresolvedText.begin();
SkUnichar unicode = utf8_next(&ch, unresolvedText.end());
auto typeface = fParagraph->fFontCollection->defaultFallback(
unicode, textStyle.getFontStyle(), textStyle.getLocale());
auto typeface = fParagraph->fFontCollection->defaultFallback(
unicode, textStyle.getFontStyle(), textStyle.getLocale());
if (typeface != nullptr) {
if (!visitor(typeface)) {
if (typeface == nullptr) {
return;
}
if (!visitor(typeface)) {
// Resolved everything
return;
} else {
// Check if anything was resolved and stop it it was not
auto last = fUnresolvedBlocks.back();
if (unresolvedRange == last.fText) {
return;
}
}
}
}
}

View File

@ -933,6 +933,8 @@ PositionWithAffinity ParagraphImpl::getGlyphPositionAtCoordinate(SkScalar dx, Sk
auto glyphStart = context.run->positionX(found) + context.fTextShift + offsetX;
auto glyphWidth = context.run->positionX(found + 1) - context.run->positionX(found);
auto clusterIndex8 = context.run->fClusterIndexes[found];
auto clusterEnd8 = context.run->fClusterIndexes[found + 1];
TextRange clusterText (clusterIndex8, clusterEnd8);
// Find the grapheme positions in codepoints that contains the point
auto codepoint = std::lower_bound(
@ -941,21 +943,29 @@ PositionWithAffinity ParagraphImpl::getGlyphPositionAtCoordinate(SkScalar dx, Sk
[](const Codepoint& lhs,size_t rhs) -> bool { return lhs.fTextIndex < rhs; });
auto codepointIndex = codepoint - fCodePoints.begin();
auto codepoints = fGraphemes16[codepoint->fGrapheme].fCodepointRange;
CodepointRange codepoints(codepointIndex, codepointIndex);
for (codepoints.end = codepointIndex + 1; codepoints.end < fCodePoints.size(); ++codepoints.end) {
auto& cp = fCodePoints[codepoints.end];
if (cp.fTextIndex >= clusterText.end) {
break;
}
}
auto graphemeSize = codepoints.width();
// We only need to inspect one glyph (maybe not even the entire glyph)
SkScalar center;
bool insideGlyph = false;
if (graphemeSize > 1) {
auto averageCodepoint = glyphWidth / graphemeSize;
auto codepointStart = glyphStart + averageCodepoint * (codepointIndex - codepoints.start);
auto codepointEnd = codepointStart + averageCodepoint;
center = (codepointStart + codepointEnd) / 2;
auto averageCodepointWidth = glyphWidth / graphemeSize;
auto delta = dx - glyphStart;
auto insideIndex = SkScalarFloorToInt(delta / averageCodepointWidth);
insideGlyph = delta > averageCodepointWidth;
center = glyphStart + averageCodepointWidth * insideIndex + averageCodepointWidth / 2;
codepointIndex += insideIndex;
} else {
SkASSERT(graphemeSize == 1);
center = glyphStart + glyphWidth / 2;
}
if ((dx < center) == context.run->leftToRight()) {
if ((dx < center) == context.run->leftToRight() || insideGlyph) {
result = { SkToS32(codepointIndex), kDownstream };
} else {
result = { SkToS32(codepointIndex + 1), kUpstream };

View File

@ -12,6 +12,7 @@ SkUnichar utf8_next(const char** ptr, const char* end) {
SkUnichar val = SkUTF::NextUTF8(ptr, end);
return val < 0 ? 0xFFFD : val;
}
}
namespace skia {
@ -88,7 +89,7 @@ void Run::copyTo(SkTextBlobBuilder& builder, size_t pos, size_t size, SkVector r
}
}
std::tuple<bool, ClusterIndex, ClusterIndex> Run::findLimitingClusters(TextRange text, bool onlyInnerClusters) const {
std::tuple<bool, ClusterIndex, ClusterIndex> Run::findLimitingClusters(TextRange text, bool extendToClusters) const {
if (text.width() == 0) {
for (auto i = fClusterRange.start; i != fClusterRange.end; ++i) {
@ -101,19 +102,27 @@ std::tuple<bool, ClusterIndex, ClusterIndex> Run::findLimitingClusters(TextRange
}
Cluster* start = nullptr;
Cluster* end = nullptr;
if (onlyInnerClusters) {
if (extendToClusters) {
for (auto i = fClusterRange.start; i != fClusterRange.end; ++i) {
auto& cluster = fMaster->cluster(i);
if (cluster.textRange().start >= text.start && start == nullptr) {
start = &cluster;
}
if (cluster.textRange().end <= text.end) {
end = &cluster;
} else {
auto clusterRange = cluster.textRange();
if (clusterRange.end <= text.start) {
continue;
} else if (clusterRange.start >= text.end) {
break;
}
TextRange s = TextRange(std::max(clusterRange.start, text.start),
std::min(clusterRange.end, text.end));
if (s.width() > 0) {
if (start == nullptr) {
start = &cluster;
}
end = &cluster;
}
}
} else {
// TODO: Do we need to use this branch?..
for (auto i = fClusterRange.start; i != fClusterRange.end; ++i) {
auto& cluster = fMaster->cluster(i);
if (cluster.textRange().end > text.start && start == nullptr) {

View File

@ -2139,91 +2139,50 @@ protected:
void onDrawContent(SkCanvas* canvas) override {
const char* text = "PESTO";
const char* text = "ffi";
canvas->drawColor(SK_ColorWHITE);
SkPaint paint;
paint.setColor(SK_ColorRED);
paint.setStyle(SkPaint::kStroke_Style);
paint.setAntiAlias(true);
paint.setStrokeWidth(1);
auto collection = getFontCollection();
ParagraphStyle paragraph_style;
paragraph_style.setTextAlign(TextAlign::kCenter);
auto collection = getFontCollection();
ParagraphBuilderImpl builder(paragraph_style, collection);
TextStyle text_style;
text_style.setColor(SK_ColorBLACK);
text_style.setFontFamilies({SkString("Roboto")});
text_style.setFontSize(48);
text_style.setFontStyle(SkFontStyle::Bold());
text_style.setLetterSpacing(3);
text_style.setFontSize(60);
builder.pushStyle(text_style);
builder.addText(text);
auto paragraph = builder.Build();
auto w = width() / 2;
paragraph->layout(w);
paragraph->layout(width());
paragraph->paint(canvas, 0, 0);
canvas->drawRect(SkRect::MakeXYWH(0, 0, width() / 2, paragraph->getHeight()), paint);
auto width = paragraph->getLongestLine();
auto height = paragraph->getHeight();
auto f1 = paragraph->getGlyphPositionAtCoordinate(width/6, height/2);
auto f2 = paragraph->getGlyphPositionAtCoordinate(width/2, height/2);
auto i = paragraph->getGlyphPositionAtCoordinate(width*5/6, height/2);
SkDebugf("%d(%s) %d(%s) %d(%s)\n",
f1.position, f1.affinity == Affinity::kUpstream ? "up" : "down",
f2.position, f2.affinity == Affinity::kUpstream ? "up" : "down",
i.position, i.affinity == Affinity::kUpstream ? "up" : "down");
auto rf1 = paragraph->getRectsForRange(0, 1, RectHeightStyle::kTight, RectWidthStyle::kTight)[0];
auto rf2 = paragraph->getRectsForRange(1, 2, RectHeightStyle::kTight, RectWidthStyle::kTight)[0];
auto rfi = paragraph->getRectsForRange(2, 3, RectHeightStyle::kTight, RectWidthStyle::kTight)[0];
SkDebugf("f1: [%f:%f] %s\n",
rf1.rect.fLeft, rf1.rect.fRight, rf1.direction == TextDirection::kRtl ? "rtl" : "ltr");
SkDebugf("f2: [%f:%f] %s\n",
rf2.rect.fLeft, rf2.rect.fRight, rf2.direction == TextDirection::kRtl ? "rtl" : "ltr");
SkDebugf("i: [%f:%f] %s\n",
rfi.rect.fLeft, rfi.rect.fRight, rfi.direction == TextDirection::kRtl ? "rtl" : "ltr");
}
private:
typedef Sample INHERITED;
};
class ParagraphView30 : public ParagraphView_Base {
protected:
SkString name() override { return SkString("Paragraph30"); }
void onDrawContent(SkCanvas* canvas) override {
/*
* text: TextSpan(
text: 'aaaa bbbb ',
style: TextStyle(fontSize: 48.0),
children: <TextSpan>[
TextSpan(text: 'cc dd', style:TextStyle(fontFamily: 'serif', fontSize: 64.0)),
],
),
textDirection: TextDirection.ltr,
textAlign: TextAlign.justify,
*/
const char* text1 = "aaaa bbbb ";
const char* text2 = "cc dd";
canvas->drawColor(SK_ColorWHITE);
ParagraphStyle paragraph_style;
paragraph_style.setTextAlign(TextAlign::kJustify);
auto collection = getFontCollection();
SkPaint red;
red.setColor(SK_ColorRED);
TextStyle text_style;
text_style.setColor(SK_ColorBLACK);
ParagraphBuilderImpl builder(paragraph_style, collection);
text_style.setFontFamilies({SkString("Roboto")});
text_style.setFontSize(48);
builder.pushStyle(text_style);
builder.addText(text1);
text_style.setFontFamilies({SkString("Google Sans")});
text_style.setFontSize(64);
builder.pushStyle(text_style);
builder.addText(text2);
auto paragraph = builder.Build();
paragraph->layout(310);
paragraph->paint(canvas, 0, 0);
}
private:
typedef Sample INHERITED;
};
//////////////////////////////////////////////////////////////////////////////
DEF_SAMPLE(return new ParagraphView1();)
@ -2254,4 +2213,3 @@ DEF_SAMPLE(return new ParagraphView26();)
DEF_SAMPLE(return new ParagraphView27();)
DEF_SAMPLE(return new ParagraphView28();)
DEF_SAMPLE(return new ParagraphView29();)
DEF_SAMPLE(return new ParagraphView30();)