/src/skia/src/ports/SkFontHost_FreeType_common.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2006-2012 The Android Open Source Project |
3 | | * Copyright 2012 Mozilla Foundation |
4 | | * |
5 | | * Use of this source code is governed by a BSD-style license that can be |
6 | | * found in the LICENSE file. |
7 | | */ |
8 | | |
9 | | #include "include/core/SkBitmap.h" |
10 | | #include "include/core/SkCanvas.h" |
11 | | #include "include/core/SkColor.h" |
12 | | #include "include/core/SkPath.h" |
13 | | #include "include/effects/SkGradientShader.h" |
14 | | #include "include/private/SkColorData.h" |
15 | | #include "include/private/SkTo.h" |
16 | | #include "src/core/SkFDot6.h" |
17 | | #include "src/ports/SkFontHost_FreeType_common.h" |
18 | | |
19 | | #include <utility> |
20 | | |
21 | | #include <ft2build.h> |
22 | | #include FT_FREETYPE_H |
23 | | #include FT_BITMAP_H |
24 | | #ifdef FT_COLOR_H |
25 | | # include FT_COLOR_H |
26 | | #endif |
27 | | #include FT_IMAGE_H |
28 | | #include FT_OUTLINE_H |
29 | | #include FT_SIZES_H |
30 | | // In the past, FT_GlyphSlot_Own_Bitmap was defined in this header file. |
31 | | #include FT_SYNTHESIS_H |
32 | | |
33 | | #ifdef TT_SUPPORT_COLRV1 |
34 | | #include "src/core/SkScopeExit.h" |
35 | | #endif |
36 | | |
37 | | // FT_LOAD_COLOR and the corresponding FT_Pixel_Mode::FT_PIXEL_MODE_BGRA |
38 | | // were introduced in FreeType 2.5.0. |
39 | | // The following may be removed once FreeType 2.5.0 is required to build. |
40 | | #ifndef FT_LOAD_COLOR |
41 | | # define FT_LOAD_COLOR ( 1L << 20 ) |
42 | | # define FT_PIXEL_MODE_BGRA 7 |
43 | | #endif |
44 | | |
45 | | #ifdef SK_DEBUG |
46 | 0 | const char* SkTraceFtrGetError(int e) { |
47 | 0 | switch ((FT_Error)e) { |
48 | 0 | #undef FTERRORS_H_ |
49 | 0 | #define FT_ERRORDEF( e, v, s ) case v: return s; |
50 | 0 | #define FT_ERROR_START_LIST |
51 | 0 | #define FT_ERROR_END_LIST |
52 | 0 | #include FT_ERRORS_H |
53 | 0 | #undef FT_ERRORDEF |
54 | 0 | #undef FT_ERROR_START_LIST |
55 | 0 | #undef FT_ERROR_END_LIST |
56 | 0 | default: return ""; |
57 | 0 | } |
58 | 0 | } |
59 | | #endif // SK_DEBUG |
60 | | |
61 | | #ifdef TT_SUPPORT_COLRV1 |
62 | 0 | bool operator==(const FT_OpaquePaint& a, const FT_OpaquePaint& b) { |
63 | 0 | return a.p == b.p && a.insert_root_transform == b.insert_root_transform; |
64 | 0 | } |
65 | | #endif |
66 | | |
67 | | namespace { |
68 | | |
69 | 0 | FT_Pixel_Mode compute_pixel_mode(SkMask::Format format) { |
70 | 0 | switch (format) { |
71 | 0 | case SkMask::kBW_Format: |
72 | 0 | return FT_PIXEL_MODE_MONO; |
73 | 0 | case SkMask::kA8_Format: |
74 | 0 | default: |
75 | 0 | return FT_PIXEL_MODE_GRAY; |
76 | 0 | } |
77 | 0 | } |
78 | | |
79 | | /////////////////////////////////////////////////////////////////////////////// |
80 | | |
81 | 0 | uint16_t packTriple(U8CPU r, U8CPU g, U8CPU b) { |
82 | | #ifdef SK_SHOW_TEXT_BLIT_COVERAGE |
83 | | r = std::max(r, (U8CPU)0x40); |
84 | | g = std::max(g, (U8CPU)0x40); |
85 | | b = std::max(b, (U8CPU)0x40); |
86 | | #endif |
87 | 0 | return SkPack888ToRGB16(r, g, b); |
88 | 0 | } |
89 | | |
90 | 0 | uint16_t grayToRGB16(U8CPU gray) { |
91 | | #ifdef SK_SHOW_TEXT_BLIT_COVERAGE |
92 | | gray = std::max(gray, (U8CPU)0x40); |
93 | | #endif |
94 | 0 | return SkPack888ToRGB16(gray, gray, gray); |
95 | 0 | } |
96 | | |
97 | 0 | int bittst(const uint8_t data[], int bitOffset) { |
98 | 0 | SkASSERT(bitOffset >= 0); |
99 | 0 | int lowBit = data[bitOffset >> 3] >> (~bitOffset & 7); |
100 | 0 | return lowBit & 1; |
101 | 0 | } Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::bittst(unsigned char const*, int) Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::bittst(unsigned char const*, int) |
102 | | |
103 | | /** |
104 | | * Copies a FT_Bitmap into an SkMask with the same dimensions. |
105 | | * |
106 | | * FT_PIXEL_MODE_MONO |
107 | | * FT_PIXEL_MODE_GRAY |
108 | | * FT_PIXEL_MODE_LCD |
109 | | * FT_PIXEL_MODE_LCD_V |
110 | | */ |
111 | | template<bool APPLY_PREBLEND> |
112 | | void copyFT2LCD16(const FT_Bitmap& bitmap, const SkMask& mask, int lcdIsBGR, |
113 | | const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) |
114 | 0 | { |
115 | 0 | SkASSERT(SkMask::kLCD16_Format == mask.fFormat); |
116 | 0 | if (FT_PIXEL_MODE_LCD != bitmap.pixel_mode) { |
117 | 0 | SkASSERT(mask.fBounds.width() == static_cast<int>(bitmap.width)); |
118 | 0 | } |
119 | 0 | if (FT_PIXEL_MODE_LCD_V != bitmap.pixel_mode) { |
120 | 0 | SkASSERT(mask.fBounds.height() == static_cast<int>(bitmap.rows)); |
121 | 0 | } |
122 | |
|
123 | 0 | const uint8_t* src = bitmap.buffer; |
124 | 0 | uint16_t* dst = reinterpret_cast<uint16_t*>(mask.fImage); |
125 | 0 | const size_t dstRB = mask.fRowBytes; |
126 | |
|
127 | 0 | const int width = mask.fBounds.width(); |
128 | 0 | const int height = mask.fBounds.height(); |
129 | |
|
130 | 0 | switch (bitmap.pixel_mode) { |
131 | 0 | case FT_PIXEL_MODE_MONO: |
132 | 0 | for (int y = height; y --> 0;) { |
133 | 0 | for (int x = 0; x < width; ++x) { |
134 | 0 | dst[x] = -bittst(src, x); |
135 | 0 | } |
136 | 0 | dst = (uint16_t*)((char*)dst + dstRB); |
137 | 0 | src += bitmap.pitch; |
138 | 0 | } |
139 | 0 | break; |
140 | 0 | case FT_PIXEL_MODE_GRAY: |
141 | 0 | for (int y = height; y --> 0;) { |
142 | 0 | for (int x = 0; x < width; ++x) { |
143 | 0 | dst[x] = grayToRGB16(src[x]); |
144 | 0 | } |
145 | 0 | dst = (uint16_t*)((char*)dst + dstRB); |
146 | 0 | src += bitmap.pitch; |
147 | 0 | } |
148 | 0 | break; |
149 | 0 | case FT_PIXEL_MODE_LCD: |
150 | 0 | SkASSERT(3 * mask.fBounds.width() == static_cast<int>(bitmap.width)); |
151 | 0 | for (int y = height; y --> 0;) { |
152 | 0 | const uint8_t* triple = src; |
153 | 0 | if (lcdIsBGR) { |
154 | 0 | for (int x = 0; x < width; x++) { |
155 | 0 | dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(triple[2], tableR), |
156 | 0 | sk_apply_lut_if<APPLY_PREBLEND>(triple[1], tableG), |
157 | 0 | sk_apply_lut_if<APPLY_PREBLEND>(triple[0], tableB)); |
158 | 0 | triple += 3; |
159 | 0 | } |
160 | 0 | } else { |
161 | 0 | for (int x = 0; x < width; x++) { |
162 | 0 | dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(triple[0], tableR), |
163 | 0 | sk_apply_lut_if<APPLY_PREBLEND>(triple[1], tableG), |
164 | 0 | sk_apply_lut_if<APPLY_PREBLEND>(triple[2], tableB)); |
165 | 0 | triple += 3; |
166 | 0 | } |
167 | 0 | } |
168 | 0 | src += bitmap.pitch; |
169 | 0 | dst = (uint16_t*)((char*)dst + dstRB); |
170 | 0 | } |
171 | 0 | break; |
172 | 0 | case FT_PIXEL_MODE_LCD_V: |
173 | 0 | SkASSERT(3 * mask.fBounds.height() == static_cast<int>(bitmap.rows)); |
174 | 0 | for (int y = height; y --> 0;) { |
175 | 0 | const uint8_t* srcR = src; |
176 | 0 | const uint8_t* srcG = srcR + bitmap.pitch; |
177 | 0 | const uint8_t* srcB = srcG + bitmap.pitch; |
178 | 0 | if (lcdIsBGR) { |
179 | 0 | using std::swap; |
180 | 0 | swap(srcR, srcB); |
181 | 0 | } |
182 | 0 | for (int x = 0; x < width; x++) { |
183 | 0 | dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(*srcR++, tableR), |
184 | 0 | sk_apply_lut_if<APPLY_PREBLEND>(*srcG++, tableG), |
185 | 0 | sk_apply_lut_if<APPLY_PREBLEND>(*srcB++, tableB)); |
186 | 0 | } |
187 | 0 | src += 3 * bitmap.pitch; |
188 | 0 | dst = (uint16_t*)((char*)dst + dstRB); |
189 | 0 | } |
190 | 0 | break; |
191 | 0 | default: |
192 | 0 | SkDEBUGF("FT_Pixel_Mode %d", bitmap.pixel_mode); |
193 | 0 | SkDEBUGFAIL("unsupported FT_Pixel_Mode for LCD16"); |
194 | 0 | break; |
195 | 0 | } |
196 | 0 | } Unexecuted instantiation: SkFontHost_FreeType_common.cpp:void (anonymous namespace)::copyFT2LCD16<false>(FT_Bitmap_ const&, SkMask const&, int, unsigned char const*, unsigned char const*, unsigned char const*) Unexecuted instantiation: SkFontHost_FreeType_common.cpp:void (anonymous namespace)::copyFT2LCD16<true>(FT_Bitmap_ const&, SkMask const&, int, unsigned char const*, unsigned char const*, unsigned char const*) Unexecuted instantiation: SkFontHost_FreeType_common.cpp:void (anonymous namespace)::copyFT2LCD16<false>(FT_Bitmap_ const&, SkMask const&, int, unsigned char const*, unsigned char const*, unsigned char const*) Unexecuted instantiation: SkFontHost_FreeType_common.cpp:void (anonymous namespace)::copyFT2LCD16<true>(FT_Bitmap_ const&, SkMask const&, int, unsigned char const*, unsigned char const*, unsigned char const*) |
197 | | |
198 | | /** |
199 | | * Copies a FT_Bitmap into an SkMask with the same dimensions. |
200 | | * |
201 | | * Yes, No, Never Requested, Never Produced |
202 | | * |
203 | | * kBW kA8 k3D kARGB32 kLCD16 |
204 | | * FT_PIXEL_MODE_MONO Y Y NR N Y |
205 | | * FT_PIXEL_MODE_GRAY N Y NR N Y |
206 | | * FT_PIXEL_MODE_GRAY2 NP NP NR NP NP |
207 | | * FT_PIXEL_MODE_GRAY4 NP NP NR NP NP |
208 | | * FT_PIXEL_MODE_LCD NP NP NR NP NP |
209 | | * FT_PIXEL_MODE_LCD_V NP NP NR NP NP |
210 | | * FT_PIXEL_MODE_BGRA N N NR Y N |
211 | | * |
212 | | * TODO: All of these N need to be Y or otherwise ruled out. |
213 | | */ |
214 | 0 | void copyFTBitmap(const FT_Bitmap& srcFTBitmap, SkMask& dstMask) { |
215 | 0 | SkASSERTF(dstMask.fBounds.width() == static_cast<int>(srcFTBitmap.width), |
216 | 0 | "dstMask.fBounds.width() = %d\n" |
217 | 0 | "static_cast<int>(srcFTBitmap.width) = %d", |
218 | 0 | dstMask.fBounds.width(), |
219 | 0 | static_cast<int>(srcFTBitmap.width) |
220 | 0 | ); |
221 | 0 | SkASSERTF(dstMask.fBounds.height() == static_cast<int>(srcFTBitmap.rows), |
222 | 0 | "dstMask.fBounds.height() = %d\n" |
223 | 0 | "static_cast<int>(srcFTBitmap.rows) = %d", |
224 | 0 | dstMask.fBounds.height(), |
225 | 0 | static_cast<int>(srcFTBitmap.rows) |
226 | 0 | ); |
227 | |
|
228 | 0 | const uint8_t* src = reinterpret_cast<const uint8_t*>(srcFTBitmap.buffer); |
229 | 0 | const FT_Pixel_Mode srcFormat = static_cast<FT_Pixel_Mode>(srcFTBitmap.pixel_mode); |
230 | | // FT_Bitmap::pitch is an int and allowed to be negative. |
231 | 0 | const int srcPitch = srcFTBitmap.pitch; |
232 | 0 | const size_t srcRowBytes = SkTAbs(srcPitch); |
233 | |
|
234 | 0 | uint8_t* dst = dstMask.fImage; |
235 | 0 | const SkMask::Format dstFormat = static_cast<SkMask::Format>(dstMask.fFormat); |
236 | 0 | const size_t dstRowBytes = dstMask.fRowBytes; |
237 | |
|
238 | 0 | const size_t width = srcFTBitmap.width; |
239 | 0 | const size_t height = srcFTBitmap.rows; |
240 | |
|
241 | 0 | if (SkMask::kLCD16_Format == dstFormat) { |
242 | 0 | copyFT2LCD16<false>(srcFTBitmap, dstMask, false, nullptr, nullptr, nullptr); |
243 | 0 | return; |
244 | 0 | } |
245 | | |
246 | 0 | if ((FT_PIXEL_MODE_MONO == srcFormat && SkMask::kBW_Format == dstFormat) || |
247 | 0 | (FT_PIXEL_MODE_GRAY == srcFormat && SkMask::kA8_Format == dstFormat)) |
248 | 0 | { |
249 | 0 | size_t commonRowBytes = std::min(srcRowBytes, dstRowBytes); |
250 | 0 | for (size_t y = height; y --> 0;) { |
251 | 0 | memcpy(dst, src, commonRowBytes); |
252 | 0 | src += srcPitch; |
253 | 0 | dst += dstRowBytes; |
254 | 0 | } |
255 | 0 | } else if (FT_PIXEL_MODE_MONO == srcFormat && SkMask::kA8_Format == dstFormat) { |
256 | 0 | for (size_t y = height; y --> 0;) { |
257 | 0 | uint8_t byte = 0; |
258 | 0 | int bits = 0; |
259 | 0 | const uint8_t* src_row = src; |
260 | 0 | uint8_t* dst_row = dst; |
261 | 0 | for (size_t x = width; x --> 0;) { |
262 | 0 | if (0 == bits) { |
263 | 0 | byte = *src_row++; |
264 | 0 | bits = 8; |
265 | 0 | } |
266 | 0 | *dst_row++ = byte & 0x80 ? 0xff : 0x00; |
267 | 0 | bits--; |
268 | 0 | byte <<= 1; |
269 | 0 | } |
270 | 0 | src += srcPitch; |
271 | 0 | dst += dstRowBytes; |
272 | 0 | } |
273 | 0 | } else if (FT_PIXEL_MODE_BGRA == srcFormat && SkMask::kARGB32_Format == dstFormat) { |
274 | | // FT_PIXEL_MODE_BGRA is pre-multiplied. |
275 | 0 | for (size_t y = height; y --> 0;) { |
276 | 0 | const uint8_t* src_row = src; |
277 | 0 | SkPMColor* dst_row = reinterpret_cast<SkPMColor*>(dst); |
278 | 0 | for (size_t x = 0; x < width; ++x) { |
279 | 0 | uint8_t b = *src_row++; |
280 | 0 | uint8_t g = *src_row++; |
281 | 0 | uint8_t r = *src_row++; |
282 | 0 | uint8_t a = *src_row++; |
283 | 0 | *dst_row++ = SkPackARGB32(a, r, g, b); |
284 | | #ifdef SK_SHOW_TEXT_BLIT_COVERAGE |
285 | | *(dst_row-1) = SkFourByteInterp256(*(dst_row-1), SK_ColorWHITE, 0x40); |
286 | | #endif |
287 | 0 | } |
288 | 0 | src += srcPitch; |
289 | 0 | dst += dstRowBytes; |
290 | 0 | } |
291 | 0 | } else { |
292 | 0 | SkDEBUGF("FT_Pixel_Mode %d, SkMask::Format %d\n", srcFormat, dstFormat); |
293 | 0 | SkDEBUGFAIL("unsupported combination of FT_Pixel_Mode and SkMask::Format"); |
294 | 0 | } |
295 | 0 | } Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::copyFTBitmap(FT_Bitmap_ const&, SkMask&) Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::copyFTBitmap(FT_Bitmap_ const&, SkMask&) |
296 | | |
297 | 0 | inline int convert_8_to_1(unsigned byte) { |
298 | 0 | SkASSERT(byte <= 0xFF); |
299 | | // Arbitrary decision that making the cutoff at 1/4 instead of 1/2 in general looks better. |
300 | 0 | return (byte >> 6) != 0; |
301 | 0 | } Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::convert_8_to_1(unsigned int) Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::convert_8_to_1(unsigned int) |
302 | | |
303 | 0 | uint8_t pack_8_to_1(const uint8_t alpha[8]) { |
304 | 0 | unsigned bits = 0; |
305 | 0 | for (int i = 0; i < 8; ++i) { |
306 | 0 | bits <<= 1; |
307 | 0 | bits |= convert_8_to_1(alpha[i]); |
308 | 0 | } |
309 | 0 | return SkToU8(bits); |
310 | 0 | } |
311 | | |
312 | 0 | void packA8ToA1(const SkMask& mask, const uint8_t* src, size_t srcRB) { |
313 | 0 | const int height = mask.fBounds.height(); |
314 | 0 | const int width = mask.fBounds.width(); |
315 | 0 | const int octs = width >> 3; |
316 | 0 | const int leftOverBits = width & 7; |
317 | |
|
318 | 0 | uint8_t* dst = mask.fImage; |
319 | 0 | const int dstPad = mask.fRowBytes - SkAlign8(width)/8; |
320 | 0 | SkASSERT(dstPad >= 0); |
321 | |
|
322 | 0 | const int srcPad = srcRB - width; |
323 | 0 | SkASSERT(srcPad >= 0); |
324 | |
|
325 | 0 | for (int y = 0; y < height; ++y) { |
326 | 0 | for (int i = 0; i < octs; ++i) { |
327 | 0 | *dst++ = pack_8_to_1(src); |
328 | 0 | src += 8; |
329 | 0 | } |
330 | 0 | if (leftOverBits > 0) { |
331 | 0 | unsigned bits = 0; |
332 | 0 | int shift = 7; |
333 | 0 | for (int i = 0; i < leftOverBits; ++i, --shift) { |
334 | 0 | bits |= convert_8_to_1(*src++) << shift; |
335 | 0 | } |
336 | 0 | *dst++ = bits; |
337 | 0 | } |
338 | 0 | src += srcPad; |
339 | 0 | dst += dstPad; |
340 | 0 | } |
341 | 0 | } Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::packA8ToA1(SkMask const&, unsigned char const*, unsigned long) Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::packA8ToA1(SkMask const&, unsigned char const*, unsigned long) |
342 | | |
343 | 0 | inline SkMask::Format SkMaskFormat_for_SkColorType(SkColorType colorType) { |
344 | 0 | switch (colorType) { |
345 | 0 | case kAlpha_8_SkColorType: |
346 | 0 | return SkMask::kA8_Format; |
347 | 0 | case kN32_SkColorType: |
348 | 0 | return SkMask::kARGB32_Format; |
349 | 0 | default: |
350 | 0 | SkDEBUGFAIL("unsupported SkBitmap::Config"); |
351 | 0 | return SkMask::kA8_Format; |
352 | 0 | } |
353 | 0 | } Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::SkMaskFormat_for_SkColorType(SkColorType) Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::SkMaskFormat_for_SkColorType(SkColorType) |
354 | | |
355 | 0 | inline SkColorType SkColorType_for_FTPixelMode(FT_Pixel_Mode pixel_mode) { |
356 | 0 | switch (pixel_mode) { |
357 | 0 | case FT_PIXEL_MODE_MONO: |
358 | 0 | case FT_PIXEL_MODE_GRAY: |
359 | 0 | return kAlpha_8_SkColorType; |
360 | 0 | case FT_PIXEL_MODE_BGRA: |
361 | 0 | return kN32_SkColorType; |
362 | 0 | default: |
363 | 0 | SkDEBUGFAIL("unsupported FT_PIXEL_MODE"); |
364 | 0 | return kAlpha_8_SkColorType; |
365 | 0 | } |
366 | 0 | } Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::SkColorType_for_FTPixelMode(FT_Pixel_Mode_) Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::SkColorType_for_FTPixelMode(FT_Pixel_Mode_) |
367 | | |
368 | 0 | inline SkColorType SkColorType_for_SkMaskFormat(SkMask::Format format) { |
369 | 0 | switch (format) { |
370 | 0 | case SkMask::kBW_Format: |
371 | 0 | case SkMask::kA8_Format: |
372 | 0 | case SkMask::kLCD16_Format: |
373 | 0 | return kAlpha_8_SkColorType; |
374 | 0 | case SkMask::kARGB32_Format: |
375 | 0 | return kN32_SkColorType; |
376 | 0 | default: |
377 | 0 | SkDEBUGFAIL("unsupported destination SkBitmap::Config"); |
378 | 0 | return kAlpha_8_SkColorType; |
379 | 0 | } |
380 | 0 | } Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::SkColorType_for_SkMaskFormat(SkMask::Format) Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::SkColorType_for_SkMaskFormat(SkMask::Format) |
381 | | |
382 | | // Only build COLRv1 rendering code if FreeType is new enough to have COLRv1 |
383 | | // additions. FreeType defines a macro in the ftoption header to tell us whether |
384 | | // it does support these features. |
385 | | #ifdef TT_SUPPORT_COLRV1 |
386 | | |
387 | | struct OpaquePaintHasher { |
388 | 0 | size_t operator()(const FT_OpaquePaint& opaque_paint) { |
389 | 0 | return SkGoodHash()(opaque_paint.p) ^ |
390 | 0 | SkGoodHash()(opaque_paint.insert_root_transform); |
391 | 0 | } |
392 | | }; |
393 | | |
394 | | using VisitedSet = SkTHashSet<FT_OpaquePaint, OpaquePaintHasher>; |
395 | | |
396 | | bool generateFacePathCOLRv1(FT_Face face, SkGlyphID glyphID, SkPath* path); |
397 | | |
398 | 0 | inline float SkColrV1AlphaToFloat(uint16_t alpha) { return (alpha / float(1 << 14)); } |
399 | | |
400 | | |
401 | 0 | inline SkTileMode ToSkTileMode(FT_PaintExtend extend_mode) { |
402 | 0 | switch (extend_mode) { |
403 | 0 | case FT_COLR_PAINT_EXTEND_REPEAT: |
404 | 0 | return SkTileMode::kRepeat; |
405 | 0 | case FT_COLR_PAINT_EXTEND_REFLECT: |
406 | 0 | return SkTileMode::kMirror; |
407 | 0 | default: |
408 | 0 | return SkTileMode::kClamp; |
409 | 0 | } |
410 | 0 | } |
411 | | |
412 | 0 | inline SkBlendMode ToSkBlendMode(FT_Composite_Mode composite) { |
413 | 0 | switch (composite) { |
414 | 0 | case FT_COLR_COMPOSITE_CLEAR: |
415 | 0 | return SkBlendMode::kClear; |
416 | 0 | case FT_COLR_COMPOSITE_SRC: |
417 | 0 | return SkBlendMode::kSrc; |
418 | 0 | case FT_COLR_COMPOSITE_DEST: |
419 | 0 | return SkBlendMode::kDst; |
420 | 0 | case FT_COLR_COMPOSITE_SRC_OVER: |
421 | 0 | return SkBlendMode::kSrcOver; |
422 | 0 | case FT_COLR_COMPOSITE_DEST_OVER: |
423 | 0 | return SkBlendMode::kDstOver; |
424 | 0 | case FT_COLR_COMPOSITE_SRC_IN: |
425 | 0 | return SkBlendMode::kSrcIn; |
426 | 0 | case FT_COLR_COMPOSITE_DEST_IN: |
427 | 0 | return SkBlendMode::kDstIn; |
428 | 0 | case FT_COLR_COMPOSITE_SRC_OUT: |
429 | 0 | return SkBlendMode::kSrcOut; |
430 | 0 | case FT_COLR_COMPOSITE_DEST_OUT: |
431 | 0 | return SkBlendMode::kDstOut; |
432 | 0 | case FT_COLR_COMPOSITE_SRC_ATOP: |
433 | 0 | return SkBlendMode::kSrcATop; |
434 | 0 | case FT_COLR_COMPOSITE_DEST_ATOP: |
435 | 0 | return SkBlendMode::kDstATop; |
436 | 0 | case FT_COLR_COMPOSITE_XOR: |
437 | 0 | return SkBlendMode::kXor; |
438 | 0 | case FT_COLR_COMPOSITE_PLUS: |
439 | 0 | return SkBlendMode::kPlus; |
440 | 0 | case FT_COLR_COMPOSITE_SCREEN: |
441 | 0 | return SkBlendMode::kScreen; |
442 | 0 | case FT_COLR_COMPOSITE_OVERLAY: |
443 | 0 | return SkBlendMode::kOverlay; |
444 | 0 | case FT_COLR_COMPOSITE_DARKEN: |
445 | 0 | return SkBlendMode::kDarken; |
446 | 0 | case FT_COLR_COMPOSITE_LIGHTEN: |
447 | 0 | return SkBlendMode::kLighten; |
448 | 0 | case FT_COLR_COMPOSITE_COLOR_DODGE: |
449 | 0 | return SkBlendMode::kColorDodge; |
450 | 0 | case FT_COLR_COMPOSITE_COLOR_BURN: |
451 | 0 | return SkBlendMode::kColorBurn; |
452 | 0 | case FT_COLR_COMPOSITE_HARD_LIGHT: |
453 | 0 | return SkBlendMode::kHardLight; |
454 | 0 | case FT_COLR_COMPOSITE_SOFT_LIGHT: |
455 | 0 | return SkBlendMode::kSoftLight; |
456 | 0 | case FT_COLR_COMPOSITE_DIFFERENCE: |
457 | 0 | return SkBlendMode::kDifference; |
458 | 0 | case FT_COLR_COMPOSITE_EXCLUSION: |
459 | 0 | return SkBlendMode::kExclusion; |
460 | 0 | case FT_COLR_COMPOSITE_MULTIPLY: |
461 | 0 | return SkBlendMode::kMultiply; |
462 | 0 | case FT_COLR_COMPOSITE_HSL_HUE: |
463 | 0 | return SkBlendMode::kHue; |
464 | 0 | case FT_COLR_COMPOSITE_HSL_SATURATION: |
465 | 0 | return SkBlendMode::kSaturation; |
466 | 0 | case FT_COLR_COMPOSITE_HSL_COLOR: |
467 | 0 | return SkBlendMode::kColor; |
468 | 0 | case FT_COLR_COMPOSITE_HSL_LUMINOSITY: |
469 | 0 | return SkBlendMode::kLuminosity; |
470 | 0 | default: |
471 | 0 | return SkBlendMode::kDst; |
472 | 0 | } |
473 | 0 | } |
474 | | |
475 | 0 | inline SkMatrix ToSkMatrix(FT_Affine23 affine23) { |
476 | | // Adjust order to convert from FreeType's FT_Affine23 column major order to SkMatrix row-major |
477 | | // order. |
478 | 0 | return SkMatrix::MakeAll( |
479 | 0 | SkFixedToScalar(affine23.xx), -SkFixedToScalar(affine23.xy), SkFixedToScalar(affine23.dx), |
480 | 0 | -SkFixedToScalar(affine23.yx), SkFixedToScalar(affine23.yy), -SkFixedToScalar(affine23.dy), |
481 | 0 | 0, 0, 1); |
482 | 0 | } |
483 | | |
484 | 0 | inline SkPoint SkVectorProjection(SkPoint a, SkPoint b) { |
485 | 0 | SkScalar length = b.length(); |
486 | 0 | if (!length) return SkPoint(); |
487 | 0 | SkPoint b_normalized = b; |
488 | 0 | b_normalized.normalize(); |
489 | 0 | b_normalized.scale(SkPoint::DotProduct(a, b) / length); |
490 | 0 | return b_normalized; |
491 | 0 | } |
492 | | |
493 | | void colrv1_configure_skpaint(FT_Face face, const FT_Color* palette, |
494 | 0 | FT_COLR_Paint colrv1_paint, SkPaint* paint) { |
495 | |
|
496 | 0 | auto fetch_color_stops = [&face, &palette](FT_ColorStopIterator& color_stop_iterator, |
497 | 0 | std::vector<SkScalar>& stops, |
498 | 0 | std::vector<SkColor>& colors) { |
499 | 0 | const FT_UInt num_color_stops = color_stop_iterator.num_color_stops; |
500 | | |
501 | | // 5.7.11.2.4 ColorIndex, ColorStop and ColorLine |
502 | | // "Applications shall apply the colorStops in increasing stopOffset order." |
503 | 0 | struct ColorStop { |
504 | 0 | SkScalar stop_pos; |
505 | 0 | SkColor color; |
506 | 0 | }; |
507 | 0 | std::vector<ColorStop> sorted_stops; |
508 | 0 | sorted_stops.resize(num_color_stops); |
509 | |
|
510 | 0 | FT_ColorStop color_stop; |
511 | 0 | while (FT_Get_Colorline_Stops(face, &color_stop, &color_stop_iterator)) { |
512 | 0 | FT_UInt index = color_stop_iterator.current_color_stop - 1; |
513 | 0 | sorted_stops[index].stop_pos = color_stop.stop_offset / float(1 << 14); |
514 | 0 | FT_UInt16& palette_index = color_stop.color.palette_index; |
515 | | // TODO(drott): Ensure palette_index is sanitized on the FreeType |
516 | | // side and 0xFFFF foreground color will be handled correctly here. |
517 | 0 | sorted_stops[index].color = SkColorSetARGB( |
518 | 0 | palette[palette_index].alpha * SkColrV1AlphaToFloat(color_stop.color.alpha), |
519 | 0 | palette[palette_index].red, |
520 | 0 | palette[palette_index].green, |
521 | 0 | palette[palette_index].blue); |
522 | 0 | } |
523 | |
|
524 | 0 | std::stable_sort( |
525 | 0 | sorted_stops.begin(), |
526 | 0 | sorted_stops.end(), |
527 | 0 | [](const ColorStop& a, const ColorStop& b) { return a.stop_pos < b.stop_pos; }); Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::colrv1_configure_skpaint(FT_FaceRec_*, FT_Color_ const*, FT_COLR_Paint_, SkPaint*)::$_0::operator()(FT_ColorStopIterator_&, std::__1::vector<float, std::__1::allocator<float> >&, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> >&) const::{lambda((anonymous namespace)::colrv1_configure_skpaint(FT_FaceRec_*, FT_Color_ const*, FT_COLR_Paint_, SkPaint*)::$_0::operator()(FT_ColorStopIterator_&, std::__1::vector<float, std::__1::allocator<float> >&, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> >&) const::ColorStop const&, (anonymous namespace)::colrv1_configure_skpaint(FT_FaceRec_*, FT_Color_ const*, FT_COLR_Paint_, SkPaint*)::$_0::operator()(FT_ColorStopIterator_&, std::__1::vector<float, std::__1::allocator<float> >&, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> >&) const::ColorStop const&)#1}::operator()((anonymous namespace)::colrv1_configure_skpaint(FT_FaceRec_*, FT_Color_ const*, FT_COLR_Paint_, SkPaint*)::$_0::operator()(FT_ColorStopIterator_&, std::__1::vector<float, std::__1::allocator<float> >&, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> >&) const::ColorStop const&, (anonymous namespace)::colrv1_configure_skpaint(FT_FaceRec_*, FT_Color_ const*, FT_COLR_Paint_, SkPaint*)::$_0::operator()(FT_ColorStopIterator_&, std::__1::vector<float, std::__1::allocator<float> >&, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> >&) const::ColorStop const&) const Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::colrv1_configure_skpaint(FT_FaceRec_*, FT_Color_ const*, FT_COLR_Paint_, SkPaint*)::$_2::operator()(FT_ColorStopIterator_&, std::__1::vector<float, std::__1::allocator<float> >&, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> >&) const::{lambda((anonymous namespace)::colrv1_configure_skpaint(FT_FaceRec_*, FT_Color_ const*, FT_COLR_Paint_, SkPaint*)::$_2::operator()(FT_ColorStopIterator_&, std::__1::vector<float, std::__1::allocator<float> >&, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> >&) const::ColorStop const&, (anonymous namespace)::colrv1_configure_skpaint(FT_FaceRec_*, FT_Color_ const*, FT_COLR_Paint_, SkPaint*)::$_2::operator()(FT_ColorStopIterator_&, std::__1::vector<float, std::__1::allocator<float> >&, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> >&) const::ColorStop const&)#1}::operator()((anonymous namespace)::colrv1_configure_skpaint(FT_FaceRec_*, FT_Color_ const*, FT_COLR_Paint_, SkPaint*)::$_2::operator()(FT_ColorStopIterator_&, std::__1::vector<float, std::__1::allocator<float> >&, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> >&) const::ColorStop const&, (anonymous namespace)::colrv1_configure_skpaint(FT_FaceRec_*, FT_Color_ const*, FT_COLR_Paint_, SkPaint*)::$_2::operator()(FT_ColorStopIterator_&, std::__1::vector<float, std::__1::allocator<float> >&, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> >&) const::ColorStop const&) const |
528 | |
|
529 | 0 | stops.resize(num_color_stops); |
530 | 0 | colors.resize(num_color_stops); |
531 | 0 | for (size_t i = 0; i < num_color_stops; ++i) { |
532 | 0 | stops[i] = sorted_stops[i].stop_pos; |
533 | 0 | colors[i] = sorted_stops[i].color; |
534 | 0 | } |
535 | 0 | }; Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::colrv1_configure_skpaint(FT_FaceRec_*, FT_Color_ const*, FT_COLR_Paint_, SkPaint*)::$_0::operator()(FT_ColorStopIterator_&, std::__1::vector<float, std::__1::allocator<float> >&, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> >&) const Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::colrv1_configure_skpaint(FT_FaceRec_*, FT_Color_ const*, FT_COLR_Paint_, SkPaint*)::$_2::operator()(FT_ColorStopIterator_&, std::__1::vector<float, std::__1::allocator<float> >&, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> >&) const |
536 | |
|
537 | 0 | switch (colrv1_paint.format) { |
538 | 0 | case FT_COLR_PAINTFORMAT_SOLID: { |
539 | 0 | FT_PaintSolid solid = colrv1_paint.u.solid; |
540 | 0 | SkColor color = |
541 | 0 | SkColorSetARGB(palette[solid.color.palette_index].alpha * |
542 | 0 | SkColrV1AlphaToFloat(solid.color.alpha), |
543 | 0 | palette[solid.color.palette_index].red, |
544 | 0 | palette[solid.color.palette_index].green, |
545 | 0 | palette[solid.color.palette_index].blue); |
546 | 0 | paint->setShader(nullptr); |
547 | 0 | paint->setColor(color); |
548 | 0 | break; |
549 | 0 | } |
550 | 0 | case FT_COLR_PAINTFORMAT_LINEAR_GRADIENT: { |
551 | 0 | FT_PaintLinearGradient& linear_gradient = colrv1_paint.u.linear_gradient; |
552 | 0 | SkPoint line_positions[2] = { |
553 | 0 | SkPoint::Make(linear_gradient.p0.x, -linear_gradient.p0.y), |
554 | 0 | SkPoint::Make(linear_gradient.p1.x, -linear_gradient.p1.y) |
555 | 0 | }; |
556 | 0 | SkPoint p0 = line_positions[0]; |
557 | 0 | SkPoint p1 = line_positions[1]; |
558 | 0 | SkPoint p2 = SkPoint::Make(linear_gradient.p2.x, -linear_gradient.p2.y); |
559 | | |
560 | | // Do not draw the gradient of p0p1 is parallel to p0p2. |
561 | 0 | if (p1 == p0 || p2 == p0 || !SkPoint::CrossProduct(p1 - p0, p2 - p0)) break; |
562 | | |
563 | | // Follow implementation note in nanoemoji: |
564 | | // https://github.com/googlefonts/nanoemoji/blob/0ac6e7bb4d8202db692574d8530a9b643f1b3b3c/src/nanoemoji/svg.py#L188 |
565 | | // to compute a new gradient end point as the orthogonal projection of the vector from p0 to p1 onto a line |
566 | | // perpendicular to line p0p2 and passing through p0. |
567 | 0 | SkVector perpendicular_to_p2_p0 = (p2 - p0); |
568 | 0 | perpendicular_to_p2_p0 = SkPoint::Make(perpendicular_to_p2_p0.y(), -perpendicular_to_p2_p0.x()); |
569 | 0 | line_positions[1] = p0 + SkVectorProjection((p1 - p0), perpendicular_to_p2_p0); |
570 | |
|
571 | 0 | std::vector<SkScalar> stops; |
572 | 0 | std::vector<SkColor> colors; |
573 | 0 | fetch_color_stops(linear_gradient.colorline.color_stop_iterator, stops, colors); |
574 | |
|
575 | 0 | if (stops.empty()) { |
576 | 0 | break; |
577 | 0 | } |
578 | | |
579 | 0 | if (stops.size() == 1) { |
580 | 0 | paint->setColor(colors[0]); |
581 | 0 | break; |
582 | 0 | } |
583 | | |
584 | | // Project/scale points according to stop extrema along p0p1 line, |
585 | | // then scale stops to to [0, 1] range so that repeat modes work. |
586 | | // The Skia linear gradient shader performs the repeat modes over |
587 | | // the 0 to 1 range, that's why we need to scale the stops to within |
588 | | // that range. |
589 | 0 | SkVector p0p1 = p1 - p0; |
590 | 0 | SkVector new_p0_offset = p0p1; |
591 | 0 | new_p0_offset.scale(stops.front()); |
592 | 0 | SkVector new_p1_offset = p0p1; |
593 | 0 | new_p1_offset.scale(stops.back()); |
594 | |
|
595 | 0 | line_positions[0] = p0 + new_p0_offset; |
596 | 0 | line_positions[1] = p0 + new_p1_offset; |
597 | |
|
598 | 0 | SkScalar scale_factor = 1 / (stops.back() - stops.front()); |
599 | 0 | SkScalar start_offset = stops.front(); |
600 | 0 | for (SkScalar& stop : stops) { |
601 | 0 | stop = (stop - start_offset) * scale_factor; |
602 | 0 | } |
603 | |
|
604 | 0 | sk_sp<SkShader> shader(SkGradientShader::MakeLinear( |
605 | 0 | line_positions, |
606 | 0 | colors.data(), |
607 | 0 | stops.data(), |
608 | 0 | stops.size(), |
609 | 0 | ToSkTileMode(linear_gradient.colorline.extend))); |
610 | 0 | SkASSERT(shader); |
611 | | // An opaque color is needed to ensure the gradient's not modulated by alpha. |
612 | 0 | paint->setColor(SK_ColorBLACK); |
613 | 0 | paint->setShader(shader); |
614 | |
|
615 | 0 | break; |
616 | 0 | } |
617 | 0 | case FT_COLR_PAINTFORMAT_RADIAL_GRADIENT: { |
618 | 0 | FT_PaintRadialGradient& radial_gradient = colrv1_paint.u.radial_gradient; |
619 | 0 | SkPoint start = SkPoint::Make(radial_gradient.c0.x, -radial_gradient.c0.y); |
620 | 0 | SkScalar radius = radial_gradient.r0; |
621 | 0 | SkPoint end = SkPoint::Make(radial_gradient.c1.x, -radial_gradient.c1.y); |
622 | 0 | SkScalar end_radius = radial_gradient.r1; |
623 | | |
624 | |
|
625 | 0 | std::vector<SkScalar> stops; |
626 | 0 | std::vector<SkColor> colors; |
627 | 0 | fetch_color_stops(radial_gradient.colorline.color_stop_iterator, stops, colors); |
628 | | |
629 | | // An opaque color is needed to ensure the gradient's not modulated by alpha. |
630 | 0 | paint->setColor(SK_ColorBLACK); |
631 | |
|
632 | 0 | paint->setShader(SkGradientShader::MakeTwoPointConical( |
633 | 0 | start, radius, end, end_radius, colors.data(), stops.data(), stops.size(), |
634 | 0 | ToSkTileMode(radial_gradient.colorline.extend))); |
635 | 0 | break; |
636 | 0 | } |
637 | 0 | case FT_COLR_PAINTFORMAT_SWEEP_GRADIENT: { |
638 | 0 | FT_PaintSweepGradient& sweep_gradient = colrv1_paint.u.sweep_gradient; |
639 | 0 | SkPoint center = SkPoint::Make(sweep_gradient.center.x, -sweep_gradient.center.y); |
640 | 0 | SkScalar startAngle = SkFixedToScalar(sweep_gradient.start_angle * 180.0f); |
641 | 0 | SkScalar endAngle = SkFixedToScalar(sweep_gradient.end_angle * 180.0f); |
642 | |
|
643 | 0 | std::vector<SkScalar> stops; |
644 | 0 | std::vector<SkColor> colors; |
645 | 0 | fetch_color_stops(sweep_gradient.colorline.color_stop_iterator, stops, colors); |
646 | | |
647 | | // An opaque color is needed to ensure the gradient's not modulated by alpha. |
648 | 0 | paint->setColor(SK_ColorBLACK); |
649 | | |
650 | | // Prepare angles to be within range for the shader. |
651 | 0 | auto clampAngleToRange= [](SkScalar angle) { |
652 | 0 | SkScalar clamped_angle = SkScalarMod(angle, 360.f); |
653 | 0 | if (clamped_angle < 0) |
654 | 0 | return clamped_angle + 360.f; |
655 | 0 | return clamped_angle; |
656 | 0 | }; Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::colrv1_configure_skpaint(FT_FaceRec_*, FT_Color_ const*, FT_COLR_Paint_, SkPaint*)::$_5::operator()(float) const Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::colrv1_configure_skpaint(FT_FaceRec_*, FT_Color_ const*, FT_COLR_Paint_, SkPaint*)::$_11::operator()(float) const |
657 | 0 | startAngle = clampAngleToRange(startAngle); |
658 | 0 | endAngle = clampAngleToRange(endAngle); |
659 | | /* TODO: Spec clarifications on which side of the gradient is to be |
660 | | * painted, repeat modes, how to handle 0 degrees transition, see |
661 | | * https://github.com/googlefonts/colr-gradients-spec/issues/250 */ |
662 | 0 | if (startAngle >= endAngle) |
663 | 0 | endAngle += 360.f; |
664 | | |
665 | | // Skia's angles start from the horizontal x-Axis, rotate left 90 |
666 | | // degrees and then mirror horizontally to correct for Skia angles |
667 | | // going clockwise, COLR v1 angles going counterclockwise. |
668 | 0 | SkMatrix angle_adjust = SkMatrix::RotateDeg(-90.f, center); |
669 | 0 | angle_adjust.postScale(-1, 1, center.x(), center.y()); |
670 | |
|
671 | 0 | paint->setShader(SkGradientShader::MakeSweep( |
672 | 0 | center.x(), center.y(), colors.data(), stops.data(), stops.size(), |
673 | 0 | SkTileMode::kDecal, startAngle, endAngle, 0, &angle_adjust)); |
674 | 0 | break; |
675 | 0 | } |
676 | 0 | default: { |
677 | 0 | SkASSERT(false); /* not reached */ |
678 | 0 | } |
679 | 0 | } |
680 | 0 | } Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::colrv1_configure_skpaint(FT_FaceRec_*, FT_Color_ const*, FT_COLR_Paint_, SkPaint*) Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::colrv1_configure_skpaint(FT_FaceRec_*, FT_Color_ const*, FT_COLR_Paint_, SkPaint*) |
681 | | |
682 | | |
683 | | void colrv1_draw_paint(SkCanvas* canvas, |
684 | | const FT_Color* palette, |
685 | | FT_Face face, |
686 | 0 | FT_COLR_Paint colrv1_paint) { |
687 | 0 | SkPaint paint; |
688 | |
|
689 | 0 | switch (colrv1_paint.format) { |
690 | 0 | case FT_COLR_PAINTFORMAT_GLYPH: { |
691 | 0 | FT_UInt glyphID = colrv1_paint.u.glyph.glyphID; |
692 | 0 | SkPath path; |
693 | | /* TODO: Currently this call retrieves the path at units_per_em size. If we want to get |
694 | | * correct hinting for the scaled size under the transforms at this point in the color |
695 | | * glyph graph, we need to extract at least the requested glyph width and height and |
696 | | * pass that to the path generation. */ |
697 | 0 | if (generateFacePathCOLRv1(face, glyphID, &path)) { |
698 | |
|
699 | | #ifdef SK_SHOW_TEXT_BLIT_COVERAGE |
700 | | SkPaint highlight_paint; |
701 | | highlight_paint.setColor(0x33FF0000); |
702 | | canvas->drawRect(path.getBounds(), highlight_paint); |
703 | | #endif |
704 | 0 | canvas->clipPath(path, true /* doAntiAlias */); |
705 | 0 | } |
706 | 0 | break; |
707 | 0 | } |
708 | 0 | case FT_COLR_PAINTFORMAT_SOLID: |
709 | 0 | case FT_COLR_PAINTFORMAT_LINEAR_GRADIENT: |
710 | 0 | case FT_COLR_PAINTFORMAT_RADIAL_GRADIENT: |
711 | 0 | case FT_COLR_PAINTFORMAT_SWEEP_GRADIENT: { |
712 | 0 | SkPaint colrPaint; |
713 | 0 | colrv1_configure_skpaint(face, palette, colrv1_paint, &colrPaint); |
714 | 0 | canvas->drawPaint(colrPaint); |
715 | 0 | break; |
716 | 0 | } |
717 | 0 | case FT_COLR_PAINTFORMAT_TRANSFORM: |
718 | 0 | case FT_COLR_PAINTFORMAT_TRANSLATE: |
719 | 0 | case FT_COLR_PAINTFORMAT_SCALE: |
720 | 0 | case FT_COLR_PAINTFORMAT_ROTATE: |
721 | 0 | case FT_COLR_PAINTFORMAT_SKEW: |
722 | 0 | SkASSERT(false); // Transforms handled in colrv1_transform. |
723 | 0 | break; |
724 | 0 | default: |
725 | 0 | paint.setShader(nullptr); |
726 | 0 | paint.setColor(SK_ColorCYAN); |
727 | 0 | break; |
728 | 0 | } |
729 | 0 | } Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::colrv1_draw_paint(SkCanvas*, FT_Color_ const*, FT_FaceRec_*, FT_COLR_Paint_) Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::colrv1_draw_paint(SkCanvas*, FT_Color_ const*, FT_FaceRec_*, FT_COLR_Paint_) |
730 | | |
731 | | void colrv1_draw_glyph_with_path(SkCanvas* canvas, const FT_Color* palette, FT_Face face, |
732 | 0 | FT_COLR_Paint glyphPaint, FT_COLR_Paint fillPaint) { |
733 | 0 | SkASSERT(glyphPaint.format == FT_COLR_PAINTFORMAT_GLYPH); |
734 | 0 | SkASSERT(fillPaint.format == FT_COLR_PAINTFORMAT_SOLID || |
735 | 0 | fillPaint.format == FT_COLR_PAINTFORMAT_LINEAR_GRADIENT || |
736 | 0 | fillPaint.format == FT_COLR_PAINTFORMAT_RADIAL_GRADIENT || |
737 | 0 | fillPaint.format == FT_COLR_PAINTFORMAT_SWEEP_GRADIENT); |
738 | |
|
739 | 0 | SkPaint skiaFillPaint; |
740 | 0 | skiaFillPaint.setAntiAlias(true); |
741 | 0 | colrv1_configure_skpaint(face, palette, fillPaint, &skiaFillPaint); |
742 | |
|
743 | 0 | FT_UInt glyphID = glyphPaint.u.glyph.glyphID; |
744 | 0 | SkPath path; |
745 | | /* TODO: Currently this call retrieves the path at units_per_em size. If we want to get |
746 | | * correct hinting for the scaled size under the transforms at this point in the color |
747 | | * glyph graph, we need to extract at least the requested glyph width and height and |
748 | | * pass that to the path generation. */ |
749 | 0 | if (generateFacePathCOLRv1(face, glyphID, &path)) { |
750 | | #ifdef SK_SHOW_TEXT_BLIT_COVERAGE |
751 | | SkPaint highlight_paint; |
752 | | highlight_paint.setColor(0x33FF0000); |
753 | | canvas->drawRect(path.getBounds(), highlight_paint); |
754 | | #endif |
755 | 0 | { |
756 | 0 | canvas->drawPath(path, skiaFillPaint); |
757 | 0 | } |
758 | 0 | } |
759 | 0 | } Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::colrv1_draw_glyph_with_path(SkCanvas*, FT_Color_ const*, FT_FaceRec_*, FT_COLR_Paint_, FT_COLR_Paint_) Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::colrv1_draw_glyph_with_path(SkCanvas*, FT_Color_ const*, FT_FaceRec_*, FT_COLR_Paint_, FT_COLR_Paint_) |
760 | | |
761 | | |
762 | | /* In drawing mode, concatenates the transforms directly on SkCanvas. In |
763 | | * bounding box calculation mode, no SkCanvas is specified, but we only want to |
764 | | * retrieve the transform from the FreeType paint object. */ |
765 | | void colrv1_transform(FT_Face face, |
766 | | FT_COLR_Paint colrv1_paint, |
767 | | SkCanvas* canvas, |
768 | 0 | SkMatrix* out_transform = 0) { |
769 | 0 | SkMatrix transform; |
770 | |
|
771 | 0 | SkASSERT(canvas || out_transform); |
772 | |
|
773 | 0 | switch (colrv1_paint.format) { |
774 | 0 | case FT_COLR_PAINTFORMAT_TRANSFORM: { |
775 | 0 | transform = ToSkMatrix(colrv1_paint.u.transform.affine); |
776 | 0 | break; |
777 | 0 | } |
778 | 0 | case FT_COLR_PAINTFORMAT_TRANSLATE: { |
779 | 0 | transform = SkMatrix::Translate( |
780 | 0 | SkFixedToScalar(colrv1_paint.u.translate.dx), |
781 | 0 | -SkFixedToScalar(colrv1_paint.u.translate.dy)); |
782 | 0 | break; |
783 | 0 | } |
784 | 0 | case FT_COLR_PAINTFORMAT_SCALE: { |
785 | 0 | transform.setScale(SkFixedToScalar(colrv1_paint.u.scale.scale_x), |
786 | 0 | SkFixedToScalar(colrv1_paint.u.scale.scale_y), |
787 | 0 | SkFixedToScalar(colrv1_paint.u.scale.center_x), |
788 | 0 | -SkFixedToScalar(colrv1_paint.u.scale.center_y)); |
789 | 0 | break; |
790 | 0 | } |
791 | 0 | case FT_COLR_PAINTFORMAT_ROTATE: { |
792 | 0 | transform = SkMatrix::RotateDeg( |
793 | 0 | SkFixedToScalar(colrv1_paint.u.rotate.angle) * 180.0f, |
794 | 0 | SkPoint::Make(SkFixedToScalar(colrv1_paint.u.rotate.center_x), |
795 | 0 | -SkFixedToScalar(colrv1_paint.u.rotate.center_y))); |
796 | 0 | break; |
797 | 0 | } |
798 | 0 | case FT_COLR_PAINTFORMAT_SKEW: { |
799 | | // In the PAINTFORMAT_ROTATE implementation, SkMatrix setRotate |
800 | | // snaps to 0 for values very close to 0. Do the same here. |
801 | |
|
802 | 0 | SkScalar rad_x = |
803 | 0 | SkDegreesToRadians(-SkFixedToFloat(colrv1_paint.u.skew.x_skew_angle) * 180.0f); |
804 | 0 | float tan_x = SkScalarTan(rad_x); |
805 | 0 | tan_x = SkScalarNearlyZero(tan_x) ? 0.0f : tan_x; |
806 | |
|
807 | 0 | SkScalar rad_y = |
808 | 0 | SkDegreesToRadians(-SkFixedToFloat(colrv1_paint.u.skew.y_skew_angle) * 180.0f); |
809 | 0 | float tan_y = SkScalarTan(rad_y); |
810 | 0 | tan_y = SkScalarNearlyZero(tan_y) ? 0.0f : tan_y; |
811 | |
|
812 | 0 | transform.setSkew(tan_x, |
813 | 0 | tan_y, |
814 | 0 | SkFixedToScalar(colrv1_paint.u.skew.center_x), |
815 | 0 | -SkFixedToFloat(colrv1_paint.u.skew.center_y)); |
816 | 0 | break; |
817 | 0 | } |
818 | 0 | default: { |
819 | | // Only transforms are handled in this function. |
820 | 0 | SkASSERT(false); |
821 | 0 | } |
822 | 0 | } |
823 | 0 | if (canvas) { |
824 | 0 | canvas->concat(transform); |
825 | 0 | } |
826 | 0 | if (out_transform) { |
827 | 0 | *out_transform = transform; |
828 | 0 | } |
829 | 0 | } Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::colrv1_transform(FT_FaceRec_*, FT_COLR_Paint_, SkCanvas*, SkMatrix*) Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::colrv1_transform(FT_FaceRec_*, FT_COLR_Paint_, SkCanvas*, SkMatrix*) |
830 | | |
831 | | bool colrv1_start_glyph(SkCanvas* canvas, |
832 | | const FT_Color* palette, |
833 | | FT_Face ft_face, |
834 | | uint16_t glyph_id, |
835 | | FT_Color_Root_Transform root_transform); |
836 | | |
837 | | bool colrv1_traverse_paint(SkCanvas* canvas, |
838 | | const FT_Color* palette, |
839 | | FT_Face face, |
840 | | FT_OpaquePaint opaque_paint, |
841 | 0 | VisitedSet* visited_set) { |
842 | | // Cycle detection, see section "5.7.11.1.9 Color glyphs as a directed acyclic graph". |
843 | 0 | if (visited_set->contains(opaque_paint)) { |
844 | 0 | return false; |
845 | 0 | } |
846 | | |
847 | 0 | visited_set->add(opaque_paint); |
848 | 0 | SK_AT_SCOPE_EXIT(visited_set->remove(opaque_paint)); |
849 | |
|
850 | 0 | FT_COLR_Paint paint; |
851 | 0 | if (!FT_Get_Paint(face, opaque_paint, &paint)) { |
852 | 0 | return false; |
853 | 0 | } |
854 | | |
855 | | // Keep track of failures to retrieve the FT_COLR_Paint from FreeType in the |
856 | | // recursion, cancel recursion when a paint retrieval fails. |
857 | 0 | bool traverse_result = true; |
858 | 0 | SkAutoCanvasRestore autoRestore(canvas, true /* do_save */); |
859 | 0 | switch (paint.format) { |
860 | 0 | case FT_COLR_PAINTFORMAT_COLR_LAYERS: { |
861 | 0 | FT_LayerIterator& layer_iterator = paint.u.colr_layers.layer_iterator; |
862 | 0 | FT_OpaquePaint opaque_paint_fetch; |
863 | 0 | opaque_paint_fetch.p = nullptr; |
864 | 0 | while (FT_Get_Paint_Layers(face, &layer_iterator, &opaque_paint_fetch)) { |
865 | 0 | colrv1_traverse_paint(canvas, palette, face, opaque_paint_fetch, visited_set); |
866 | 0 | } |
867 | 0 | break; |
868 | 0 | } |
869 | 0 | case FT_COLR_PAINTFORMAT_GLYPH: |
870 | | // Special case paint graph leaf situations to improve |
871 | | // performance. These are situations in the graph where a GlyphPaint |
872 | | // is followed by either a solid or a gradient fill. Here we can use |
873 | | // drawPath() + SkPaint directly which is faster than setting a |
874 | | // clipPath() followed by a drawPaint(). |
875 | 0 | FT_COLR_Paint fillPaint; |
876 | 0 | if (!FT_Get_Paint(face, paint.u.glyph.paint, &fillPaint)) { |
877 | 0 | return false; |
878 | 0 | } |
879 | 0 | if (fillPaint.format == FT_COLR_PAINTFORMAT_SOLID || |
880 | 0 | fillPaint.format == FT_COLR_PAINTFORMAT_LINEAR_GRADIENT || |
881 | 0 | fillPaint.format == FT_COLR_PAINTFORMAT_RADIAL_GRADIENT || |
882 | 0 | fillPaint.format == FT_COLR_PAINTFORMAT_SWEEP_GRADIENT) { |
883 | 0 | colrv1_draw_glyph_with_path(canvas, palette, face, paint, fillPaint); |
884 | 0 | } else { |
885 | 0 | colrv1_draw_paint(canvas, palette, face, paint); |
886 | 0 | traverse_result = colrv1_traverse_paint(canvas, palette, face, |
887 | 0 | paint.u.glyph.paint, visited_set); |
888 | 0 | } |
889 | 0 | break; |
890 | 0 | case FT_COLR_PAINTFORMAT_COLR_GLYPH: |
891 | 0 | traverse_result = colrv1_start_glyph(canvas, palette, face, paint.u.colr_glyph.glyphID, |
892 | 0 | FT_COLOR_NO_ROOT_TRANSFORM); |
893 | 0 | break; |
894 | 0 | case FT_COLR_PAINTFORMAT_TRANSFORM: |
895 | 0 | colrv1_transform(face, paint, canvas); |
896 | 0 | traverse_result = colrv1_traverse_paint(canvas, palette, face, |
897 | 0 | paint.u.transform.paint, visited_set); |
898 | 0 | break; |
899 | 0 | case FT_COLR_PAINTFORMAT_TRANSLATE: |
900 | 0 | colrv1_transform(face, paint, canvas); |
901 | 0 | traverse_result = colrv1_traverse_paint(canvas, palette, face, |
902 | 0 | paint.u.translate.paint, visited_set); |
903 | 0 | break; |
904 | 0 | case FT_COLR_PAINTFORMAT_SCALE: |
905 | 0 | colrv1_transform(face, paint, canvas); |
906 | 0 | traverse_result = colrv1_traverse_paint(canvas, palette, face, |
907 | 0 | paint.u.scale.paint, visited_set); |
908 | 0 | break; |
909 | 0 | case FT_COLR_PAINTFORMAT_ROTATE: |
910 | 0 | colrv1_transform(face, paint, canvas); |
911 | 0 | traverse_result = |
912 | 0 | colrv1_traverse_paint(canvas, palette, face, |
913 | 0 | paint.u.rotate.paint, visited_set); |
914 | 0 | break; |
915 | 0 | case FT_COLR_PAINTFORMAT_SKEW: |
916 | 0 | colrv1_transform(face, paint, canvas); |
917 | 0 | traverse_result = |
918 | 0 | colrv1_traverse_paint(canvas, palette, face, |
919 | 0 | paint.u.skew.paint, visited_set); |
920 | 0 | break; |
921 | 0 | case FT_COLR_PAINTFORMAT_COMPOSITE: { |
922 | 0 | traverse_result = colrv1_traverse_paint( |
923 | 0 | canvas, palette, face, paint.u.composite.backdrop_paint, visited_set); |
924 | 0 | SkPaint blend_mode_paint; |
925 | 0 | blend_mode_paint.setBlendMode(ToSkBlendMode(paint.u.composite.composite_mode)); |
926 | 0 | canvas->saveLayer(nullptr, &blend_mode_paint); |
927 | 0 | traverse_result = |
928 | 0 | traverse_result && |
929 | 0 | colrv1_traverse_paint( |
930 | 0 | canvas, palette, face, paint.u.composite.source_paint, visited_set); |
931 | 0 | canvas->restore(); |
932 | 0 | break; |
933 | 0 | } |
934 | 0 | case FT_COLR_PAINTFORMAT_SOLID: |
935 | 0 | case FT_COLR_PAINTFORMAT_LINEAR_GRADIENT: |
936 | 0 | case FT_COLR_PAINTFORMAT_RADIAL_GRADIENT: |
937 | 0 | case FT_COLR_PAINTFORMAT_SWEEP_GRADIENT: { |
938 | 0 | colrv1_draw_paint(canvas, palette, face, paint); |
939 | 0 | break; |
940 | 0 | } |
941 | 0 | default: |
942 | 0 | SkASSERT(false); |
943 | 0 | break; |
944 | 0 | } |
945 | 0 | return traverse_result; |
946 | 0 | } Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::colrv1_traverse_paint(SkCanvas*, FT_Color_ const*, FT_FaceRec_*, FT_Opaque_Paint_, SkTHashSet<FT_Opaque_Paint_, (anonymous namespace)::OpaquePaintHasher>*) Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::colrv1_traverse_paint(SkCanvas*, FT_Color_ const*, FT_FaceRec_*, FT_Opaque_Paint_, SkTHashSet<FT_Opaque_Paint_, (anonymous namespace)::OpaquePaintHasher>*) |
947 | | |
948 | 0 | SkPath GetClipBoxPath(FT_Face ft_face, uint16_t glyph_id, bool untransformed) { |
949 | 0 | SkPath resultPath; |
950 | |
|
951 | 0 | using DoneFTSize = SkFunctionWrapper<decltype(FT_Done_Size), FT_Done_Size>; |
952 | 0 | std::unique_ptr<std::remove_pointer_t<FT_Size>, DoneFTSize> unscaledFtSize = nullptr; |
953 | |
|
954 | 0 | FT_Size oldSize = ft_face->size; |
955 | 0 | FT_Matrix oldTransform; |
956 | 0 | FT_Vector oldDelta; |
957 | 0 | FT_Error err = 0; |
958 | |
|
959 | 0 | if (untransformed) { |
960 | 0 | unscaledFtSize.reset( |
961 | 0 | [ft_face]() -> FT_Size { |
962 | 0 | FT_Size size; |
963 | 0 | FT_Error err = FT_New_Size(ft_face, &size); |
964 | 0 | if (err != 0) { |
965 | 0 | SK_TRACEFTR(err, |
966 | 0 | "FT_New_Size(%s) failed in generateFacePathStaticCOLRv1.", |
967 | 0 | ft_face->family_name); |
968 | 0 | return nullptr; |
969 | 0 | } |
970 | 0 | return size; |
971 | 0 | }()); Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::GetClipBoxPath(FT_FaceRec_*, unsigned short, bool)::$_4::operator()() const Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::GetClipBoxPath(FT_FaceRec_*, unsigned short, bool)::$_6::operator()() const |
972 | 0 | if (!unscaledFtSize) { |
973 | 0 | return resultPath; |
974 | 0 | } |
975 | | |
976 | 0 | err = FT_Activate_Size(unscaledFtSize.get()); |
977 | 0 | if (err != 0) { |
978 | 0 | return resultPath; |
979 | 0 | } |
980 | | |
981 | 0 | err = FT_Set_Char_Size(ft_face, SkIntToFDot6(ft_face->units_per_EM), 0, 0, 0); |
982 | 0 | if (err != 0) { |
983 | 0 | return resultPath; |
984 | 0 | } |
985 | | |
986 | 0 | FT_Get_Transform(ft_face, &oldTransform, &oldDelta); |
987 | 0 | FT_Set_Transform(ft_face, nullptr, nullptr); |
988 | 0 | } |
989 | |
|
990 | 0 | FT_ClipBox colrGlyphClipBox; |
991 | 0 | if (FT_Get_Color_Glyph_ClipBox(ft_face, glyph_id, &colrGlyphClipBox)) { |
992 | 0 | resultPath = SkPath::Polygon({{SkFDot6ToScalar(colrGlyphClipBox.bottom_left.x), |
993 | 0 | -SkFDot6ToScalar(colrGlyphClipBox.bottom_left.y)}, |
994 | 0 | {SkFDot6ToScalar(colrGlyphClipBox.top_left.x), |
995 | 0 | -SkFDot6ToScalar(colrGlyphClipBox.top_left.y)}, |
996 | 0 | {SkFDot6ToScalar(colrGlyphClipBox.top_right.x), |
997 | 0 | -SkFDot6ToScalar(colrGlyphClipBox.top_right.y)}, |
998 | 0 | {SkFDot6ToScalar(colrGlyphClipBox.bottom_right.x), |
999 | 0 | -SkFDot6ToScalar(colrGlyphClipBox.bottom_right.y)}}, |
1000 | 0 | true); |
1001 | 0 | } |
1002 | |
|
1003 | 0 | if (untransformed) { |
1004 | 0 | err = FT_Activate_Size(oldSize); |
1005 | 0 | if (err != 0) { |
1006 | 0 | return resultPath; |
1007 | 0 | } |
1008 | 0 | FT_Set_Transform(ft_face, &oldTransform, &oldDelta); |
1009 | 0 | } |
1010 | |
|
1011 | 0 | return resultPath; |
1012 | 0 | } Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::GetClipBoxPath(FT_FaceRec_*, unsigned short, bool) Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::GetClipBoxPath(FT_FaceRec_*, unsigned short, bool) |
1013 | | |
1014 | | bool colrv1_start_glyph(SkCanvas* canvas, |
1015 | | const FT_Color* palette, |
1016 | | FT_Face ft_face, |
1017 | | uint16_t glyph_id, |
1018 | 0 | FT_Color_Root_Transform root_transform) { |
1019 | 0 | FT_OpaquePaint opaque_paint; |
1020 | 0 | opaque_paint.p = nullptr; |
1021 | 0 | bool has_colrv1_layers = false; |
1022 | 0 | if (FT_Get_Color_Glyph_Paint(ft_face, glyph_id, root_transform, &opaque_paint)) { |
1023 | 0 | has_colrv1_layers = true; |
1024 | |
|
1025 | 0 | SkPath clipBoxPath = |
1026 | 0 | GetClipBoxPath(ft_face, glyph_id, root_transform == FT_COLOR_NO_ROOT_TRANSFORM); |
1027 | 0 | if (!clipBoxPath.isEmpty()) { |
1028 | 0 | canvas->clipPath(clipBoxPath, true); |
1029 | 0 | } |
1030 | |
|
1031 | 0 | VisitedSet visited_set; |
1032 | 0 | colrv1_traverse_paint(canvas, palette, ft_face, opaque_paint, &visited_set); |
1033 | 0 | } |
1034 | 0 | return has_colrv1_layers; |
1035 | 0 | } |
1036 | | |
1037 | | bool colrv1_start_glyph_bounds(SkMatrix *ctm, |
1038 | | SkRect* bounds, |
1039 | | FT_Face ft_face, |
1040 | | uint16_t glyph_id, |
1041 | | FT_Color_Root_Transform root_transform); |
1042 | | |
1043 | | bool colrv1_traverse_paint_bounds(SkMatrix* ctm, |
1044 | | SkRect* bounds, |
1045 | | FT_Face face, |
1046 | | FT_OpaquePaint opaque_paint, |
1047 | 0 | VisitedSet* visited_set) { |
1048 | | // Cycle detection, see section "5.7.11.1.9 Color glyphs as a directed acyclic graph". |
1049 | 0 | if (visited_set->contains(opaque_paint)) { |
1050 | 0 | return false; |
1051 | 0 | } |
1052 | | |
1053 | 0 | visited_set->add(opaque_paint); |
1054 | 0 | SK_AT_SCOPE_EXIT(visited_set->remove(opaque_paint)); |
1055 | |
|
1056 | 0 | FT_COLR_Paint paint; |
1057 | 0 | if (!FT_Get_Paint(face, opaque_paint, &paint)) { |
1058 | 0 | return false; |
1059 | 0 | } |
1060 | | |
1061 | | // Keep track of failures to retrieve the FT_COLR_Paint from FreeType in the |
1062 | | // recursion, cancel recursion when a paint retrieval fails. |
1063 | 0 | bool traverse_result = true; |
1064 | 0 | SkMatrix restore_matrix = *ctm; |
1065 | 0 | SK_AT_SCOPE_EXIT(*ctm = restore_matrix); |
1066 | |
|
1067 | 0 | switch (paint.format) { |
1068 | 0 | case FT_COLR_PAINTFORMAT_COLR_LAYERS: { |
1069 | 0 | FT_LayerIterator& layer_iterator = paint.u.colr_layers.layer_iterator; |
1070 | 0 | FT_OpaquePaint opaque_paint_fetch; |
1071 | 0 | opaque_paint_fetch.p = nullptr; |
1072 | 0 | while (FT_Get_Paint_Layers(face, &layer_iterator, &opaque_paint_fetch)) { |
1073 | 0 | colrv1_traverse_paint_bounds(ctm, bounds, face, opaque_paint_fetch, visited_set); |
1074 | 0 | } |
1075 | 0 | break; |
1076 | 0 | } |
1077 | 0 | case FT_COLR_PAINTFORMAT_GLYPH: { |
1078 | 0 | FT_UInt glyphID = paint.u.glyph.glyphID; |
1079 | 0 | SkPath path; |
1080 | 0 | if ((traverse_result = generateFacePathCOLRv1(face, glyphID, &path))) { |
1081 | 0 | path.transform(*ctm); |
1082 | 0 | bounds->join(path.getBounds()); |
1083 | 0 | } |
1084 | 0 | break; |
1085 | 0 | } |
1086 | 0 | case FT_COLR_PAINTFORMAT_COLR_GLYPH: |
1087 | 0 | traverse_result = colrv1_start_glyph_bounds( |
1088 | 0 | ctm, bounds, face, paint.u.colr_glyph.glyphID, FT_COLOR_NO_ROOT_TRANSFORM); |
1089 | 0 | break; |
1090 | | |
1091 | 0 | case FT_COLR_PAINTFORMAT_TRANSFORM: { |
1092 | 0 | SkMatrix transform_matrix; |
1093 | 0 | colrv1_transform(face, paint, nullptr, &transform_matrix); |
1094 | 0 | ctm->preConcat(transform_matrix); |
1095 | 0 | traverse_result = colrv1_traverse_paint_bounds( |
1096 | 0 | ctm, bounds, face, paint.u.transform.paint, visited_set); |
1097 | 0 | break; |
1098 | 0 | } |
1099 | 0 | case FT_COLR_PAINTFORMAT_TRANSLATE: { |
1100 | 0 | SkMatrix transform_matrix; |
1101 | 0 | colrv1_transform(face, paint, nullptr, &transform_matrix); |
1102 | 0 | ctm->preConcat(transform_matrix); |
1103 | 0 | traverse_result = colrv1_traverse_paint_bounds( |
1104 | 0 | ctm, bounds, face, paint.u.translate.paint, visited_set); |
1105 | 0 | break; |
1106 | 0 | } |
1107 | 0 | case FT_COLR_PAINTFORMAT_SCALE: { |
1108 | 0 | SkMatrix transform_matrix; |
1109 | 0 | colrv1_transform(face, paint, nullptr, &transform_matrix); |
1110 | 0 | ctm->preConcat(transform_matrix); |
1111 | 0 | traverse_result = colrv1_traverse_paint_bounds( |
1112 | 0 | ctm, bounds, face, paint.u.scale.paint, visited_set); |
1113 | 0 | break; |
1114 | 0 | } |
1115 | 0 | case FT_COLR_PAINTFORMAT_ROTATE: { |
1116 | 0 | SkMatrix transform_matrix; |
1117 | 0 | colrv1_transform(face, paint, nullptr, &transform_matrix); |
1118 | 0 | ctm->preConcat(transform_matrix); |
1119 | 0 | traverse_result = colrv1_traverse_paint_bounds( |
1120 | 0 | ctm, bounds, face, paint.u.rotate.paint, visited_set); |
1121 | 0 | break; |
1122 | 0 | } |
1123 | 0 | case FT_COLR_PAINTFORMAT_SKEW: { |
1124 | 0 | SkMatrix transform_matrix; |
1125 | 0 | colrv1_transform(face, paint, nullptr, &transform_matrix); |
1126 | 0 | ctm->preConcat(transform_matrix); |
1127 | 0 | traverse_result = colrv1_traverse_paint_bounds( |
1128 | 0 | ctm, bounds, face, paint.u.skew.paint, visited_set); |
1129 | 0 | break; |
1130 | 0 | } |
1131 | 0 | case FT_COLR_PAINTFORMAT_COMPOSITE: { |
1132 | 0 | traverse_result = colrv1_traverse_paint_bounds( |
1133 | 0 | ctm, bounds, face, paint.u.composite.backdrop_paint, visited_set); |
1134 | 0 | traverse_result = colrv1_traverse_paint_bounds( |
1135 | 0 | ctm, bounds, face, paint.u.composite.source_paint, visited_set); |
1136 | 0 | break; |
1137 | 0 | } |
1138 | 0 | case FT_COLR_PAINTFORMAT_SOLID: |
1139 | 0 | case FT_COLR_PAINTFORMAT_LINEAR_GRADIENT: |
1140 | 0 | case FT_COLR_PAINTFORMAT_RADIAL_GRADIENT: |
1141 | 0 | case FT_COLR_PAINTFORMAT_SWEEP_GRADIENT: { |
1142 | 0 | break; |
1143 | 0 | } |
1144 | 0 | default: |
1145 | 0 | SkASSERT(false); |
1146 | 0 | break; |
1147 | 0 | } |
1148 | 0 | return traverse_result; |
1149 | 0 | } Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::colrv1_traverse_paint_bounds(SkMatrix*, SkRect*, FT_FaceRec_*, FT_Opaque_Paint_, SkTHashSet<FT_Opaque_Paint_, (anonymous namespace)::OpaquePaintHasher>*) Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::colrv1_traverse_paint_bounds(SkMatrix*, SkRect*, FT_FaceRec_*, FT_Opaque_Paint_, SkTHashSet<FT_Opaque_Paint_, (anonymous namespace)::OpaquePaintHasher>*) |
1150 | | |
1151 | | |
1152 | | bool colrv1_start_glyph_bounds(SkMatrix *ctm, |
1153 | | SkRect* bounds, |
1154 | | FT_Face ft_face, |
1155 | | uint16_t glyph_id, |
1156 | 0 | FT_Color_Root_Transform root_transform) { |
1157 | 0 | FT_OpaquePaint opaque_paint; |
1158 | 0 | opaque_paint.p = nullptr; |
1159 | 0 | bool has_colrv1_layers = false; |
1160 | 0 | if (FT_Get_Color_Glyph_Paint(ft_face, glyph_id, root_transform, &opaque_paint)) { |
1161 | 0 | has_colrv1_layers = true; |
1162 | 0 | VisitedSet visited_set; |
1163 | 0 | colrv1_traverse_paint_bounds(ctm, bounds, ft_face, opaque_paint, &visited_set); |
1164 | 0 | } |
1165 | 0 | return has_colrv1_layers; |
1166 | 0 | } |
1167 | | #endif // TT_SUPPORT_COLRV1 |
1168 | | |
1169 | | } // namespace |
1170 | | |
1171 | | void SkScalerContext_FreeType_Base::generateGlyphImage( |
1172 | | FT_Face face, |
1173 | | const SkGlyph& glyph, |
1174 | | const SkMatrix& bitmapTransform) |
1175 | 0 | { |
1176 | 0 | const bool doBGR = SkToBool(fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag); |
1177 | 0 | const bool doVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag); |
1178 | |
|
1179 | 0 | switch ( face->glyph->format ) { |
1180 | 0 | case FT_GLYPH_FORMAT_OUTLINE: { |
1181 | 0 | FT_Outline* outline = &face->glyph->outline; |
1182 | |
|
1183 | 0 | int dx = 0, dy = 0; |
1184 | 0 | if (this->isSubpixel()) { |
1185 | 0 | dx = SkFixedToFDot6(glyph.getSubXFixed()); |
1186 | 0 | dy = SkFixedToFDot6(glyph.getSubYFixed()); |
1187 | | // negate dy since freetype-y-goes-up and skia-y-goes-down |
1188 | 0 | dy = -dy; |
1189 | 0 | } |
1190 | |
|
1191 | 0 | memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight); |
1192 | |
|
1193 | 0 | #ifdef FT_COLOR_H |
1194 | 0 | if (SkMask::kARGB32_Format == glyph.fMaskFormat) { |
1195 | 0 | SkBitmap dstBitmap; |
1196 | | // TODO: mark this as sRGB when the blits will be sRGB. |
1197 | 0 | dstBitmap.setInfo(SkImageInfo::Make(glyph.fWidth, glyph.fHeight, |
1198 | 0 | kN32_SkColorType, |
1199 | 0 | kPremul_SkAlphaType), |
1200 | 0 | glyph.rowBytes()); |
1201 | 0 | dstBitmap.setPixels(glyph.fImage); |
1202 | | |
1203 | | // Scale unscaledBitmap into dstBitmap. |
1204 | 0 | SkCanvas canvas(dstBitmap); |
1205 | | #ifdef SK_SHOW_TEXT_BLIT_COVERAGE |
1206 | | canvas.clear(0x33FF0000); |
1207 | | #else |
1208 | 0 | canvas.clear(SK_ColorTRANSPARENT); |
1209 | 0 | #endif |
1210 | 0 | canvas.translate(-glyph.fLeft, -glyph.fTop); |
1211 | |
|
1212 | 0 | if (this->isSubpixel()) { |
1213 | 0 | canvas.translate(SkFixedToScalar(glyph.getSubXFixed()), |
1214 | 0 | SkFixedToScalar(glyph.getSubYFixed())); |
1215 | 0 | } |
1216 | |
|
1217 | 0 | SkPaint paint; |
1218 | 0 | paint.setAntiAlias(true); |
1219 | |
|
1220 | 0 | FT_Color *palette; |
1221 | 0 | FT_Error err = FT_Palette_Select(face, 0, &palette); |
1222 | 0 | if (err) { |
1223 | 0 | SK_TRACEFTR(err, "Could not get palette from %s fontFace.", face->family_name); |
1224 | 0 | return; |
1225 | 0 | } |
1226 | | |
1227 | 0 | FT_Bool haveLayers = false; |
1228 | |
|
1229 | 0 | #ifdef TT_SUPPORT_COLRV1 |
1230 | | // Only attempt to draw COLRv1 glyph is FreeType is new enough |
1231 | | // to have the COLRv1 additions, as indicated by the |
1232 | | // TT_SUPPORT_COLRV1 flag defined by the FreeType headers in |
1233 | | // that case. |
1234 | |
|
1235 | 0 | haveLayers = colrv1_start_glyph(&canvas, palette, face, glyph.getGlyphID(), |
1236 | 0 | FT_COLOR_INCLUDE_ROOT_TRANSFORM); |
1237 | | #else |
1238 | | haveLayers = false; |
1239 | | #endif |
1240 | 0 | if (!haveLayers) { |
1241 | | // If we didn't have colr v1 layers, try v0 layers. |
1242 | 0 | FT_LayerIterator layerIterator; |
1243 | 0 | layerIterator.p = NULL; |
1244 | 0 | FT_UInt layerGlyphIndex = 0; |
1245 | 0 | FT_UInt layerColorIndex = 0; |
1246 | 0 | while (FT_Get_Color_Glyph_Layer(face, glyph.getGlyphID(), &layerGlyphIndex, |
1247 | 0 | &layerColorIndex, &layerIterator)) { |
1248 | 0 | haveLayers = true; |
1249 | 0 | if (layerColorIndex == 0xFFFF) { |
1250 | 0 | paint.setColor(SK_ColorBLACK); |
1251 | 0 | } else { |
1252 | 0 | SkColor color = SkColorSetARGB(palette[layerColorIndex].alpha, |
1253 | 0 | palette[layerColorIndex].red, |
1254 | 0 | palette[layerColorIndex].green, |
1255 | 0 | palette[layerColorIndex].blue); |
1256 | 0 | paint.setColor(color); |
1257 | 0 | } |
1258 | 0 | SkPath path; |
1259 | 0 | if (this->generateFacePath(face, layerGlyphIndex, &path)) { |
1260 | 0 | canvas.drawPath(path, paint); |
1261 | 0 | } |
1262 | 0 | } |
1263 | 0 | } |
1264 | |
|
1265 | 0 | if (!haveLayers) { |
1266 | 0 | SK_TRACEFTR(err, "Could not get layers (neither v0, nor v1) from %s fontFace.", |
1267 | 0 | face->family_name); |
1268 | 0 | return; |
1269 | 0 | } |
1270 | 0 | } else |
1271 | 0 | #endif |
1272 | 0 | if (SkMask::kLCD16_Format == glyph.fMaskFormat) { |
1273 | 0 | FT_Outline_Translate(outline, dx, dy); |
1274 | 0 | FT_Error err = FT_Render_Glyph(face->glyph, doVert ? FT_RENDER_MODE_LCD_V : |
1275 | 0 | FT_RENDER_MODE_LCD); |
1276 | 0 | if (err) { |
1277 | 0 | SK_TRACEFTR(err, "Could not render glyph %p.", face->glyph); |
1278 | 0 | return; |
1279 | 0 | } |
1280 | | |
1281 | 0 | SkMask mask = glyph.mask(); |
1282 | | #ifdef SK_SHOW_TEXT_BLIT_COVERAGE |
1283 | | memset(mask.fImage, 0x80, mask.fBounds.height() * mask.fRowBytes); |
1284 | | #endif |
1285 | 0 | FT_GlyphSlotRec& ftGlyph = *face->glyph; |
1286 | |
|
1287 | 0 | if (!SkIRect::Intersects(mask.fBounds, |
1288 | 0 | SkIRect::MakeXYWH( ftGlyph.bitmap_left, |
1289 | 0 | -ftGlyph.bitmap_top, |
1290 | 0 | ftGlyph.bitmap.width, |
1291 | 0 | ftGlyph.bitmap.rows))) |
1292 | 0 | { |
1293 | 0 | return; |
1294 | 0 | } |
1295 | | |
1296 | | // If the FT_Bitmap extent is larger, discard bits of the bitmap outside the mask. |
1297 | | // If the SkMask extent is larger, shrink mask to fit bitmap (clearing discarded). |
1298 | 0 | unsigned char* origBuffer = ftGlyph.bitmap.buffer; |
1299 | | // First align the top left (origin). |
1300 | 0 | if (-ftGlyph.bitmap_top < mask.fBounds.fTop) { |
1301 | 0 | int32_t topDiff = mask.fBounds.fTop - (-ftGlyph.bitmap_top); |
1302 | 0 | ftGlyph.bitmap.buffer += ftGlyph.bitmap.pitch * topDiff; |
1303 | 0 | ftGlyph.bitmap.rows -= topDiff; |
1304 | 0 | ftGlyph.bitmap_top = -mask.fBounds.fTop; |
1305 | 0 | } |
1306 | 0 | if (ftGlyph.bitmap_left < mask.fBounds.fLeft) { |
1307 | 0 | int32_t leftDiff = mask.fBounds.fLeft - ftGlyph.bitmap_left; |
1308 | 0 | ftGlyph.bitmap.buffer += leftDiff; |
1309 | 0 | ftGlyph.bitmap.width -= leftDiff; |
1310 | 0 | ftGlyph.bitmap_left = mask.fBounds.fLeft; |
1311 | 0 | } |
1312 | 0 | if (mask.fBounds.fTop < -ftGlyph.bitmap_top) { |
1313 | 0 | mask.fImage += mask.fRowBytes * (-ftGlyph.bitmap_top - mask.fBounds.fTop); |
1314 | 0 | mask.fBounds.fTop = -ftGlyph.bitmap_top; |
1315 | 0 | } |
1316 | 0 | if (mask.fBounds.fLeft < ftGlyph.bitmap_left) { |
1317 | 0 | mask.fImage += sizeof(uint16_t) * (ftGlyph.bitmap_left - mask.fBounds.fLeft); |
1318 | 0 | mask.fBounds.fLeft = ftGlyph.bitmap_left; |
1319 | 0 | } |
1320 | | // Origins aligned, clean up the width and height. |
1321 | 0 | int ftVertScale = (doVert ? 3 : 1); |
1322 | 0 | int ftHoriScale = (doVert ? 1 : 3); |
1323 | 0 | if (mask.fBounds.height() * ftVertScale < SkToInt(ftGlyph.bitmap.rows)) { |
1324 | 0 | ftGlyph.bitmap.rows = mask.fBounds.height() * ftVertScale; |
1325 | 0 | } |
1326 | 0 | if (mask.fBounds.width() * ftHoriScale < SkToInt(ftGlyph.bitmap.width)) { |
1327 | 0 | ftGlyph.bitmap.width = mask.fBounds.width() * ftHoriScale; |
1328 | 0 | } |
1329 | 0 | if (SkToInt(ftGlyph.bitmap.rows) < mask.fBounds.height() * ftVertScale) { |
1330 | 0 | mask.fBounds.fBottom = mask.fBounds.fTop + ftGlyph.bitmap.rows / ftVertScale; |
1331 | 0 | } |
1332 | 0 | if (SkToInt(ftGlyph.bitmap.width) < mask.fBounds.width() * ftHoriScale) { |
1333 | 0 | mask.fBounds.fRight = mask.fBounds.fLeft + ftGlyph.bitmap.width / ftHoriScale; |
1334 | 0 | } |
1335 | 0 | if (fPreBlend.isApplicable()) { |
1336 | 0 | copyFT2LCD16<true>(ftGlyph.bitmap, mask, doBGR, |
1337 | 0 | fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); |
1338 | 0 | } else { |
1339 | 0 | copyFT2LCD16<false>(ftGlyph.bitmap, mask, doBGR, |
1340 | 0 | fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); |
1341 | 0 | } |
1342 | | // Restore the buffer pointer so FreeType can properly free it. |
1343 | 0 | ftGlyph.bitmap.buffer = origBuffer; |
1344 | 0 | } else { |
1345 | 0 | FT_BBox bbox; |
1346 | 0 | FT_Bitmap target; |
1347 | 0 | FT_Outline_Get_CBox(outline, &bbox); |
1348 | | /* |
1349 | | what we really want to do for subpixel is |
1350 | | offset(dx, dy) |
1351 | | compute_bounds |
1352 | | offset(bbox & !63) |
1353 | | but that is two calls to offset, so we do the following, which |
1354 | | achieves the same thing with only one offset call. |
1355 | | */ |
1356 | 0 | FT_Outline_Translate(outline, dx - ((bbox.xMin + dx) & ~63), |
1357 | 0 | dy - ((bbox.yMin + dy) & ~63)); |
1358 | |
|
1359 | 0 | target.width = glyph.fWidth; |
1360 | 0 | target.rows = glyph.fHeight; |
1361 | 0 | target.pitch = glyph.rowBytes(); |
1362 | 0 | target.buffer = reinterpret_cast<uint8_t*>(glyph.fImage); |
1363 | 0 | target.pixel_mode = compute_pixel_mode(glyph.fMaskFormat); |
1364 | 0 | target.num_grays = 256; |
1365 | |
|
1366 | 0 | FT_Outline_Get_Bitmap(face->glyph->library, outline, &target); |
1367 | | #ifdef SK_SHOW_TEXT_BLIT_COVERAGE |
1368 | | for (int y = 0; y < glyph.fHeight; ++y) { |
1369 | | for (int x = 0; x < glyph.fWidth; ++x) { |
1370 | | uint8_t& a = ((uint8_t*)glyph.fImage)[(glyph.rowBytes() * y) + x]; |
1371 | | a = std::max<uint8_t>(a, 0x20); |
1372 | | } |
1373 | | } |
1374 | | #endif |
1375 | 0 | } |
1376 | 0 | } break; |
1377 | |
|
1378 | 0 | case FT_GLYPH_FORMAT_BITMAP: { |
1379 | 0 | FT_Pixel_Mode pixel_mode = static_cast<FT_Pixel_Mode>(face->glyph->bitmap.pixel_mode); |
1380 | 0 | SkMask::Format maskFormat = static_cast<SkMask::Format>(glyph.fMaskFormat); |
1381 | | |
1382 | | // Assume that the other formats do not exist. |
1383 | 0 | SkASSERT(FT_PIXEL_MODE_MONO == pixel_mode || |
1384 | 0 | FT_PIXEL_MODE_GRAY == pixel_mode || |
1385 | 0 | FT_PIXEL_MODE_BGRA == pixel_mode); |
1386 | | |
1387 | | // These are the only formats this ScalerContext should request. |
1388 | 0 | SkASSERT(SkMask::kBW_Format == maskFormat || |
1389 | 0 | SkMask::kA8_Format == maskFormat || |
1390 | 0 | SkMask::kARGB32_Format == maskFormat || |
1391 | 0 | SkMask::kLCD16_Format == maskFormat); |
1392 | | |
1393 | | // If no scaling needed, directly copy glyph bitmap. |
1394 | 0 | if (bitmapTransform.isIdentity()) { |
1395 | 0 | SkMask dstMask = glyph.mask(); |
1396 | 0 | copyFTBitmap(face->glyph->bitmap, dstMask); |
1397 | 0 | break; |
1398 | 0 | } |
1399 | | |
1400 | | // Otherwise, scale the bitmap. |
1401 | | |
1402 | | // Copy the FT_Bitmap into an SkBitmap (either A8 or ARGB) |
1403 | 0 | SkBitmap unscaledBitmap; |
1404 | | // TODO: mark this as sRGB when the blits will be sRGB. |
1405 | 0 | unscaledBitmap.allocPixels(SkImageInfo::Make(face->glyph->bitmap.width, |
1406 | 0 | face->glyph->bitmap.rows, |
1407 | 0 | SkColorType_for_FTPixelMode(pixel_mode), |
1408 | 0 | kPremul_SkAlphaType)); |
1409 | |
|
1410 | 0 | SkMask unscaledBitmapAlias; |
1411 | 0 | unscaledBitmapAlias.fImage = reinterpret_cast<uint8_t*>(unscaledBitmap.getPixels()); |
1412 | 0 | unscaledBitmapAlias.fBounds.setWH(unscaledBitmap.width(), unscaledBitmap.height()); |
1413 | 0 | unscaledBitmapAlias.fRowBytes = unscaledBitmap.rowBytes(); |
1414 | 0 | unscaledBitmapAlias.fFormat = SkMaskFormat_for_SkColorType(unscaledBitmap.colorType()); |
1415 | 0 | copyFTBitmap(face->glyph->bitmap, unscaledBitmapAlias); |
1416 | | |
1417 | | // Wrap the glyph's mask in a bitmap, unless the glyph's mask is BW or LCD. |
1418 | | // BW requires an A8 target for resizing, which can then be down sampled. |
1419 | | // LCD should use a 4x A8 target, which will then be down sampled. |
1420 | | // For simplicity, LCD uses A8 and is replicated. |
1421 | 0 | int bitmapRowBytes = 0; |
1422 | 0 | if (SkMask::kBW_Format != maskFormat && SkMask::kLCD16_Format != maskFormat) { |
1423 | 0 | bitmapRowBytes = glyph.rowBytes(); |
1424 | 0 | } |
1425 | 0 | SkBitmap dstBitmap; |
1426 | | // TODO: mark this as sRGB when the blits will be sRGB. |
1427 | 0 | dstBitmap.setInfo(SkImageInfo::Make(glyph.fWidth, glyph.fHeight, |
1428 | 0 | SkColorType_for_SkMaskFormat(maskFormat), |
1429 | 0 | kPremul_SkAlphaType), |
1430 | 0 | bitmapRowBytes); |
1431 | 0 | if (SkMask::kBW_Format == maskFormat || SkMask::kLCD16_Format == maskFormat) { |
1432 | 0 | dstBitmap.allocPixels(); |
1433 | 0 | } else { |
1434 | 0 | dstBitmap.setPixels(glyph.fImage); |
1435 | 0 | } |
1436 | | |
1437 | | // Scale unscaledBitmap into dstBitmap. |
1438 | 0 | SkCanvas canvas(dstBitmap); |
1439 | | #ifdef SK_SHOW_TEXT_BLIT_COVERAGE |
1440 | | canvas.clear(0x33FF0000); |
1441 | | #else |
1442 | 0 | canvas.clear(SK_ColorTRANSPARENT); |
1443 | 0 | #endif |
1444 | 0 | canvas.translate(-glyph.fLeft, -glyph.fTop); |
1445 | 0 | canvas.concat(bitmapTransform); |
1446 | 0 | canvas.translate(face->glyph->bitmap_left, -face->glyph->bitmap_top); |
1447 | |
|
1448 | 0 | SkSamplingOptions sampling(SkFilterMode::kLinear, SkMipmapMode::kNearest); |
1449 | 0 | canvas.drawImage(unscaledBitmap.asImage().get(), 0, 0, sampling, nullptr); |
1450 | | |
1451 | | // If the destination is BW or LCD, convert from A8. |
1452 | 0 | if (SkMask::kBW_Format == maskFormat) { |
1453 | | // Copy the A8 dstBitmap into the A1 glyph.fImage. |
1454 | 0 | SkMask dstMask = glyph.mask(); |
1455 | 0 | packA8ToA1(dstMask, dstBitmap.getAddr8(0, 0), dstBitmap.rowBytes()); |
1456 | 0 | } else if (SkMask::kLCD16_Format == maskFormat) { |
1457 | | // Copy the A8 dstBitmap into the LCD16 glyph.fImage. |
1458 | 0 | uint8_t* src = dstBitmap.getAddr8(0, 0); |
1459 | 0 | uint16_t* dst = reinterpret_cast<uint16_t*>(glyph.fImage); |
1460 | 0 | for (int y = dstBitmap.height(); y --> 0;) { |
1461 | 0 | for (int x = 0; x < dstBitmap.width(); ++x) { |
1462 | 0 | dst[x] = grayToRGB16(src[x]); |
1463 | 0 | } |
1464 | 0 | dst = (uint16_t*)((char*)dst + glyph.rowBytes()); |
1465 | 0 | src += dstBitmap.rowBytes(); |
1466 | 0 | } |
1467 | 0 | } |
1468 | |
|
1469 | 0 | } break; |
1470 | |
|
1471 | 0 | default: |
1472 | 0 | SkDEBUGFAIL("unknown glyph format"); |
1473 | 0 | memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight); |
1474 | 0 | return; |
1475 | 0 | } |
1476 | | |
1477 | | // We used to always do this pre-USE_COLOR_LUMINANCE, but with colorlum, |
1478 | | // it is optional |
1479 | 0 | #if defined(SK_GAMMA_APPLY_TO_A8) |
1480 | 0 | if (SkMask::kA8_Format == glyph.fMaskFormat && fPreBlend.isApplicable()) { |
1481 | 0 | uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage; |
1482 | 0 | unsigned rowBytes = glyph.rowBytes(); |
1483 | |
|
1484 | 0 | for (int y = glyph.fHeight - 1; y >= 0; --y) { |
1485 | 0 | for (int x = glyph.fWidth - 1; x >= 0; --x) { |
1486 | 0 | dst[x] = fPreBlend.fG[dst[x]]; |
1487 | 0 | } |
1488 | 0 | dst += rowBytes; |
1489 | 0 | } |
1490 | 0 | } |
1491 | 0 | #endif |
1492 | 0 | } Unexecuted instantiation: SkScalerContext_FreeType_Base::generateGlyphImage(FT_FaceRec_*, SkGlyph const&, SkMatrix const&) Unexecuted instantiation: SkScalerContext_FreeType_Base::generateGlyphImage(FT_FaceRec_*, SkGlyph const&, SkMatrix const&) |
1493 | | |
1494 | | /////////////////////////////////////////////////////////////////////////////// |
1495 | | |
1496 | | namespace { |
1497 | | |
1498 | | class SkFTGeometrySink { |
1499 | | SkPath* fPath; |
1500 | | bool fStarted; |
1501 | | FT_Vector fCurrent; |
1502 | | |
1503 | 0 | void goingTo(const FT_Vector* pt) { |
1504 | 0 | if (!fStarted) { |
1505 | 0 | fStarted = true; |
1506 | 0 | fPath->moveTo(SkFDot6ToScalar(fCurrent.x), -SkFDot6ToScalar(fCurrent.y)); |
1507 | 0 | } |
1508 | 0 | fCurrent = *pt; |
1509 | 0 | } |
1510 | | |
1511 | 0 | bool currentIsNot(const FT_Vector* pt) { |
1512 | 0 | return fCurrent.x != pt->x || fCurrent.y != pt->y; |
1513 | 0 | } |
1514 | | |
1515 | 0 | static int Move(const FT_Vector* pt, void* ctx) { |
1516 | 0 | SkFTGeometrySink& self = *(SkFTGeometrySink*)ctx; |
1517 | 0 | if (self.fStarted) { |
1518 | 0 | self.fPath->close(); |
1519 | 0 | self.fStarted = false; |
1520 | 0 | } |
1521 | 0 | self.fCurrent = *pt; |
1522 | 0 | return 0; |
1523 | 0 | } |
1524 | | |
1525 | 0 | static int Line(const FT_Vector* pt, void* ctx) { |
1526 | 0 | SkFTGeometrySink& self = *(SkFTGeometrySink*)ctx; |
1527 | 0 | if (self.currentIsNot(pt)) { |
1528 | 0 | self.goingTo(pt); |
1529 | 0 | self.fPath->lineTo(SkFDot6ToScalar(pt->x), -SkFDot6ToScalar(pt->y)); |
1530 | 0 | } |
1531 | 0 | return 0; |
1532 | 0 | } |
1533 | | |
1534 | 0 | static int Quad(const FT_Vector* pt0, const FT_Vector* pt1, void* ctx) { |
1535 | 0 | SkFTGeometrySink& self = *(SkFTGeometrySink*)ctx; |
1536 | 0 | if (self.currentIsNot(pt0) || self.currentIsNot(pt1)) { |
1537 | 0 | self.goingTo(pt1); |
1538 | 0 | self.fPath->quadTo(SkFDot6ToScalar(pt0->x), -SkFDot6ToScalar(pt0->y), |
1539 | 0 | SkFDot6ToScalar(pt1->x), -SkFDot6ToScalar(pt1->y)); |
1540 | 0 | } |
1541 | 0 | return 0; |
1542 | 0 | } |
1543 | | |
1544 | 0 | static int Cubic(const FT_Vector* pt0, const FT_Vector* pt1, const FT_Vector* pt2, void* ctx) { |
1545 | 0 | SkFTGeometrySink& self = *(SkFTGeometrySink*)ctx; |
1546 | 0 | if (self.currentIsNot(pt0) || self.currentIsNot(pt1) || self.currentIsNot(pt2)) { |
1547 | 0 | self.goingTo(pt2); |
1548 | 0 | self.fPath->cubicTo(SkFDot6ToScalar(pt0->x), -SkFDot6ToScalar(pt0->y), |
1549 | 0 | SkFDot6ToScalar(pt1->x), -SkFDot6ToScalar(pt1->y), |
1550 | 0 | SkFDot6ToScalar(pt2->x), -SkFDot6ToScalar(pt2->y)); |
1551 | 0 | } |
1552 | 0 | return 0; |
1553 | 0 | } |
1554 | | |
1555 | | public: |
1556 | 0 | SkFTGeometrySink(SkPath* path) : fPath{path}, fStarted{false}, fCurrent{0,0} {} |
1557 | | |
1558 | | static constexpr const FT_Outline_Funcs Funcs{ |
1559 | | /*move_to =*/ SkFTGeometrySink::Move, |
1560 | | /*line_to =*/ SkFTGeometrySink::Line, |
1561 | | /*conic_to =*/ SkFTGeometrySink::Quad, |
1562 | | /*cubic_to =*/ SkFTGeometrySink::Cubic, |
1563 | | /*shift = */ 0, |
1564 | | /*delta =*/ 0, |
1565 | | }; |
1566 | | }; |
1567 | | |
1568 | 0 | bool generateGlyphPathStatic(FT_Face face, SkPath* path) { |
1569 | 0 | SkFTGeometrySink sink{path}; |
1570 | 0 | FT_Error err = FT_Outline_Decompose(&face->glyph->outline, &SkFTGeometrySink::Funcs, &sink); |
1571 | |
|
1572 | 0 | if (err != 0) { |
1573 | 0 | path->reset(); |
1574 | 0 | return false; |
1575 | 0 | } |
1576 | | |
1577 | 0 | path->close(); |
1578 | 0 | return true; |
1579 | 0 | } |
1580 | | |
1581 | 0 | bool generateFacePathStatic(FT_Face face, SkGlyphID glyphID, SkPath* path) { |
1582 | 0 | uint32_t flags = 0; //fLoadGlyphFlags; |
1583 | 0 | flags |= FT_LOAD_NO_BITMAP; // ignore embedded bitmaps so we're sure to get the outline |
1584 | 0 | flags &= ~FT_LOAD_RENDER; // don't scan convert (we just want the outline) |
1585 | |
|
1586 | 0 | FT_Error err = FT_Load_Glyph(face, glyphID, flags); |
1587 | 0 | if (err != 0) { |
1588 | 0 | path->reset(); |
1589 | 0 | return false; |
1590 | 0 | } |
1591 | | |
1592 | 0 | if (!generateGlyphPathStatic(face, path)) { |
1593 | 0 | path->reset(); |
1594 | 0 | return false; |
1595 | 0 | } |
1596 | 0 | return true; |
1597 | 0 | } |
1598 | | |
1599 | | #ifdef TT_SUPPORT_COLRV1 |
1600 | 0 | bool generateFacePathCOLRv1(FT_Face face, SkGlyphID glyphID, SkPath* path) { |
1601 | 0 | uint32_t flags = 0; |
1602 | 0 | flags |= FT_LOAD_NO_BITMAP; // ignore embedded bitmaps so we're sure to get the outline |
1603 | 0 | flags &= ~FT_LOAD_RENDER; // don't scan convert (we just want the outline) |
1604 | |
|
1605 | 0 | flags |= FT_LOAD_IGNORE_TRANSFORM; |
1606 | | |
1607 | |
|
1608 | 0 | using DoneFTSize = SkFunctionWrapper<decltype(FT_Done_Size), FT_Done_Size>; |
1609 | 0 | std::unique_ptr<std::remove_pointer_t<FT_Size>, DoneFTSize> unscaledFtSize([face]() -> FT_Size { |
1610 | 0 | FT_Size size; |
1611 | 0 | FT_Error err = FT_New_Size(face, &size); |
1612 | 0 | if (err != 0) { |
1613 | 0 | SK_TRACEFTR(err, "FT_New_Size(%s) failed in generateFacePathStaticCOLRv1.", face->family_name); |
1614 | 0 | return nullptr; |
1615 | 0 | } |
1616 | 0 | return size; |
1617 | 0 | }()); Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::generateFacePathCOLRv1(FT_FaceRec_*, unsigned short, SkPath*)::$_6::operator()() const Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::generateFacePathCOLRv1(FT_FaceRec_*, unsigned short, SkPath*)::$_13::operator()() const |
1618 | |
|
1619 | 0 | if (!unscaledFtSize) { |
1620 | 0 | return false; |
1621 | 0 | } |
1622 | | |
1623 | 0 | FT_Size oldSize = face->size; |
1624 | |
|
1625 | 0 | auto try_generate_path = [face, &unscaledFtSize, glyphID, flags, path]() { |
1626 | 0 | FT_Error err = 0; |
1627 | |
|
1628 | 0 | err = FT_Activate_Size(unscaledFtSize.get()); |
1629 | 0 | if (err != 0) { |
1630 | 0 | return false; |
1631 | 0 | } |
1632 | | |
1633 | 0 | err = FT_Set_Char_Size(face, SkIntToFDot6(face->units_per_EM), |
1634 | 0 | SkIntToFDot6(face->units_per_EM), 72, 72); |
1635 | 0 | if (err != 0) { |
1636 | 0 | return false; |
1637 | 0 | } |
1638 | | |
1639 | 0 | err = FT_Load_Glyph(face, glyphID, flags); |
1640 | 0 | if (err != 0) { |
1641 | 0 | path->reset(); |
1642 | 0 | return false; |
1643 | 0 | } |
1644 | | |
1645 | 0 | if (!generateGlyphPathStatic(face, path)) { |
1646 | 0 | path->reset(); |
1647 | 0 | return false; |
1648 | 0 | } |
1649 | | |
1650 | 0 | return true; |
1651 | 0 | }; Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::generateFacePathCOLRv1(FT_FaceRec_*, unsigned short, SkPath*)::$_7::operator()() const Unexecuted instantiation: SkFontHost_FreeType_common.cpp:(anonymous namespace)::generateFacePathCOLRv1(FT_FaceRec_*, unsigned short, SkPath*)::$_14::operator()() const |
1652 | |
|
1653 | 0 | bool path_generation_result = try_generate_path(); |
1654 | |
|
1655 | 0 | FT_Activate_Size(oldSize); |
1656 | |
|
1657 | 0 | return path_generation_result; |
1658 | 0 | } |
1659 | | #endif |
1660 | | |
1661 | | } // namespace |
1662 | | |
1663 | 0 | bool SkScalerContext_FreeType_Base::generateGlyphPath(FT_Face face, SkPath* path) { |
1664 | 0 | return generateGlyphPathStatic(face, path); |
1665 | 0 | } |
1666 | | |
1667 | | bool SkScalerContext_FreeType_Base::generateFacePath(FT_Face face, |
1668 | | SkGlyphID glyphID, |
1669 | 0 | SkPath* path) { |
1670 | 0 | return generateFacePathStatic(face, glyphID, path); |
1671 | 0 | } |
1672 | | |
1673 | | bool SkScalerContext_FreeType_Base::computeColrV1GlyphBoundingBox(FT_Face face, |
1674 | | SkGlyphID glyphID, |
1675 | 0 | FT_BBox* boundingBox) { |
1676 | 0 | #ifdef TT_SUPPORT_COLRV1 |
1677 | 0 | SkMatrix ctm; |
1678 | 0 | SkRect bounds = SkRect::MakeEmpty(); |
1679 | 0 | if (!colrv1_start_glyph_bounds(&ctm, &bounds, face, glyphID, FT_COLOR_INCLUDE_ROOT_TRANSFORM)) { |
1680 | 0 | return false; |
1681 | 0 | } |
1682 | | |
1683 | | /* Convert back to FT_BBox as caller needs it in this format. */ |
1684 | 0 | bounds.sort(); |
1685 | 0 | boundingBox->xMin = SkScalarToFDot6(bounds.left()); |
1686 | 0 | boundingBox->xMax = SkScalarToFDot6(bounds.right()); |
1687 | 0 | boundingBox->yMin = SkScalarToFDot6(-bounds.bottom()); |
1688 | 0 | boundingBox->yMax = SkScalarToFDot6(-bounds.top()); |
1689 | |
|
1690 | 0 | return true; |
1691 | | #else |
1692 | | SkASSERT(false); |
1693 | | return false; |
1694 | | #endif |
1695 | 0 | } |