Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkFontHost_FreeType_common.cpp
Go to the documentation of this file.
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
10
18#include "include/core/SkPath.h"
23#include "src/core/SkFDot6.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
43using namespace skia_private;
44
45namespace {
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
84const 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
100bool 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.
106static_assert(sizeof(FT_Fixed) != sizeof(FT_F2Dot14));
107constexpr float kColorStopShift =
108 sizeof(FT_ColorStop::stop_offset) == sizeof(FT_F2Dot14) ? 1 << 14 : 1 << 16;
109#endif
110
111namespace {
112using SkUniqueFTSize = std::unique_ptr<FT_SizeRec, SkFunctionObject<FT_Done_Size>>;
113
114FT_Pixel_Mode compute_pixel_mode(SkMask::Format format) {
115 switch (format) {
117 return FT_PIXEL_MODE_MONO;
119 default:
120 return FT_PIXEL_MODE_GRAY;
121 }
122}
123
124///////////////////////////////////////////////////////////////////////////////
125
126uint16_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
135uint16_t grayToRGB16(U8CPU gray) {
136 if constexpr (kSkShowTextBlitCoverage) {
137 gray = std::max(gray, (U8CPU)0x40);
138 }
139 return SkPack888ToRGB16(gray, gray, gray);
140}
141
142int 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 */
156template<bool APPLY_PREBLEND>
157void copyFT2LCD16(const FT_Bitmap& bitmap, SkMaskBuilder* dstMask, int lcdIsBGR,
158 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB)
159{
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 */
259void 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
342inline 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
348uint8_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
357void 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
388inline SkMask::Format SkMaskFormat_for_SkColorType(SkColorType colorType) {
389 switch (colorType) {
391 return SkMask::kA8_Format;
392 case kN32_SkColorType:
394 default:
395 SkDEBUGFAIL("unsupported SkBitmap::Config");
396 return SkMask::kA8_Format;
397 }
398}
399
400inline 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:
406 return kN32_SkColorType;
407 default:
408 SkDEBUGFAIL("unsupported FT_PIXEL_MODE");
410 }
411}
412
413inline SkColorType SkColorType_for_SkMaskFormat(SkMask::Format format) {
414 switch (format) {
420 return kN32_SkColorType;
421 default:
422 SkDEBUGFAIL("unsupported destination SkBitmap::Config");
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
432const 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.
439SkColor4f 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
458enum 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.
465void 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
496struct OpaquePaintHasher {
497 size_t operator()(const FT_OpaquePaint& opaquePaint) {
498 return SkGoodHash()(opaquePaint.p) ^
499 SkGoodHash()(opaquePaint.insert_root_transform);
500 }
501};
502
504
505bool generateFacePathCOLRv1(FT_Face face, SkGlyphID glyphID, SkPath* path);
506
507inline float SkColrV1AlphaToFloat(uint16_t alpha) { return (alpha / float(1 << 14)); }
508
509
510inline 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
521inline 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:
531 case FT_COLR_COMPOSITE_DEST_OVER:
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:
539 case FT_COLR_COMPOSITE_DEST_OUT:
541 case FT_COLR_COMPOSITE_SRC_ATOP:
543 case FT_COLR_COMPOSITE_DEST_ATOP:
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:
551 case FT_COLR_COMPOSITE_OVERLAY:
553 case FT_COLR_COMPOSITE_DARKEN:
555 case FT_COLR_COMPOSITE_LIGHTEN:
557 case FT_COLR_COMPOSITE_COLOR_DODGE:
559 case FT_COLR_COMPOSITE_COLOR_BURN:
561 case FT_COLR_COMPOSITE_HARD_LIGHT:
563 case FT_COLR_COMPOSITE_SOFT_LIGHT:
565 case FT_COLR_COMPOSITE_DIFFERENCE:
567 case FT_COLR_COMPOSITE_EXCLUSION:
569 case FT_COLR_COMPOSITE_MULTIPLY:
571 case FT_COLR_COMPOSITE_HSL_HUE:
572 return SkBlendMode::kHue;
573 case FT_COLR_COMPOSITE_HSL_SATURATION:
575 case FT_COLR_COMPOSITE_HSL_COLOR:
576 return SkBlendMode::kColor;
577 case FT_COLR_COMPOSITE_HSL_LUMINOSITY:
579 default:
580 return SkBlendMode::kDst;
581 }
582}
583
584inline 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
592inline SkPoint SkVectorProjection(SkPoint a, SkPoint b) {
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
603bool 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 {
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.
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
766 linePositions,
767 colors.data(), SkColorSpace::MakeSRGB(), stops.data(), stops.size(),
768 tileMode,
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
950 start, startRadius, end, endRadius,
951 colors.data(), SkColorSpace::MakeSRGB(), stops.data(), stops.size(),
952 tileMode,
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
1059 center.x(), center.y(),
1060 colors.data(), SkColorSpace::MakeSRGB(), stops.data(), stops.size(),
1061 tileMode,
1062 startAngleScaled, endAngleScaled,
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 }
1078}
1079
1080bool 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 }
1126}
1127
1128bool 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. */
1166void colrv1_transform(FT_Face face,
1167 const FT_COLR_Paint& colrPaint,
1168 SkCanvas* canvas,
1169 SkMatrix* outTransform = nullptr) {
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
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
1233bool 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
1241bool 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 }
1344}
1345
1346SkPath 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
1409bool 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
1435bool 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
1442bool 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 }
1541}
1542
1543
1544bool 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
1564
1565#ifdef TT_SUPPORT_COLRV1
1566bool 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
1581bool 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)
1614bool 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()) {
1630 SkFixedToScalar(glyph.getSubYFixed()));
1631 }
1632 canvas->concat(m);
1633
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
1647void 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()) {
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),
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),
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
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
1913namespace {
1914
1915class SkFTGeometrySink {
1916 SkPath* fPath;
1917 bool fStarted;
1918 FT_Vector fCurrent;
1919
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
1928 bool currentIsNot(const FT_Vector* pt) {
1929 return fCurrent.x != pt->x || fCurrent.y != pt->y;
1930 }
1931
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
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
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
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
1972public:
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
1985bool 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
1997bool generateFacePathStatic(FT_Face face, SkGlyphID glyphID,
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
2011bool 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
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
2090bool 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
2097 SkRect* bounds) {
2098 SkMatrix ctm;
2100 VisitedSet activePaints;
2101 return colrv1_start_glyph_bounds(&ctm, bounds, face, glyphID,
2102 FT_COLOR_INCLUDE_ROOT_TRANSFORM, &activePaints);
2103}
2104#endif
SkPath fPath
GrTriangulator::Line Line
SkPoint pos
SkColor4f color
static constexpr T SkAlign8(T x)
Definition SkAlign.h:17
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition SkAlphaType.h:29
#define SkUNREACHABLE
Definition SkAssert.h:135
#define SkDEBUGFAIL(message)
Definition SkAssert.h:118
#define SkASSERT(cond)
Definition SkAssert.h:116
#define SkASSERTF(cond, fmt,...)
Definition SkAssert.h:117
SkBlendMode
Definition SkBlendMode.h:38
@ kSrcOut
r = s * (1-da)
@ kExclusion
rc = s + d - two(s*d), ra = kSrcOver
@ kSaturation
saturation of source with hue and luminosity of destination
@ kColorBurn
darken destination to reflect source
@ kPlus
r = min(s + d, 1)
@ kLighten
rc = s + d - min(s*da, d*sa), ra = kSrcOver
@ kHue
hue of source with saturation and luminosity of destination
@ kDstIn
r = d * sa
@ kMultiply
r = s*(1-da) + d*(1-sa) + s*d
@ kColorDodge
brighten destination to reflect source
@ kScreen
r = s + d - s*d
@ kSrcOver
r = s + (1-sa)*d
@ kXor
r = s*(1-da) + d*(1-sa)
@ kLuminosity
luminosity of source with hue and saturation of destination
@ kSoftLight
lighten or darken, depending on source
@ kDifference
rc = s + d - 2*(min(s*da, d*sa)), ra = kSrcOver
@ kOverlay
multiply or screen, depending on destination
@ kSrcATop
r = s*da + d*(1-sa)
@ kDstATop
r = d*sa + s*(1-da)
@ kDstOver
r = d + (1-da)*s
@ kColor
hue and saturation of source with luminosity of destination
@ kHardLight
multiply or screen, depending on source
@ kDstOut
r = d * (1-sa)
@ kDarken
rc = s + d - max(s*da, d*sa), ra = kSrcOver
@ kSrcIn
r = s * da
@ kClear
r = 0
unsigned U8CPU
Definition SkCPUTypes.h:18
static U16CPU SkPack888ToRGB16(U8CPU r, U8CPU g, U8CPU b)
static SkPMColor SkFourByteInterp256(SkPMColor src, SkPMColor dst, int scale)
static SkPMColor SkPackARGB32(U8CPU a, U8CPU r, U8CPU g, U8CPU b)
SkColorType
Definition SkColorType.h:19
@ kAlpha_8_SkColorType
pixel with alpha in 8-bit byte
Definition SkColorType.h:21
uint32_t SkColor
Definition SkColor.h:37
constexpr SkColor SK_ColorTRANSPARENT
Definition SkColor.h:99
uint32_t SkPMColor
Definition SkColor.h:205
constexpr SkColor SK_ColorBLACK
Definition SkColor.h:103
constexpr SkColor SK_ColorWHITE
Definition SkColor.h:122
#define SkDEBUGF(...)
Definition SkDebug.h:24
#define SkFDot6ToScalar(x)
Definition SkFDot6.h:65
#define SkFixedToFDot6(x)
Definition SkFDot6.h:56
#define SkIntToFDot6(x)
Definition SkFDot6.h:49
#define SK_RESTRICT
Definition SkFeatures.h:42
#define SkFixedToScalar(x)
Definition SkFixed.h:124
#define SkFixedToFloat(x)
Definition SkFixed.h:41
#define FT_OUTLINE_OVERLAP
#define FT_PIXEL_MODE_BGRA
#define SK_TRACEFTR(ERR,...)
struct FT_FaceRec_ * FT_Face
static SkColorType colorType(AImageDecoder *decoder, const AImageDecoderHeaderInfo *headerInfo)
bool SK_API AsWinding(const SkPath &path, SkPath *result)
bool SK_API Simplify(const SkPath &path, SkPath *result)
#define SkDegreesToRadians(degrees)
Definition SkScalar.h:77
#define SkScalarTan(radians)
Definition SkScalar.h:47
static bool SkScalarNearlyZero(SkScalar x, SkScalar tolerance=SK_ScalarNearlyZero)
Definition SkScalar.h:101
static int convert_8_to_1(unsigned byte)
static uint8_t pack_8_to_1(const uint8_t alpha[8])
static void packA8ToA1(SkMaskBuilder &dstMask, const uint8_t *src, size_t srcRB)
#define SK_AT_SCOPE_EXIT(stmt)
Definition SkScopeExit.h:56
static T SkTAbs(T value)
Definition SkTemplates.h:43
SkTileMode
Definition SkTileMode.h:13
constexpr int SkToInt(S x)
Definition SkTo.h:29
static constexpr bool SkToBool(const T &x)
Definition SkTo.h:35
constexpr uint8_t SkToU8(S x)
Definition SkTo.h:22
struct FT_BBox_ FT_BBox
uint16_t SkGlyphID
Definition SkTypes.h:179
static SkScalar center(float pos0, float pos1)
sk_sp< SkImage > asImage() const
Definition SkBitmap.cpp:645
uint8_t * getAddr8(int x, int y) const
Definition SkBitmap.h:1270
int width() const
Definition SkBitmap.h:149
size_t rowBytes() const
Definition SkBitmap.h:238
SkColorType colorType() const
Definition SkBitmap.h:160
void setPixels(void *pixels)
Definition SkBitmap.cpp:207
void * getPixels() const
Definition SkBitmap.h:283
bool setInfo(const SkImageInfo &imageInfo, size_t rowBytes=0)
Definition SkBitmap.cpp:114
int height() const
Definition SkBitmap.h:158
bool tryAllocPixels(const SkImageInfo &info, size_t rowBytes)
Definition SkBitmap.cpp:271
int saveLayer(const SkRect *bounds, const SkPaint *paint)
Definition SkCanvas.cpp:500
void drawRect(const SkRect &rect, const SkPaint &paint)
void translate(SkScalar dx, SkScalar dy)
void drawPaint(const SkPaint &paint)
void clear(SkColor color)
Definition SkCanvas.h:1199
void clipPath(const SkPath &path, SkClipOp op, bool doAntiAlias)
void drawPath(const SkPath &path, const SkPaint &paint)
void concat(const SkMatrix &matrix)
void drawImage(const SkImage *image, SkScalar left, SkScalar top)
Definition SkCanvas.h:1528
static sk_sp< SkColorSpace > MakeSRGB()
int top() const
Definition SkGlyph.h:511
size_t rowBytes() const
Definition SkGlyph.cpp:233
SkGlyphID getGlyphID() const
Definition SkGlyph.h:429
SkMask::Format maskFormat() const
Definition SkGlyph.h:500
int height() const
Definition SkGlyph.h:513
SkFixed getSubYFixed() const
Definition SkGlyph.h:432
SkFixed getSubXFixed() const
Definition SkGlyph.h:431
SkIRect iRect() const
Definition SkGlyph.h:505
int width() const
Definition SkGlyph.h:512
int left() const
Definition SkGlyph.h:510
static sk_sp< SkShader > MakeTwoPointConical(const SkPoint &start, SkScalar startRadius, const SkPoint &end, SkScalar endRadius, const SkColor colors[], const SkScalar pos[], int count, SkTileMode mode, uint32_t flags=0, const SkMatrix *localMatrix=nullptr)
static sk_sp< SkShader > MakeSweep(SkScalar cx, SkScalar cy, const SkColor colors[], const SkScalar pos[], int count, SkTileMode mode, SkScalar startAngle, SkScalar endAngle, uint32_t flags, const SkMatrix *localMatrix)
static sk_sp< SkShader > MakeLinear(const SkPoint pts[2], const SkColor colors[], const SkScalar pos[], int count, SkTileMode mode, uint32_t flags=0, const SkMatrix *localMatrix=nullptr)
std::unique_ptr< SkOpenTypeSVGDecoder >(*)(const uint8_t *svg, size_t length) OpenTypeSVGDecoderFactory
Definition SkGraphics.h:164
static OpenTypeSVGDecoderFactory GetOpenTypeSVGDecoderFactory()
SkMatrix & postTranslate(SkScalar dx, SkScalar dy)
Definition SkMatrix.cpp:281
static SkMatrix RotateDeg(SkScalar deg)
Definition SkMatrix.h:104
static SkMatrix Translate(SkScalar dx, SkScalar dy)
Definition SkMatrix.h:91
static SkMatrix MakeAll(SkScalar scaleX, SkScalar skewX, SkScalar transX, SkScalar skewY, SkScalar scaleY, SkScalar transY, SkScalar pers0, SkScalar pers1, SkScalar pers2)
Definition SkMatrix.h:179
SkMatrix & preConcat(const SkMatrix &other)
Definition SkMatrix.cpp:674
bool isIdentity() const
Definition SkMatrix.h:223
void setColor(SkColor color)
Definition SkPaint.cpp:119
void setAntiAlias(bool aa)
Definition SkPaint.h:170
void setBlendMode(SkBlendMode mode)
Definition SkPaint.cpp:151
bool isEmpty() const
Definition SkPath.cpp:406
SkPath & moveTo(SkScalar x, SkScalar y)
Definition SkPath.cpp:678
SkPath & reset()
Definition SkPath.cpp:360
static SkPath Polygon(const SkPoint pts[], int count, bool isClosed, SkPathFillType=SkPathFillType::kWinding, bool isVolatile=false)
Definition SkPath.cpp:3546
SkPath & close()
Definition SkPath.cpp:813
constexpr size_t size() const
Definition SkSpan_impl.h:95
const uint8_t * fG
const uint8_t * fB
bool isApplicable() const
const uint8_t * fR
T * get() const
Definition SkRefCnt.h:303
const Paint & paint
bool operator==(const FlutterPoint &a, const FlutterPoint &b)
float SkScalar
Definition extension.cpp:12
static bool b
struct MyStruct a[10]
FlutterSemanticsFlag flags
glong glong end
uint32_t uint32_t * format
uint32_t * target
size_t length
double y
double x
constexpr SkColor4f kTransparent
Definition SkColor.h:434
Optional< SkRect > bounds
Definition SkRecords.h:189
SkScalar startAngle
Definition SkRecords.h:250
PODArray< SkColor > colors
Definition SkRecords.h:276
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition switches.h:57
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switches.h:41
SkMatrix ToSkMatrix(const tonic::Float64List &matrix4)
Definition matrix.cc:32
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition switches.h:259
dst
Definition cp.py:12
std::array< Point, 4 > Quad
Definition point.h:321
static void swap(TArray< T, M > &a, TArray< T, M > &b)
Definition SkTArray.h:737
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
Definition p3.cpp:47
int32_t height
int32_t width
static bool Intersects(const SkIRect &a, const SkIRect &b)
Definition SkRect.h:535
int32_t fBottom
larger y-axis bounds
Definition SkRect.h:36
constexpr int32_t height() const
Definition SkRect.h:165
int32_t fTop
smaller y-axis bounds
Definition SkRect.h:34
constexpr int32_t width() const
Definition SkRect.h:158
static constexpr SkIRect MakeWH(int32_t w, int32_t h)
Definition SkRect.h:56
static constexpr SkIRect MakeXYWH(int32_t x, int32_t y, int32_t w, int32_t h)
Definition SkRect.h:104
int32_t fLeft
smaller x-axis bounds
Definition SkRect.h:33
int32_t fRight
larger x-axis bounds
Definition SkRect.h:35
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
SkIRect & bounds()
Definition SkMask.h:237
uint8_t *& image()
Definition SkMask.h:236
const uint32_t fRowBytes
Definition SkMask.h:43
Format
Definition SkMask.h:26
@ kA8_Format
8bits per pixel mask (e.g. antialiasing)
Definition SkMask.h:28
@ kLCD16_Format
565 alpha for r/g/b
Definition SkMask.h:31
@ kARGB32_Format
SkPMColor.
Definition SkMask.h:30
@ kBW_Format
1bit per pixel mask (e.g. monochrome)
Definition SkMask.h:27
const SkIRect fBounds
Definition SkMask.h:42
const Format fFormat
Definition SkMask.h:44
static float CrossProduct(const SkVector &a, const SkVector &b)
static float DotProduct(const SkVector &a, const SkVector &b)
static constexpr SkPoint Make(float x, float y)
float length() const
void scale(float scale, SkPoint *dst) const
Definition SkPoint.cpp:17
constexpr float y() const
bool normalize()
Definition SkPoint.cpp:22
constexpr float x() const
static SkRGBA4f FromColor(SkColor color)
static constexpr SkRect MakeEmpty()
Definition SkRect.h:595
bool drawCOLRv1Glyph(FT_Face, const SkGlyph &, LoadGlyphFlags, SkSpan< SkColor > palette, SkCanvas *) const
bool drawSVGGlyph(FT_Face, const SkGlyph &, LoadGlyphFlags, SkSpan< SkColor > palette, SkCanvas *) const
static bool computeColrV1GlyphBoundingBox(FT_Face, SkGlyphID, SkRect *bounds)
void generateGlyphImage(FT_Face, const SkGlyph &, void *, const SkMatrix &bitmapTransform, const SkMaskGamma::PreBlend &) const
bool generateGlyphPath(FT_Face, SkPath *) const
void init(SkColor fgColor, SkScalerContext::Flags)
bool drawCOLRv0Glyph(FT_Face, const SkGlyph &, LoadGlyphFlags, SkSpan< SkColor > palette, SkCanvas *) const
static SKVX_ALWAYS_INLINE Vec Load(const void *ptr)
Definition SkVx.h:109