blob: 6c56eb6b45cc51127a42de2453bc2e80e2d1075b [file] [log] [blame]
Hal Canary1521c8a2018-03-28 09:51:00 -07001/*
2 * Copyright 2018 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkClusterator.h"
9
Hal Canaryc640d0d2018-06-13 09:59:02 -040010#include "SkTo.h"
Hal Canary1521c8a2018-03-28 09:51:00 -070011#include "SkUtils.h"
12
13static bool is_reversed(const uint32_t* clusters, uint32_t count) {
14 // "ReversedChars" is how PDF deals with RTL text.
15 // return true if more than one cluster and monotonicly decreasing to zero.
16 if (count < 2 || clusters[0] == 0 || clusters[count - 1] != 0) {
17 return false;
18 }
19 for (uint32_t i = 0; i + 1 < count; ++i) {
20 if (clusters[i + 1] > clusters[i]) {
21 return false;
22 }
23 }
24 return true;
25}
26
27SkClusterator::SkClusterator(const void* sourceText,
28 size_t sourceByteCount,
29 const SkPaint& paint,
30 const uint32_t* clusters,
31 uint32_t utf8TextByteLength,
32 const char* utf8Text) {
33 if (SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding()) {
34 fGlyphs = reinterpret_cast<const SkGlyphID*>(sourceText);
35 fClusters = clusters;
36 fUtf8Text = utf8Text;
37 fGlyphCount = sourceByteCount / sizeof(SkGlyphID);
38 fTextByteLength = utf8TextByteLength;
39 if (fClusters) {
40 SkASSERT(fUtf8Text && fTextByteLength > 0 && fGlyphCount > 0);
41 fReversedChars = is_reversed(fClusters, fGlyphCount);
42 } else {
43 SkASSERT(!fUtf8Text && fTextByteLength == 0);
44 }
45 return;
46 }
47
48 // If Skia is given text (not glyphs), then our fallback primitive shaping will
49 // produce a simple 1-1 cluster mapping.
50 fGlyphCount = SkToU32(paint.textToGlyphs(sourceText, sourceByteCount, nullptr));
51 fGlyphStorage.resize(fGlyphCount);
52 (void)paint.textToGlyphs(sourceText, sourceByteCount, fGlyphStorage.data());
53 fGlyphs = fGlyphStorage.data();
54 fClusterStorage.resize(fGlyphCount);
55 fClusters = fClusterStorage.data();
56
57 switch (paint.getTextEncoding()) {
58 case SkPaint::kUTF8_TextEncoding:
59 {
60 fUtf8Text = reinterpret_cast<const char*>(sourceText);
61 fTextByteLength = SkToU32(sourceByteCount);
62 const char* txtPtr = fUtf8Text;
63 for (uint32_t i = 0; i < fGlyphCount; ++i) {
64 fClusterStorage[i] = SkToU32(txtPtr - fUtf8Text);
65 txtPtr += SkUTF8_LeadByteToCount(*(const unsigned char*)txtPtr);
66 SkASSERT(txtPtr <= fUtf8Text + sourceByteCount);
67 }
68 SkASSERT(txtPtr == fUtf8Text + sourceByteCount);
69 return;
70 }
71 case SkPaint::kUTF16_TextEncoding:
72 {
73 const uint16_t* utf16ptr = reinterpret_cast<const uint16_t*>(sourceText);
74 int utf16count = SkToInt(sourceByteCount / sizeof(uint16_t));
75 fTextByteLength = SkToU32(SkUTF16_ToUTF8(utf16ptr, utf16count));
76 fUtf8textStorage.resize(fTextByteLength);
77 fUtf8Text = fUtf8textStorage.data();
78 char* txtPtr = fUtf8textStorage.data();
79 uint32_t clusterIndex = 0;
80 while (utf16ptr < (const uint16_t*)sourceText + utf16count) {
81 fClusterStorage[clusterIndex++] = SkToU32(txtPtr - fUtf8Text);
82 SkUnichar uni = SkUTF16_NextUnichar(&utf16ptr);
83 txtPtr += SkUTF8_FromUnichar(uni, txtPtr);
84 }
85 SkASSERT(clusterIndex == fGlyphCount);
86 SkASSERT(txtPtr == fUtf8textStorage.data() + fTextByteLength);
87 SkASSERT(utf16ptr == (const uint16_t*)sourceText + utf16count);
88 return;
89 }
90 case SkPaint::kUTF32_TextEncoding:
91 {
92 const SkUnichar* utf32 = reinterpret_cast<const SkUnichar*>(sourceText);
93 uint32_t utf32count = SkToU32(sourceByteCount / sizeof(SkUnichar));
94 SkASSERT(fGlyphCount == utf32count);
95 fTextByteLength = 0;
96 for (uint32_t i = 0; i < utf32count; ++i) {
97 fTextByteLength += SkToU32(SkUTF8_FromUnichar(utf32[i]));
98 }
99 fUtf8textStorage.resize(SkToSizeT(fTextByteLength));
100 fUtf8Text = fUtf8textStorage.data();
101 char* txtPtr = fUtf8textStorage.data();
102 for (uint32_t i = 0; i < utf32count; ++i) {
103 fClusterStorage[i] = SkToU32(txtPtr - fUtf8Text);
104 txtPtr += SkUTF8_FromUnichar(utf32[i], txtPtr);
105 }
106 return;
107 }
108 default:
109 SkDEBUGFAIL("");
110 break;
111 }
112}
113
114SkClusterator::Cluster SkClusterator::next() {
115 if (fCurrentGlyphIndex >= fGlyphCount) {
116 return Cluster{nullptr, 0, 0, 0};
117 }
118 if (!fClusters || !fUtf8Text) {
119 return Cluster{nullptr, 0, fCurrentGlyphIndex++, 1};
120 }
121 uint32_t clusterGlyphIndex = fCurrentGlyphIndex;
122 uint32_t cluster = fClusters[clusterGlyphIndex];
123 do {
124 ++fCurrentGlyphIndex;
125 } while (fCurrentGlyphIndex < fGlyphCount && cluster == fClusters[fCurrentGlyphIndex]);
126 uint32_t clusterGlyphCount = fCurrentGlyphIndex - clusterGlyphIndex;
127 uint32_t clusterEnd = fTextByteLength;
128 for (unsigned i = 0; i < fGlyphCount; ++i) {
129 uint32_t c = fClusters[i];
130 if (c > cluster && c < clusterEnd) {
131 clusterEnd = c;
132 }
133 }
134 uint32_t clusterLen = clusterEnd - cluster;
135 return Cluster{fUtf8Text + cluster, clusterLen, clusterGlyphIndex, clusterGlyphCount};
136}
137