blob: 36fe0a247c951788c274055f8604d4e3960b5850 [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
Ben Wagner67e3a302017-09-05 14:46:19 -040013#include "SkFontMgr.h"
Ben Wagnera25fbef2017-08-30 13:56:19 -040014#include "SkShaper.h"
15#include "SkStream.h"
Ben Wagnere0001732017-08-31 16:26:26 -040016#include "SkTemplates.h"
Ben Wagnera25fbef2017-08-30 13:56:19 -040017#include "SkTextBlob.h"
18#include "SkTypeface.h"
19#include "SkUtils.h"
20
21static const int FONT_SIZE_SCALE = 512;
22
23namespace {
24template <class T, void(*P)(T*)> using resource = std::unique_ptr<T, SkFunctionWrapper<void, T, P>>;
25using HBBlob = resource<hb_blob_t , hb_blob_destroy >;
26using HBFace = resource<hb_face_t , hb_face_destroy >;
27using HBFont = resource<hb_font_t , hb_font_destroy >;
28using HBBuffer = resource<hb_buffer_t, hb_buffer_destroy>;
29using ICUBiDi = resource<UBiDi , ubidi_close >;
30
31HBBlob stream_to_blob(std::unique_ptr<SkStreamAsset> asset) {
32 size_t size = asset->getLength();
33 HBBlob blob;
34 if (const void* base = asset->getMemoryBase()) {
35 blob.reset(hb_blob_create((char*)base, SkToUInt(size),
36 HB_MEMORY_MODE_READONLY, asset.release(),
37 [](void* p) { delete (SkStreamAsset*)p; }));
38 } else {
39 // SkDebugf("Extra SkStreamAsset copy\n");
40 void* ptr = size ? sk_malloc_throw(size) : nullptr;
41 asset->read(ptr, size);
42 blob.reset(hb_blob_create((char*)ptr, SkToUInt(size),
43 HB_MEMORY_MODE_READONLY, ptr, sk_free));
44 }
45 SkASSERT(blob);
46 hb_blob_make_immutable(blob.get());
47 return blob;
48}
49} // namespace
50
51struct SkShaper::Impl {
52 HBFont fHarfBuzzFont;
53 HBBuffer fBuffer;
54 sk_sp<SkTypeface> fTypeface;
55};
56
Ben Wagnere0001732017-08-31 16:26:26 -040057static HBFont create_hb_font(SkTypeface* tf) {
Ben Wagnera25fbef2017-08-30 13:56:19 -040058 int index;
Ben Wagnere0001732017-08-31 16:26:26 -040059 HBBlob blob(stream_to_blob(std::unique_ptr<SkStreamAsset>(tf->openStream(&index))));
Ben Wagnera25fbef2017-08-30 13:56:19 -040060 HBFace face(hb_face_create(blob.get(), (unsigned)index));
61 SkASSERT(face);
62 if (!face) {
Ben Wagnere0001732017-08-31 16:26:26 -040063 return nullptr;
Ben Wagnera25fbef2017-08-30 13:56:19 -040064 }
65 hb_face_set_index(face.get(), (unsigned)index);
Ben Wagnere0001732017-08-31 16:26:26 -040066 hb_face_set_upem(face.get(), tf->getUnitsPerEm());
Ben Wagnera25fbef2017-08-30 13:56:19 -040067
Ben Wagnere0001732017-08-31 16:26:26 -040068 HBFont font(hb_font_create(face.get()));
69 SkASSERT(font);
70 if (!font) {
71 return nullptr;
72 }
73 hb_font_set_scale(font.get(), FONT_SIZE_SCALE, FONT_SIZE_SCALE);
74 hb_ot_font_set_funcs(font.get());
75 int axis_count = tf->getVariationDesignPosition(nullptr, 0);
76 if (axis_count > 0) {
77 SkAutoSTMalloc<4, SkFontArguments::VariationPosition::Coordinate> axis_values(axis_count);
78 if (tf->getVariationDesignPosition(axis_values, axis_count) == axis_count) {
79 hb_font_set_variations(font.get(),
80 reinterpret_cast<hb_variation_t*>(axis_values.get()),
81 axis_count);
82 }
83 }
84 return font;
85}
86
87SkShaper::SkShaper(sk_sp<SkTypeface> tf) : fImpl(new Impl) {
88 fImpl->fTypeface = tf ? std::move(tf) : SkTypeface::MakeDefault();
89 fImpl->fHarfBuzzFont = create_hb_font(fImpl->fTypeface.get());
Ben Wagnera25fbef2017-08-30 13:56:19 -040090 SkASSERT(fImpl->fHarfBuzzFont);
Ben Wagnera25fbef2017-08-30 13:56:19 -040091 fImpl->fBuffer.reset(hb_buffer_create());
92}
93
94SkShaper::~SkShaper() {}
95
96bool SkShaper::good() const { return fImpl->fHarfBuzzFont != nullptr; }
97
98SkScalar SkShaper::shape(SkTextBlobBuilder* builder,
99 const SkPaint& srcPaint,
100 const char* utf8text,
101 size_t textBytes,
102 bool leftToRight,
103 SkPoint point) const {
Ben Wagner67e3a302017-09-05 14:46:19 -0400104 sk_sp<SkFontMgr> fontMgr = SkFontMgr::RefDefault();
Ben Wagnera25fbef2017-08-30 13:56:19 -0400105 SkASSERT(builder);
106 UBiDiLevel bidiLevel = leftToRight ? UBIDI_DEFAULT_LTR : UBIDI_DEFAULT_RTL;
107 //hb_script_t script = ...
108 UErrorCode status = U_ZERO_ERROR;
109 double x = point.x();
110 double y = point.y();
111
112 // This function only accepts utf8.
113 // ubidi only accepts utf16 (though internally it basically works on utf32 chars).
114 // Internally, harfbuzz is all utf32, but always makes a copy.
115
116 if (!SkTFitsIn<int32_t>(textBytes)) {
117 SkDebugf("Bidi error: text too long");
118 return (SkScalar)x;
119 }
120 icu::UnicodeString utf16 = icu::UnicodeString::fromUTF8(icu::StringPiece(utf8text, textBytes));
121
122 ICUBiDi bidi(ubidi_openSized(utf16.length(), 0, &status));
123 if (U_FAILURE(status)) {
124 SkDebugf("Bidi error: %s", u_errorName(status));
125 return (SkScalar)x;
126 }
127 SkASSERT(bidi);
128
129 ubidi_setPara(bidi.get(), utf16.getBuffer(), utf16.length(), bidiLevel, nullptr, &status);
130 if (U_FAILURE(status)) {
131 SkDebugf("Bidi error: %s", u_errorName(status));
132 return (SkScalar)x;
133 }
134
135 int32_t runCount = ubidi_countRuns(bidi.get(), &status);
136 if (U_FAILURE(status)) {
137 SkDebugf("Bidi error: %s", u_errorName(status));
138 return (SkScalar)x;
139 }
140
Ben Wagner2868b782017-08-31 14:12:27 -0400141 const UChar* utf16End = utf16.getBuffer();
142 const char* utf8End = utf8text;
Ben Wagnera25fbef2017-08-30 13:56:19 -0400143 for (int32_t i = 0; i < runCount; ++i) {
144 int32_t start;
145 int32_t length;
146 UBiDiDirection direction = ubidi_getVisualRun(bidi.get(), i, &start, &length);
147
148 SkPaint paint(srcPaint);
149 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
150 paint.setTypeface(fImpl->fTypeface);
Ben Wagner67e3a302017-09-05 14:46:19 -0400151 hb_font_t* hbfont = fImpl->fHarfBuzzFont.get();
152 HBFont fallbackHBFont;
153 sk_sp<SkTypeface> fallback;
154 while (utf16End < utf16.getBuffer() + start + length) {
155 hb_buffer_t* buffer = fImpl->fBuffer.get();
156 SkAutoTCallVProc<hb_buffer_t, hb_buffer_clear_contents> autoClearBuffer(buffer);
Ben Wagnera25fbef2017-08-30 13:56:19 -0400157
Ben Wagner67e3a302017-09-05 14:46:19 -0400158 // The difficulty here is the cluster mapping.
159 // If the hb_buffer is created with utf16, clusters will point to utf16 indexes,
160 // but the SkTextBlob can only take utf8 and utf8 cluster indexes.
161 // So populate the hb_buffer directly with utf32 and utf8 cluster indexes.
162 // Since this steps through the visual runs in order, it is expected that each run will
163 // start just after the previous one ended.
164 const UChar* utf16Start = utf16.getBuffer() + start;
165 const char* utf8Start;
166 if (utf16End == utf16Start) {
167 utf16Start = utf16End;
168 utf8Start = utf8End;
169 } else {
170 SkDEBUGFAIL("Did not expect to ever get here.");
171 utf16Start = utf16.getBuffer();
172 utf8Start = utf8text;
173 while (utf16Start < utf16.getBuffer() + start) {
174 SkUTF16_NextUnichar(&utf16Start);
175 SkUTF8_NextUnichar(&utf8Start);
176 }
Ben Wagner2868b782017-08-31 14:12:27 -0400177 }
Ben Wagner67e3a302017-09-05 14:46:19 -0400178 const char* utf8Current = utf8Start;
179 const UChar* utf16Current = utf16Start;
180 utf16End = utf16Current + length;
181 while (utf16Current < utf16End) {
182 const UChar* utf16Prev = utf16Current;
183 hb_codepoint_t u = SkUTF16_NextUnichar(&utf16Current);
184 bool doPartialRun = false;
Ben Wagnera25fbef2017-08-30 13:56:19 -0400185
Ben Wagner67e3a302017-09-05 14:46:19 -0400186 // If using a fallback and the initial typeface has this character, stop fallback.
187 if (fallbackHBFont &&
188 fImpl->fTypeface->charsToGlyphs(&u, SkTypeface::kUTF32_Encoding, nullptr, 1))
189 {
190 fallback.reset();
191 doPartialRun = true;
Ben Wagnera25fbef2017-08-30 13:56:19 -0400192
Ben Wagner67e3a302017-09-05 14:46:19 -0400193 // If the current typeface does not have this character, try a fallback.
194 } else if (!paint.getTypeface()->charsToGlyphs(&u, SkTypeface::kUTF32_Encoding,
195 nullptr, 1))
196 {
197 fallback.reset(fontMgr->matchFamilyStyleCharacter(nullptr,
198 fImpl->fTypeface->fontStyle(),
199 nullptr, 0,
200 u));
201 if (fallback) {
202 doPartialRun = true;
203 }
204 }
205 if (doPartialRun) {
206 utf16End = utf16Prev;
207 int32_t oldStart = start;
208 start = utf16End - utf16.getBuffer();
209 length -= start - oldStart;
210 break;
211 }
212 hb_buffer_add(buffer, u, utf8Current - utf8Start);
213 SkUTF8_NextUnichar(&utf8Current);
214 }
215 hb_buffer_set_content_type(buffer, HB_BUFFER_CONTENT_TYPE_UNICODE);
216 utf8End = utf8Current;
217 size_t utf8runLength = utf8End - utf8Start;
218 if (!SkTFitsIn<int>(utf8runLength)) {
219 SkDebugf("Shaping error: utf8 too long");
220 return (SkScalar)x;
221 }
222 hb_buffer_guess_segment_properties(buffer);
223 //hb_buffer_set_script(buffer, script);
224 hb_buffer_set_direction(buffer, direction ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
225 hb_shape(hbfont, buffer, nullptr, 0);
226 unsigned len = hb_buffer_get_length(buffer);
227 if (len > 0) {
228 hb_glyph_info_t* info = hb_buffer_get_glyph_infos(buffer, nullptr);
229 hb_glyph_position_t* pos = hb_buffer_get_glyph_positions(buffer, nullptr);
Ben Wagnera25fbef2017-08-30 13:56:19 -0400230
Ben Wagner67e3a302017-09-05 14:46:19 -0400231 if (!SkTFitsIn<int>(len)) {
232 SkDebugf("Shaping error: too many glyphs");
233 return (SkScalar)x;
234 }
235 auto runBuffer = builder->allocRunTextPos(paint, len, utf8runLength, SkString());
236 memcpy(runBuffer.utf8text, utf8Start, utf8runLength);
Ben Wagnera25fbef2017-08-30 13:56:19 -0400237
Ben Wagner67e3a302017-09-05 14:46:19 -0400238 double textSizeY = paint.getTextSize() / (double)FONT_SIZE_SCALE;
239 double textSizeX = textSizeY * paint.getTextScaleX();
240
241 for (unsigned i = 0; i < len; i++) {
242 runBuffer.glyphs[i] = info[i].codepoint;
243 runBuffer.clusters[i] = info[i].cluster;
244 reinterpret_cast<SkPoint*>(runBuffer.pos)[i] =
245 SkPoint::Make(SkDoubleToScalar(x + pos[i].x_offset * textSizeX),
246 SkDoubleToScalar(y - pos[i].y_offset * textSizeY));
247 x += pos[i].x_advance * textSizeX;
248 y += pos[i].y_advance * textSizeY;
249 }
250 }
251
252 if (fallback) {
253 paint.setTypeface(std::move(fallback));
254 fallbackHBFont = create_hb_font(paint.getTypeface());
255 hbfont = fallbackHBFont.get();
256 } else {
257 paint.setTypeface(fImpl->fTypeface);
258 fallbackHBFont = nullptr;
259 hbfont = fImpl->fHarfBuzzFont.get();
260 }
Ben Wagnera25fbef2017-08-30 13:56:19 -0400261 }
262 }
263 return (SkScalar)x;
264}