1cb93a386Sopenharmony_ci// Copyright 2019 Google LLC. 2cb93a386Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. 3cb93a386Sopenharmony_ci 4cb93a386Sopenharmony_ci#include "modules/skplaintexteditor/src/shape.h" 5cb93a386Sopenharmony_ci 6cb93a386Sopenharmony_ci#include "include/core/SkFont.h" 7cb93a386Sopenharmony_ci#include "include/core/SkFontMetrics.h" 8cb93a386Sopenharmony_ci#include "include/core/SkPoint.h" 9cb93a386Sopenharmony_ci#include "include/core/SkRefCnt.h" 10cb93a386Sopenharmony_ci#include "include/core/SkScalar.h" 11cb93a386Sopenharmony_ci#include "include/core/SkString.h" 12cb93a386Sopenharmony_ci#include "include/core/SkTextBlob.h" 13cb93a386Sopenharmony_ci#include "include/core/SkTypes.h" 14cb93a386Sopenharmony_ci#include "include/private/SkTFitsIn.h" 15cb93a386Sopenharmony_ci#include "modules/skplaintexteditor/src/word_boundaries.h" 16cb93a386Sopenharmony_ci#include "modules/skshaper/include/SkShaper.h" 17cb93a386Sopenharmony_ci#include "src/core/SkTextBlobPriv.h" 18cb93a386Sopenharmony_ci#include "src/utils/SkUTF.h" 19cb93a386Sopenharmony_ci 20cb93a386Sopenharmony_ci#include <limits.h> 21cb93a386Sopenharmony_ci#include <string.h> 22cb93a386Sopenharmony_ci 23cb93a386Sopenharmony_ci 24cb93a386Sopenharmony_ciusing namespace SkPlainTextEditor; 25cb93a386Sopenharmony_ci 26cb93a386Sopenharmony_cinamespace { 27cb93a386Sopenharmony_ciclass RunHandler final : public SkShaper::RunHandler { 28cb93a386Sopenharmony_cipublic: 29cb93a386Sopenharmony_ci RunHandler(const char* utf8Text, size_t) : fUtf8Text(utf8Text) {} 30cb93a386Sopenharmony_ci using RunCallback = void (*)(void* context, 31cb93a386Sopenharmony_ci const char* utf8Text, 32cb93a386Sopenharmony_ci size_t utf8TextBytes, 33cb93a386Sopenharmony_ci size_t glyphCount, 34cb93a386Sopenharmony_ci const SkGlyphID* glyphs, 35cb93a386Sopenharmony_ci const SkPoint* positions, 36cb93a386Sopenharmony_ci const uint32_t* clusters, 37cb93a386Sopenharmony_ci const SkFont& font); 38cb93a386Sopenharmony_ci void setRunCallback(RunCallback f, void* context) { 39cb93a386Sopenharmony_ci fCallbackContext = context; 40cb93a386Sopenharmony_ci fCallbackFunction = f; 41cb93a386Sopenharmony_ci } 42cb93a386Sopenharmony_ci 43cb93a386Sopenharmony_ci sk_sp<SkTextBlob> makeBlob(); 44cb93a386Sopenharmony_ci SkPoint endPoint() const { return fOffset; } 45cb93a386Sopenharmony_ci SkPoint finalPosition() const { return fCurrentPosition; } 46cb93a386Sopenharmony_ci 47cb93a386Sopenharmony_ci void beginLine() override; 48cb93a386Sopenharmony_ci void runInfo(const RunInfo&) override; 49cb93a386Sopenharmony_ci void commitRunInfo() override; 50cb93a386Sopenharmony_ci SkShaper::RunHandler::Buffer runBuffer(const RunInfo&) override; 51cb93a386Sopenharmony_ci void commitRunBuffer(const RunInfo&) override; 52cb93a386Sopenharmony_ci void commitLine() override; 53cb93a386Sopenharmony_ci 54cb93a386Sopenharmony_ci const std::vector<size_t>& lineEndOffsets() const { return fLineEndOffsets; } 55cb93a386Sopenharmony_ci 56cb93a386Sopenharmony_ci SkRect finalRect(const SkFont& font) const { 57cb93a386Sopenharmony_ci if (0 == fMaxRunAscent || 0 == fMaxRunDescent) { 58cb93a386Sopenharmony_ci SkFontMetrics metrics; 59cb93a386Sopenharmony_ci font.getMetrics(&metrics); 60cb93a386Sopenharmony_ci return {fCurrentPosition.x(), 61cb93a386Sopenharmony_ci fCurrentPosition.y(), 62cb93a386Sopenharmony_ci fCurrentPosition.x() + font.getSize(), 63cb93a386Sopenharmony_ci fCurrentPosition.y() + metrics.fDescent - metrics.fAscent}; 64cb93a386Sopenharmony_ci } else { 65cb93a386Sopenharmony_ci return {fCurrentPosition.x(), 66cb93a386Sopenharmony_ci fCurrentPosition.y() + fMaxRunAscent, 67cb93a386Sopenharmony_ci fCurrentPosition.x() + font.getSize(), 68cb93a386Sopenharmony_ci fCurrentPosition.y() + fMaxRunDescent}; 69cb93a386Sopenharmony_ci } 70cb93a386Sopenharmony_ci } 71cb93a386Sopenharmony_ci 72cb93a386Sopenharmony_ci 73cb93a386Sopenharmony_ciprivate: 74cb93a386Sopenharmony_ci SkTextBlobBuilder fBuilder; 75cb93a386Sopenharmony_ci std::vector<size_t> fLineEndOffsets; 76cb93a386Sopenharmony_ci const SkGlyphID* fCurrentGlyphs = nullptr; 77cb93a386Sopenharmony_ci const SkPoint* fCurrentPoints = nullptr; 78cb93a386Sopenharmony_ci void* fCallbackContext = nullptr; 79cb93a386Sopenharmony_ci RunCallback fCallbackFunction = nullptr; 80cb93a386Sopenharmony_ci char const * const fUtf8Text; 81cb93a386Sopenharmony_ci size_t fTextOffset = 0; 82cb93a386Sopenharmony_ci uint32_t* fClusters = nullptr; 83cb93a386Sopenharmony_ci int fClusterOffset = 0; 84cb93a386Sopenharmony_ci int fGlyphCount = 0; 85cb93a386Sopenharmony_ci SkScalar fMaxRunAscent = 0; 86cb93a386Sopenharmony_ci SkScalar fMaxRunDescent = 0; 87cb93a386Sopenharmony_ci SkScalar fMaxRunLeading = 0; 88cb93a386Sopenharmony_ci SkPoint fCurrentPosition = {0, 0}; 89cb93a386Sopenharmony_ci SkPoint fOffset = {0, 0}; 90cb93a386Sopenharmony_ci}; 91cb93a386Sopenharmony_ci} // namespace 92cb93a386Sopenharmony_ci 93cb93a386Sopenharmony_civoid RunHandler::beginLine() { 94cb93a386Sopenharmony_ci fCurrentPosition = fOffset; 95cb93a386Sopenharmony_ci fMaxRunAscent = 0; 96cb93a386Sopenharmony_ci fMaxRunDescent = 0; 97cb93a386Sopenharmony_ci fMaxRunLeading = 0; 98cb93a386Sopenharmony_ci} 99cb93a386Sopenharmony_ci 100cb93a386Sopenharmony_civoid RunHandler::runInfo(const SkShaper::RunHandler::RunInfo& info) { 101cb93a386Sopenharmony_ci SkFontMetrics metrics; 102cb93a386Sopenharmony_ci info.fFont.getMetrics(&metrics); 103cb93a386Sopenharmony_ci fMaxRunAscent = std::min(fMaxRunAscent, metrics.fAscent); 104cb93a386Sopenharmony_ci fMaxRunDescent = std::max(fMaxRunDescent, metrics.fDescent); 105cb93a386Sopenharmony_ci fMaxRunLeading = std::max(fMaxRunLeading, metrics.fLeading); 106cb93a386Sopenharmony_ci} 107cb93a386Sopenharmony_ci 108cb93a386Sopenharmony_civoid RunHandler::commitRunInfo() { 109cb93a386Sopenharmony_ci fCurrentPosition.fY -= fMaxRunAscent; 110cb93a386Sopenharmony_ci} 111cb93a386Sopenharmony_ci 112cb93a386Sopenharmony_ciSkShaper::RunHandler::Buffer RunHandler::runBuffer(const RunInfo& info) { 113cb93a386Sopenharmony_ci int glyphCount = SkTFitsIn<int>(info.glyphCount) ? info.glyphCount : INT_MAX; 114cb93a386Sopenharmony_ci int utf8RangeSize = SkTFitsIn<int>(info.utf8Range.size()) ? info.utf8Range.size() : INT_MAX; 115cb93a386Sopenharmony_ci 116cb93a386Sopenharmony_ci const auto& runBuffer = fBuilder.allocRunTextPos(info.fFont, glyphCount, utf8RangeSize); 117cb93a386Sopenharmony_ci fCurrentGlyphs = runBuffer.glyphs; 118cb93a386Sopenharmony_ci fCurrentPoints = runBuffer.points(); 119cb93a386Sopenharmony_ci 120cb93a386Sopenharmony_ci if (runBuffer.utf8text && fUtf8Text) { 121cb93a386Sopenharmony_ci memcpy(runBuffer.utf8text, fUtf8Text + info.utf8Range.begin(), utf8RangeSize); 122cb93a386Sopenharmony_ci } 123cb93a386Sopenharmony_ci fClusters = runBuffer.clusters; 124cb93a386Sopenharmony_ci fGlyphCount = glyphCount; 125cb93a386Sopenharmony_ci fClusterOffset = info.utf8Range.begin(); 126cb93a386Sopenharmony_ci 127cb93a386Sopenharmony_ci return {runBuffer.glyphs, 128cb93a386Sopenharmony_ci runBuffer.points(), 129cb93a386Sopenharmony_ci nullptr, 130cb93a386Sopenharmony_ci runBuffer.clusters, 131cb93a386Sopenharmony_ci fCurrentPosition}; 132cb93a386Sopenharmony_ci} 133cb93a386Sopenharmony_ci 134cb93a386Sopenharmony_civoid RunHandler::commitRunBuffer(const RunInfo& info) { 135cb93a386Sopenharmony_ci // for (size_t i = 0; i < info.glyphCount; ++i) { 136cb93a386Sopenharmony_ci // SkASSERT(fClusters[i] >= info.utf8Range.begin()); 137cb93a386Sopenharmony_ci // // this fails for khmer example. 138cb93a386Sopenharmony_ci // SkASSERT(fClusters[i] < info.utf8Range.end()); 139cb93a386Sopenharmony_ci // } 140cb93a386Sopenharmony_ci if (fCallbackFunction) { 141cb93a386Sopenharmony_ci fCallbackFunction(fCallbackContext, 142cb93a386Sopenharmony_ci fUtf8Text, 143cb93a386Sopenharmony_ci info.utf8Range.end(), 144cb93a386Sopenharmony_ci info.glyphCount, 145cb93a386Sopenharmony_ci fCurrentGlyphs, 146cb93a386Sopenharmony_ci fCurrentPoints, 147cb93a386Sopenharmony_ci fClusters, 148cb93a386Sopenharmony_ci info.fFont); 149cb93a386Sopenharmony_ci } 150cb93a386Sopenharmony_ci SkASSERT(0 <= fClusterOffset); 151cb93a386Sopenharmony_ci for (int i = 0; i < fGlyphCount; ++i) { 152cb93a386Sopenharmony_ci SkASSERT(fClusters[i] >= (unsigned)fClusterOffset); 153cb93a386Sopenharmony_ci fClusters[i] -= fClusterOffset; 154cb93a386Sopenharmony_ci } 155cb93a386Sopenharmony_ci fCurrentPosition += info.fAdvance; 156cb93a386Sopenharmony_ci fTextOffset = std::max(fTextOffset, info.utf8Range.end()); 157cb93a386Sopenharmony_ci} 158cb93a386Sopenharmony_ci 159cb93a386Sopenharmony_civoid RunHandler::commitLine() { 160cb93a386Sopenharmony_ci if (fLineEndOffsets.empty() || fTextOffset > fLineEndOffsets.back()) { 161cb93a386Sopenharmony_ci // Ensure that fLineEndOffsets is monotonic. 162cb93a386Sopenharmony_ci fLineEndOffsets.push_back(fTextOffset); 163cb93a386Sopenharmony_ci } 164cb93a386Sopenharmony_ci fOffset += { 0, fMaxRunDescent + fMaxRunLeading - fMaxRunAscent }; 165cb93a386Sopenharmony_ci} 166cb93a386Sopenharmony_ci 167cb93a386Sopenharmony_cisk_sp<SkTextBlob> RunHandler::makeBlob() { 168cb93a386Sopenharmony_ci return fBuilder.make(); 169cb93a386Sopenharmony_ci} 170cb93a386Sopenharmony_ci 171cb93a386Sopenharmony_cistatic SkRect selection_box(const SkFontMetrics& metrics, 172cb93a386Sopenharmony_ci float advance, 173cb93a386Sopenharmony_ci SkPoint pos) { 174cb93a386Sopenharmony_ci if (fabsf(advance) < 1.0f) { 175cb93a386Sopenharmony_ci advance = copysignf(1.0f, advance); 176cb93a386Sopenharmony_ci } 177cb93a386Sopenharmony_ci return SkRect{pos.x(), 178cb93a386Sopenharmony_ci pos.y() + metrics.fAscent, 179cb93a386Sopenharmony_ci pos.x() + advance, 180cb93a386Sopenharmony_ci pos.y() + metrics.fDescent}.makeSorted(); 181cb93a386Sopenharmony_ci} 182cb93a386Sopenharmony_ci 183cb93a386Sopenharmony_cistatic void set_character_bounds(void* context, 184cb93a386Sopenharmony_ci const char* utf8Text, 185cb93a386Sopenharmony_ci size_t utf8TextBytes, 186cb93a386Sopenharmony_ci size_t glyphCount, 187cb93a386Sopenharmony_ci const SkGlyphID* glyphs, 188cb93a386Sopenharmony_ci const SkPoint* positions, 189cb93a386Sopenharmony_ci const uint32_t* clusters, 190cb93a386Sopenharmony_ci const SkFont& font) 191cb93a386Sopenharmony_ci{ 192cb93a386Sopenharmony_ci SkASSERT(context); 193cb93a386Sopenharmony_ci SkASSERT(glyphCount > 0); 194cb93a386Sopenharmony_ci SkRect* cursors = (SkRect*)context; 195cb93a386Sopenharmony_ci 196cb93a386Sopenharmony_ci SkFontMetrics metrics; 197cb93a386Sopenharmony_ci font.getMetrics(&metrics); 198cb93a386Sopenharmony_ci std::unique_ptr<float[]> advances(new float[glyphCount]); 199cb93a386Sopenharmony_ci font.getWidths(glyphs, glyphCount, advances.get()); 200cb93a386Sopenharmony_ci 201cb93a386Sopenharmony_ci // Loop over each cluster in this run. 202cb93a386Sopenharmony_ci size_t clusterStart = 0; 203cb93a386Sopenharmony_ci for (size_t glyphIndex = 0; glyphIndex < glyphCount; ++glyphIndex) { 204cb93a386Sopenharmony_ci if (glyphIndex + 1 < glyphCount // more glyphs 205cb93a386Sopenharmony_ci && clusters[glyphIndex] == clusters[glyphIndex + 1]) { 206cb93a386Sopenharmony_ci continue; // multi-glyph cluster 207cb93a386Sopenharmony_ci } 208cb93a386Sopenharmony_ci unsigned textBegin = clusters[glyphIndex]; 209cb93a386Sopenharmony_ci unsigned textEnd = utf8TextBytes; 210cb93a386Sopenharmony_ci for (size_t i = 0; i < glyphCount; ++i) { 211cb93a386Sopenharmony_ci if (clusters[i] >= textEnd) { 212cb93a386Sopenharmony_ci textEnd = clusters[i] + 1; 213cb93a386Sopenharmony_ci } 214cb93a386Sopenharmony_ci } 215cb93a386Sopenharmony_ci for (size_t i = 0; i < glyphCount; ++i) { 216cb93a386Sopenharmony_ci if (clusters[i] > textBegin && clusters[i] < textEnd) { 217cb93a386Sopenharmony_ci textEnd = clusters[i]; 218cb93a386Sopenharmony_ci if (textEnd == textBegin + 1) { break; } 219cb93a386Sopenharmony_ci } 220cb93a386Sopenharmony_ci } 221cb93a386Sopenharmony_ci SkASSERT(glyphIndex + 1 > clusterStart); 222cb93a386Sopenharmony_ci unsigned clusterGlyphCount = glyphIndex + 1 - clusterStart; 223cb93a386Sopenharmony_ci const SkPoint* clusterGlyphPositions = &positions[clusterStart]; 224cb93a386Sopenharmony_ci const float* clusterAdvances = &advances[clusterStart]; 225cb93a386Sopenharmony_ci clusterStart = glyphIndex + 1; // for next loop 226cb93a386Sopenharmony_ci 227cb93a386Sopenharmony_ci SkRect clusterBox = selection_box(metrics, clusterAdvances[0], clusterGlyphPositions[0]); 228cb93a386Sopenharmony_ci for (unsigned i = 1; i < clusterGlyphCount; ++i) { // multiple glyphs 229cb93a386Sopenharmony_ci clusterBox.join(selection_box(metrics, clusterAdvances[i], clusterGlyphPositions[i])); 230cb93a386Sopenharmony_ci } 231cb93a386Sopenharmony_ci if (textBegin + 1 == textEnd) { // single byte, fast path. 232cb93a386Sopenharmony_ci cursors[textBegin] = clusterBox; 233cb93a386Sopenharmony_ci continue; 234cb93a386Sopenharmony_ci } 235cb93a386Sopenharmony_ci int textCount = textEnd - textBegin; 236cb93a386Sopenharmony_ci int codePointCount = SkUTF::CountUTF8(utf8Text + textBegin, textCount); 237cb93a386Sopenharmony_ci if (codePointCount == 1) { // single codepoint, fast path. 238cb93a386Sopenharmony_ci cursors[textBegin] = clusterBox; 239cb93a386Sopenharmony_ci continue; 240cb93a386Sopenharmony_ci } 241cb93a386Sopenharmony_ci 242cb93a386Sopenharmony_ci float width = clusterBox.width() / codePointCount; 243cb93a386Sopenharmony_ci SkASSERT(width > 0); 244cb93a386Sopenharmony_ci const char* ptr = utf8Text + textBegin; 245cb93a386Sopenharmony_ci const char* end = utf8Text + textEnd; 246cb93a386Sopenharmony_ci float x = clusterBox.left(); 247cb93a386Sopenharmony_ci while (ptr < end) { // for each codepoint in cluster 248cb93a386Sopenharmony_ci const char* nextPtr = ptr; 249cb93a386Sopenharmony_ci SkUTF::NextUTF8(&nextPtr, end); 250cb93a386Sopenharmony_ci int firstIndex = ptr - utf8Text; 251cb93a386Sopenharmony_ci float nextX = x + width; 252cb93a386Sopenharmony_ci cursors[firstIndex] = SkRect{x, clusterBox.top(), nextX, clusterBox.bottom()}; 253cb93a386Sopenharmony_ci x = nextX; 254cb93a386Sopenharmony_ci ptr = nextPtr; 255cb93a386Sopenharmony_ci } 256cb93a386Sopenharmony_ci } 257cb93a386Sopenharmony_ci} 258cb93a386Sopenharmony_ci 259cb93a386Sopenharmony_ciShapeResult SkPlainTextEditor::Shape(const char* utf8Text, 260cb93a386Sopenharmony_ci size_t textByteLen, 261cb93a386Sopenharmony_ci const SkFont& font, 262cb93a386Sopenharmony_ci const char* locale, 263cb93a386Sopenharmony_ci float width) 264cb93a386Sopenharmony_ci{ 265cb93a386Sopenharmony_ci ShapeResult result; 266cb93a386Sopenharmony_ci if (SkUTF::CountUTF8(utf8Text, textByteLen) < 0) { 267cb93a386Sopenharmony_ci utf8Text = nullptr; 268cb93a386Sopenharmony_ci textByteLen = 0; 269cb93a386Sopenharmony_ci } 270cb93a386Sopenharmony_ci std::unique_ptr<SkShaper> shaper = SkShaper::Make(); 271cb93a386Sopenharmony_ci float height = font.getSpacing(); 272cb93a386Sopenharmony_ci RunHandler runHandler(utf8Text, textByteLen); 273cb93a386Sopenharmony_ci if (textByteLen) { 274cb93a386Sopenharmony_ci result.glyphBounds.resize(textByteLen); 275cb93a386Sopenharmony_ci for (SkRect& c : result.glyphBounds) { 276cb93a386Sopenharmony_ci c = SkRect{-FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX}; 277cb93a386Sopenharmony_ci } 278cb93a386Sopenharmony_ci runHandler.setRunCallback(set_character_bounds, result.glyphBounds.data()); 279cb93a386Sopenharmony_ci // TODO: make use of locale in shaping. 280cb93a386Sopenharmony_ci shaper->shape(utf8Text, textByteLen, font, true, width, &runHandler); 281cb93a386Sopenharmony_ci if (runHandler.lineEndOffsets().size() > 1) { 282cb93a386Sopenharmony_ci result.lineBreakOffsets = runHandler.lineEndOffsets(); 283cb93a386Sopenharmony_ci SkASSERT(result.lineBreakOffsets.size() > 0); 284cb93a386Sopenharmony_ci result.lineBreakOffsets.pop_back(); 285cb93a386Sopenharmony_ci } 286cb93a386Sopenharmony_ci height = std::max(height, runHandler.endPoint().y()); 287cb93a386Sopenharmony_ci result.blob = runHandler.makeBlob(); 288cb93a386Sopenharmony_ci } 289cb93a386Sopenharmony_ci result.glyphBounds.push_back(runHandler.finalRect(font)); 290cb93a386Sopenharmony_ci result.verticalAdvance = (int)ceilf(height); 291cb93a386Sopenharmony_ci result.wordBreaks = GetUtf8WordBoundaries(utf8Text, textByteLen, locale); 292cb93a386Sopenharmony_ci return result; 293cb93a386Sopenharmony_ci} 294