blob: 3947406b853ec4191e1f09cbb870f6c1e8d3a783 [file] [log] [blame]
Ben Wagnera25fbef2017-08-30 13:56:19 -04001/*
2 * Copyright 2016 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 <hb-ot.h>
9#include <unicode/stringpiece.h>
10#include <unicode/ubidi.h>
11#include <unicode/unistr.h>
12
13#include "SkShaper.h"
14#include "SkStream.h"
15#include "SkTextBlob.h"
16#include "SkTypeface.h"
17#include "SkUtils.h"
18
19static const int FONT_SIZE_SCALE = 512;
20
21namespace {
22template <class T, void(*P)(T*)> using resource = std::unique_ptr<T, SkFunctionWrapper<void, T, P>>;
23using HBBlob = resource<hb_blob_t , hb_blob_destroy >;
24using HBFace = resource<hb_face_t , hb_face_destroy >;
25using HBFont = resource<hb_font_t , hb_font_destroy >;
26using HBBuffer = resource<hb_buffer_t, hb_buffer_destroy>;
27using ICUBiDi = resource<UBiDi , ubidi_close >;
28
29HBBlob stream_to_blob(std::unique_ptr<SkStreamAsset> asset) {
30 size_t size = asset->getLength();
31 HBBlob blob;
32 if (const void* base = asset->getMemoryBase()) {
33 blob.reset(hb_blob_create((char*)base, SkToUInt(size),
34 HB_MEMORY_MODE_READONLY, asset.release(),
35 [](void* p) { delete (SkStreamAsset*)p; }));
36 } else {
37 // SkDebugf("Extra SkStreamAsset copy\n");
38 void* ptr = size ? sk_malloc_throw(size) : nullptr;
39 asset->read(ptr, size);
40 blob.reset(hb_blob_create((char*)ptr, SkToUInt(size),
41 HB_MEMORY_MODE_READONLY, ptr, sk_free));
42 }
43 SkASSERT(blob);
44 hb_blob_make_immutable(blob.get());
45 return blob;
46}
47} // namespace
48
49struct SkShaper::Impl {
50 HBFont fHarfBuzzFont;
51 HBBuffer fBuffer;
52 sk_sp<SkTypeface> fTypeface;
53};
54
55SkShaper::SkShaper(sk_sp<SkTypeface> tf) : fImpl(new Impl) {
56 fImpl->fTypeface = tf ? std::move(tf) : SkTypeface::MakeDefault();
57 int index;
58 HBBlob blob(stream_to_blob(std::unique_ptr<SkStreamAsset>(
59 fImpl->fTypeface->openStream(&index))));
60 HBFace face(hb_face_create(blob.get(), (unsigned)index));
61 SkASSERT(face);
62 if (!face) {
63 return;
64 }
65 hb_face_set_index(face.get(), (unsigned)index);
66 hb_face_set_upem(face.get(), fImpl->fTypeface->getUnitsPerEm());
67
68 fImpl->fHarfBuzzFont.reset(hb_font_create(face.get()));
69 SkASSERT(fImpl->fHarfBuzzFont);
70 hb_font_set_scale(fImpl->fHarfBuzzFont.get(), FONT_SIZE_SCALE, FONT_SIZE_SCALE);
71 hb_ot_font_set_funcs(fImpl->fHarfBuzzFont.get());
72
73 fImpl->fBuffer.reset(hb_buffer_create());
74}
75
76SkShaper::~SkShaper() {}
77
78bool SkShaper::good() const { return fImpl->fHarfBuzzFont != nullptr; }
79
80SkScalar SkShaper::shape(SkTextBlobBuilder* builder,
81 const SkPaint& srcPaint,
82 const char* utf8text,
83 size_t textBytes,
84 bool leftToRight,
85 SkPoint point) const {
86 SkASSERT(builder);
87 UBiDiLevel bidiLevel = leftToRight ? UBIDI_DEFAULT_LTR : UBIDI_DEFAULT_RTL;
88 //hb_script_t script = ...
89 UErrorCode status = U_ZERO_ERROR;
90 double x = point.x();
91 double y = point.y();
92
93 // This function only accepts utf8.
94 // ubidi only accepts utf16 (though internally it basically works on utf32 chars).
95 // Internally, harfbuzz is all utf32, but always makes a copy.
96
97 if (!SkTFitsIn<int32_t>(textBytes)) {
98 SkDebugf("Bidi error: text too long");
99 return (SkScalar)x;
100 }
101 icu::UnicodeString utf16 = icu::UnicodeString::fromUTF8(icu::StringPiece(utf8text, textBytes));
102
103 ICUBiDi bidi(ubidi_openSized(utf16.length(), 0, &status));
104 if (U_FAILURE(status)) {
105 SkDebugf("Bidi error: %s", u_errorName(status));
106 return (SkScalar)x;
107 }
108 SkASSERT(bidi);
109
110 ubidi_setPara(bidi.get(), utf16.getBuffer(), utf16.length(), bidiLevel, nullptr, &status);
111 if (U_FAILURE(status)) {
112 SkDebugf("Bidi error: %s", u_errorName(status));
113 return (SkScalar)x;
114 }
115
116 int32_t runCount = ubidi_countRuns(bidi.get(), &status);
117 if (U_FAILURE(status)) {
118 SkDebugf("Bidi error: %s", u_errorName(status));
119 return (SkScalar)x;
120 }
121
122 for (int32_t i = 0; i < runCount; ++i) {
123 int32_t start;
124 int32_t length;
125 UBiDiDirection direction = ubidi_getVisualRun(bidi.get(), i, &start, &length);
126
127 SkPaint paint(srcPaint);
128 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
129 paint.setTypeface(fImpl->fTypeface);
130
131 hb_buffer_t* buffer = fImpl->fBuffer.get();
132 SkAutoTCallVProc<hb_buffer_t, hb_buffer_clear_contents> autoClearBuffer(buffer);
133
134 // The difficulty here is the cluster mapping.
135 // If the hb_buffer is created with utf16, clusters will be pointing to the utf16 indexes,
136 // but the SkTextBlob can only take utf8 and utf8 cluster indexes.
137 // Instead of updating each cluster index, create the hb_buffer from the utf8.
138 // TODO: this is horribly inefficient.
139 const char* utf8textStart = utf8text;
140 const UChar* utf16Start = utf16.getBuffer();
141 while (utf16Start < utf16.getBuffer() + start) {
142 SkUTF16_NextUnichar(&utf16Start);
143 SkUTF8_NextUnichar(&utf8textStart);
144 }
145 const char* utf8textEnd = utf8textStart;
146 const UChar* utf16End = utf16Start;
147 while (utf16End < utf16.getBuffer() + start + length) {
148 SkUTF16_NextUnichar(&utf16End);
149 SkUTF8_NextUnichar(&utf8textEnd);
150 }
151 size_t utf8runLength = utf8textEnd - utf8textStart;
152 if (!SkTFitsIn<int>(utf8runLength)) {
153 SkDebugf("Shaping error: utf8 too long");
154 return (SkScalar)x;
155 }
156 hb_buffer_add_utf8(buffer, utf8textStart, utf8runLength, 0, -1);
157 hb_buffer_guess_segment_properties(buffer);
158 //hb_buffer_set_script(buffer, script);
159 hb_buffer_set_direction(buffer, direction ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
160 hb_shape(fImpl->fHarfBuzzFont.get(), buffer, nullptr, 0);
161 unsigned len = hb_buffer_get_length(buffer);
162 if (len == 0) {
163 continue;
164 }
165
166 hb_glyph_info_t* info = hb_buffer_get_glyph_infos(buffer, nullptr);
167 hb_glyph_position_t* pos = hb_buffer_get_glyph_positions(buffer, nullptr);
168
169 if (!SkTFitsIn<int>(len)) {
170 SkDebugf("Shaping error: too many glyphs");
171 return (SkScalar)x;
172 }
173 auto runBuffer = builder->allocRunTextPos(paint, len, utf8runLength, SkString());
174 memcpy(runBuffer.utf8text, utf8textStart, utf8runLength);
175
176 double textSizeY = paint.getTextSize() / (double)FONT_SIZE_SCALE;
177 double textSizeX = textSizeY * paint.getTextScaleX();
178
179 for (unsigned i = 0; i < len; i++) {
180 runBuffer.glyphs[i] = info[i].codepoint;
181 runBuffer.clusters[i] = info[i].cluster;
182 reinterpret_cast<SkPoint*>(runBuffer.pos)[i] =
183 SkPoint::Make(SkDoubleToScalar(x + pos[i].x_offset * textSizeX),
184 SkDoubleToScalar(y - pos[i].y_offset * textSizeY));
185 x += pos[i].x_advance * textSizeX;
186 y += pos[i].y_advance * textSizeY;
187 }
188 }
189 return (SkScalar)x;
190}