blob: bdb38b145640f948a3b7e5d2c73729016cc94c13 [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"
Ben Wagnere0001732017-08-31 16:26:26 -040015#include "SkTemplates.h"
Ben Wagnera25fbef2017-08-30 13:56:19 -040016#include "SkTextBlob.h"
17#include "SkTypeface.h"
18#include "SkUtils.h"
19
20static const int FONT_SIZE_SCALE = 512;
21
22namespace {
23template <class T, void(*P)(T*)> using resource = std::unique_ptr<T, SkFunctionWrapper<void, T, P>>;
24using HBBlob = resource<hb_blob_t , hb_blob_destroy >;
25using HBFace = resource<hb_face_t , hb_face_destroy >;
26using HBFont = resource<hb_font_t , hb_font_destroy >;
27using HBBuffer = resource<hb_buffer_t, hb_buffer_destroy>;
28using ICUBiDi = resource<UBiDi , ubidi_close >;
29
30HBBlob stream_to_blob(std::unique_ptr<SkStreamAsset> asset) {
31 size_t size = asset->getLength();
32 HBBlob blob;
33 if (const void* base = asset->getMemoryBase()) {
34 blob.reset(hb_blob_create((char*)base, SkToUInt(size),
35 HB_MEMORY_MODE_READONLY, asset.release(),
36 [](void* p) { delete (SkStreamAsset*)p; }));
37 } else {
38 // SkDebugf("Extra SkStreamAsset copy\n");
39 void* ptr = size ? sk_malloc_throw(size) : nullptr;
40 asset->read(ptr, size);
41 blob.reset(hb_blob_create((char*)ptr, SkToUInt(size),
42 HB_MEMORY_MODE_READONLY, ptr, sk_free));
43 }
44 SkASSERT(blob);
45 hb_blob_make_immutable(blob.get());
46 return blob;
47}
48} // namespace
49
50struct SkShaper::Impl {
51 HBFont fHarfBuzzFont;
52 HBBuffer fBuffer;
53 sk_sp<SkTypeface> fTypeface;
54};
55
Ben Wagnere0001732017-08-31 16:26:26 -040056static HBFont create_hb_font(SkTypeface* tf) {
Ben Wagnera25fbef2017-08-30 13:56:19 -040057 int index;
Ben Wagnere0001732017-08-31 16:26:26 -040058 HBBlob blob(stream_to_blob(std::unique_ptr<SkStreamAsset>(tf->openStream(&index))));
Ben Wagnera25fbef2017-08-30 13:56:19 -040059 HBFace face(hb_face_create(blob.get(), (unsigned)index));
60 SkASSERT(face);
61 if (!face) {
Ben Wagnere0001732017-08-31 16:26:26 -040062 return nullptr;
Ben Wagnera25fbef2017-08-30 13:56:19 -040063 }
64 hb_face_set_index(face.get(), (unsigned)index);
Ben Wagnere0001732017-08-31 16:26:26 -040065 hb_face_set_upem(face.get(), tf->getUnitsPerEm());
Ben Wagnera25fbef2017-08-30 13:56:19 -040066
Ben Wagnere0001732017-08-31 16:26:26 -040067 HBFont font(hb_font_create(face.get()));
68 SkASSERT(font);
69 if (!font) {
70 return nullptr;
71 }
72 hb_font_set_scale(font.get(), FONT_SIZE_SCALE, FONT_SIZE_SCALE);
73 hb_ot_font_set_funcs(font.get());
74 int axis_count = tf->getVariationDesignPosition(nullptr, 0);
75 if (axis_count > 0) {
76 SkAutoSTMalloc<4, SkFontArguments::VariationPosition::Coordinate> axis_values(axis_count);
77 if (tf->getVariationDesignPosition(axis_values, axis_count) == axis_count) {
78 hb_font_set_variations(font.get(),
79 reinterpret_cast<hb_variation_t*>(axis_values.get()),
80 axis_count);
81 }
82 }
83 return font;
84}
85
86SkShaper::SkShaper(sk_sp<SkTypeface> tf) : fImpl(new Impl) {
87 fImpl->fTypeface = tf ? std::move(tf) : SkTypeface::MakeDefault();
88 fImpl->fHarfBuzzFont = create_hb_font(fImpl->fTypeface.get());
Ben Wagnera25fbef2017-08-30 13:56:19 -040089 SkASSERT(fImpl->fHarfBuzzFont);
Ben Wagnera25fbef2017-08-30 13:56:19 -040090 fImpl->fBuffer.reset(hb_buffer_create());
91}
92
93SkShaper::~SkShaper() {}
94
95bool SkShaper::good() const { return fImpl->fHarfBuzzFont != nullptr; }
96
97SkScalar SkShaper::shape(SkTextBlobBuilder* builder,
98 const SkPaint& srcPaint,
99 const char* utf8text,
100 size_t textBytes,
101 bool leftToRight,
102 SkPoint point) const {
103 SkASSERT(builder);
104 UBiDiLevel bidiLevel = leftToRight ? UBIDI_DEFAULT_LTR : UBIDI_DEFAULT_RTL;
105 //hb_script_t script = ...
106 UErrorCode status = U_ZERO_ERROR;
107 double x = point.x();
108 double y = point.y();
109
110 // This function only accepts utf8.
111 // ubidi only accepts utf16 (though internally it basically works on utf32 chars).
112 // Internally, harfbuzz is all utf32, but always makes a copy.
113
114 if (!SkTFitsIn<int32_t>(textBytes)) {
115 SkDebugf("Bidi error: text too long");
116 return (SkScalar)x;
117 }
118 icu::UnicodeString utf16 = icu::UnicodeString::fromUTF8(icu::StringPiece(utf8text, textBytes));
119
120 ICUBiDi bidi(ubidi_openSized(utf16.length(), 0, &status));
121 if (U_FAILURE(status)) {
122 SkDebugf("Bidi error: %s", u_errorName(status));
123 return (SkScalar)x;
124 }
125 SkASSERT(bidi);
126
127 ubidi_setPara(bidi.get(), utf16.getBuffer(), utf16.length(), bidiLevel, nullptr, &status);
128 if (U_FAILURE(status)) {
129 SkDebugf("Bidi error: %s", u_errorName(status));
130 return (SkScalar)x;
131 }
132
133 int32_t runCount = ubidi_countRuns(bidi.get(), &status);
134 if (U_FAILURE(status)) {
135 SkDebugf("Bidi error: %s", u_errorName(status));
136 return (SkScalar)x;
137 }
138
Ben Wagner2868b782017-08-31 14:12:27 -0400139 const UChar* utf16End = utf16.getBuffer();
140 const char* utf8End = utf8text;
Ben Wagnera25fbef2017-08-30 13:56:19 -0400141 for (int32_t i = 0; i < runCount; ++i) {
142 int32_t start;
143 int32_t length;
144 UBiDiDirection direction = ubidi_getVisualRun(bidi.get(), i, &start, &length);
145
146 SkPaint paint(srcPaint);
147 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
148 paint.setTypeface(fImpl->fTypeface);
149
150 hb_buffer_t* buffer = fImpl->fBuffer.get();
151 SkAutoTCallVProc<hb_buffer_t, hb_buffer_clear_contents> autoClearBuffer(buffer);
152
153 // The difficulty here is the cluster mapping.
154 // If the hb_buffer is created with utf16, clusters will be pointing to the utf16 indexes,
155 // but the SkTextBlob can only take utf8 and utf8 cluster indexes.
Ben Wagner2868b782017-08-31 14:12:27 -0400156 // So populate the hb_buffer directly with utf32 and utf8 cluster indexes.
157 // Since this steps through the visual runs in order, it is expected that each run will
158 // start just after the previous one ended.
159 const UChar* utf16Start = utf16.getBuffer() + start;
160 const char* utf8Start;
161 if (utf16End == utf16Start) {
162 utf16Start = utf16End;
163 utf8Start = utf8End;
164 } else {
165 SkDEBUGFAIL("Did not expect to ever get here.");
166 utf16Start = utf16.getBuffer();
167 utf8Start = utf8text;
168 while (utf16Start < utf16.getBuffer() + start) {
169 SkUTF16_NextUnichar(&utf16Start);
170 SkUTF8_NextUnichar(&utf8Start);
171 }
Ben Wagnera25fbef2017-08-30 13:56:19 -0400172 }
Ben Wagner2868b782017-08-31 14:12:27 -0400173 const char* utf8Current = utf8Start;
174 const UChar* utf16Current = utf16Start;
175 utf16End = utf16Current + length;
176 while (utf16Current < utf16End) {
177 hb_codepoint_t u;
178 u = SkUTF16_NextUnichar(&utf16Current);
179 hb_buffer_add(buffer, u, utf8Current - utf8Start);
180 SkUTF8_NextUnichar(&utf8Current);
Ben Wagnera25fbef2017-08-30 13:56:19 -0400181 }
Ben Wagner2868b782017-08-31 14:12:27 -0400182 hb_buffer_set_content_type(buffer, HB_BUFFER_CONTENT_TYPE_UNICODE);
183 utf8End = utf8Current;
184 size_t utf8runLength = utf8End - utf8Start;
Ben Wagnera25fbef2017-08-30 13:56:19 -0400185 if (!SkTFitsIn<int>(utf8runLength)) {
186 SkDebugf("Shaping error: utf8 too long");
187 return (SkScalar)x;
188 }
Ben Wagnera25fbef2017-08-30 13:56:19 -0400189 hb_buffer_guess_segment_properties(buffer);
190 //hb_buffer_set_script(buffer, script);
191 hb_buffer_set_direction(buffer, direction ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
192 hb_shape(fImpl->fHarfBuzzFont.get(), buffer, nullptr, 0);
193 unsigned len = hb_buffer_get_length(buffer);
194 if (len == 0) {
195 continue;
196 }
197
198 hb_glyph_info_t* info = hb_buffer_get_glyph_infos(buffer, nullptr);
199 hb_glyph_position_t* pos = hb_buffer_get_glyph_positions(buffer, nullptr);
200
201 if (!SkTFitsIn<int>(len)) {
202 SkDebugf("Shaping error: too many glyphs");
203 return (SkScalar)x;
204 }
205 auto runBuffer = builder->allocRunTextPos(paint, len, utf8runLength, SkString());
Ben Wagner2868b782017-08-31 14:12:27 -0400206 memcpy(runBuffer.utf8text, utf8Start, utf8runLength);
Ben Wagnera25fbef2017-08-30 13:56:19 -0400207
208 double textSizeY = paint.getTextSize() / (double)FONT_SIZE_SCALE;
209 double textSizeX = textSizeY * paint.getTextScaleX();
210
211 for (unsigned i = 0; i < len; i++) {
212 runBuffer.glyphs[i] = info[i].codepoint;
213 runBuffer.clusters[i] = info[i].cluster;
214 reinterpret_cast<SkPoint*>(runBuffer.pos)[i] =
215 SkPoint::Make(SkDoubleToScalar(x + pos[i].x_offset * textSizeX),
216 SkDoubleToScalar(y - pos[i].y_offset * textSizeY));
217 x += pos[i].x_advance * textSizeX;
218 y += pos[i].y_advance * textSizeY;
219 }
220 }
221 return (SkScalar)x;
222}