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 "src/ports/SkFontHost_FreeType_common.h"
10
11 #include "include/core/SkBitmap.h"
12 #include "include/core/SkCanvas.h"
13 #include "include/core/SkColor.h"
14 #include "include/core/SkDrawable.h"
15 #include "include/core/SkGraphics.h"
16 #include "include/core/SkImage.h"
17 #include "include/core/SkOpenTypeSVGDecoder.h"
18 #include "include/core/SkPath.h"
19 #include "include/effects/SkGradientShader.h"
20 #include "include/pathops/SkPathOps.h"
21 #include "include/private/SkColorData.h"
22 #include "include/private/base/SkTo.h"
23 #include "src/core/SkFDot6.h"
24 #include "src/core/SkSwizzlePriv.h"
25 #include "src/core/SkTHash.h"
26
27 #include <algorithm>
28 #include <utility>
29 #include <vector>
30
31 #include <ft2build.h>
32 #include <freetype/freetype.h>
33 #include <freetype/ftbitmap.h>
34 #ifdef FT_COLOR_H
35 # include <freetype/ftcolor.h>
36 #endif
37 #include <freetype/ftimage.h>
38 #include <freetype/ftoutln.h>
39 #include <freetype/ftsizes.h>
40 // In the past, FT_GlyphSlot_Own_Bitmap was defined in this header file.
41 #include <freetype/ftsynth.h>
42
43 using namespace skia_private;
44
45 namespace {
46 [[maybe_unused]] static inline const constexpr bool kSkShowTextBlitCoverage = false;
47 }
48
49 #if defined(FT_CONFIG_OPTION_SVG)
50 # include <freetype/otsvg.h>
51 #endif
52
53 #ifdef TT_SUPPORT_COLRV1
54 // FT_ClipBox and FT_Get_Color_Glyph_ClipBox introduced VER-2-11-0-18-g47cf8ebf4
55 // FT_COLR_COMPOSITE_PLUS and renumbering introduced VER-2-11-0-21-ge40ae7569
56 // FT_SIZEOF_LONG_LONG introduced VER-2-11-0-31-gffdac8d67
57 // FT_PaintRadialGradient changed size and layout at VER-2-11-0-147-gd3d3ff76d
58 // FT_STATIC_CAST introduced VER-2-11-0-172-g9079c5d91
59 // So undefine TT_SUPPORT_COLRV1 before 2.11.1 but not if FT_STATIC_CAST is defined.
60 #if (((FREETYPE_MAJOR) < 2) || \
61 ((FREETYPE_MAJOR) == 2 && (FREETYPE_MINOR) < 11) || \
62 ((FREETYPE_MAJOR) == 2 && (FREETYPE_MINOR) == 11 && (FREETYPE_PATCH) < 1)) && \
63 !defined(FT_STATIC_CAST)
64 # undef TT_SUPPORT_COLRV1
65 #else
66 # include "src/base/SkScopeExit.h"
67 #endif
68 #endif
69
70 // FT_OUTLINE_OVERLAP was added in FreeType 2.10.3
71 #ifndef FT_OUTLINE_OVERLAP
72 # define FT_OUTLINE_OVERLAP 0x40
73 #endif
74
75 // FT_LOAD_COLOR and the corresponding FT_Pixel_Mode::FT_PIXEL_MODE_BGRA
76 // were introduced in FreeType 2.5.0.
77 // The following may be removed once FreeType 2.5.0 is required to build.
78 #ifndef FT_LOAD_COLOR
79 # define FT_LOAD_COLOR ( 1L << 20 )
80 # define FT_PIXEL_MODE_BGRA 7
81 #endif
82
83 #ifdef SK_DEBUG
SkTraceFtrGetError(int e)84 const char* SkTraceFtrGetError(int e) {
85 switch ((FT_Error)e) {
86 #undef FTERRORS_H_
87 #define FT_ERRORDEF( e, v, s ) case v: return s;
88 #define FT_ERROR_START_LIST
89 #define FT_ERROR_END_LIST
90 #include FT_ERRORS_H
91 #undef FT_ERRORDEF
92 #undef FT_ERROR_START_LIST
93 #undef FT_ERROR_END_LIST
94 default: return "";
95 }
96 }
97 #endif // SK_DEBUG
98
99 #ifdef TT_SUPPORT_COLRV1
operator ==(const FT_OpaquePaint & a,const FT_OpaquePaint & b)100 bool operator==(const FT_OpaquePaint& a, const FT_OpaquePaint& b) {
101 return a.p == b.p && a.insert_root_transform == b.insert_root_transform;
102 }
103
104 // The stop_offset field is being upgraded to a larger representation in FreeType, and changed from
105 // 2.14 to 16.16. Adjust the shift factor depending on size type.
106 static_assert(sizeof(FT_Fixed) != sizeof(FT_F2Dot14));
107 constexpr float kColorStopShift =
108 sizeof(FT_ColorStop::stop_offset) == sizeof(FT_F2Dot14) ? 1 << 14 : 1 << 16;
109 #endif
110
111 namespace {
112 using SkUniqueFTSize = std::unique_ptr<FT_SizeRec, SkFunctionObject<FT_Done_Size>>;
113
compute_pixel_mode(SkMask::Format format)114 FT_Pixel_Mode compute_pixel_mode(SkMask::Format format) {
115 switch (format) {
116 case SkMask::kBW_Format:
117 return FT_PIXEL_MODE_MONO;
118 case SkMask::kA8_Format:
119 default:
120 return FT_PIXEL_MODE_GRAY;
121 }
122 }
123
124 ///////////////////////////////////////////////////////////////////////////////
125
packTriple(U8CPU r,U8CPU g,U8CPU b)126 uint16_t packTriple(U8CPU r, U8CPU g, U8CPU b) {
127 if constexpr (kSkShowTextBlitCoverage) {
128 r = std::max(r, (U8CPU)0x40);
129 g = std::max(g, (U8CPU)0x40);
130 b = std::max(b, (U8CPU)0x40);
131 }
132 return SkPack888ToRGB16(r, g, b);
133 }
134
grayToRGB16(U8CPU gray)135 uint16_t grayToRGB16(U8CPU gray) {
136 if constexpr (kSkShowTextBlitCoverage) {
137 gray = std::max(gray, (U8CPU)0x40);
138 }
139 return SkPack888ToRGB16(gray, gray, gray);
140 }
141
bittst(const uint8_t data[],int bitOffset)142 int bittst(const uint8_t data[], int bitOffset) {
143 SkASSERT(bitOffset >= 0);
144 int lowBit = data[bitOffset >> 3] >> (~bitOffset & 7);
145 return lowBit & 1;
146 }
147
148 /**
149 * Copies a FT_Bitmap into an SkMask with the same dimensions.
150 *
151 * FT_PIXEL_MODE_MONO
152 * FT_PIXEL_MODE_GRAY
153 * FT_PIXEL_MODE_LCD
154 * FT_PIXEL_MODE_LCD_V
155 */
156 template<bool APPLY_PREBLEND>
copyFT2LCD16(const FT_Bitmap & bitmap,SkMaskBuilder * dstMask,int lcdIsBGR,const uint8_t * tableR,const uint8_t * tableG,const uint8_t * tableB)157 void copyFT2LCD16(const FT_Bitmap& bitmap, SkMaskBuilder* dstMask, int lcdIsBGR,
158 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB)
159 {
160 SkASSERT(SkMask::kLCD16_Format == dstMask->fFormat);
161 if (FT_PIXEL_MODE_LCD != bitmap.pixel_mode) {
162 SkASSERT(dstMask->fBounds.width() == static_cast<int>(bitmap.width));
163 }
164 if (FT_PIXEL_MODE_LCD_V != bitmap.pixel_mode) {
165 SkASSERT(dstMask->fBounds.height() == static_cast<int>(bitmap.rows));
166 }
167
168 const uint8_t* src = bitmap.buffer;
169 uint16_t* dst = reinterpret_cast<uint16_t*>(dstMask->image());
170 const size_t dstRB = dstMask->fRowBytes;
171
172 const int width = dstMask->fBounds.width();
173 const int height = dstMask->fBounds.height();
174
175 switch (bitmap.pixel_mode) {
176 case FT_PIXEL_MODE_MONO:
177 for (int y = height; y --> 0;) {
178 for (int x = 0; x < width; ++x) {
179 dst[x] = -bittst(src, x);
180 }
181 dst = (uint16_t*)((char*)dst + dstRB);
182 src += bitmap.pitch;
183 }
184 break;
185 case FT_PIXEL_MODE_GRAY:
186 for (int y = height; y --> 0;) {
187 for (int x = 0; x < width; ++x) {
188 dst[x] = grayToRGB16(src[x]);
189 }
190 dst = (uint16_t*)((char*)dst + dstRB);
191 src += bitmap.pitch;
192 }
193 break;
194 case FT_PIXEL_MODE_LCD:
195 SkASSERT(3 * dstMask->fBounds.width() == static_cast<int>(bitmap.width));
196 for (int y = height; y --> 0;) {
197 const uint8_t* triple = src;
198 if (lcdIsBGR) {
199 for (int x = 0; x < width; x++) {
200 dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(triple[2], tableR),
201 sk_apply_lut_if<APPLY_PREBLEND>(triple[1], tableG),
202 sk_apply_lut_if<APPLY_PREBLEND>(triple[0], tableB));
203 triple += 3;
204 }
205 } else {
206 for (int x = 0; x < width; x++) {
207 dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(triple[0], tableR),
208 sk_apply_lut_if<APPLY_PREBLEND>(triple[1], tableG),
209 sk_apply_lut_if<APPLY_PREBLEND>(triple[2], tableB));
210 triple += 3;
211 }
212 }
213 src += bitmap.pitch;
214 dst = (uint16_t*)((char*)dst + dstRB);
215 }
216 break;
217 case FT_PIXEL_MODE_LCD_V:
218 SkASSERT(3 * dstMask->fBounds.height() == static_cast<int>(bitmap.rows));
219 for (int y = height; y --> 0;) {
220 const uint8_t* srcR = src;
221 const uint8_t* srcG = srcR + bitmap.pitch;
222 const uint8_t* srcB = srcG + bitmap.pitch;
223 if (lcdIsBGR) {
224 using std::swap;
225 swap(srcR, srcB);
226 }
227 for (int x = 0; x < width; x++) {
228 dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(*srcR++, tableR),
229 sk_apply_lut_if<APPLY_PREBLEND>(*srcG++, tableG),
230 sk_apply_lut_if<APPLY_PREBLEND>(*srcB++, tableB));
231 }
232 src += 3 * bitmap.pitch;
233 dst = (uint16_t*)((char*)dst + dstRB);
234 }
235 break;
236 default:
237 SkDEBUGF("FT_Pixel_Mode %d", bitmap.pixel_mode);
238 SkDEBUGFAIL("unsupported FT_Pixel_Mode for LCD16");
239 break;
240 }
241 }
242
243 /**
244 * Copies a FT_Bitmap into an SkMask with the same dimensions.
245 *
246 * Yes, No, Never Requested, Never Produced
247 *
248 * kBW kA8 k3D kARGB32 kLCD16
249 * FT_PIXEL_MODE_MONO Y Y NR N Y
250 * FT_PIXEL_MODE_GRAY N Y NR N Y
251 * FT_PIXEL_MODE_GRAY2 NP NP NR NP NP
252 * FT_PIXEL_MODE_GRAY4 NP NP NR NP NP
253 * FT_PIXEL_MODE_LCD NP NP NR NP NP
254 * FT_PIXEL_MODE_LCD_V NP NP NR NP NP
255 * FT_PIXEL_MODE_BGRA N N NR Y N
256 *
257 * TODO: All of these N need to be Y or otherwise ruled out.
258 */
copyFTBitmap(const FT_Bitmap & srcFTBitmap,SkMaskBuilder * dstMask)259 void copyFTBitmap(const FT_Bitmap& srcFTBitmap, SkMaskBuilder* dstMask) {
260 SkASSERTF(dstMask->fBounds.width() == static_cast<int>(srcFTBitmap.width),
261 "dstMask.fBounds.width() = %d\n"
262 "static_cast<int>(srcFTBitmap.width) = %d",
263 dstMask->fBounds.width(),
264 static_cast<int>(srcFTBitmap.width)
265 );
266 SkASSERTF(dstMask->fBounds.height() == static_cast<int>(srcFTBitmap.rows),
267 "dstMask.fBounds.height() = %d\n"
268 "static_cast<int>(srcFTBitmap.rows) = %d",
269 dstMask->fBounds.height(),
270 static_cast<int>(srcFTBitmap.rows)
271 );
272
273 const uint8_t* src = reinterpret_cast<const uint8_t*>(srcFTBitmap.buffer);
274 const FT_Pixel_Mode srcFormat = static_cast<FT_Pixel_Mode>(srcFTBitmap.pixel_mode);
275 // FT_Bitmap::pitch is an int and allowed to be negative.
276 const int srcPitch = srcFTBitmap.pitch;
277 const size_t srcRowBytes = SkTAbs(srcPitch);
278
279 uint8_t* dst = dstMask->image();
280 const SkMask::Format dstFormat = dstMask->fFormat;
281 const size_t dstRowBytes = dstMask->fRowBytes;
282
283 const size_t width = srcFTBitmap.width;
284 const size_t height = srcFTBitmap.rows;
285
286 if (SkMask::kLCD16_Format == dstFormat) {
287 copyFT2LCD16<false>(srcFTBitmap, dstMask, false, nullptr, nullptr, nullptr);
288 return;
289 }
290
291 if ((FT_PIXEL_MODE_MONO == srcFormat && SkMask::kBW_Format == dstFormat) ||
292 (FT_PIXEL_MODE_GRAY == srcFormat && SkMask::kA8_Format == dstFormat))
293 {
294 size_t commonRowBytes = std::min(srcRowBytes, dstRowBytes);
295 for (size_t y = height; y --> 0;) {
296 memcpy(dst, src, commonRowBytes);
297 src += srcPitch;
298 dst += dstRowBytes;
299 }
300 } else if (FT_PIXEL_MODE_MONO == srcFormat && SkMask::kA8_Format == dstFormat) {
301 for (size_t y = height; y --> 0;) {
302 uint8_t byte = 0;
303 int bits = 0;
304 const uint8_t* src_row = src;
305 uint8_t* dst_row = dst;
306 for (size_t x = width; x --> 0;) {
307 if (0 == bits) {
308 byte = *src_row++;
309 bits = 8;
310 }
311 *dst_row++ = byte & 0x80 ? 0xff : 0x00;
312 bits--;
313 byte <<= 1;
314 }
315 src += srcPitch;
316 dst += dstRowBytes;
317 }
318 } else if (FT_PIXEL_MODE_BGRA == srcFormat && SkMask::kARGB32_Format == dstFormat) {
319 // FT_PIXEL_MODE_BGRA is pre-multiplied.
320 for (size_t y = height; y --> 0;) {
321 const uint8_t* src_row = src;
322 SkPMColor* dst_row = reinterpret_cast<SkPMColor*>(dst);
323 for (size_t x = 0; x < width; ++x) {
324 uint8_t b = *src_row++;
325 uint8_t g = *src_row++;
326 uint8_t r = *src_row++;
327 uint8_t a = *src_row++;
328 *dst_row++ = SkPackARGB32(a, r, g, b);
329 if constexpr (kSkShowTextBlitCoverage) {
330 *(dst_row-1) = SkFourByteInterp256(*(dst_row-1), SK_ColorWHITE, 0x40);
331 }
332 }
333 src += srcPitch;
334 dst += dstRowBytes;
335 }
336 } else {
337 SkDEBUGF("FT_Pixel_Mode %d, SkMask::Format %d\n", srcFormat, dstFormat);
338 SkDEBUGFAIL("unsupported combination of FT_Pixel_Mode and SkMask::Format");
339 }
340 }
341
convert_8_to_1(unsigned byte)342 inline int convert_8_to_1(unsigned byte) {
343 SkASSERT(byte <= 0xFF);
344 // Arbitrary decision that making the cutoff at 1/4 instead of 1/2 in general looks better.
345 return (byte >> 6) != 0;
346 }
347
pack_8_to_1(const uint8_t alpha[8])348 uint8_t pack_8_to_1(const uint8_t alpha[8]) {
349 unsigned bits = 0;
350 for (int i = 0; i < 8; ++i) {
351 bits <<= 1;
352 bits |= convert_8_to_1(alpha[i]);
353 }
354 return SkToU8(bits);
355 }
356
packA8ToA1(SkMaskBuilder * dstMask,const uint8_t * src,size_t srcRB)357 void packA8ToA1(SkMaskBuilder* dstMask, const uint8_t* src, size_t srcRB) {
358 const int height = dstMask->fBounds.height();
359 const int width = dstMask->fBounds.width();
360 const int octs = width >> 3;
361 const int leftOverBits = width & 7;
362
363 uint8_t* dst = dstMask->image();
364 const int dstPad = dstMask->fRowBytes - SkAlign8(width)/8;
365 SkASSERT(dstPad >= 0);
366
367 const int srcPad = srcRB - width;
368 SkASSERT(srcPad >= 0);
369
370 for (int y = 0; y < height; ++y) {
371 for (int i = 0; i < octs; ++i) {
372 *dst++ = pack_8_to_1(src);
373 src += 8;
374 }
375 if (leftOverBits > 0) {
376 unsigned bits = 0;
377 int shift = 7;
378 for (int i = 0; i < leftOverBits; ++i, --shift) {
379 bits |= convert_8_to_1(*src++) << shift;
380 }
381 *dst++ = bits;
382 }
383 src += srcPad;
384 dst += dstPad;
385 }
386 }
387
SkMaskFormat_for_SkColorType(SkColorType colorType)388 inline SkMask::Format SkMaskFormat_for_SkColorType(SkColorType colorType) {
389 switch (colorType) {
390 case kAlpha_8_SkColorType:
391 return SkMask::kA8_Format;
392 case kN32_SkColorType:
393 return SkMask::kARGB32_Format;
394 default:
395 SkDEBUGFAIL("unsupported SkBitmap::Config");
396 return SkMask::kA8_Format;
397 }
398 }
399
SkColorType_for_FTPixelMode(FT_Pixel_Mode pixel_mode)400 inline SkColorType SkColorType_for_FTPixelMode(FT_Pixel_Mode pixel_mode) {
401 switch (pixel_mode) {
402 case FT_PIXEL_MODE_MONO:
403 case FT_PIXEL_MODE_GRAY:
404 return kAlpha_8_SkColorType;
405 case FT_PIXEL_MODE_BGRA:
406 return kN32_SkColorType;
407 default:
408 SkDEBUGFAIL("unsupported FT_PIXEL_MODE");
409 return kAlpha_8_SkColorType;
410 }
411 }
412
SkColorType_for_SkMaskFormat(SkMask::Format format)413 inline SkColorType SkColorType_for_SkMaskFormat(SkMask::Format format) {
414 switch (format) {
415 case SkMask::kBW_Format:
416 case SkMask::kA8_Format:
417 case SkMask::kLCD16_Format:
418 return kAlpha_8_SkColorType;
419 case SkMask::kARGB32_Format:
420 return kN32_SkColorType;
421 default:
422 SkDEBUGFAIL("unsupported destination SkBitmap::Config");
423 return kAlpha_8_SkColorType;
424 }
425 }
426
427 // Only build COLRv1 rendering code if FreeType is new enough to have COLRv1
428 // additions. FreeType defines a macro in the ftoption header to tell us whether
429 // it does support these features.
430 #ifdef TT_SUPPORT_COLRV1
431
432 const uint16_t kForegroundColorPaletteIndex = 0xFFFF;
433
434 // This linear interpolation is used for calculating a truncated color line in special edge cases.
435 // This interpolation needs to be kept in sync with what the gradient shader would normally do when
436 // truncating and drawing color lines. When drawing into N32 surfaces, this is expected to be true.
437 // If that changes, or if we support other color spaces in CPAL tables at some point, this needs to
438 // be looked at.
lerpSkColor(SkColor4f c0,SkColor4f c1,float t)439 SkColor4f lerpSkColor(SkColor4f c0, SkColor4f c1, float t) {
440 // Due to the floating point calculation in the caller, when interpolating between very narrow
441 // stops, we may get values outside the interpolation range, guard against these.
442 if (t < 0) {
443 return c0;
444 }
445 if (t > 1) {
446 return c1;
447 }
448
449 const auto c0_4f = skvx::float4::Load(c0.vec());
450 const auto c1_4f = skvx::float4::Load(c1.vec());
451 const auto c_4f = c0_4f + (c1_4f - c0_4f) * t;
452
453 SkColor4f l;
454 c_4f.store(l.vec());
455 return l;
456 }
457
458 enum TruncateStops {
459 TruncateStart,
460 TruncateEnd
461 };
462
463 // Truncate a vector of color stops at a previously computed stop position and insert at that
464 // position the color interpolated between the surrounding stops.
truncateToStopInterpolating(SkScalar zeroRadiusStop,std::vector<SkColor4f> & colors,std::vector<SkScalar> & stops,TruncateStops truncateStops)465 void truncateToStopInterpolating(SkScalar zeroRadiusStop,
466 std::vector<SkColor4f>& colors,
467 std::vector<SkScalar>& stops,
468 TruncateStops truncateStops) {
469 if (stops.size() <= 1u ||
470 zeroRadiusStop < stops.front() || stops.back() < zeroRadiusStop)
471 {
472 return;
473 }
474
475 size_t afterIndex = (truncateStops == TruncateStart)
476 ? std::lower_bound(stops.begin(), stops.end(), zeroRadiusStop) - stops.begin()
477 : std::upper_bound(stops.begin(), stops.end(), zeroRadiusStop) - stops.begin();
478
479 const float t = (zeroRadiusStop - stops[afterIndex - 1]) /
480 (stops[afterIndex] - stops[afterIndex - 1]);
481 SkColor4f lerpColor = lerpSkColor(colors[afterIndex - 1], colors[afterIndex], t);
482
483 if (truncateStops == TruncateStart) {
484 stops.erase(stops.begin(), stops.begin() + afterIndex);
485 colors.erase(colors.begin(), colors.begin() + afterIndex);
486 stops.insert(stops.begin(), 0);
487 colors.insert(colors.begin(), lerpColor);
488 } else {
489 stops.erase(stops.begin() + afterIndex, stops.end());
490 colors.erase(colors.begin() + afterIndex, colors.end());
491 stops.insert(stops.end(), 1);
492 colors.insert(colors.end(), lerpColor);
493 }
494 }
495
496 struct OpaquePaintHasher {
operator ()__anondad6fc2b0211::OpaquePaintHasher497 size_t operator()(const FT_OpaquePaint& opaquePaint) {
498 return SkGoodHash()(opaquePaint.p) ^
499 SkGoodHash()(opaquePaint.insert_root_transform);
500 }
501 };
502
503 using VisitedSet = THashSet<FT_OpaquePaint, OpaquePaintHasher>;
504
505 bool generateFacePathCOLRv1(FT_Face face, SkGlyphID glyphID, SkPath* path);
506
SkColrV1AlphaToFloat(uint16_t alpha)507 inline float SkColrV1AlphaToFloat(uint16_t alpha) { return (alpha / float(1 << 14)); }
508
509
ToSkTileMode(FT_PaintExtend extendMode)510 inline SkTileMode ToSkTileMode(FT_PaintExtend extendMode) {
511 switch (extendMode) {
512 case FT_COLR_PAINT_EXTEND_REPEAT:
513 return SkTileMode::kRepeat;
514 case FT_COLR_PAINT_EXTEND_REFLECT:
515 return SkTileMode::kMirror;
516 default:
517 return SkTileMode::kClamp;
518 }
519 }
520
ToSkBlendMode(FT_Composite_Mode compositeMode)521 inline SkBlendMode ToSkBlendMode(FT_Composite_Mode compositeMode) {
522 switch (compositeMode) {
523 case FT_COLR_COMPOSITE_CLEAR:
524 return SkBlendMode::kClear;
525 case FT_COLR_COMPOSITE_SRC:
526 return SkBlendMode::kSrc;
527 case FT_COLR_COMPOSITE_DEST:
528 return SkBlendMode::kDst;
529 case FT_COLR_COMPOSITE_SRC_OVER:
530 return SkBlendMode::kSrcOver;
531 case FT_COLR_COMPOSITE_DEST_OVER:
532 return SkBlendMode::kDstOver;
533 case FT_COLR_COMPOSITE_SRC_IN:
534 return SkBlendMode::kSrcIn;
535 case FT_COLR_COMPOSITE_DEST_IN:
536 return SkBlendMode::kDstIn;
537 case FT_COLR_COMPOSITE_SRC_OUT:
538 return SkBlendMode::kSrcOut;
539 case FT_COLR_COMPOSITE_DEST_OUT:
540 return SkBlendMode::kDstOut;
541 case FT_COLR_COMPOSITE_SRC_ATOP:
542 return SkBlendMode::kSrcATop;
543 case FT_COLR_COMPOSITE_DEST_ATOP:
544 return SkBlendMode::kDstATop;
545 case FT_COLR_COMPOSITE_XOR:
546 return SkBlendMode::kXor;
547 case FT_COLR_COMPOSITE_PLUS:
548 return SkBlendMode::kPlus;
549 case FT_COLR_COMPOSITE_SCREEN:
550 return SkBlendMode::kScreen;
551 case FT_COLR_COMPOSITE_OVERLAY:
552 return SkBlendMode::kOverlay;
553 case FT_COLR_COMPOSITE_DARKEN:
554 return SkBlendMode::kDarken;
555 case FT_COLR_COMPOSITE_LIGHTEN:
556 return SkBlendMode::kLighten;
557 case FT_COLR_COMPOSITE_COLOR_DODGE:
558 return SkBlendMode::kColorDodge;
559 case FT_COLR_COMPOSITE_COLOR_BURN:
560 return SkBlendMode::kColorBurn;
561 case FT_COLR_COMPOSITE_HARD_LIGHT:
562 return SkBlendMode::kHardLight;
563 case FT_COLR_COMPOSITE_SOFT_LIGHT:
564 return SkBlendMode::kSoftLight;
565 case FT_COLR_COMPOSITE_DIFFERENCE:
566 return SkBlendMode::kDifference;
567 case FT_COLR_COMPOSITE_EXCLUSION:
568 return SkBlendMode::kExclusion;
569 case FT_COLR_COMPOSITE_MULTIPLY:
570 return SkBlendMode::kMultiply;
571 case FT_COLR_COMPOSITE_HSL_HUE:
572 return SkBlendMode::kHue;
573 case FT_COLR_COMPOSITE_HSL_SATURATION:
574 return SkBlendMode::kSaturation;
575 case FT_COLR_COMPOSITE_HSL_COLOR:
576 return SkBlendMode::kColor;
577 case FT_COLR_COMPOSITE_HSL_LUMINOSITY:
578 return SkBlendMode::kLuminosity;
579 default:
580 return SkBlendMode::kDst;
581 }
582 }
583
ToSkMatrix(FT_Affine23 affine23)584 inline SkMatrix ToSkMatrix(FT_Affine23 affine23) {
585 // Convert from FreeType's FT_Affine23 column major order to SkMatrix row-major order.
586 return SkMatrix::MakeAll(
587 SkFixedToScalar(affine23.xx), -SkFixedToScalar(affine23.xy), SkFixedToScalar(affine23.dx),
588 -SkFixedToScalar(affine23.yx), SkFixedToScalar(affine23.yy), -SkFixedToScalar(affine23.dy),
589 0, 0, 1);
590 }
591
SkVectorProjection(SkPoint a,SkPoint b)592 inline SkPoint SkVectorProjection(SkPoint a, SkPoint b) {
593 SkScalar length = b.length();
594 if (!length) {
595 return SkPoint();
596 }
597 SkPoint bNormalized = b;
598 bNormalized.normalize();
599 bNormalized.scale(SkPoint::DotProduct(a, b) / length);
600 return bNormalized;
601 }
602
colrv1_configure_skpaint(FT_Face face,const SkSpan<SkColor> & palette,const SkColor foregroundColor,const FT_COLR_Paint & colrPaint,SkPaint * paint)603 bool colrv1_configure_skpaint(FT_Face face,
604 const SkSpan<SkColor>& palette,
605 const SkColor foregroundColor,
606 const FT_COLR_Paint& colrPaint,
607 SkPaint* paint) {
608 auto fetchColorStops = [&face, &palette, &foregroundColor](
609 const FT_ColorStopIterator& colorStopIterator,
610 std::vector<SkScalar>& stops,
611 std::vector<SkColor4f>& colors) -> bool {
612 const FT_UInt colorStopCount = colorStopIterator.num_color_stops;
613 if (colorStopCount == 0) {
614 return false;
615 }
616
617 // 5.7.11.2.4 ColorIndex, ColorStop and ColorLine
618 // "Applications shall apply the colorStops in increasing stopOffset order."
619 struct ColorStop {
620 SkScalar pos;
621 SkColor4f color;
622 };
623 std::vector<ColorStop> colorStopsSorted;
624 colorStopsSorted.resize(colorStopCount);
625
626 FT_ColorStop ftStop;
627 FT_ColorStopIterator mutable_color_stop_iterator = colorStopIterator;
628 while (FT_Get_Colorline_Stops(face, &ftStop, &mutable_color_stop_iterator)) {
629 FT_UInt index = mutable_color_stop_iterator.current_color_stop - 1;
630 ColorStop& skStop = colorStopsSorted[index];
631 skStop.pos = ftStop.stop_offset / kColorStopShift;
632 FT_UInt16& palette_index = ftStop.color.palette_index;
633 if (palette_index == kForegroundColorPaletteIndex) {
634 skStop.color = SkColor4f::FromColor(foregroundColor);
635 } else if (palette_index >= palette.size()) {
636 return false;
637 } else {
638 skStop.color = SkColor4f::FromColor(palette[palette_index]);
639 }
640 skStop.color.fA *= SkColrV1AlphaToFloat(ftStop.color.alpha);
641 }
642
643 std::stable_sort(colorStopsSorted.begin(), colorStopsSorted.end(),
644 [](const ColorStop& a, const ColorStop& b) { return a.pos < b.pos; });
645
646 stops.resize(colorStopCount);
647 colors.resize(colorStopCount);
648 for (size_t i = 0; i < colorStopCount; ++i) {
649 stops[i] = colorStopsSorted[i].pos;
650 colors[i] = colorStopsSorted[i].color;
651 }
652 return true;
653 };
654
655 switch (colrPaint.format) {
656 case FT_COLR_PAINTFORMAT_SOLID: {
657 FT_PaintSolid solid = colrPaint.u.solid;
658
659 // Dont' draw anything with this color if the palette index is out of bounds.
660 SkColor4f color = SkColors::kTransparent;
661 if (solid.color.palette_index == kForegroundColorPaletteIndex) {
662 color = SkColor4f::FromColor(foregroundColor);
663 } else if (solid.color.palette_index >= palette.size()) {
664 return false;
665 } else {
666 color = SkColor4f::FromColor(palette[solid.color.palette_index]);
667 }
668 color.fA *= SkColrV1AlphaToFloat(solid.color.alpha);
669 paint->setShader(nullptr);
670 paint->setColor(color);
671 return true;
672 }
673 case FT_COLR_PAINTFORMAT_LINEAR_GRADIENT: {
674 const FT_PaintLinearGradient& linearGradient = colrPaint.u.linear_gradient;
675 std::vector<SkScalar> stops;
676 std::vector<SkColor4f> colors;
677
678 if (!fetchColorStops(linearGradient.colorline.color_stop_iterator, stops, colors)) {
679 return false;
680 }
681
682 if (stops.size() == 1) {
683 paint->setColor(colors[0]);
684 return true;
685 }
686
687 SkPoint linePositions[2] = {SkPoint::Make( SkFixedToScalar(linearGradient.p0.x),
688 -SkFixedToScalar(linearGradient.p0.y)),
689 SkPoint::Make( SkFixedToScalar(linearGradient.p1.x),
690 -SkFixedToScalar(linearGradient.p1.y))};
691 SkPoint p0 = linePositions[0];
692 SkPoint p1 = linePositions[1];
693 SkPoint p2 = SkPoint::Make( SkFixedToScalar(linearGradient.p2.x),
694 -SkFixedToScalar(linearGradient.p2.y));
695
696 // If p0p1 or p0p2 are degenerate probably nothing should be drawn.
697 // If p0p1 and p0p2 are parallel then one side is the first color and the other side is
698 // the last color, depending on the direction.
699 // For now, just use the first color.
700 if (p1 == p0 || p2 == p0 || !SkPoint::CrossProduct(p1 - p0, p2 - p0)) {
701 paint->setColor(colors[0]);
702 return true;
703 }
704
705 // Follow implementation note in nanoemoji:
706 // https://github.com/googlefonts/nanoemoji/blob/0ac6e7bb4d8202db692574d8530a9b643f1b3b3c/src/nanoemoji/svg.py#L188
707 // to compute a new gradient end point P3 as the orthogonal
708 // projection of the vector from p0 to p1 onto a line perpendicular
709 // to line p0p2 and passing through p0.
710 SkVector perpendicularToP2P0 = (p2 - p0);
711 perpendicularToP2P0 = SkPoint::Make( perpendicularToP2P0.y(),
712 -perpendicularToP2P0.x());
713 SkVector p3 = p0 + SkVectorProjection((p1 - p0), perpendicularToP2P0);
714 linePositions[1] = p3;
715
716 // Project/scale points according to stop extrema along p0p3 line,
717 // p3 being the result of the projection above, then scale stops to
718 // to [0, 1] range so that repeat modes work. The Skia linear
719 // gradient shader performs the repeat modes over the 0 to 1 range,
720 // that's why we need to scale the stops to within that range.
721 SkTileMode tileMode = ToSkTileMode(linearGradient.colorline.extend);
722 SkScalar colorStopRange = stops.back() - stops.front();
723 // If the color stops are all at the same offset position, repeat and reflect modes
724 // become meaningless.
725 if (colorStopRange == 0.f) {
726 if (tileMode != SkTileMode::kClamp) {
727 paint->setColor(SK_ColorTRANSPARENT);
728 return true;
729 } else {
730 // Insert duplicated fake color stop in pad case at +1.0f to enable the projection
731 // of circles for an originally 0-length color stop range. Adding this stop will
732 // paint the equivalent gradient, because: All font specified color stops are in the
733 // same spot, mode is pad, so everything before this spot is painted with the first
734 // color, everything after this spot is painted with the last color. Not adding this
735 // stop will skip the projection and result in specifying non-normalized color stops
736 // to the shader.
737 stops.push_back(stops.back() + 1.0f);
738 colors.push_back(colors.back());
739 colorStopRange = 1.0f;
740 }
741 }
742 SkASSERT(colorStopRange != 0.f);
743
744 // If the colorStopRange is 0 at this point, the default behavior of the shader is to
745 // clamp to 1 color stops that are above 1, clamp to 0 for color stops that are below 0,
746 // and repeat the outer color stops at 0 and 1 if the color stops are inside the
747 // range. That will result in the correct rendering.
748 if ((colorStopRange != 1 || stops.front() != 0.f)) {
749 SkVector p0p3 = p3 - p0;
750 SkVector p0Offset = p0p3;
751 p0Offset.scale(stops.front());
752 SkVector p1Offset = p0p3;
753 p1Offset.scale(stops.back());
754
755 linePositions[0] = p0 + p0Offset;
756 linePositions[1] = p0 + p1Offset;
757
758 SkScalar scaleFactor = 1 / colorStopRange;
759 SkScalar startOffset = stops.front();
760 for (SkScalar& stop : stops) {
761 stop = (stop - startOffset) * scaleFactor;
762 }
763 }
764
765 sk_sp<SkShader> shader(SkGradientShader::MakeLinear(
766 linePositions,
767 colors.data(), SkColorSpace::MakeSRGB(), stops.data(), stops.size(),
768 tileMode,
769 SkGradientShader::Interpolation{
770 SkGradientShader::Interpolation::InPremul::kNo,
771 SkGradientShader::Interpolation::ColorSpace::kSRGB,
772 SkGradientShader::Interpolation::HueMethod::kShorter
773 },
774 nullptr));
775
776 SkASSERT(shader);
777 // An opaque color is needed to ensure the gradient is not modulated by alpha.
778 paint->setColor(SK_ColorBLACK);
779 paint->setShader(shader);
780 return true;
781 }
782 case FT_COLR_PAINTFORMAT_RADIAL_GRADIENT: {
783 const FT_PaintRadialGradient& radialGradient = colrPaint.u.radial_gradient;
784 SkPoint start = SkPoint::Make( SkFixedToScalar(radialGradient.c0.x),
785 -SkFixedToScalar(radialGradient.c0.y));
786 SkScalar startRadius = SkFixedToScalar(radialGradient.r0);
787 SkPoint end = SkPoint::Make( SkFixedToScalar(radialGradient.c1.x),
788 -SkFixedToScalar(radialGradient.c1.y));
789 SkScalar endRadius = SkFixedToScalar(radialGradient.r1);
790
791
792 std::vector<SkScalar> stops;
793 std::vector<SkColor4f> colors;
794 if (!fetchColorStops(radialGradient.colorline.color_stop_iterator, stops, colors)) {
795 return false;
796 }
797
798 if (stops.size() == 1) {
799 paint->setColor(colors[0]);
800 return true;
801 }
802
803 SkScalar colorStopRange = stops.back() - stops.front();
804 SkTileMode tileMode = ToSkTileMode(radialGradient.colorline.extend);
805
806 if (colorStopRange == 0.f) {
807 if (tileMode != SkTileMode::kClamp) {
808 paint->setColor(SK_ColorTRANSPARENT);
809 return true;
810 } else {
811 // Insert duplicated fake color stop in pad case at +1.0f to enable the projection
812 // of circles for an originally 0-length color stop range. Adding this stop will
813 // paint the equivalent gradient, because: All font specified color stops are in the
814 // same spot, mode is pad, so everything before this spot is painted with the first
815 // color, everything after this spot is painted with the last color. Not adding this
816 // stop will skip the projection and result in specifying non-normalized color stops
817 // to the shader.
818 stops.push_back(stops.back() + 1.0f);
819 colors.push_back(colors.back());
820 colorStopRange = 1.0f;
821 }
822 }
823 SkASSERT(colorStopRange != 0.f);
824
825 // If the colorStopRange is 0 at this point, the default behavior of the shader is to
826 // clamp to 1 color stops that are above 1, clamp to 0 for color stops that are below 0,
827 // and repeat the outer color stops at 0 and 1 if the color stops are inside the
828 // range. That will result in the correct rendering.
829 if (colorStopRange != 1 || stops.front() != 0.f) {
830 // For the Skia two-point caonical shader to understand the
831 // COLRv1 color stops we need to scale stops to 0 to 1 range and
832 // interpolate new centers and radii. Otherwise the shader
833 // clamps stops outside the range to 0 and 1 (larger interval)
834 // or repeats the outer stops at 0 and 1 if the (smaller
835 // interval).
836 SkVector startToEnd = end - start;
837 SkScalar radiusDiff = endRadius - startRadius;
838 SkScalar scaleFactor = 1 / colorStopRange;
839 SkScalar stopsStartOffset = stops.front();
840
841 SkVector startOffset = startToEnd;
842 startOffset.scale(stops.front());
843 SkVector endOffset = startToEnd;
844 endOffset.scale(stops.back());
845
846 // The order of the following computations is important in order to avoid
847 // overwriting start or startRadius before the second reassignment.
848 end = start + endOffset;
849 start = start + startOffset;
850 endRadius = startRadius + radiusDiff * stops.back();
851 startRadius = startRadius + radiusDiff * stops.front();
852
853 for (auto& stop : stops) {
854 stop = (stop - stopsStartOffset) * scaleFactor;
855 }
856 }
857
858 // For negative radii, interpolation is needed to prepare parameters suitable
859 // for invoking the shader. Implementation below as resolution discussed in
860 // https://github.com/googlefonts/colr-gradients-spec/issues/367.
861 // Truncate to manually interpolated color for tile mode clamp, otherwise
862 // calculate positive projected circles.
863 if (startRadius < 0 || endRadius < 0) {
864 if (startRadius == endRadius && startRadius < 0) {
865 paint->setColor(SK_ColorTRANSPARENT);
866 return true;
867 }
868
869 if (tileMode == SkTileMode::kClamp) {
870 SkVector startToEnd = end - start;
871 SkScalar radiusDiff = endRadius - startRadius;
872 SkScalar zeroRadiusStop = 0.f;
873 TruncateStops truncateSide = TruncateStart;
874 if (startRadius < 0) {
875 truncateSide = TruncateStart;
876
877 // Compute color stop position where radius is = 0. After the scaling
878 // of stop positions to the normal 0,1 range that we have done above,
879 // the size of the radius as a function of the color stops is: r(x) = r0
880 // + x*(r1-r0) Solving this function for r(x) = 0, we get: x = -r0 /
881 // (r1-r0)
882 zeroRadiusStop = -startRadius / (endRadius - startRadius);
883 startRadius = 0.f;
884 SkVector startEndDiff = end - start;
885 startEndDiff.scale(zeroRadiusStop);
886 start = start + startEndDiff;
887 }
888
889 if (endRadius < 0) {
890 truncateSide = TruncateEnd;
891 zeroRadiusStop = -startRadius / (endRadius - startRadius);
892 endRadius = 0.f;
893 SkVector startEndDiff = end - start;
894 startEndDiff.scale(1 - zeroRadiusStop);
895 end = end - startEndDiff;
896 }
897
898 if (!(startRadius == 0 && endRadius == 0)) {
899 truncateToStopInterpolating(
900 zeroRadiusStop, colors, stops, truncateSide);
901 } else {
902 // If both radii have become negative and where clamped to 0, we need to
903 // produce a single color cone, otherwise the shader colors the whole
904 // plane in a single color when two radii are specified as 0.
905 if (radiusDiff > 0) {
906 end = start + startToEnd;
907 endRadius = radiusDiff;
908 colors.erase(colors.begin(), colors.end() - 1);
909 stops.erase(stops.begin(), stops.end() - 1);
910 } else {
911 start -= startToEnd;
912 startRadius = -radiusDiff;
913 colors.erase(colors.begin() + 1, colors.end());
914 stops.erase(stops.begin() + 1, stops.end());
915 }
916 }
917 } else {
918 if (startRadius < 0 || endRadius < 0) {
919 auto roundIntegerMultiple = [](SkScalar factorZeroCrossing,
920 SkTileMode tileMode) {
921 int roundedMultiple = factorZeroCrossing > 0
922 ? ceilf(factorZeroCrossing)
923 : floorf(factorZeroCrossing) - 1;
924 if (tileMode == SkTileMode::kMirror && roundedMultiple % 2 != 0) {
925 roundedMultiple += roundedMultiple < 0 ? -1 : 1;
926 }
927 return roundedMultiple;
928 };
929
930 SkVector startToEnd = end - start;
931 SkScalar radiusDiff = endRadius - startRadius;
932 SkScalar factorZeroCrossing = (startRadius / (startRadius - endRadius));
933 bool inRange = 0.f <= factorZeroCrossing && factorZeroCrossing <= 1.0f;
934 SkScalar direction = inRange && radiusDiff < 0 ? -1.0f : 1.0f;
935 SkScalar circleProjectionFactor =
936 roundIntegerMultiple(factorZeroCrossing * direction, tileMode);
937 startToEnd.scale(circleProjectionFactor);
938 startRadius += circleProjectionFactor * radiusDiff;
939 endRadius += circleProjectionFactor * radiusDiff;
940 start += startToEnd;
941 end += startToEnd;
942 }
943 }
944 }
945
946 // An opaque color is needed to ensure the gradient is not modulated by alpha.
947 paint->setColor(SK_ColorBLACK);
948
949 paint->setShader(SkGradientShader::MakeTwoPointConical(
950 start, startRadius, end, endRadius,
951 colors.data(), SkColorSpace::MakeSRGB(), stops.data(), stops.size(),
952 tileMode,
953 SkGradientShader::Interpolation{
954 SkGradientShader::Interpolation::InPremul::kNo,
955 SkGradientShader::Interpolation::ColorSpace::kSRGB,
956 SkGradientShader::Interpolation::HueMethod::kShorter
957 },
958 nullptr));
959
960 return true;
961 }
962 case FT_COLR_PAINTFORMAT_SWEEP_GRADIENT: {
963 const FT_PaintSweepGradient& sweepGradient = colrPaint.u.sweep_gradient;
964 SkPoint center = SkPoint::Make( SkFixedToScalar(sweepGradient.center.x),
965 -SkFixedToScalar(sweepGradient.center.y));
966
967
968 SkScalar startAngle = SkFixedToScalar(sweepGradient.start_angle * 180.0f);
969 SkScalar endAngle = SkFixedToScalar(sweepGradient.end_angle * 180.0f);
970 // OpenType 1.9.1 adds a shift to the angle to ease specification of a 0 to 360
971 // degree sweep.
972 startAngle += 180.0f;
973 endAngle += 180.0f;
974
975 std::vector<SkScalar> stops;
976 std::vector<SkColor4f> colors;
977 if (!fetchColorStops(sweepGradient.colorline.color_stop_iterator, stops, colors)) {
978 return false;
979 }
980
981 if (stops.size() == 1) {
982 paint->setColor(colors[0]);
983 return true;
984 }
985
986 // An opaque color is needed to ensure the gradient is not modulated by alpha.
987 paint->setColor(SK_ColorBLACK);
988
989 // New (Var)SweepGradient implementation compliant with OpenType 1.9.1 from here.
990
991 // The shader expects stops from 0 to 1, so we need to account for
992 // minimum and maximum stop positions being different from 0 and
993 // 1. We do that by scaling minimum and maximum stop positions to
994 // the 0 to 1 interval and scaling the angles inverse proportionally.
995
996 // 1) Scale angles to their equivalent positions if stops were from 0 to 1.
997
998 SkScalar sectorAngle = endAngle - startAngle;
999 SkTileMode tileMode = ToSkTileMode(sweepGradient.colorline.extend);
1000 if (sectorAngle == 0 && tileMode != SkTileMode::kClamp) {
1001 // "If the ColorLine's extend mode is reflect or repeat and start and end angle
1002 // are equal, nothing is drawn.".
1003 paint->setColor(SK_ColorTRANSPARENT);
1004 return true;
1005 }
1006
1007
1008 SkScalar startAngleScaled = startAngle + sectorAngle * stops.front();
1009 SkScalar endAngleScaled = startAngle + sectorAngle * stops.back();
1010
1011 // 2) Scale stops accordingly to 0 to 1 range.
1012
1013 float colorStopRange = stops.back() - stops.front();
1014 if (colorStopRange == 0.f) {
1015 if (tileMode != SkTileMode::kClamp) {
1016 paint->setColor(SK_ColorTRANSPARENT);
1017 return true;
1018 } else {
1019 // Insert duplicated fake color stop in pad case at +1.0f to feed the shader correct
1020 // values and enable painting a pad sweep gradient with two colors. Adding this stop
1021 // will paint the equivalent gradient, because: All font specified color stops are
1022 // in the same spot, mode is pad, so everything before this spot is painted with the
1023 // first color, everything after this spot is painted with the last color. Not
1024 // adding this stop will skip the projection and result in specifying non-normalized
1025 // color stops to the shader.
1026 stops.push_back(stops.back() + 1.0f);
1027 colors.push_back(colors.back());
1028 colorStopRange = 1.0f;
1029 }
1030 }
1031
1032 SkScalar scaleFactor = 1 / colorStopRange;
1033 SkScalar startOffset = stops.front();
1034
1035 for (SkScalar& stop : stops) {
1036 stop = (stop - startOffset) * scaleFactor;
1037 }
1038
1039 /* https://docs.microsoft.com/en-us/typography/opentype/spec/colr#sweep-gradients
1040 * "The angles are expressed in counter-clockwise degrees from
1041 * the direction of the positive x-axis on the design
1042 * grid. [...] The color line progresses from the start angle
1043 * to the end angle in the counter-clockwise direction;" -
1044 * Convert angles and stops from counter-clockwise to clockwise
1045 * for the shader if the gradient is not already reversed due to
1046 * start angle being larger than end angle. */
1047 startAngleScaled = 360.f - startAngleScaled;
1048 endAngleScaled = 360.f - endAngleScaled;
1049 if (startAngleScaled >= endAngleScaled) {
1050 std::swap(startAngleScaled, endAngleScaled);
1051 std::reverse(stops.begin(), stops.end());
1052 std::reverse(colors.begin(), colors.end());
1053 for (auto& stop : stops) {
1054 stop = 1.0f - stop;
1055 }
1056 }
1057
1058 paint->setShader(SkGradientShader::MakeSweep(
1059 center.x(), center.y(),
1060 colors.data(), SkColorSpace::MakeSRGB(), stops.data(), stops.size(),
1061 tileMode,
1062 startAngleScaled, endAngleScaled,
1063 SkGradientShader::Interpolation{
1064 SkGradientShader::Interpolation::InPremul::kNo,
1065 SkGradientShader::Interpolation::ColorSpace::kSRGB,
1066 SkGradientShader::Interpolation::HueMethod::kShorter
1067 },
1068 nullptr));
1069
1070 return true;
1071 }
1072 default: {
1073 SkASSERT(false);
1074 return false;
1075 }
1076 }
1077 SkUNREACHABLE;
1078 }
1079
colrv1_draw_paint(SkCanvas * canvas,const SkSpan<SkColor> & palette,const SkColor foregroundColor,FT_Face face,const FT_COLR_Paint & colrPaint)1080 bool colrv1_draw_paint(SkCanvas* canvas,
1081 const SkSpan<SkColor>& palette,
1082 const SkColor foregroundColor,
1083 FT_Face face,
1084 const FT_COLR_Paint& colrPaint) {
1085 switch (colrPaint.format) {
1086 case FT_COLR_PAINTFORMAT_GLYPH: {
1087 FT_UInt glyphID = colrPaint.u.glyph.glyphID;
1088 SkPath path;
1089 /* TODO: Currently this call retrieves the path at units_per_em size. If we want to get
1090 * correct hinting for the scaled size under the transforms at this point in the color
1091 * glyph graph, we need to extract at least the requested glyph width and height and
1092 * pass that to the path generation. */
1093 if (!generateFacePathCOLRv1(face, glyphID, &path)) {
1094 return false;
1095 }
1096 if constexpr (kSkShowTextBlitCoverage) {
1097 SkPaint highlight_paint;
1098 highlight_paint.setColor(0x33FF0000);
1099 canvas->drawRect(path.getBounds(), highlight_paint);
1100 }
1101 canvas->clipPath(path, true /* doAntiAlias */);
1102 return true;
1103 }
1104 case FT_COLR_PAINTFORMAT_SOLID:
1105 case FT_COLR_PAINTFORMAT_LINEAR_GRADIENT:
1106 case FT_COLR_PAINTFORMAT_RADIAL_GRADIENT:
1107 case FT_COLR_PAINTFORMAT_SWEEP_GRADIENT: {
1108 SkPaint skPaint;
1109 if (!colrv1_configure_skpaint(face, palette, foregroundColor, colrPaint, &skPaint)) {
1110 return false;
1111 }
1112 canvas->drawPaint(skPaint);
1113 return true;
1114 }
1115 case FT_COLR_PAINTFORMAT_TRANSFORM:
1116 case FT_COLR_PAINTFORMAT_TRANSLATE:
1117 case FT_COLR_PAINTFORMAT_SCALE:
1118 case FT_COLR_PAINTFORMAT_ROTATE:
1119 case FT_COLR_PAINTFORMAT_SKEW:
1120 [[fallthrough]]; // Transforms handled in colrv1_transform.
1121 default:
1122 SkASSERT(false);
1123 return false;
1124 }
1125 SkUNREACHABLE;
1126 }
1127
colrv1_draw_glyph_with_path(SkCanvas * canvas,const SkSpan<SkColor> & palette,SkColor foregroundColor,FT_Face face,const FT_COLR_Paint & glyphPaint,const FT_COLR_Paint & fillPaint)1128 bool colrv1_draw_glyph_with_path(SkCanvas* canvas,
1129 const SkSpan<SkColor>& palette, SkColor foregroundColor,
1130 FT_Face face,
1131 const FT_COLR_Paint& glyphPaint, const FT_COLR_Paint& fillPaint) {
1132 SkASSERT(glyphPaint.format == FT_COLR_PAINTFORMAT_GLYPH);
1133 SkASSERT(fillPaint.format == FT_COLR_PAINTFORMAT_SOLID ||
1134 fillPaint.format == FT_COLR_PAINTFORMAT_LINEAR_GRADIENT ||
1135 fillPaint.format == FT_COLR_PAINTFORMAT_RADIAL_GRADIENT ||
1136 fillPaint.format == FT_COLR_PAINTFORMAT_SWEEP_GRADIENT);
1137
1138 SkPaint skiaFillPaint;
1139 skiaFillPaint.setAntiAlias(true);
1140 if (!colrv1_configure_skpaint(face, palette, foregroundColor, fillPaint, &skiaFillPaint)) {
1141 return false;
1142 }
1143
1144 FT_UInt glyphID = glyphPaint.u.glyph.glyphID;
1145 SkPath path;
1146 /* TODO: Currently this call retrieves the path at units_per_em size. If we want to get
1147 * correct hinting for the scaled size under the transforms at this point in the color
1148 * glyph graph, we need to extract at least the requested glyph width and height and
1149 * pass that to the path generation. */
1150 if (!generateFacePathCOLRv1(face, glyphID, &path)) {
1151 return false;
1152 }
1153 if constexpr (kSkShowTextBlitCoverage) {
1154 SkPaint highlightPaint;
1155 highlightPaint.setColor(0x33FF0000);
1156 canvas->drawRect(path.getBounds(), highlightPaint);
1157 }
1158 canvas->drawPath(path, skiaFillPaint);
1159 return true;
1160 }
1161
1162
1163 /* In drawing mode, concatenates the transforms directly on SkCanvas. In
1164 * bounding box calculation mode, no SkCanvas is specified, but we only want to
1165 * retrieve the transform from the FreeType paint object. */
colrv1_transform(FT_Face face,const FT_COLR_Paint & colrPaint,SkCanvas * canvas,SkMatrix * outTransform=nullptr)1166 void colrv1_transform(FT_Face face,
1167 const FT_COLR_Paint& colrPaint,
1168 SkCanvas* canvas,
1169 SkMatrix* outTransform = nullptr) {
1170 SkMatrix transform;
1171
1172 SkASSERT(canvas || outTransform);
1173
1174 switch (colrPaint.format) {
1175 case FT_COLR_PAINTFORMAT_TRANSFORM: {
1176 transform = ToSkMatrix(colrPaint.u.transform.affine);
1177 break;
1178 }
1179 case FT_COLR_PAINTFORMAT_TRANSLATE: {
1180 transform = SkMatrix::Translate( SkFixedToScalar(colrPaint.u.translate.dx),
1181 -SkFixedToScalar(colrPaint.u.translate.dy));
1182 break;
1183 }
1184 case FT_COLR_PAINTFORMAT_SCALE: {
1185 transform.setScale( SkFixedToScalar(colrPaint.u.scale.scale_x),
1186 SkFixedToScalar(colrPaint.u.scale.scale_y),
1187 SkFixedToScalar(colrPaint.u.scale.center_x),
1188 -SkFixedToScalar(colrPaint.u.scale.center_y));
1189 break;
1190 }
1191 case FT_COLR_PAINTFORMAT_ROTATE: {
1192 // COLRv1 angles are counter-clockwise, compare
1193 // https://docs.microsoft.com/en-us/typography/opentype/spec/colr#formats-24-to-27-paintrotate-paintvarrotate-paintrotatearoundcenter-paintvarrotatearoundcenter
1194 transform = SkMatrix::RotateDeg(
1195 -SkFixedToScalar(colrPaint.u.rotate.angle) * 180.0f,
1196 SkPoint::Make( SkFixedToScalar(colrPaint.u.rotate.center_x),
1197 -SkFixedToScalar(colrPaint.u.rotate.center_y)));
1198 break;
1199 }
1200 case FT_COLR_PAINTFORMAT_SKEW: {
1201 // In the PAINTFORMAT_ROTATE implementation, SkMatrix setRotate
1202 // snaps to 0 for values very close to 0. Do the same here.
1203
1204 SkScalar xDeg = SkFixedToScalar(colrPaint.u.skew.x_skew_angle) * 180.0f;
1205 SkScalar xRad = SkDegreesToRadians(xDeg);
1206 SkScalar xTan = SkScalarTan(xRad);
1207 xTan = SkScalarNearlyZero(xTan) ? 0.0f : xTan;
1208
1209 SkScalar yDeg = SkFixedToScalar(colrPaint.u.skew.y_skew_angle) * 180.0f;
1210 // Negate y_skew_angle due to Skia's y-down coordinate system to achieve
1211 // counter-clockwise skew along the y-axis.
1212 SkScalar yRad = SkDegreesToRadians(-yDeg);
1213 SkScalar yTan = SkScalarTan(yRad);
1214 yTan = SkScalarNearlyZero(yTan) ? 0.0f : yTan;
1215
1216 transform.setSkew(xTan, yTan,
1217 SkFixedToScalar(colrPaint.u.skew.center_x),
1218 -SkFixedToScalar(colrPaint.u.skew.center_y));
1219 break;
1220 }
1221 default: {
1222 SkASSERT(false); // Only transforms are handled in this function.
1223 }
1224 }
1225 if (canvas) {
1226 canvas->concat(transform);
1227 }
1228 if (outTransform) {
1229 *outTransform = transform;
1230 }
1231 }
1232
1233 bool colrv1_start_glyph(SkCanvas* canvas,
1234 const SkSpan<SkColor>& palette,
1235 const SkColor foregroundColor,
1236 FT_Face face,
1237 uint16_t glyphId,
1238 FT_Color_Root_Transform rootTransform,
1239 VisitedSet* activePaints);
1240
colrv1_traverse_paint(SkCanvas * canvas,const SkSpan<SkColor> & palette,const SkColor foregroundColor,FT_Face face,FT_OpaquePaint opaquePaint,VisitedSet * activePaints)1241 bool colrv1_traverse_paint(SkCanvas* canvas,
1242 const SkSpan<SkColor>& palette,
1243 const SkColor foregroundColor,
1244 FT_Face face,
1245 FT_OpaquePaint opaquePaint,
1246 VisitedSet* activePaints) {
1247 // Cycle detection, see section "5.7.11.1.9 Color glyphs as a directed acyclic graph".
1248 if (activePaints->contains(opaquePaint)) {
1249 return true;
1250 }
1251
1252 activePaints->add(opaquePaint);
1253 SK_AT_SCOPE_EXIT(activePaints->remove(opaquePaint));
1254
1255 FT_COLR_Paint paint;
1256 if (!FT_Get_Paint(face, opaquePaint, &paint)) {
1257 return false;
1258 }
1259
1260 SkAutoCanvasRestore autoRestore(canvas, true /* doSave */);
1261 switch (paint.format) {
1262 case FT_COLR_PAINTFORMAT_COLR_LAYERS: {
1263 FT_LayerIterator& layerIterator = paint.u.colr_layers.layer_iterator;
1264 FT_OpaquePaint layerPaint{nullptr, 1};
1265 while (FT_Get_Paint_Layers(face, &layerIterator, &layerPaint)) {
1266 if (!colrv1_traverse_paint(canvas, palette, foregroundColor, face,
1267 layerPaint, activePaints)) {
1268 return false;
1269 }
1270 }
1271 return true;
1272 }
1273 case FT_COLR_PAINTFORMAT_GLYPH:
1274 // Special case paint graph leaf situations to improve
1275 // performance. These are situations in the graph where a GlyphPaint
1276 // is followed by either a solid or a gradient fill. Here we can use
1277 // drawPath() + SkPaint directly which is faster than setting a
1278 // clipPath() followed by a drawPaint().
1279 FT_COLR_Paint fillPaint;
1280 if (!FT_Get_Paint(face, paint.u.glyph.paint, &fillPaint)) {
1281 return false;
1282 }
1283 if (fillPaint.format == FT_COLR_PAINTFORMAT_SOLID ||
1284 fillPaint.format == FT_COLR_PAINTFORMAT_LINEAR_GRADIENT ||
1285 fillPaint.format == FT_COLR_PAINTFORMAT_RADIAL_GRADIENT ||
1286 fillPaint.format == FT_COLR_PAINTFORMAT_SWEEP_GRADIENT)
1287 {
1288 return colrv1_draw_glyph_with_path(canvas, palette, foregroundColor,
1289 face, paint, fillPaint);
1290 }
1291 if (!colrv1_draw_paint(canvas, palette, foregroundColor, face, paint)) {
1292 return false;
1293 }
1294 return colrv1_traverse_paint(canvas, palette, foregroundColor,
1295 face, paint.u.glyph.paint, activePaints);
1296 case FT_COLR_PAINTFORMAT_COLR_GLYPH:
1297 return colrv1_start_glyph(canvas, palette, foregroundColor,
1298 face, paint.u.colr_glyph.glyphID, FT_COLOR_NO_ROOT_TRANSFORM,
1299 activePaints);
1300 case FT_COLR_PAINTFORMAT_TRANSFORM:
1301 colrv1_transform(face, paint, canvas);
1302 return colrv1_traverse_paint(canvas, palette, foregroundColor,
1303 face, paint.u.transform.paint, activePaints);
1304 case FT_COLR_PAINTFORMAT_TRANSLATE:
1305 colrv1_transform(face, paint, canvas);
1306 return colrv1_traverse_paint(canvas, palette, foregroundColor,
1307 face, paint.u.translate.paint, activePaints);
1308 case FT_COLR_PAINTFORMAT_SCALE:
1309 colrv1_transform(face, paint, canvas);
1310 return colrv1_traverse_paint(canvas, palette, foregroundColor,
1311 face, paint.u.scale.paint, activePaints);
1312 case FT_COLR_PAINTFORMAT_ROTATE:
1313 colrv1_transform(face, paint, canvas);
1314 return colrv1_traverse_paint(canvas, palette, foregroundColor,
1315 face, paint.u.rotate.paint, activePaints);
1316 case FT_COLR_PAINTFORMAT_SKEW:
1317 colrv1_transform(face, paint, canvas);
1318 return colrv1_traverse_paint(canvas, palette, foregroundColor,
1319 face, paint.u.skew.paint, activePaints);
1320 case FT_COLR_PAINTFORMAT_COMPOSITE: {
1321 SkAutoCanvasRestore acr(canvas, false);
1322 canvas->saveLayer(nullptr, nullptr);
1323 if (!colrv1_traverse_paint(canvas, palette, foregroundColor,
1324 face, paint.u.composite.backdrop_paint, activePaints)) {
1325 return false;
1326 }
1327 SkPaint blendModePaint;
1328 blendModePaint.setBlendMode(ToSkBlendMode(paint.u.composite.composite_mode));
1329 canvas->saveLayer(nullptr, &blendModePaint);
1330 return colrv1_traverse_paint(canvas, palette, foregroundColor,
1331 face, paint.u.composite.source_paint, activePaints);
1332 }
1333 case FT_COLR_PAINTFORMAT_SOLID:
1334 case FT_COLR_PAINTFORMAT_LINEAR_GRADIENT:
1335 case FT_COLR_PAINTFORMAT_RADIAL_GRADIENT:
1336 case FT_COLR_PAINTFORMAT_SWEEP_GRADIENT: {
1337 return colrv1_draw_paint(canvas, palette, foregroundColor, face, paint);
1338 }
1339 default:
1340 SkASSERT(false);
1341 return false;
1342 }
1343 SkUNREACHABLE;
1344 }
1345
GetClipBoxPath(FT_Face face,uint16_t glyphId,bool untransformed)1346 SkPath GetClipBoxPath(FT_Face face, uint16_t glyphId, bool untransformed) {
1347 SkPath resultPath;
1348 SkUniqueFTSize unscaledFtSize = nullptr;
1349 FT_Size oldSize = face->size;
1350 FT_Matrix oldTransform;
1351 FT_Vector oldDelta;
1352 FT_Error err = 0;
1353
1354 if (untransformed) {
1355 unscaledFtSize.reset(
1356 [face]() -> FT_Size {
1357 FT_Size size;
1358 FT_Error err = FT_New_Size(face, &size);
1359 if (err != 0) {
1360 SK_TRACEFTR(err,
1361 "FT_New_Size(%s) failed in generateFacePathStaticCOLRv1.",
1362 face->family_name);
1363 return nullptr;
1364 }
1365 return size;
1366 }());
1367 if (!unscaledFtSize) {
1368 return resultPath;
1369 }
1370
1371 err = FT_Activate_Size(unscaledFtSize.get());
1372 if (err != 0) {
1373 return resultPath;
1374 }
1375
1376 err = FT_Set_Char_Size(face, SkIntToFDot6(face->units_per_EM), 0, 0, 0);
1377 if (err != 0) {
1378 return resultPath;
1379 }
1380
1381 FT_Get_Transform(face, &oldTransform, &oldDelta);
1382 FT_Set_Transform(face, nullptr, nullptr);
1383 }
1384
1385 FT_ClipBox colrGlyphClipBox;
1386 if (FT_Get_Color_Glyph_ClipBox(face, glyphId, &colrGlyphClipBox)) {
1387 resultPath = SkPath::Polygon({{ SkFDot6ToScalar(colrGlyphClipBox.bottom_left.x),
1388 -SkFDot6ToScalar(colrGlyphClipBox.bottom_left.y)},
1389 { SkFDot6ToScalar(colrGlyphClipBox.top_left.x),
1390 -SkFDot6ToScalar(colrGlyphClipBox.top_left.y)},
1391 { SkFDot6ToScalar(colrGlyphClipBox.top_right.x),
1392 -SkFDot6ToScalar(colrGlyphClipBox.top_right.y)},
1393 { SkFDot6ToScalar(colrGlyphClipBox.bottom_right.x),
1394 -SkFDot6ToScalar(colrGlyphClipBox.bottom_right.y)}},
1395 true);
1396 }
1397
1398 if (untransformed) {
1399 err = FT_Activate_Size(oldSize);
1400 if (err != 0) {
1401 return resultPath;
1402 }
1403 FT_Set_Transform(face, &oldTransform, &oldDelta);
1404 }
1405
1406 return resultPath;
1407 }
1408
colrv1_start_glyph(SkCanvas * canvas,const SkSpan<SkColor> & palette,const SkColor foregroundColor,FT_Face face,uint16_t glyphId,FT_Color_Root_Transform rootTransform,VisitedSet * activePaints)1409 bool colrv1_start_glyph(SkCanvas* canvas,
1410 const SkSpan<SkColor>& palette,
1411 const SkColor foregroundColor,
1412 FT_Face face,
1413 uint16_t glyphId,
1414 FT_Color_Root_Transform rootTransform,
1415 VisitedSet* activePaints) {
1416 FT_OpaquePaint opaquePaint{nullptr, 1};
1417 if (!FT_Get_Color_Glyph_Paint(face, glyphId, rootTransform, &opaquePaint)) {
1418 return false;
1419 }
1420
1421 bool untransformed = rootTransform == FT_COLOR_NO_ROOT_TRANSFORM;
1422 SkPath clipBoxPath = GetClipBoxPath(face, glyphId, untransformed);
1423 if (!clipBoxPath.isEmpty()) {
1424 canvas->clipPath(clipBoxPath, true);
1425 }
1426
1427 if (!colrv1_traverse_paint(canvas, palette, foregroundColor,
1428 face, opaquePaint, activePaints)) {
1429 return false;
1430 }
1431
1432 return true;
1433 }
1434
1435 bool colrv1_start_glyph_bounds(SkMatrix *ctm,
1436 SkRect* bounds,
1437 FT_Face face,
1438 uint16_t glyphId,
1439 FT_Color_Root_Transform rootTransform,
1440 VisitedSet* activePaints);
1441
colrv1_traverse_paint_bounds(SkMatrix * ctm,SkRect * bounds,FT_Face face,FT_OpaquePaint opaquePaint,VisitedSet * activePaints)1442 bool colrv1_traverse_paint_bounds(SkMatrix* ctm,
1443 SkRect* bounds,
1444 FT_Face face,
1445 FT_OpaquePaint opaquePaint,
1446 VisitedSet* activePaints) {
1447 // Cycle detection, see section "5.7.11.1.9 Color glyphs as a directed acyclic graph".
1448 if (activePaints->contains(opaquePaint)) {
1449 return false;
1450 }
1451
1452 activePaints->add(opaquePaint);
1453 SK_AT_SCOPE_EXIT(activePaints->remove(opaquePaint));
1454
1455 FT_COLR_Paint paint;
1456 if (!FT_Get_Paint(face, opaquePaint, &paint)) {
1457 return false;
1458 }
1459
1460 SkMatrix restoreMatrix = *ctm;
1461 SK_AT_SCOPE_EXIT(*ctm = restoreMatrix);
1462
1463 switch (paint.format) {
1464 case FT_COLR_PAINTFORMAT_COLR_LAYERS: {
1465 FT_LayerIterator& layerIterator = paint.u.colr_layers.layer_iterator;
1466 FT_OpaquePaint layerPaint{nullptr, 1};
1467 while (FT_Get_Paint_Layers(face, &layerIterator, &layerPaint)) {
1468 if (!colrv1_traverse_paint_bounds(ctm, bounds, face, layerPaint, activePaints)) {
1469 return false;
1470 }
1471 }
1472 return true;
1473 }
1474 case FT_COLR_PAINTFORMAT_GLYPH: {
1475 FT_UInt glyphID = paint.u.glyph.glyphID;
1476 SkPath path;
1477 if (!generateFacePathCOLRv1(face, glyphID, &path)) {
1478 return false;
1479 }
1480 path.transform(*ctm);
1481 bounds->join(path.getBounds());
1482 return true;
1483 }
1484 case FT_COLR_PAINTFORMAT_COLR_GLYPH: {
1485 FT_UInt glyphID = paint.u.colr_glyph.glyphID;
1486 return colrv1_start_glyph_bounds(ctm, bounds, face, glyphID, FT_COLOR_NO_ROOT_TRANSFORM,
1487 activePaints);
1488 }
1489 case FT_COLR_PAINTFORMAT_TRANSFORM: {
1490 SkMatrix transformMatrix;
1491 colrv1_transform(face, paint, nullptr, &transformMatrix);
1492 ctm->preConcat(transformMatrix);
1493 FT_OpaquePaint& transformPaint = paint.u.transform.paint;
1494 return colrv1_traverse_paint_bounds(ctm, bounds, face, transformPaint, activePaints);
1495 }
1496 case FT_COLR_PAINTFORMAT_TRANSLATE: {
1497 SkMatrix transformMatrix;
1498 colrv1_transform(face, paint, nullptr, &transformMatrix);
1499 ctm->preConcat(transformMatrix);
1500 FT_OpaquePaint& translatePaint = paint.u.translate.paint;
1501 return colrv1_traverse_paint_bounds(ctm, bounds, face, translatePaint, activePaints);
1502 }
1503 case FT_COLR_PAINTFORMAT_SCALE: {
1504 SkMatrix transformMatrix;
1505 colrv1_transform(face, paint, nullptr, &transformMatrix);
1506 ctm->preConcat(transformMatrix);
1507 FT_OpaquePaint& scalePaint = paint.u.scale.paint;
1508 return colrv1_traverse_paint_bounds(ctm, bounds, face, scalePaint, activePaints);
1509 }
1510 case FT_COLR_PAINTFORMAT_ROTATE: {
1511 SkMatrix transformMatrix;
1512 colrv1_transform(face, paint, nullptr, &transformMatrix);
1513 ctm->preConcat(transformMatrix);
1514 FT_OpaquePaint& rotatePaint = paint.u.rotate.paint;
1515 return colrv1_traverse_paint_bounds(ctm, bounds, face, rotatePaint, activePaints);
1516 }
1517 case FT_COLR_PAINTFORMAT_SKEW: {
1518 SkMatrix transformMatrix;
1519 colrv1_transform(face, paint, nullptr, &transformMatrix);
1520 ctm->preConcat(transformMatrix);
1521 FT_OpaquePaint& skewPaint = paint.u.skew.paint;
1522 return colrv1_traverse_paint_bounds(ctm, bounds, face, skewPaint, activePaints);
1523 }
1524 case FT_COLR_PAINTFORMAT_COMPOSITE: {
1525 FT_OpaquePaint& backdropPaint = paint.u.composite.backdrop_paint;
1526 FT_OpaquePaint& sourcePaint = paint.u.composite. source_paint;
1527 return colrv1_traverse_paint_bounds(ctm, bounds, face, backdropPaint, activePaints) &&
1528 colrv1_traverse_paint_bounds(ctm, bounds, face, sourcePaint, activePaints);
1529 }
1530 case FT_COLR_PAINTFORMAT_SOLID:
1531 case FT_COLR_PAINTFORMAT_LINEAR_GRADIENT:
1532 case FT_COLR_PAINTFORMAT_RADIAL_GRADIENT:
1533 case FT_COLR_PAINTFORMAT_SWEEP_GRADIENT: {
1534 return true;
1535 }
1536 default:
1537 SkASSERT(false);
1538 return false;
1539 }
1540 SkUNREACHABLE;
1541 }
1542
1543
colrv1_start_glyph_bounds(SkMatrix * ctm,SkRect * bounds,FT_Face face,uint16_t glyphId,FT_Color_Root_Transform rootTransform,VisitedSet * activePaints)1544 bool colrv1_start_glyph_bounds(SkMatrix *ctm,
1545 SkRect* bounds,
1546 FT_Face face,
1547 uint16_t glyphId,
1548 FT_Color_Root_Transform rootTransform,
1549 VisitedSet* activePaints) {
1550 FT_OpaquePaint opaquePaint{nullptr, 1};
1551 return FT_Get_Color_Glyph_Paint(face, glyphId, rootTransform, &opaquePaint) &&
1552 colrv1_traverse_paint_bounds(ctm, bounds, face, opaquePaint, activePaints);
1553 }
1554 #endif // TT_SUPPORT_COLRV1
1555
1556 } // namespace
1557
1558 ////////////////
1559
init(SkColor fgColor,SkScalerContext::Flags flags)1560 void SkScalerContextFTUtils::init(SkColor fgColor, SkScalerContext::Flags flags) {
1561 fForegroundColor = fgColor;
1562 fFlags = flags;
1563 }
1564
1565 #ifdef TT_SUPPORT_COLRV1
drawCOLRv1Glyph(FT_Face face,const SkGlyph & glyph,uint32_t loadGlyphFlags,SkSpan<SkColor> palette,SkCanvas * canvas) const1566 bool SkScalerContextFTUtils::drawCOLRv1Glyph(FT_Face face, const SkGlyph& glyph, uint32_t loadGlyphFlags,
1567 SkSpan<SkColor> palette, SkCanvas* canvas) const {
1568 if (this->isSubpixel()) {
1569 canvas->translate(SkFixedToScalar(glyph.getSubXFixed()),
1570 SkFixedToScalar(glyph.getSubYFixed()));
1571 }
1572
1573 VisitedSet activePaints;
1574 return colrv1_start_glyph(canvas, palette, fForegroundColor,
1575 face, glyph.getGlyphID(),
1576 FT_COLOR_INCLUDE_ROOT_TRANSFORM, &activePaints);
1577 }
1578 #endif // TT_SUPPORT_COLRV1
1579
1580 #ifdef FT_COLOR_H
drawCOLRv0Glyph(FT_Face face,const SkGlyph & glyph,LoadGlyphFlags flags,SkSpan<SkColor> palette,SkCanvas * canvas) const1581 bool SkScalerContextFTUtils::drawCOLRv0Glyph(FT_Face face, const SkGlyph& glyph, LoadGlyphFlags flags,
1582 SkSpan<SkColor> palette, SkCanvas* canvas) const {
1583 if (this->isSubpixel()) {
1584 canvas->translate(SkFixedToScalar(glyph.getSubXFixed()),
1585 SkFixedToScalar(glyph.getSubYFixed()));
1586 }
1587
1588 bool haveLayers = false;
1589 FT_LayerIterator layerIterator;
1590 layerIterator.p = nullptr;
1591 FT_UInt layerGlyphIndex = 0;
1592 FT_UInt layerColorIndex = 0;
1593 SkPaint paint;
1594 paint.setAntiAlias(!(flags & FT_LOAD_TARGET_MONO));
1595 while (FT_Get_Color_Glyph_Layer(face, glyph.getGlyphID(), &layerGlyphIndex,
1596 &layerColorIndex, &layerIterator)) {
1597 haveLayers = true;
1598 if (layerColorIndex == 0xFFFF) {
1599 paint.setColor(fForegroundColor);
1600 } else {
1601 paint.setColor(palette[layerColorIndex]);
1602 }
1603 SkPath path;
1604 if (this->generateFacePath(face, layerGlyphIndex, flags, &path)) {
1605 canvas->drawPath(path, paint);
1606 }
1607 }
1608 SkASSERTF(haveLayers, "Could not get COLRv0 layers from '%s'.", face->family_name);
1609 return haveLayers;
1610 }
1611 #endif // FT_COLOR_H
1612
1613 #if defined(FT_CONFIG_OPTION_SVG)
drawSVGGlyph(FT_Face face,const SkGlyph & glyph,LoadGlyphFlags flags,SkSpan<SkColor> palette,SkCanvas * canvas) const1614 bool SkScalerContextFTUtils::drawSVGGlyph(FT_Face face, const SkGlyph& glyph, LoadGlyphFlags flags,
1615 SkSpan<SkColor> palette, SkCanvas* canvas) const {
1616 SkASSERT(face->glyph->format == FT_GLYPH_FORMAT_SVG);
1617
1618 FT_SVG_Document ftSvg = (FT_SVG_Document)face->glyph->other;
1619 SkMatrix m;
1620 FT_Matrix ftMatrix = ftSvg->transform;
1621 FT_Vector ftOffset = ftSvg->delta;
1622 m.setAll(
1623 SkFixedToFloat(ftMatrix.xx), -SkFixedToFloat(ftMatrix.xy), SkFixedToFloat(ftOffset.x),
1624 -SkFixedToFloat(ftMatrix.yx), SkFixedToFloat(ftMatrix.yy), -SkFixedToFloat(ftOffset.y),
1625 0 , 0 , 1 );
1626 m.postScale(SkFixedToFloat(ftSvg->metrics.x_scale) / 64.0f,
1627 SkFixedToFloat(ftSvg->metrics.y_scale) / 64.0f);
1628 if (this->isSubpixel()) {
1629 m.postTranslate(SkFixedToScalar(glyph.getSubXFixed()),
1630 SkFixedToScalar(glyph.getSubYFixed()));
1631 }
1632 canvas->concat(m);
1633
1634 SkGraphics::OpenTypeSVGDecoderFactory svgFactory = SkGraphics::GetOpenTypeSVGDecoderFactory();
1635 if (!svgFactory) {
1636 return false;
1637 }
1638 auto svgDecoder = svgFactory(ftSvg->svg_document, ftSvg->svg_document_length);
1639 if (!svgDecoder) {
1640 return false;
1641 }
1642 return svgDecoder->render(*canvas, ftSvg->units_per_EM, glyph.getGlyphID(),
1643 fForegroundColor, palette);
1644 }
1645 #endif // FT_CONFIG_OPTION_SVG
1646
generateGlyphImage(FT_Face face,const SkGlyph & glyph,void * imageBuffer,const SkMatrix & bitmapTransform,const SkMaskGamma::PreBlend & preBlend) const1647 void SkScalerContextFTUtils::generateGlyphImage(FT_Face face, const SkGlyph& glyph, void* imageBuffer,
1648 const SkMatrix& bitmapTransform,
1649 const SkMaskGamma::PreBlend& preBlend) const {
1650 switch ( face->glyph->format ) {
1651 case FT_GLYPH_FORMAT_OUTLINE: {
1652 FT_Outline* outline = &face->glyph->outline;
1653
1654 int dx = 0, dy = 0;
1655 if (this->isSubpixel()) {
1656 dx = SkFixedToFDot6(glyph.getSubXFixed());
1657 dy = SkFixedToFDot6(glyph.getSubYFixed());
1658 // negate dy since freetype-y-goes-up and skia-y-goes-down
1659 dy = -dy;
1660 }
1661
1662 memset(imageBuffer, 0, glyph.rowBytes() * glyph.height());
1663
1664 if (SkMask::kLCD16_Format == glyph.maskFormat()) {
1665 const bool doBGR = SkToBool(fFlags & SkScalerContext::kLCD_BGROrder_Flag);
1666 const bool doVert = SkToBool(fFlags & SkScalerContext::kLCD_Vertical_Flag);
1667
1668 FT_Outline_Translate(outline, dx, dy);
1669 FT_Error err = FT_Render_Glyph(face->glyph, doVert ? FT_RENDER_MODE_LCD_V :
1670 FT_RENDER_MODE_LCD);
1671 if (err) {
1672 SK_TRACEFTR(err, "Could not render glyph %p.", face->glyph);
1673 return;
1674 }
1675
1676 SkMaskBuilder mask(static_cast<uint8_t*>(imageBuffer),
1677 glyph.iRect(), glyph.rowBytes(), glyph.maskFormat());
1678
1679 if constexpr (kSkShowTextBlitCoverage) {
1680 memset(mask.image(), 0x80, mask.fBounds.height() * mask.fRowBytes);
1681 }
1682 FT_GlyphSlotRec& ftGlyph = *face->glyph;
1683
1684 if (!SkIRect::Intersects(mask.fBounds,
1685 SkIRect::MakeXYWH( ftGlyph.bitmap_left,
1686 -ftGlyph.bitmap_top,
1687 ftGlyph.bitmap.width,
1688 ftGlyph.bitmap.rows)))
1689 {
1690 return;
1691 }
1692
1693 // If the FT_Bitmap extent is larger, discard bits of the bitmap outside the mask.
1694 // If the SkMask extent is larger, shrink mask to fit bitmap (clearing discarded).
1695 unsigned char* origBuffer = ftGlyph.bitmap.buffer;
1696 // First align the top left (origin).
1697 if (-ftGlyph.bitmap_top < mask.fBounds.fTop) {
1698 int32_t topDiff = mask.fBounds.fTop - (-ftGlyph.bitmap_top);
1699 ftGlyph.bitmap.buffer += ftGlyph.bitmap.pitch * topDiff;
1700 ftGlyph.bitmap.rows -= topDiff;
1701 ftGlyph.bitmap_top = -mask.fBounds.fTop;
1702 }
1703 if (ftGlyph.bitmap_left < mask.fBounds.fLeft) {
1704 int32_t leftDiff = mask.fBounds.fLeft - ftGlyph.bitmap_left;
1705 ftGlyph.bitmap.buffer += leftDiff;
1706 ftGlyph.bitmap.width -= leftDiff;
1707 ftGlyph.bitmap_left = mask.fBounds.fLeft;
1708 }
1709 if (mask.fBounds.fTop < -ftGlyph.bitmap_top) {
1710 mask.image() += mask.fRowBytes * (-ftGlyph.bitmap_top - mask.fBounds.fTop);
1711 mask.bounds().fTop = -ftGlyph.bitmap_top;
1712 }
1713 if (mask.fBounds.fLeft < ftGlyph.bitmap_left) {
1714 mask.image() += sizeof(uint16_t) * (ftGlyph.bitmap_left - mask.fBounds.fLeft);
1715 mask.bounds().fLeft = ftGlyph.bitmap_left;
1716 }
1717 // Origins aligned, clean up the width and height.
1718 int ftVertScale = (doVert ? 3 : 1);
1719 int ftHoriScale = (doVert ? 1 : 3);
1720 if (mask.fBounds.height() * ftVertScale < SkToInt(ftGlyph.bitmap.rows)) {
1721 ftGlyph.bitmap.rows = mask.fBounds.height() * ftVertScale;
1722 }
1723 if (mask.fBounds.width() * ftHoriScale < SkToInt(ftGlyph.bitmap.width)) {
1724 ftGlyph.bitmap.width = mask.fBounds.width() * ftHoriScale;
1725 }
1726 if (SkToInt(ftGlyph.bitmap.rows) < mask.fBounds.height() * ftVertScale) {
1727 mask.bounds().fBottom = mask.fBounds.fTop + ftGlyph.bitmap.rows / ftVertScale;
1728 }
1729 if (SkToInt(ftGlyph.bitmap.width) < mask.fBounds.width() * ftHoriScale) {
1730 mask.bounds().fRight = mask.fBounds.fLeft + ftGlyph.bitmap.width / ftHoriScale;
1731 }
1732 if (preBlend.isApplicable()) {
1733 copyFT2LCD16<true>(ftGlyph.bitmap, &mask, doBGR,
1734 preBlend.fR, preBlend.fG, preBlend.fB);
1735 } else {
1736 copyFT2LCD16<false>(ftGlyph.bitmap, &mask, doBGR,
1737 preBlend.fR, preBlend.fG, preBlend.fB);
1738 }
1739 // Restore the buffer pointer so FreeType can properly free it.
1740 ftGlyph.bitmap.buffer = origBuffer;
1741 } else {
1742 FT_BBox bbox;
1743 FT_Bitmap target;
1744 FT_Outline_Get_CBox(outline, &bbox);
1745 /*
1746 what we really want to do for subpixel is
1747 offset(dx, dy)
1748 compute_bounds
1749 offset(bbox & !63)
1750 but that is two calls to offset, so we do the following, which
1751 achieves the same thing with only one offset call.
1752 */
1753 FT_Outline_Translate(outline, dx - ((bbox.xMin + dx) & ~63),
1754 dy - ((bbox.yMin + dy) & ~63));
1755
1756 target.width = glyph.width();
1757 target.rows = glyph.height();
1758 target.pitch = glyph.rowBytes();
1759 target.buffer = static_cast<uint8_t*>(imageBuffer);
1760 target.pixel_mode = compute_pixel_mode(glyph.maskFormat());
1761 target.num_grays = 256;
1762
1763 FT_Outline_Get_Bitmap(face->glyph->library, outline, &target);
1764 if constexpr (kSkShowTextBlitCoverage) {
1765 if (glyph.maskFormat() == SkMask::kBW_Format) {
1766 for (unsigned y = 0; y < target.rows; y += 2) {
1767 for (unsigned x = (y & 0x2); x < target.width; x+=4) {
1768 uint8_t& b = target.buffer[(target.pitch * y) + (x >> 3)];
1769 b = b ^ (1 << (0x7 - (x & 0x7)));
1770 }
1771 }
1772 } else {
1773 for (unsigned y = 0; y < target.rows; ++y) {
1774 for (unsigned x = 0; x < target.width; ++x) {
1775 uint8_t& a = target.buffer[(target.pitch * y) + x];
1776 a = std::max<uint8_t>(a, 0x20);
1777 }
1778 }
1779 }
1780 }
1781 }
1782 } break;
1783
1784 case FT_GLYPH_FORMAT_BITMAP: {
1785 FT_Pixel_Mode pixel_mode = static_cast<FT_Pixel_Mode>(face->glyph->bitmap.pixel_mode);
1786 SkMask::Format maskFormat = glyph.maskFormat();
1787
1788 // Assume that the other formats do not exist.
1789 SkASSERT(FT_PIXEL_MODE_MONO == pixel_mode ||
1790 FT_PIXEL_MODE_GRAY == pixel_mode ||
1791 FT_PIXEL_MODE_BGRA == pixel_mode);
1792
1793 // These are the only formats this ScalerContext should request.
1794 SkASSERT(SkMask::kBW_Format == maskFormat ||
1795 SkMask::kA8_Format == maskFormat ||
1796 SkMask::kARGB32_Format == maskFormat ||
1797 SkMask::kLCD16_Format == maskFormat);
1798
1799 // If no scaling needed, directly copy glyph bitmap.
1800 if (bitmapTransform.isIdentity()) {
1801 SkMaskBuilder dstMask = SkMaskBuilder(static_cast<uint8_t*>(imageBuffer),
1802 glyph.iRect(), glyph.rowBytes(),
1803 glyph.maskFormat());
1804 copyFTBitmap(face->glyph->bitmap, &dstMask);
1805 break;
1806 }
1807
1808 // Otherwise, scale the bitmap.
1809
1810 // Copy the FT_Bitmap into an SkBitmap (either A8 or ARGB)
1811 SkBitmap unscaledBitmap;
1812 // TODO: mark this as sRGB when the blits will be sRGB.
1813 unscaledBitmap.setInfo(SkImageInfo::Make(face->glyph->bitmap.width,
1814 face->glyph->bitmap.rows,
1815 SkColorType_for_FTPixelMode(pixel_mode),
1816 kPremul_SkAlphaType));
1817 if (!unscaledBitmap.tryAllocPixels()) {
1818 // TODO: set the imageBuffer to indicate "missing"
1819 memset(imageBuffer, 0, glyph.rowBytes() * glyph.height());
1820 return;
1821 }
1822
1823 SkMaskBuilder unscaledBitmapAlias(
1824 reinterpret_cast<uint8_t*>(unscaledBitmap.getPixels()),
1825 SkIRect::MakeWH(unscaledBitmap.width(), unscaledBitmap.height()),
1826 unscaledBitmap.rowBytes(),
1827 SkMaskFormat_for_SkColorType(unscaledBitmap.colorType()));
1828 copyFTBitmap(face->glyph->bitmap, &unscaledBitmapAlias);
1829
1830 // Wrap the glyph's mask in a bitmap, unless the glyph's mask is BW or LCD.
1831 // BW requires an A8 target for resizing, which can then be down sampled.
1832 // LCD should use a 4x A8 target, which will then be down sampled.
1833 // For simplicity, LCD uses A8 and is replicated.
1834 int bitmapRowBytes = 0;
1835 if (SkMask::kBW_Format != maskFormat && SkMask::kLCD16_Format != maskFormat) {
1836 bitmapRowBytes = glyph.rowBytes();
1837 }
1838 SkBitmap dstBitmap;
1839 // TODO: mark this as sRGB when the blits will be sRGB.
1840 dstBitmap.setInfo(SkImageInfo::Make(glyph.width(), glyph.height(),
1841 SkColorType_for_SkMaskFormat(maskFormat),
1842 kPremul_SkAlphaType),
1843 bitmapRowBytes);
1844 if (SkMask::kBW_Format == maskFormat || SkMask::kLCD16_Format == maskFormat) {
1845 if (!dstBitmap.tryAllocPixels()) {
1846 // TODO: set the fImage to indicate "missing"
1847 memset(imageBuffer, 0, glyph.rowBytes() * glyph.height());
1848 return;
1849 }
1850 } else {
1851 dstBitmap.setPixels(imageBuffer);
1852 }
1853
1854 // Scale unscaledBitmap into dstBitmap.
1855 SkCanvas canvas(dstBitmap);
1856 if constexpr (kSkShowTextBlitCoverage) {
1857 canvas.clear(0x33FF0000);
1858 } else {
1859 canvas.clear(SK_ColorTRANSPARENT);
1860 }
1861 canvas.translate(-glyph.left(), -glyph.top());
1862 canvas.concat(bitmapTransform);
1863 canvas.translate(face->glyph->bitmap_left, -face->glyph->bitmap_top);
1864
1865 SkSamplingOptions sampling(SkFilterMode::kLinear, SkMipmapMode::kNearest);
1866 canvas.drawImage(unscaledBitmap.asImage().get(), 0, 0, sampling, nullptr);
1867
1868 // If the destination is BW or LCD, convert from A8.
1869 if (SkMask::kBW_Format == maskFormat) {
1870 // Copy the A8 dstBitmap into the A1 imageBuffer.
1871 SkMaskBuilder dstMask(static_cast<uint8_t*>(imageBuffer),
1872 glyph.iRect(), glyph.rowBytes(), glyph.maskFormat());
1873 packA8ToA1(&dstMask, dstBitmap.getAddr8(0, 0), dstBitmap.rowBytes());
1874 } else if (SkMask::kLCD16_Format == maskFormat) {
1875 // Copy the A8 dstBitmap into the LCD16 imageBuffer.
1876 uint8_t* src = dstBitmap.getAddr8(0, 0);
1877 uint16_t* dst = reinterpret_cast<uint16_t*>(imageBuffer);
1878 for (int y = dstBitmap.height(); y --> 0;) {
1879 for (int x = 0; x < dstBitmap.width(); ++x) {
1880 dst[x] = grayToRGB16(src[x]);
1881 }
1882 dst = (uint16_t*)((char*)dst + glyph.rowBytes());
1883 src += dstBitmap.rowBytes();
1884 }
1885 }
1886 } break;
1887
1888 default:
1889 SkDEBUGFAIL("unknown glyph format");
1890 memset(imageBuffer, 0, glyph.rowBytes() * glyph.height());
1891 return;
1892 }
1893
1894 // We used to always do this pre-USE_COLOR_LUMINANCE, but with colorlum,
1895 // it is optional
1896 #if defined(SK_GAMMA_APPLY_TO_A8)
1897 if (SkMask::kA8_Format == glyph.maskFormat() && preBlend.isApplicable()) {
1898 uint8_t* SK_RESTRICT dst = (uint8_t*)imageBuffer;
1899 unsigned rowBytes = glyph.rowBytes();
1900
1901 for (int y = glyph.height() - 1; y >= 0; --y) {
1902 for (int x = glyph.width() - 1; x >= 0; --x) {
1903 dst[x] = preBlend.fG[dst[x]];
1904 }
1905 dst += rowBytes;
1906 }
1907 }
1908 #endif
1909 }
1910
1911 ///////////////////////////////////////////////////////////////////////////////
1912
1913 namespace {
1914
1915 class SkFTGeometrySink {
1916 SkPath* fPath;
1917 bool fStarted;
1918 FT_Vector fCurrent;
1919
goingTo(const FT_Vector * pt)1920 void goingTo(const FT_Vector* pt) {
1921 if (!fStarted) {
1922 fStarted = true;
1923 fPath->moveTo(SkFDot6ToScalar(fCurrent.x), -SkFDot6ToScalar(fCurrent.y));
1924 }
1925 fCurrent = *pt;
1926 }
1927
currentIsNot(const FT_Vector * pt)1928 bool currentIsNot(const FT_Vector* pt) {
1929 return fCurrent.x != pt->x || fCurrent.y != pt->y;
1930 }
1931
Move(const FT_Vector * pt,void * ctx)1932 static int Move(const FT_Vector* pt, void* ctx) {
1933 SkFTGeometrySink& self = *(SkFTGeometrySink*)ctx;
1934 if (self.fStarted) {
1935 self.fPath->close();
1936 self.fStarted = false;
1937 }
1938 self.fCurrent = *pt;
1939 return 0;
1940 }
1941
Line(const FT_Vector * pt,void * ctx)1942 static int Line(const FT_Vector* pt, void* ctx) {
1943 SkFTGeometrySink& self = *(SkFTGeometrySink*)ctx;
1944 if (self.currentIsNot(pt)) {
1945 self.goingTo(pt);
1946 self.fPath->lineTo(SkFDot6ToScalar(pt->x), -SkFDot6ToScalar(pt->y));
1947 }
1948 return 0;
1949 }
1950
Quad(const FT_Vector * pt0,const FT_Vector * pt1,void * ctx)1951 static int Quad(const FT_Vector* pt0, const FT_Vector* pt1, void* ctx) {
1952 SkFTGeometrySink& self = *(SkFTGeometrySink*)ctx;
1953 if (self.currentIsNot(pt0) || self.currentIsNot(pt1)) {
1954 self.goingTo(pt1);
1955 self.fPath->quadTo(SkFDot6ToScalar(pt0->x), -SkFDot6ToScalar(pt0->y),
1956 SkFDot6ToScalar(pt1->x), -SkFDot6ToScalar(pt1->y));
1957 }
1958 return 0;
1959 }
1960
Cubic(const FT_Vector * pt0,const FT_Vector * pt1,const FT_Vector * pt2,void * ctx)1961 static int Cubic(const FT_Vector* pt0, const FT_Vector* pt1, const FT_Vector* pt2, void* ctx) {
1962 SkFTGeometrySink& self = *(SkFTGeometrySink*)ctx;
1963 if (self.currentIsNot(pt0) || self.currentIsNot(pt1) || self.currentIsNot(pt2)) {
1964 self.goingTo(pt2);
1965 self.fPath->cubicTo(SkFDot6ToScalar(pt0->x), -SkFDot6ToScalar(pt0->y),
1966 SkFDot6ToScalar(pt1->x), -SkFDot6ToScalar(pt1->y),
1967 SkFDot6ToScalar(pt2->x), -SkFDot6ToScalar(pt2->y));
1968 }
1969 return 0;
1970 }
1971
1972 public:
SkFTGeometrySink(SkPath * path)1973 SkFTGeometrySink(SkPath* path) : fPath{path}, fStarted{false}, fCurrent{0,0} {}
1974
1975 inline static constexpr const FT_Outline_Funcs Funcs{
1976 /*move_to =*/ SkFTGeometrySink::Move,
1977 /*line_to =*/ SkFTGeometrySink::Line,
1978 /*conic_to =*/ SkFTGeometrySink::Quad,
1979 /*cubic_to =*/ SkFTGeometrySink::Cubic,
1980 /*shift = */ 0,
1981 /*delta =*/ 0,
1982 };
1983 };
1984
generateGlyphPathStatic(FT_Face face,SkPath * path)1985 bool generateGlyphPathStatic(FT_Face face, SkPath* path) {
1986 SkFTGeometrySink sink{path};
1987 if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE ||
1988 FT_Outline_Decompose(&face->glyph->outline, &SkFTGeometrySink::Funcs, &sink))
1989 {
1990 path->reset();
1991 return false;
1992 }
1993 path->close();
1994 return true;
1995 }
1996
generateFacePathStatic(FT_Face face,SkGlyphID glyphID,SkScalerContextFTUtils::LoadGlyphFlags flags,SkPath * path)1997 bool generateFacePathStatic(FT_Face face, SkGlyphID glyphID,
1998 SkScalerContextFTUtils::LoadGlyphFlags flags, SkPath* path){
1999 flags |= FT_LOAD_BITMAP_METRICS_ONLY; // Don't decode any bitmaps.
2000 flags |= FT_LOAD_NO_BITMAP; // Ignore embedded bitmaps.
2001 flags &= ~FT_LOAD_RENDER; // Don't scan convert.
2002 flags &= ~FT_LOAD_COLOR; // Ignore SVG.
2003 if (FT_Load_Glyph(face, glyphID, flags)) {
2004 path->reset();
2005 return false;
2006 }
2007 return generateGlyphPathStatic(face, path);
2008 }
2009
2010 #ifdef TT_SUPPORT_COLRV1
generateFacePathCOLRv1(FT_Face face,SkGlyphID glyphID,SkPath * path)2011 bool generateFacePathCOLRv1(FT_Face face, SkGlyphID glyphID, SkPath* path) {
2012 uint32_t flags = 0;
2013 flags |= FT_LOAD_BITMAP_METRICS_ONLY; // Don't decode any bitmaps.
2014 flags |= FT_LOAD_NO_BITMAP; // Ignore embedded bitmaps.
2015 flags &= ~FT_LOAD_RENDER; // Don't scan convert.
2016 flags &= ~FT_LOAD_COLOR; // Ignore SVG.
2017 flags |= FT_LOAD_NO_HINTING;
2018 flags |= FT_LOAD_NO_AUTOHINT;
2019 flags |= FT_LOAD_IGNORE_TRANSFORM;
2020
2021 SkUniqueFTSize unscaledFtSize([face]() -> FT_Size {
2022 FT_Size size;
2023 FT_Error err = FT_New_Size(face, &size);
2024 if (err != 0) {
2025 SK_TRACEFTR(err, "FT_New_Size(%s) failed in generateFacePathStaticCOLRv1.",
2026 face->family_name);
2027 return nullptr;
2028 }
2029 return size;
2030 }());
2031
2032 if (!unscaledFtSize) {
2033 return false;
2034 }
2035
2036 FT_Size oldSize = face->size;
2037
2038 auto tryGeneratePath = [face, &unscaledFtSize, glyphID, flags, path]() {
2039 FT_Error err = 0;
2040
2041 err = FT_Activate_Size(unscaledFtSize.get());
2042 if (err != 0) {
2043 return false;
2044 }
2045
2046 err = FT_Set_Char_Size(face, SkIntToFDot6(face->units_per_EM),
2047 SkIntToFDot6(face->units_per_EM), 72, 72);
2048 if (err != 0) {
2049 return false;
2050 }
2051
2052 err = FT_Load_Glyph(face, glyphID, flags);
2053 if (err != 0) {
2054 path->reset();
2055 return false;
2056 }
2057
2058 if (!generateGlyphPathStatic(face, path)) {
2059 path->reset();
2060 return false;
2061 }
2062
2063 return true;
2064 };
2065
2066 bool pathGenerationResult = tryGeneratePath();
2067
2068 FT_Activate_Size(oldSize);
2069
2070 return pathGenerationResult;
2071 }
2072 #endif
2073
2074 } // namespace
2075
generateGlyphPath(FT_Face face,SkPath * path) const2076 bool SkScalerContextFTUtils::generateGlyphPath(FT_Face face, SkPath* path) const {
2077 if (!generateGlyphPathStatic(face, path)) {
2078 return false;
2079 }
2080 if (face->glyph->outline.flags & FT_OUTLINE_OVERLAP) {
2081 Simplify(*path, path);
2082 // Simplify will return an even-odd path.
2083 // A stroke+fill (for fake bold) may be incorrect for even-odd.
2084 // https://github.com/flutter/flutter/issues/112546
2085 AsWinding(*path, path);
2086 }
2087 return true;
2088 }
2089
generateFacePath(FT_Face face,SkGlyphID glyphID,LoadGlyphFlags flags,SkPath * path) const2090 bool SkScalerContextFTUtils::generateFacePath(FT_Face face, SkGlyphID glyphID, LoadGlyphFlags flags,
2091 SkPath* path) const {
2092 return generateFacePathStatic(face, glyphID, flags, path);
2093 }
2094
2095 #ifdef TT_SUPPORT_COLRV1
computeColrV1GlyphBoundingBox(FT_Face face,SkGlyphID glyphID,SkRect * bounds)2096 bool SkScalerContextFTUtils::computeColrV1GlyphBoundingBox(FT_Face face, SkGlyphID glyphID,
2097 SkRect* bounds) {
2098 SkMatrix ctm;
2099 *bounds = SkRect::MakeEmpty();
2100 VisitedSet activePaints;
2101 return colrv1_start_glyph_bounds(&ctm, bounds, face, glyphID,
2102 FT_COLOR_INCLUDE_ROOT_TRANSFORM, &activePaints);
2103 }
2104 #endif
2105