Hal Canary | 1521c8a | 2018-03-28 09:51:00 -0700 | [diff] [blame] | 1 | /* |
| 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 Canary | c640d0d | 2018-06-13 09:59:02 -0400 | [diff] [blame] | 10 | #include "SkTo.h" |
Hal Canary | 1521c8a | 2018-03-28 09:51:00 -0700 | [diff] [blame] | 11 | #include "SkUtils.h" |
| 12 | |
| 13 | static 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 | |
| 27 | SkClusterator::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 | |
| 114 | SkClusterator::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 | |