blob: e204c8308650ca5b1aca3f2fc15e39414b391628 [file] [log] [blame]
george@mozilla.comc59b5da2012-08-23 00:39:08 +00001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05009#include "include/core/SkBitmap.h"
10#include "include/core/SkCanvas.h"
11#include "include/core/SkColor.h"
12#include "include/core/SkPath.h"
Dominik Röttsches691a7942021-02-11 21:08:44 +020013#include "include/effects/SkGradientShader.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050014#include "include/private/SkColorData.h"
15#include "include/private/SkTo.h"
16#include "src/core/SkFDot6.h"
17#include "src/ports/SkFontHost_FreeType_common.h"
george@mozilla.comc59b5da2012-08-23 00:39:08 +000018
Ben Wagnerf08d1d02018-06-18 15:11:00 -040019#include <utility>
20
george@mozilla.comc59b5da2012-08-23 00:39:08 +000021#include <ft2build.h>
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +000022#include FT_FREETYPE_H
george@mozilla.comc59b5da2012-08-23 00:39:08 +000023#include FT_BITMAP_H
Bruce Wangf3ca1c62018-07-09 10:08:36 -040024#ifdef FT_COLOR_H
25# include FT_COLOR_H
26#endif
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +000027#include FT_IMAGE_H
28#include FT_OUTLINE_H
Dominik Röttsches691a7942021-02-11 21:08:44 +020029#include FT_SIZES_H
george@mozilla.comc59b5da2012-08-23 00:39:08 +000030// In the past, FT_GlyphSlot_Own_Bitmap was defined in this header file.
31#include FT_SYNTHESIS_H
32
Dominik Röttsches1c847f52021-04-29 17:03:01 +030033#ifdef TT_SUPPORT_COLRV1
34#include "src/core/SkScopeExit.h"
35#endif
36
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +000037// FT_LOAD_COLOR and the corresponding FT_Pixel_Mode::FT_PIXEL_MODE_BGRA
38// were introduced in FreeType 2.5.0.
39// The following may be removed once FreeType 2.5.0 is required to build.
40#ifndef FT_LOAD_COLOR
41# define FT_LOAD_COLOR ( 1L << 20 )
42# define FT_PIXEL_MODE_BGRA 7
43#endif
44
Hal Canary2bcd8432018-01-26 10:35:07 -050045#ifdef SK_DEBUG
46const char* SkTraceFtrGetError(int e) {
47 switch ((FT_Error)e) {
48 #undef FTERRORS_H_
49 #define FT_ERRORDEF( e, v, s ) case v: return s;
50 #define FT_ERROR_START_LIST
51 #define FT_ERROR_END_LIST
52 #include FT_ERRORS_H
53 #undef FT_ERRORDEF
54 #undef FT_ERROR_START_LIST
55 #undef FT_ERROR_END_LIST
56 default: return "";
57 }
58}
59#endif // SK_DEBUG
60
Dominik Röttsches1c847f52021-04-29 17:03:01 +030061#ifdef TT_SUPPORT_COLRV1
62bool operator==(const FT_OpaquePaint& a, const FT_OpaquePaint& b) {
63 return a.p == b.p && a.insert_root_transform == b.insert_root_transform;
64}
65#endif
66
Ben Wagner2512db22016-11-10 16:14:52 -050067namespace {
68
69FT_Pixel_Mode compute_pixel_mode(SkMask::Format format) {
george@mozilla.comc59b5da2012-08-23 00:39:08 +000070 switch (format) {
71 case SkMask::kBW_Format:
72 return FT_PIXEL_MODE_MONO;
73 case SkMask::kA8_Format:
74 default:
75 return FT_PIXEL_MODE_GRAY;
76 }
77}
78
79///////////////////////////////////////////////////////////////////////////////
80
Ben Wagner2512db22016-11-10 16:14:52 -050081uint16_t packTriple(U8CPU r, U8CPU g, U8CPU b) {
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +000082#ifdef SK_SHOW_TEXT_BLIT_COVERAGE
Brian Osman788b9162020-02-07 10:36:46 -050083 r = std::max(r, (U8CPU)0x40);
84 g = std::max(g, (U8CPU)0x40);
85 b = std::max(b, (U8CPU)0x40);
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +000086#endif
87 return SkPack888ToRGB16(r, g, b);
george@mozilla.comc59b5da2012-08-23 00:39:08 +000088}
89
Ben Wagner2512db22016-11-10 16:14:52 -050090uint16_t grayToRGB16(U8CPU gray) {
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +000091#ifdef SK_SHOW_TEXT_BLIT_COVERAGE
Brian Osman788b9162020-02-07 10:36:46 -050092 gray = std::max(gray, (U8CPU)0x40);
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +000093#endif
94 return SkPack888ToRGB16(gray, gray, gray);
george@mozilla.comc59b5da2012-08-23 00:39:08 +000095}
96
Ben Wagner2512db22016-11-10 16:14:52 -050097int bittst(const uint8_t data[], int bitOffset) {
george@mozilla.comc59b5da2012-08-23 00:39:08 +000098 SkASSERT(bitOffset >= 0);
99 int lowBit = data[bitOffset >> 3] >> (~bitOffset & 7);
100 return lowBit & 1;
101}
102
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000103/**
104 * Copies a FT_Bitmap into an SkMask with the same dimensions.
105 *
106 * FT_PIXEL_MODE_MONO
107 * FT_PIXEL_MODE_GRAY
108 * FT_PIXEL_MODE_LCD
109 * FT_PIXEL_MODE_LCD_V
110 */
george@mozilla.comc59b5da2012-08-23 00:39:08 +0000111template<bool APPLY_PREBLEND>
Ben Wagner2512db22016-11-10 16:14:52 -0500112void copyFT2LCD16(const FT_Bitmap& bitmap, const SkMask& mask, int lcdIsBGR,
113 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB)
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000114{
115 SkASSERT(SkMask::kLCD16_Format == mask.fFormat);
116 if (FT_PIXEL_MODE_LCD != bitmap.pixel_mode) {
fmalitacf808aa2015-02-27 08:46:39 -0800117 SkASSERT(mask.fBounds.width() == static_cast<int>(bitmap.width));
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000118 }
119 if (FT_PIXEL_MODE_LCD_V != bitmap.pixel_mode) {
fmalitacf808aa2015-02-27 08:46:39 -0800120 SkASSERT(mask.fBounds.height() == static_cast<int>(bitmap.rows));
george@mozilla.comc59b5da2012-08-23 00:39:08 +0000121 }
122
george@mozilla.comc59b5da2012-08-23 00:39:08 +0000123 const uint8_t* src = bitmap.buffer;
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000124 uint16_t* dst = reinterpret_cast<uint16_t*>(mask.fImage);
125 const size_t dstRB = mask.fRowBytes;
126
127 const int width = mask.fBounds.width();
128 const int height = mask.fBounds.height();
george@mozilla.comc59b5da2012-08-23 00:39:08 +0000129
130 switch (bitmap.pixel_mode) {
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000131 case FT_PIXEL_MODE_MONO:
132 for (int y = height; y --> 0;) {
george@mozilla.comc59b5da2012-08-23 00:39:08 +0000133 for (int x = 0; x < width; ++x) {
134 dst[x] = -bittst(src, x);
135 }
136 dst = (uint16_t*)((char*)dst + dstRB);
137 src += bitmap.pitch;
138 }
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000139 break;
140 case FT_PIXEL_MODE_GRAY:
141 for (int y = height; y --> 0;) {
george@mozilla.comc59b5da2012-08-23 00:39:08 +0000142 for (int x = 0; x < width; ++x) {
143 dst[x] = grayToRGB16(src[x]);
144 }
145 dst = (uint16_t*)((char*)dst + dstRB);
146 src += bitmap.pitch;
147 }
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000148 break;
149 case FT_PIXEL_MODE_LCD:
fmalitacf808aa2015-02-27 08:46:39 -0800150 SkASSERT(3 * mask.fBounds.width() == static_cast<int>(bitmap.width));
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000151 for (int y = height; y --> 0;) {
152 const uint8_t* triple = src;
153 if (lcdIsBGR) {
george@mozilla.comc59b5da2012-08-23 00:39:08 +0000154 for (int x = 0; x < width; x++) {
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000155 dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(triple[2], tableR),
156 sk_apply_lut_if<APPLY_PREBLEND>(triple[1], tableG),
157 sk_apply_lut_if<APPLY_PREBLEND>(triple[0], tableB));
158 triple += 3;
george@mozilla.comc59b5da2012-08-23 00:39:08 +0000159 }
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000160 } else {
161 for (int x = 0; x < width; x++) {
162 dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(triple[0], tableR),
163 sk_apply_lut_if<APPLY_PREBLEND>(triple[1], tableG),
164 sk_apply_lut_if<APPLY_PREBLEND>(triple[2], tableB));
165 triple += 3;
george@mozilla.comc59b5da2012-08-23 00:39:08 +0000166 }
george@mozilla.comc59b5da2012-08-23 00:39:08 +0000167 }
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000168 src += bitmap.pitch;
george@mozilla.comc59b5da2012-08-23 00:39:08 +0000169 dst = (uint16_t*)((char*)dst + dstRB);
170 }
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000171 break;
172 case FT_PIXEL_MODE_LCD_V:
fmalitacf808aa2015-02-27 08:46:39 -0800173 SkASSERT(3 * mask.fBounds.height() == static_cast<int>(bitmap.rows));
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000174 for (int y = height; y --> 0;) {
175 const uint8_t* srcR = src;
176 const uint8_t* srcG = srcR + bitmap.pitch;
177 const uint8_t* srcB = srcG + bitmap.pitch;
178 if (lcdIsBGR) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -0400179 using std::swap;
180 swap(srcR, srcB);
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000181 }
182 for (int x = 0; x < width; x++) {
183 dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(*srcR++, tableR),
184 sk_apply_lut_if<APPLY_PREBLEND>(*srcG++, tableG),
185 sk_apply_lut_if<APPLY_PREBLEND>(*srcB++, tableB));
186 }
187 src += 3 * bitmap.pitch;
188 dst = (uint16_t*)((char*)dst + dstRB);
189 }
190 break;
191 default:
Hal Canary2b0e6cd2018-07-09 12:43:39 -0400192 SkDEBUGF("FT_Pixel_Mode %d", bitmap.pixel_mode);
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000193 SkDEBUGFAIL("unsupported FT_Pixel_Mode for LCD16");
194 break;
195 }
196}
197
198/**
199 * Copies a FT_Bitmap into an SkMask with the same dimensions.
200 *
201 * Yes, No, Never Requested, Never Produced
202 *
reedd54d3fc2014-11-13 14:39:58 -0800203 * kBW kA8 k3D kARGB32 kLCD16
204 * FT_PIXEL_MODE_MONO Y Y NR N Y
205 * FT_PIXEL_MODE_GRAY N Y NR N Y
206 * FT_PIXEL_MODE_GRAY2 NP NP NR NP NP
207 * FT_PIXEL_MODE_GRAY4 NP NP NR NP NP
208 * FT_PIXEL_MODE_LCD NP NP NR NP NP
209 * FT_PIXEL_MODE_LCD_V NP NP NR NP NP
210 * FT_PIXEL_MODE_BGRA N N NR Y N
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000211 *
212 * TODO: All of these N need to be Y or otherwise ruled out.
213 */
Ben Wagner2512db22016-11-10 16:14:52 -0500214void copyFTBitmap(const FT_Bitmap& srcFTBitmap, SkMask& dstMask) {
bungeman401ae2d2016-07-18 15:46:27 -0700215 SkASSERTF(dstMask.fBounds.width() == static_cast<int>(srcFTBitmap.width),
216 "dstMask.fBounds.width() = %d\n"
217 "static_cast<int>(srcFTBitmap.width) = %d",
218 dstMask.fBounds.width(),
219 static_cast<int>(srcFTBitmap.width)
220 );
221 SkASSERTF(dstMask.fBounds.height() == static_cast<int>(srcFTBitmap.rows),
222 "dstMask.fBounds.height() = %d\n"
223 "static_cast<int>(srcFTBitmap.rows) = %d",
224 dstMask.fBounds.height(),
225 static_cast<int>(srcFTBitmap.rows)
226 );
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000227
228 const uint8_t* src = reinterpret_cast<const uint8_t*>(srcFTBitmap.buffer);
229 const FT_Pixel_Mode srcFormat = static_cast<FT_Pixel_Mode>(srcFTBitmap.pixel_mode);
230 // FT_Bitmap::pitch is an int and allowed to be negative.
231 const int srcPitch = srcFTBitmap.pitch;
232 const size_t srcRowBytes = SkTAbs(srcPitch);
233
234 uint8_t* dst = dstMask.fImage;
235 const SkMask::Format dstFormat = static_cast<SkMask::Format>(dstMask.fFormat);
236 const size_t dstRowBytes = dstMask.fRowBytes;
237
238 const size_t width = srcFTBitmap.width;
239 const size_t height = srcFTBitmap.rows;
240
241 if (SkMask::kLCD16_Format == dstFormat) {
halcanary96fcdcc2015-08-27 07:41:13 -0700242 copyFT2LCD16<false>(srcFTBitmap, dstMask, false, nullptr, nullptr, nullptr);
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000243 return;
244 }
245
246 if ((FT_PIXEL_MODE_MONO == srcFormat && SkMask::kBW_Format == dstFormat) ||
247 (FT_PIXEL_MODE_GRAY == srcFormat && SkMask::kA8_Format == dstFormat))
248 {
Brian Osman788b9162020-02-07 10:36:46 -0500249 size_t commonRowBytes = std::min(srcRowBytes, dstRowBytes);
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000250 for (size_t y = height; y --> 0;) {
251 memcpy(dst, src, commonRowBytes);
252 src += srcPitch;
253 dst += dstRowBytes;
254 }
255 } else if (FT_PIXEL_MODE_MONO == srcFormat && SkMask::kA8_Format == dstFormat) {
256 for (size_t y = height; y --> 0;) {
257 uint8_t byte = 0;
258 int bits = 0;
259 const uint8_t* src_row = src;
260 uint8_t* dst_row = dst;
261 for (size_t x = width; x --> 0;) {
262 if (0 == bits) {
263 byte = *src_row++;
264 bits = 8;
265 }
266 *dst_row++ = byte & 0x80 ? 0xff : 0x00;
267 bits--;
268 byte <<= 1;
269 }
270 src += srcPitch;
271 dst += dstRowBytes;
272 }
273 } else if (FT_PIXEL_MODE_BGRA == srcFormat && SkMask::kARGB32_Format == dstFormat) {
274 // FT_PIXEL_MODE_BGRA is pre-multiplied.
275 for (size_t y = height; y --> 0;) {
276 const uint8_t* src_row = src;
277 SkPMColor* dst_row = reinterpret_cast<SkPMColor*>(dst);
278 for (size_t x = 0; x < width; ++x) {
279 uint8_t b = *src_row++;
280 uint8_t g = *src_row++;
281 uint8_t r = *src_row++;
282 uint8_t a = *src_row++;
283 *dst_row++ = SkPackARGB32(a, r, g, b);
284#ifdef SK_SHOW_TEXT_BLIT_COVERAGE
285 *(dst_row-1) = SkFourByteInterp256(*(dst_row-1), SK_ColorWHITE, 0x40);
286#endif
287 }
288 src += srcPitch;
289 dst += dstRowBytes;
290 }
291 } else {
Hal Canary2b0e6cd2018-07-09 12:43:39 -0400292 SkDEBUGF("FT_Pixel_Mode %d, SkMask::Format %d\n", srcFormat, dstFormat);
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000293 SkDEBUGFAIL("unsupported combination of FT_Pixel_Mode and SkMask::Format");
294 }
295}
296
Ben Wagner2512db22016-11-10 16:14:52 -0500297inline int convert_8_to_1(unsigned byte) {
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000298 SkASSERT(byte <= 0xFF);
299 // Arbitrary decision that making the cutoff at 1/4 instead of 1/2 in general looks better.
300 return (byte >> 6) != 0;
301}
302
Ben Wagner2512db22016-11-10 16:14:52 -0500303uint8_t pack_8_to_1(const uint8_t alpha[8]) {
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000304 unsigned bits = 0;
305 for (int i = 0; i < 8; ++i) {
306 bits <<= 1;
307 bits |= convert_8_to_1(alpha[i]);
308 }
309 return SkToU8(bits);
310}
311
Ben Wagner2512db22016-11-10 16:14:52 -0500312void packA8ToA1(const SkMask& mask, const uint8_t* src, size_t srcRB) {
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000313 const int height = mask.fBounds.height();
314 const int width = mask.fBounds.width();
315 const int octs = width >> 3;
316 const int leftOverBits = width & 7;
317
318 uint8_t* dst = mask.fImage;
319 const int dstPad = mask.fRowBytes - SkAlign8(width)/8;
320 SkASSERT(dstPad >= 0);
321
322 const int srcPad = srcRB - width;
323 SkASSERT(srcPad >= 0);
324
325 for (int y = 0; y < height; ++y) {
326 for (int i = 0; i < octs; ++i) {
327 *dst++ = pack_8_to_1(src);
328 src += 8;
329 }
330 if (leftOverBits > 0) {
331 unsigned bits = 0;
332 int shift = 7;
333 for (int i = 0; i < leftOverBits; ++i, --shift) {
334 bits |= convert_8_to_1(*src++) << shift;
335 }
336 *dst++ = bits;
337 }
338 src += srcPad;
339 dst += dstPad;
340 }
341}
342
reed6c225732014-06-09 19:52:07 -0700343inline SkMask::Format SkMaskFormat_for_SkColorType(SkColorType colorType) {
344 switch (colorType) {
345 case kAlpha_8_SkColorType:
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000346 return SkMask::kA8_Format;
reed6c225732014-06-09 19:52:07 -0700347 case kN32_SkColorType:
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000348 return SkMask::kARGB32_Format;
349 default:
350 SkDEBUGFAIL("unsupported SkBitmap::Config");
351 return SkMask::kA8_Format;
352 }
353}
354
reed6c225732014-06-09 19:52:07 -0700355inline SkColorType SkColorType_for_FTPixelMode(FT_Pixel_Mode pixel_mode) {
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000356 switch (pixel_mode) {
357 case FT_PIXEL_MODE_MONO:
358 case FT_PIXEL_MODE_GRAY:
reed6c225732014-06-09 19:52:07 -0700359 return kAlpha_8_SkColorType;
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000360 case FT_PIXEL_MODE_BGRA:
reed6c225732014-06-09 19:52:07 -0700361 return kN32_SkColorType;
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000362 default:
363 SkDEBUGFAIL("unsupported FT_PIXEL_MODE");
reed6c225732014-06-09 19:52:07 -0700364 return kAlpha_8_SkColorType;
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000365 }
366}
367
reed6c225732014-06-09 19:52:07 -0700368inline SkColorType SkColorType_for_SkMaskFormat(SkMask::Format format) {
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000369 switch (format) {
370 case SkMask::kBW_Format:
371 case SkMask::kA8_Format:
372 case SkMask::kLCD16_Format:
reed6c225732014-06-09 19:52:07 -0700373 return kAlpha_8_SkColorType;
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000374 case SkMask::kARGB32_Format:
reed6c225732014-06-09 19:52:07 -0700375 return kN32_SkColorType;
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000376 default:
377 SkDEBUGFAIL("unsupported destination SkBitmap::Config");
reed6c225732014-06-09 19:52:07 -0700378 return kAlpha_8_SkColorType;
george@mozilla.comc59b5da2012-08-23 00:39:08 +0000379 }
380}
381
Dominik Röttsches691a7942021-02-11 21:08:44 +0200382// Only build COLRv1 rendering code if FreeType is new enough to have COLRv1
383// additions. FreeType defines a macro in the ftoption header to tell us whether
384// it does support these features.
385#ifdef TT_SUPPORT_COLRV1
386
Dominik Röttsches4be20572021-10-27 17:58:29 +0300387const uint16_t kForegroundColorPaletteIndex = 0xFFFF;
388
Dominik Röttsches1c847f52021-04-29 17:03:01 +0300389struct OpaquePaintHasher {
390 size_t operator()(const FT_OpaquePaint& opaque_paint) {
391 return SkGoodHash()(opaque_paint.p) ^
392 SkGoodHash()(opaque_paint.insert_root_transform);
393 }
394};
395
396using VisitedSet = SkTHashSet<FT_OpaquePaint, OpaquePaintHasher>;
397
Dominik Röttsches691a7942021-02-11 21:08:44 +0200398bool generateFacePathCOLRv1(FT_Face face, SkGlyphID glyphID, SkPath* path);
399
400inline float SkColrV1AlphaToFloat(uint16_t alpha) { return (alpha / float(1 << 14)); }
401
402
403inline SkTileMode ToSkTileMode(FT_PaintExtend extend_mode) {
404 switch (extend_mode) {
405 case FT_COLR_PAINT_EXTEND_REPEAT:
406 return SkTileMode::kRepeat;
407 case FT_COLR_PAINT_EXTEND_REFLECT:
408 return SkTileMode::kMirror;
409 default:
410 return SkTileMode::kClamp;
411 }
412}
413
414inline SkBlendMode ToSkBlendMode(FT_Composite_Mode composite) {
415 switch (composite) {
416 case FT_COLR_COMPOSITE_CLEAR:
417 return SkBlendMode::kClear;
418 case FT_COLR_COMPOSITE_SRC:
419 return SkBlendMode::kSrc;
420 case FT_COLR_COMPOSITE_DEST:
421 return SkBlendMode::kDst;
422 case FT_COLR_COMPOSITE_SRC_OVER:
423 return SkBlendMode::kSrcOver;
424 case FT_COLR_COMPOSITE_DEST_OVER:
425 return SkBlendMode::kDstOver;
426 case FT_COLR_COMPOSITE_SRC_IN:
427 return SkBlendMode::kSrcIn;
428 case FT_COLR_COMPOSITE_DEST_IN:
429 return SkBlendMode::kDstIn;
430 case FT_COLR_COMPOSITE_SRC_OUT:
431 return SkBlendMode::kSrcOut;
432 case FT_COLR_COMPOSITE_DEST_OUT:
433 return SkBlendMode::kDstOut;
434 case FT_COLR_COMPOSITE_SRC_ATOP:
435 return SkBlendMode::kSrcATop;
436 case FT_COLR_COMPOSITE_DEST_ATOP:
437 return SkBlendMode::kDstATop;
438 case FT_COLR_COMPOSITE_XOR:
439 return SkBlendMode::kXor;
Dominik Röttsches90006032021-08-10 20:33:27 +0300440 case FT_COLR_COMPOSITE_PLUS:
441 return SkBlendMode::kPlus;
Dominik Röttsches691a7942021-02-11 21:08:44 +0200442 case FT_COLR_COMPOSITE_SCREEN:
443 return SkBlendMode::kScreen;
444 case FT_COLR_COMPOSITE_OVERLAY:
445 return SkBlendMode::kOverlay;
446 case FT_COLR_COMPOSITE_DARKEN:
447 return SkBlendMode::kDarken;
448 case FT_COLR_COMPOSITE_LIGHTEN:
449 return SkBlendMode::kLighten;
450 case FT_COLR_COMPOSITE_COLOR_DODGE:
451 return SkBlendMode::kColorDodge;
452 case FT_COLR_COMPOSITE_COLOR_BURN:
453 return SkBlendMode::kColorBurn;
454 case FT_COLR_COMPOSITE_HARD_LIGHT:
455 return SkBlendMode::kHardLight;
456 case FT_COLR_COMPOSITE_SOFT_LIGHT:
457 return SkBlendMode::kSoftLight;
458 case FT_COLR_COMPOSITE_DIFFERENCE:
459 return SkBlendMode::kDifference;
460 case FT_COLR_COMPOSITE_EXCLUSION:
461 return SkBlendMode::kExclusion;
462 case FT_COLR_COMPOSITE_MULTIPLY:
463 return SkBlendMode::kMultiply;
464 case FT_COLR_COMPOSITE_HSL_HUE:
465 return SkBlendMode::kHue;
466 case FT_COLR_COMPOSITE_HSL_SATURATION:
467 return SkBlendMode::kSaturation;
468 case FT_COLR_COMPOSITE_HSL_COLOR:
469 return SkBlendMode::kColor;
470 case FT_COLR_COMPOSITE_HSL_LUMINOSITY:
471 return SkBlendMode::kLuminosity;
472 default:
473 return SkBlendMode::kDst;
474 }
475}
476
477inline SkMatrix ToSkMatrix(FT_Affine23 affine23) {
478 // Adjust order to convert from FreeType's FT_Affine23 column major order to SkMatrix row-major
479 // order.
480 return SkMatrix::MakeAll(
481 SkFixedToScalar(affine23.xx), -SkFixedToScalar(affine23.xy), SkFixedToScalar(affine23.dx),
482 -SkFixedToScalar(affine23.yx), SkFixedToScalar(affine23.yy), -SkFixedToScalar(affine23.dy),
483 0, 0, 1);
484}
485
486inline SkPoint SkVectorProjection(SkPoint a, SkPoint b) {
487 SkScalar length = b.length();
488 if (!length) return SkPoint();
489 SkPoint b_normalized = b;
490 b_normalized.normalize();
491 b_normalized.scale(SkPoint::DotProduct(a, b) / length);
492 return b_normalized;
493}
494
Dominik Röttsches0bbce332021-11-04 12:20:44 +0200495bool colrv1_configure_skpaint(FT_Face face,
496 const SkSpan<FT_Color>& palette,
497 const SkColor foregroundColor,
498 FT_COLR_Paint colrv1_paint,
499 SkPaint* paint) {
500 auto fetch_color_stops = [&face, &palette, &foregroundColor](
501 FT_ColorStopIterator& color_stop_iterator,
Dominik Röttschesee98a422021-05-10 11:43:52 +0300502 std::vector<SkScalar>& stops,
Dominik Röttsches4be20572021-10-27 17:58:29 +0300503 std::vector<SkColor>& colors) -> bool {
Dominik Röttschesee98a422021-05-10 11:43:52 +0300504 const FT_UInt num_color_stops = color_stop_iterator.num_color_stops;
Dominik Röttsches2da029b2021-05-11 15:59:43 +0300505
506 // 5.7.11.2.4 ColorIndex, ColorStop and ColorLine
507 // "Applications shall apply the colorStops in increasing stopOffset order."
508 struct ColorStop {
509 SkScalar stop_pos;
510 SkColor color;
511 };
512 std::vector<ColorStop> sorted_stops;
513 sorted_stops.resize(num_color_stops);
Dominik Röttschesee98a422021-05-10 11:43:52 +0300514
515 FT_ColorStop color_stop;
516 while (FT_Get_Colorline_Stops(face, &color_stop, &color_stop_iterator)) {
517 FT_UInt index = color_stop_iterator.current_color_stop - 1;
Dominik Röttsches2da029b2021-05-11 15:59:43 +0300518 sorted_stops[index].stop_pos = color_stop.stop_offset / float(1 << 14);
Dominik Röttschesee98a422021-05-10 11:43:52 +0300519 FT_UInt16& palette_index = color_stop.color.palette_index;
Dominik Röttsches4be20572021-10-27 17:58:29 +0300520 if (palette_index == kForegroundColorPaletteIndex) {
Dominik Röttsches0bbce332021-11-04 12:20:44 +0200521 U8CPU newAlpha = SkColorGetA(foregroundColor) *
522 SkColrV1AlphaToFloat(color_stop.color.alpha);
523 sorted_stops[index].color = SkColorSetA(foregroundColor, newAlpha);
Dominik Röttsches4be20572021-10-27 17:58:29 +0300524 } else if (palette_index >= palette.size()) {
525 return false;
526 } else {
Dominik Röttsches0bbce332021-11-04 12:20:44 +0200527 U8CPU newAlpha = palette[palette_index].alpha *
528 SkColrV1AlphaToFloat(color_stop.color.alpha);
529 sorted_stops[index].color = SkColorSetARGB(newAlpha,
530 palette[palette_index].red,
531 palette[palette_index].green,
532 palette[palette_index].blue);
Dominik Röttsches4be20572021-10-27 17:58:29 +0300533 }
Dominik Röttschesee98a422021-05-10 11:43:52 +0300534 }
Dominik Röttsches2da029b2021-05-11 15:59:43 +0300535
536 std::stable_sort(
537 sorted_stops.begin(),
538 sorted_stops.end(),
539 [](const ColorStop& a, const ColorStop& b) { return a.stop_pos < b.stop_pos; });
540
541 stops.resize(num_color_stops);
542 colors.resize(num_color_stops);
543 for (size_t i = 0; i < num_color_stops; ++i) {
544 stops[i] = sorted_stops[i].stop_pos;
545 colors[i] = sorted_stops[i].color;
546 }
Dominik Röttsches4be20572021-10-27 17:58:29 +0300547 return true;
Dominik Röttschesee98a422021-05-10 11:43:52 +0300548 };
549
Dominik Röttsches691a7942021-02-11 21:08:44 +0200550 switch (colrv1_paint.format) {
Dominik Röttsches691a7942021-02-11 21:08:44 +0200551 case FT_COLR_PAINTFORMAT_SOLID: {
Dominik Röttsches2da029b2021-05-11 15:59:43 +0300552 FT_PaintSolid solid = colrv1_paint.u.solid;
Dominik Röttsches4be20572021-10-27 17:58:29 +0300553
554 // Dont' draw anything with this color if the palette index is out of bounds.
555 SkColor color = SK_ColorTRANSPARENT;
556 if (solid.color.palette_index == kForegroundColorPaletteIndex) {
Dominik Röttsches0bbce332021-11-04 12:20:44 +0200557 U8CPU newAlpha = SkColorGetA(foregroundColor) *
558 SkColrV1AlphaToFloat(solid.color.alpha);
559 color = SkColorSetA(foregroundColor, newAlpha);
Dominik Röttsches4be20572021-10-27 17:58:29 +0300560 } else if (solid.color.palette_index >= palette.size()) {
561 return false;
562 } else {
Dominik Röttsches0bbce332021-11-04 12:20:44 +0200563 U8CPU newAlpha = palette[solid.color.palette_index].alpha *
564 SkColrV1AlphaToFloat(solid.color.alpha);
565 color = SkColorSetARGB(newAlpha,
Dominik Röttsches4be20572021-10-27 17:58:29 +0300566 palette[solid.color.palette_index].red,
567 palette[solid.color.palette_index].green,
568 palette[solid.color.palette_index].blue);
569 }
Dominik Röttschesf37b23d2021-04-13 21:48:07 +0300570 paint->setShader(nullptr);
571 paint->setColor(color);
Dominik Röttsches691a7942021-02-11 21:08:44 +0200572 break;
573 }
574 case FT_COLR_PAINTFORMAT_LINEAR_GRADIENT: {
Dominik Röttsches2da029b2021-05-11 15:59:43 +0300575 FT_PaintLinearGradient& linear_gradient = colrv1_paint.u.linear_gradient;
Dominik Röttschesa9860f02021-11-03 14:34:00 +0200576 SkPoint line_positions[2] = {SkPoint::Make(SkFixedToScalar(linear_gradient.p0.x),
577 -SkFixedToScalar(linear_gradient.p0.y)),
578 SkPoint::Make(SkFixedToScalar(linear_gradient.p1.x),
579 -SkFixedToScalar(linear_gradient.p1.y))};
Dominik Röttsches2da029b2021-05-11 15:59:43 +0300580 SkPoint p0 = line_positions[0];
581 SkPoint p1 = line_positions[1];
Dominik Röttschesa9860f02021-11-03 14:34:00 +0200582 SkPoint p2 = SkPoint::Make(SkFixedToScalar(linear_gradient.p2.x),
583 -SkFixedToScalar(linear_gradient.p2.y));
Dominik Röttsches691a7942021-02-11 21:08:44 +0200584
Dominik Röttsches7368c6d2021-11-01 17:54:47 +0200585 // Do not draw the gradient if p0p1 is parallel to p0p2.
Dominik Röttsches691a7942021-02-11 21:08:44 +0200586 if (p1 == p0 || p2 == p0 || !SkPoint::CrossProduct(p1 - p0, p2 - p0)) break;
587
588 // Follow implementation note in nanoemoji:
589 // https://github.com/googlefonts/nanoemoji/blob/0ac6e7bb4d8202db692574d8530a9b643f1b3b3c/src/nanoemoji/svg.py#L188
590 // to compute a new gradient end point as the orthogonal projection of the vector from p0 to p1 onto a line
591 // perpendicular to line p0p2 and passing through p0.
Dominik Röttsches2da029b2021-05-11 15:59:43 +0300592 SkVector perpendicular_to_p2_p0 = (p2 - p0);
Dominik Röttsches691a7942021-02-11 21:08:44 +0200593 perpendicular_to_p2_p0 = SkPoint::Make(perpendicular_to_p2_p0.y(), -perpendicular_to_p2_p0.x());
594 line_positions[1] = p0 + SkVectorProjection((p1 - p0), perpendicular_to_p2_p0);
595
Dominik Röttsches691a7942021-02-11 21:08:44 +0200596 std::vector<SkScalar> stops;
597 std::vector<SkColor> colors;
Dominik Röttsches2da029b2021-05-11 15:59:43 +0300598
Dominik Röttsches4be20572021-10-27 17:58:29 +0300599 if (!fetch_color_stops(linear_gradient.colorline.color_stop_iterator, stops, colors) ||
600 stops.empty()) {
601 return false;
Dominik Röttsches2da029b2021-05-11 15:59:43 +0300602 }
603
604 if (stops.size() == 1) {
605 paint->setColor(colors[0]);
606 break;
607 }
608
609 // Project/scale points according to stop extrema along p0p1 line,
610 // then scale stops to to [0, 1] range so that repeat modes work.
611 // The Skia linear gradient shader performs the repeat modes over
612 // the 0 to 1 range, that's why we need to scale the stops to within
613 // that range.
614 SkVector p0p1 = p1 - p0;
615 SkVector new_p0_offset = p0p1;
616 new_p0_offset.scale(stops.front());
617 SkVector new_p1_offset = p0p1;
618 new_p1_offset.scale(stops.back());
619
620 line_positions[0] = p0 + new_p0_offset;
621 line_positions[1] = p0 + new_p1_offset;
622
623 SkScalar scale_factor = 1 / (stops.back() - stops.front());
624 SkScalar start_offset = stops.front();
625 for (SkScalar& stop : stops) {
626 stop = (stop - start_offset) * scale_factor;
627 }
Dominik Röttsches691a7942021-02-11 21:08:44 +0200628
629 sk_sp<SkShader> shader(SkGradientShader::MakeLinear(
Dominik Röttsches2da029b2021-05-11 15:59:43 +0300630 line_positions,
631 colors.data(),
632 stops.data(),
633 stops.size(),
634 ToSkTileMode(linear_gradient.colorline.extend)));
Dominik Röttsches691a7942021-02-11 21:08:44 +0200635 SkASSERT(shader);
Dominik Röttsches7368c6d2021-11-01 17:54:47 +0200636 // An opaque color is needed to ensure the gradient is not modulated by alpha.
Dominik Röttschesf37b23d2021-04-13 21:48:07 +0300637 paint->setColor(SK_ColorBLACK);
638 paint->setShader(shader);
Dominik Röttsches691a7942021-02-11 21:08:44 +0200639
Dominik Röttsches691a7942021-02-11 21:08:44 +0200640 break;
641 }
642 case FT_COLR_PAINTFORMAT_RADIAL_GRADIENT: {
Dominik Röttsches2da029b2021-05-11 15:59:43 +0300643 FT_PaintRadialGradient& radial_gradient = colrv1_paint.u.radial_gradient;
Dominik Röttschesa9860f02021-11-03 14:34:00 +0200644 SkPoint start = SkPoint::Make(SkFixedToScalar(radial_gradient.c0.x),
645 -SkFixedToScalar(radial_gradient.c0.y));
646 SkScalar radius = SkFixedToScalar(radial_gradient.r0);
647 SkPoint end = SkPoint::Make(SkFixedToScalar(radial_gradient.c1.x),
648 -SkFixedToScalar(radial_gradient.c1.y));
649 SkScalar end_radius = SkFixedToScalar(radial_gradient.r1);
Dominik Röttsches691a7942021-02-11 21:08:44 +0200650
Dominik Röttschesee98a422021-05-10 11:43:52 +0300651
Dominik Röttsches691a7942021-02-11 21:08:44 +0200652 std::vector<SkScalar> stops;
653 std::vector<SkColor> colors;
Dominik Röttsches4be20572021-10-27 17:58:29 +0300654 if (!fetch_color_stops(radial_gradient.colorline.color_stop_iterator, stops, colors)) {
655 return false;
656 }
Dominik Röttsches691a7942021-02-11 21:08:44 +0200657
Dominik Röttsches7368c6d2021-11-01 17:54:47 +0200658 // An opaque color is needed to ensure the gradient is not modulated by alpha.
Dominik Röttschesf37b23d2021-04-13 21:48:07 +0300659 paint->setColor(SK_ColorBLACK);
Dominik Röttsches691a7942021-02-11 21:08:44 +0200660
Dominik Röttschesf37b23d2021-04-13 21:48:07 +0300661 paint->setShader(SkGradientShader::MakeTwoPointConical(
Dominik Röttschesee98a422021-05-10 11:43:52 +0300662 start, radius, end, end_radius, colors.data(), stops.data(), stops.size(),
Dominik Röttsches2da029b2021-05-11 15:59:43 +0300663 ToSkTileMode(radial_gradient.colorline.extend)));
Dominik Röttsches691a7942021-02-11 21:08:44 +0200664 break;
665 }
666 case FT_COLR_PAINTFORMAT_SWEEP_GRADIENT: {
Dominik Röttsches2da029b2021-05-11 15:59:43 +0300667 FT_PaintSweepGradient& sweep_gradient = colrv1_paint.u.sweep_gradient;
Dominik Röttschesa9860f02021-11-03 14:34:00 +0200668 SkPoint center = SkPoint::Make(SkFixedToScalar(sweep_gradient.center.x),
669 -SkFixedToScalar(sweep_gradient.center.y));
Dominik Röttsches58920bb2021-07-29 11:37:33 +0300670 SkScalar startAngle = SkFixedToScalar(sweep_gradient.start_angle * 180.0f);
671 SkScalar endAngle = SkFixedToScalar(sweep_gradient.end_angle * 180.0f);
Dominik Röttsches691a7942021-02-11 21:08:44 +0200672
Dominik Röttsches691a7942021-02-11 21:08:44 +0200673 std::vector<SkScalar> stops;
674 std::vector<SkColor> colors;
Dominik Röttsches4be20572021-10-27 17:58:29 +0300675 if (!fetch_color_stops(sweep_gradient.colorline.color_stop_iterator, stops, colors)) {
676 return false;
677 }
Dominik Röttsches691a7942021-02-11 21:08:44 +0200678
Dominik Röttsches7368c6d2021-11-01 17:54:47 +0200679 // An opaque color is needed to ensure the gradient is not modulated by alpha.
Dominik Röttschesf37b23d2021-04-13 21:48:07 +0300680 paint->setColor(SK_ColorBLACK);
Dominik Röttsches691a7942021-02-11 21:08:44 +0200681
682 // Prepare angles to be within range for the shader.
683 auto clampAngleToRange= [](SkScalar angle) {
684 SkScalar clamped_angle = SkScalarMod(angle, 360.f);
685 if (clamped_angle < 0)
686 return clamped_angle + 360.f;
687 return clamped_angle;
688 };
689 startAngle = clampAngleToRange(startAngle);
690 endAngle = clampAngleToRange(endAngle);
691 /* TODO: Spec clarifications on which side of the gradient is to be
692 * painted, repeat modes, how to handle 0 degrees transition, see
693 * https://github.com/googlefonts/colr-gradients-spec/issues/250 */
694 if (startAngle >= endAngle)
695 endAngle += 360.f;
696
697 // Skia's angles start from the horizontal x-Axis, rotate left 90
698 // degrees and then mirror horizontally to correct for Skia angles
699 // going clockwise, COLR v1 angles going counterclockwise.
700 SkMatrix angle_adjust = SkMatrix::RotateDeg(-90.f, center);
701 angle_adjust.postScale(-1, 1, center.x(), center.y());
702
Dominik Röttschesf37b23d2021-04-13 21:48:07 +0300703 paint->setShader(SkGradientShader::MakeSweep(
Dominik Röttschesee98a422021-05-10 11:43:52 +0300704 center.x(), center.y(), colors.data(), stops.data(), stops.size(),
Dominik Röttsches691a7942021-02-11 21:08:44 +0200705 SkTileMode::kDecal, startAngle, endAngle, 0, &angle_adjust));
Dominik Röttschesf37b23d2021-04-13 21:48:07 +0300706 break;
707 }
708 default: {
709 SkASSERT(false); /* not reached */
710 }
711 }
Dominik Röttsches4be20572021-10-27 17:58:29 +0300712 return true;
Dominik Röttschesf37b23d2021-04-13 21:48:07 +0300713}
714
Dominik Röttschesf37b23d2021-04-13 21:48:07 +0300715void colrv1_draw_paint(SkCanvas* canvas,
Dominik Röttsches4be20572021-10-27 17:58:29 +0300716 const SkSpan<FT_Color>& palette,
Dominik Röttsches0bbce332021-11-04 12:20:44 +0200717 const SkColor foregroundColor,
Dominik Röttschesf37b23d2021-04-13 21:48:07 +0300718 FT_Face face,
719 FT_COLR_Paint colrv1_paint) {
720 SkPaint paint;
721
722 switch (colrv1_paint.format) {
723 case FT_COLR_PAINTFORMAT_GLYPH: {
724 FT_UInt glyphID = colrv1_paint.u.glyph.glyphID;
725 SkPath path;
726 /* TODO: Currently this call retrieves the path at units_per_em size. If we want to get
727 * correct hinting for the scaled size under the transforms at this point in the color
728 * glyph graph, we need to extract at least the requested glyph width and height and
729 * pass that to the path generation. */
730 if (generateFacePathCOLRv1(face, glyphID, &path)) {
731
732#ifdef SK_SHOW_TEXT_BLIT_COVERAGE
733 SkPaint highlight_paint;
734 highlight_paint.setColor(0x33FF0000);
735 canvas->drawRect(path.getBounds(), highlight_paint);
736#endif
737 canvas->clipPath(path, true /* doAntiAlias */);
738 }
739 break;
740 }
741 case FT_COLR_PAINTFORMAT_SOLID:
742 case FT_COLR_PAINTFORMAT_LINEAR_GRADIENT:
743 case FT_COLR_PAINTFORMAT_RADIAL_GRADIENT:
744 case FT_COLR_PAINTFORMAT_SWEEP_GRADIENT: {
745 SkPaint colrPaint;
Dominik Röttsches0bbce332021-11-04 12:20:44 +0200746 if (colrv1_configure_skpaint(
747 face, palette, foregroundColor, colrv1_paint, &colrPaint)) {
Dominik Röttsches4be20572021-10-27 17:58:29 +0300748 canvas->drawPaint(colrPaint);
749 }
Dominik Röttsches691a7942021-02-11 21:08:44 +0200750 break;
751 }
Dominik Röttsches7b9b7382021-06-23 15:08:04 +0300752 case FT_COLR_PAINTFORMAT_TRANSFORM:
Dominik Röttschesac04d952021-04-13 21:49:54 +0300753 case FT_COLR_PAINTFORMAT_TRANSLATE:
Ben Wagnera149e8f2021-07-01 14:10:55 -0400754 case FT_COLR_PAINTFORMAT_SCALE:
Dominik Röttschesac04d952021-04-13 21:49:54 +0300755 case FT_COLR_PAINTFORMAT_ROTATE:
756 case FT_COLR_PAINTFORMAT_SKEW:
757 SkASSERT(false); // Transforms handled in colrv1_transform.
758 break;
759 default:
760 paint.setShader(nullptr);
761 paint.setColor(SK_ColorCYAN);
762 break;
763 }
764}
Dominik Röttsches691a7942021-02-11 21:08:44 +0200765
Dominik Röttsches0bbce332021-11-04 12:20:44 +0200766void colrv1_draw_glyph_with_path(SkCanvas* canvas, const SkSpan<FT_Color>& palette, SkColor foregroundColor, FT_Face face,
Dominik Röttsches4df814c2021-04-13 21:31:07 +0300767 FT_COLR_Paint glyphPaint, FT_COLR_Paint fillPaint) {
768 SkASSERT(glyphPaint.format == FT_COLR_PAINTFORMAT_GLYPH);
769 SkASSERT(fillPaint.format == FT_COLR_PAINTFORMAT_SOLID ||
770 fillPaint.format == FT_COLR_PAINTFORMAT_LINEAR_GRADIENT ||
771 fillPaint.format == FT_COLR_PAINTFORMAT_RADIAL_GRADIENT ||
772 fillPaint.format == FT_COLR_PAINTFORMAT_SWEEP_GRADIENT);
773
774 SkPaint skiaFillPaint;
775 skiaFillPaint.setAntiAlias(true);
Dominik Röttsches0bbce332021-11-04 12:20:44 +0200776 if (!colrv1_configure_skpaint(face, palette, foregroundColor, fillPaint, &skiaFillPaint)) {
Dominik Röttsches4be20572021-10-27 17:58:29 +0300777 return;
778 }
Dominik Röttsches4df814c2021-04-13 21:31:07 +0300779
780 FT_UInt glyphID = glyphPaint.u.glyph.glyphID;
781 SkPath path;
782 /* TODO: Currently this call retrieves the path at units_per_em size. If we want to get
783 * correct hinting for the scaled size under the transforms at this point in the color
784 * glyph graph, we need to extract at least the requested glyph width and height and
785 * pass that to the path generation. */
786 if (generateFacePathCOLRv1(face, glyphID, &path)) {
787#ifdef SK_SHOW_TEXT_BLIT_COVERAGE
788 SkPaint highlight_paint;
789 highlight_paint.setColor(0x33FF0000);
790 canvas->drawRect(path.getBounds(), highlight_paint);
791#endif
792 {
793 canvas->drawPath(path, skiaFillPaint);
794 }
795 }
796}
Dominik Röttschesac04d952021-04-13 21:49:54 +0300797
Dominik Röttsches2fa273e2021-08-10 12:10:45 +0300798
799/* In drawing mode, concatenates the transforms directly on SkCanvas. In
800 * bounding box calculation mode, no SkCanvas is specified, but we only want to
801 * retrieve the transform from the FreeType paint object. */
802void colrv1_transform(FT_Face face,
803 FT_COLR_Paint colrv1_paint,
804 SkCanvas* canvas,
805 SkMatrix* out_transform = 0) {
Dominik Röttschesac04d952021-04-13 21:49:54 +0300806 SkMatrix transform;
807
Dominik Röttsches2fa273e2021-08-10 12:10:45 +0300808 SkASSERT(canvas || out_transform);
809
Dominik Röttschesac04d952021-04-13 21:49:54 +0300810 switch (colrv1_paint.format) {
Dominik Röttsches7b9b7382021-06-23 15:08:04 +0300811 case FT_COLR_PAINTFORMAT_TRANSFORM: {
812 transform = ToSkMatrix(colrv1_paint.u.transform.affine);
Dominik Röttsches691a7942021-02-11 21:08:44 +0200813 break;
814 }
Dominik Röttsches2e04df92021-03-25 16:13:46 +0200815 case FT_COLR_PAINTFORMAT_TRANSLATE: {
Dominik Röttschesac04d952021-04-13 21:49:54 +0300816 transform = SkMatrix::Translate(
Dominik Röttsches2e04df92021-03-25 16:13:46 +0200817 SkFixedToScalar(colrv1_paint.u.translate.dx),
818 -SkFixedToScalar(colrv1_paint.u.translate.dy));
Dominik Röttsches2e04df92021-03-25 16:13:46 +0200819 break;
820 }
Ben Wagnera149e8f2021-07-01 14:10:55 -0400821 case FT_COLR_PAINTFORMAT_SCALE: {
Dominik Röttsches613ca2b2021-07-26 11:13:47 +0300822 transform.setScale(SkFixedToScalar(colrv1_paint.u.scale.scale_x),
823 SkFixedToScalar(colrv1_paint.u.scale.scale_y),
824 SkFixedToScalar(colrv1_paint.u.scale.center_x),
825 -SkFixedToScalar(colrv1_paint.u.scale.center_y));
Ben Wagnera149e8f2021-07-01 14:10:55 -0400826 break;
827 }
Dominik Röttsches691a7942021-02-11 21:08:44 +0200828 case FT_COLR_PAINTFORMAT_ROTATE: {
Dominik Röttschesac04d952021-04-13 21:49:54 +0300829 transform = SkMatrix::RotateDeg(
Dominik Röttsches58920bb2021-07-29 11:37:33 +0300830 SkFixedToScalar(colrv1_paint.u.rotate.angle) * 180.0f,
Dominik Röttsches691a7942021-02-11 21:08:44 +0200831 SkPoint::Make(SkFixedToScalar(colrv1_paint.u.rotate.center_x),
832 -SkFixedToScalar(colrv1_paint.u.rotate.center_y)));
Dominik Röttsches691a7942021-02-11 21:08:44 +0200833 break;
834 }
835 case FT_COLR_PAINTFORMAT_SKEW: {
836 // In the PAINTFORMAT_ROTATE implementation, SkMatrix setRotate
837 // snaps to 0 for values very close to 0. Do the same here.
838
Dominik Röttsches58920bb2021-07-29 11:37:33 +0300839 SkScalar rad_x =
840 SkDegreesToRadians(-SkFixedToFloat(colrv1_paint.u.skew.x_skew_angle) * 180.0f);
Dominik Röttsches691a7942021-02-11 21:08:44 +0200841 float tan_x = SkScalarTan(rad_x);
842 tan_x = SkScalarNearlyZero(tan_x) ? 0.0f : tan_x;
843
Dominik Röttsches58920bb2021-07-29 11:37:33 +0300844 SkScalar rad_y =
845 SkDegreesToRadians(-SkFixedToFloat(colrv1_paint.u.skew.y_skew_angle) * 180.0f);
Dominik Röttsches691a7942021-02-11 21:08:44 +0200846 float tan_y = SkScalarTan(rad_y);
847 tan_y = SkScalarNearlyZero(tan_y) ? 0.0f : tan_y;
848
Dominik Röttsches58920bb2021-07-29 11:37:33 +0300849 transform.setSkew(tan_x,
850 tan_y,
851 SkFixedToScalar(colrv1_paint.u.skew.center_x),
852 -SkFixedToFloat(colrv1_paint.u.skew.center_y));
Dominik Röttsches691a7942021-02-11 21:08:44 +0200853 break;
854 }
Dominik Röttschesac04d952021-04-13 21:49:54 +0300855 default: {
856 // Only transforms are handled in this function.
857 SkASSERT(false);
858 }
Dominik Röttsches691a7942021-02-11 21:08:44 +0200859 }
Dominik Röttsches2fa273e2021-08-10 12:10:45 +0300860 if (canvas) {
861 canvas->concat(transform);
862 }
863 if (out_transform) {
864 *out_transform = transform;
865 }
Dominik Röttsches691a7942021-02-11 21:08:44 +0200866}
867
868bool colrv1_start_glyph(SkCanvas* canvas,
Dominik Röttsches4be20572021-10-27 17:58:29 +0300869 const SkSpan<FT_Color>& palette,
Dominik Röttsches0bbce332021-11-04 12:20:44 +0200870 const SkColor foregroundColor,
Dominik Röttsches691a7942021-02-11 21:08:44 +0200871 FT_Face ft_face,
872 uint16_t glyph_id,
873 FT_Color_Root_Transform root_transform);
874
875bool colrv1_traverse_paint(SkCanvas* canvas,
Dominik Röttsches4be20572021-10-27 17:58:29 +0300876 const SkSpan<FT_Color>& palette,
Dominik Röttsches0bbce332021-11-04 12:20:44 +0200877 const SkColor foregroundColor,
Dominik Röttsches691a7942021-02-11 21:08:44 +0200878 FT_Face face,
Dominik Röttsches1c847f52021-04-29 17:03:01 +0300879 FT_OpaquePaint opaque_paint,
880 VisitedSet* visited_set) {
881 // Cycle detection, see section "5.7.11.1.9 Color glyphs as a directed acyclic graph".
882 if (visited_set->contains(opaque_paint)) {
883 return false;
884 }
885
886 visited_set->add(opaque_paint);
887 SK_AT_SCOPE_EXIT(visited_set->remove(opaque_paint));
888
Dominik Röttsches691a7942021-02-11 21:08:44 +0200889 FT_COLR_Paint paint;
890 if (!FT_Get_Paint(face, opaque_paint, &paint)) {
891 return false;
892 }
893
894 // Keep track of failures to retrieve the FT_COLR_Paint from FreeType in the
895 // recursion, cancel recursion when a paint retrieval fails.
896 bool traverse_result = true;
Dominik Röttschese14d7252021-04-12 17:43:29 +0300897 SkAutoCanvasRestore autoRestore(canvas, true /* do_save */);
Dominik Röttsches691a7942021-02-11 21:08:44 +0200898 switch (paint.format) {
899 case FT_COLR_PAINTFORMAT_COLR_LAYERS: {
900 FT_LayerIterator& layer_iterator = paint.u.colr_layers.layer_iterator;
901 FT_OpaquePaint opaque_paint_fetch;
902 opaque_paint_fetch.p = nullptr;
903 while (FT_Get_Paint_Layers(face, &layer_iterator, &opaque_paint_fetch)) {
Dominik Röttsches0bbce332021-11-04 12:20:44 +0200904 colrv1_traverse_paint(canvas, palette, foregroundColor, face,
905 opaque_paint_fetch, visited_set);
Dominik Röttsches691a7942021-02-11 21:08:44 +0200906 }
907 break;
908 }
909 case FT_COLR_PAINTFORMAT_GLYPH:
Dominik Röttsches4df814c2021-04-13 21:31:07 +0300910 // Special case paint graph leaf situations to improve
911 // performance. These are situations in the graph where a GlyphPaint
912 // is followed by either a solid or a gradient fill. Here we can use
913 // drawPath() + SkPaint directly which is faster than setting a
914 // clipPath() followed by a drawPaint().
915 FT_COLR_Paint fillPaint;
916 if (!FT_Get_Paint(face, paint.u.glyph.paint, &fillPaint)) {
917 return false;
918 }
919 if (fillPaint.format == FT_COLR_PAINTFORMAT_SOLID ||
920 fillPaint.format == FT_COLR_PAINTFORMAT_LINEAR_GRADIENT ||
921 fillPaint.format == FT_COLR_PAINTFORMAT_RADIAL_GRADIENT ||
922 fillPaint.format == FT_COLR_PAINTFORMAT_SWEEP_GRADIENT) {
Dominik Röttsches0bbce332021-11-04 12:20:44 +0200923 colrv1_draw_glyph_with_path(canvas, palette, foregroundColor,
924 face, paint, fillPaint);
Dominik Röttsches4df814c2021-04-13 21:31:07 +0300925 } else {
Dominik Röttsches0bbce332021-11-04 12:20:44 +0200926 colrv1_draw_paint(canvas, palette, foregroundColor, face, paint);
927 traverse_result = colrv1_traverse_paint(canvas, palette,
928 foregroundColor, face,
929 paint.u.glyph.paint,
930 visited_set);
Dominik Röttsches4df814c2021-04-13 21:31:07 +0300931 }
Dominik Röttsches691a7942021-02-11 21:08:44 +0200932 break;
933 case FT_COLR_PAINTFORMAT_COLR_GLYPH:
Dominik Röttsches0bbce332021-11-04 12:20:44 +0200934 traverse_result = colrv1_start_glyph(canvas, palette, foregroundColor,
935 face, paint.u.colr_glyph.glyphID,
Dominik Röttsches691a7942021-02-11 21:08:44 +0200936 FT_COLOR_NO_ROOT_TRANSFORM);
937 break;
Dominik Röttsches7b9b7382021-06-23 15:08:04 +0300938 case FT_COLR_PAINTFORMAT_TRANSFORM:
Dominik Röttsches2fa273e2021-08-10 12:10:45 +0300939 colrv1_transform(face, paint, canvas);
Dominik Röttsches0bbce332021-11-04 12:20:44 +0200940 traverse_result = colrv1_traverse_paint(canvas, palette, foregroundColor,
941 face, paint.u.transform.paint,
942 visited_set);
Dominik Röttsches691a7942021-02-11 21:08:44 +0200943 break;
Dominik Röttschese14d7252021-04-12 17:43:29 +0300944 case FT_COLR_PAINTFORMAT_TRANSLATE:
Dominik Röttsches2fa273e2021-08-10 12:10:45 +0300945 colrv1_transform(face, paint, canvas);
Dominik Röttsches0bbce332021-11-04 12:20:44 +0200946 traverse_result = colrv1_traverse_paint(canvas, palette, foregroundColor,
947 face, paint.u.translate.paint,
948 visited_set);
Dominik Röttsches2e04df92021-03-25 16:13:46 +0200949 break;
Ben Wagnera149e8f2021-07-01 14:10:55 -0400950 case FT_COLR_PAINTFORMAT_SCALE:
Dominik Röttsches2fa273e2021-08-10 12:10:45 +0300951 colrv1_transform(face, paint, canvas);
Dominik Röttsches0bbce332021-11-04 12:20:44 +0200952 traverse_result = colrv1_traverse_paint(canvas, palette, foregroundColor,
953 face, paint.u.scale.paint,
954 visited_set);
Ben Wagnera149e8f2021-07-01 14:10:55 -0400955 break;
Dominik Röttschese14d7252021-04-12 17:43:29 +0300956 case FT_COLR_PAINTFORMAT_ROTATE:
Dominik Röttsches2fa273e2021-08-10 12:10:45 +0300957 colrv1_transform(face, paint, canvas);
Dominik Röttsches1c847f52021-04-29 17:03:01 +0300958 traverse_result =
Dominik Röttsches0bbce332021-11-04 12:20:44 +0200959 colrv1_traverse_paint(canvas, palette, foregroundColor, face,
Dominik Röttsches1c847f52021-04-29 17:03:01 +0300960 paint.u.rotate.paint, visited_set);
Dominik Röttsches691a7942021-02-11 21:08:44 +0200961 break;
962 case FT_COLR_PAINTFORMAT_SKEW:
Dominik Röttsches2fa273e2021-08-10 12:10:45 +0300963 colrv1_transform(face, paint, canvas);
Dominik Röttsches1c847f52021-04-29 17:03:01 +0300964 traverse_result =
Dominik Röttsches0bbce332021-11-04 12:20:44 +0200965 colrv1_traverse_paint(canvas, palette, foregroundColor, face,
Dominik Röttsches1c847f52021-04-29 17:03:01 +0300966 paint.u.skew.paint, visited_set);
Dominik Röttsches691a7942021-02-11 21:08:44 +0200967 break;
968 case FT_COLR_PAINTFORMAT_COMPOSITE: {
Dominik Röttsches09961b52021-09-06 15:58:40 +0300969 canvas->saveLayer(nullptr, nullptr);
Dominik Röttsches1c847f52021-04-29 17:03:01 +0300970 traverse_result = colrv1_traverse_paint(
Dominik Röttsches0bbce332021-11-04 12:20:44 +0200971 canvas, palette, foregroundColor, face,
972 paint.u.composite.backdrop_paint, visited_set);
Dominik Röttsches691a7942021-02-11 21:08:44 +0200973 SkPaint blend_mode_paint;
974 blend_mode_paint.setBlendMode(ToSkBlendMode(paint.u.composite.composite_mode));
975 canvas->saveLayer(nullptr, &blend_mode_paint);
976 traverse_result =
977 traverse_result &&
Dominik Röttsches1c847f52021-04-29 17:03:01 +0300978 colrv1_traverse_paint(
Dominik Röttsches0bbce332021-11-04 12:20:44 +0200979 canvas, palette, foregroundColor,
980 face, paint.u.composite.source_paint, visited_set);
Dominik Röttsches691a7942021-02-11 21:08:44 +0200981 canvas->restore();
Dominik Röttsches09961b52021-09-06 15:58:40 +0300982 canvas->restore();
Dominik Röttsches691a7942021-02-11 21:08:44 +0200983 break;
984 }
985 case FT_COLR_PAINTFORMAT_SOLID:
986 case FT_COLR_PAINTFORMAT_LINEAR_GRADIENT:
987 case FT_COLR_PAINTFORMAT_RADIAL_GRADIENT:
988 case FT_COLR_PAINTFORMAT_SWEEP_GRADIENT: {
Dominik Röttsches0bbce332021-11-04 12:20:44 +0200989 colrv1_draw_paint(canvas, palette, foregroundColor, face, paint);
Dominik Röttsches691a7942021-02-11 21:08:44 +0200990 break;
991 }
992 default:
993 SkASSERT(false);
994 break;
995 }
996 return traverse_result;
997}
998
Dominik Röttsches2fa273e2021-08-10 12:10:45 +0300999SkPath GetClipBoxPath(FT_Face ft_face, uint16_t glyph_id, bool untransformed) {
1000 SkPath resultPath;
1001
1002 using DoneFTSize = SkFunctionWrapper<decltype(FT_Done_Size), FT_Done_Size>;
1003 std::unique_ptr<std::remove_pointer_t<FT_Size>, DoneFTSize> unscaledFtSize = nullptr;
1004
1005 FT_Size oldSize = ft_face->size;
1006 FT_Matrix oldTransform;
1007 FT_Vector oldDelta;
1008 FT_Error err = 0;
1009
1010 if (untransformed) {
1011 unscaledFtSize.reset(
1012 [ft_face]() -> FT_Size {
1013 FT_Size size;
1014 FT_Error err = FT_New_Size(ft_face, &size);
1015 if (err != 0) {
1016 SK_TRACEFTR(err,
1017 "FT_New_Size(%s) failed in generateFacePathStaticCOLRv1.",
1018 ft_face->family_name);
1019 return nullptr;
1020 }
1021 return size;
1022 }());
1023 if (!unscaledFtSize) {
1024 return resultPath;
1025 }
1026
1027 err = FT_Activate_Size(unscaledFtSize.get());
1028 if (err != 0) {
1029 return resultPath;
1030 }
1031
1032 err = FT_Set_Char_Size(ft_face, SkIntToFDot6(ft_face->units_per_EM), 0, 0, 0);
1033 if (err != 0) {
1034 return resultPath;
1035 }
1036
1037 FT_Get_Transform(ft_face, &oldTransform, &oldDelta);
1038 FT_Set_Transform(ft_face, nullptr, nullptr);
1039 }
1040
1041 FT_ClipBox colrGlyphClipBox;
1042 if (FT_Get_Color_Glyph_ClipBox(ft_face, glyph_id, &colrGlyphClipBox)) {
1043 resultPath = SkPath::Polygon({{SkFDot6ToScalar(colrGlyphClipBox.bottom_left.x),
1044 -SkFDot6ToScalar(colrGlyphClipBox.bottom_left.y)},
1045 {SkFDot6ToScalar(colrGlyphClipBox.top_left.x),
1046 -SkFDot6ToScalar(colrGlyphClipBox.top_left.y)},
1047 {SkFDot6ToScalar(colrGlyphClipBox.top_right.x),
1048 -SkFDot6ToScalar(colrGlyphClipBox.top_right.y)},
1049 {SkFDot6ToScalar(colrGlyphClipBox.bottom_right.x),
1050 -SkFDot6ToScalar(colrGlyphClipBox.bottom_right.y)}},
1051 true);
1052 }
1053
1054 if (untransformed) {
1055 err = FT_Activate_Size(oldSize);
1056 if (err != 0) {
1057 return resultPath;
1058 }
1059 FT_Set_Transform(ft_face, &oldTransform, &oldDelta);
1060 }
1061
1062 return resultPath;
1063}
1064
Dominik Röttsches691a7942021-02-11 21:08:44 +02001065bool colrv1_start_glyph(SkCanvas* canvas,
Dominik Röttsches4be20572021-10-27 17:58:29 +03001066 const SkSpan<FT_Color>& palette,
Dominik Röttsches0bbce332021-11-04 12:20:44 +02001067 const SkColor foregroundColor,
Dominik Röttsches691a7942021-02-11 21:08:44 +02001068 FT_Face ft_face,
1069 uint16_t glyph_id,
1070 FT_Color_Root_Transform root_transform) {
1071 FT_OpaquePaint opaque_paint;
1072 opaque_paint.p = nullptr;
1073 bool has_colrv1_layers = false;
1074 if (FT_Get_Color_Glyph_Paint(ft_face, glyph_id, root_transform, &opaque_paint)) {
1075 has_colrv1_layers = true;
Dominik Röttsches2fa273e2021-08-10 12:10:45 +03001076
1077 SkPath clipBoxPath =
1078 GetClipBoxPath(ft_face, glyph_id, root_transform == FT_COLOR_NO_ROOT_TRANSFORM);
1079 if (!clipBoxPath.isEmpty()) {
1080 canvas->clipPath(clipBoxPath, true);
1081 }
1082
Dominik Röttsches1c847f52021-04-29 17:03:01 +03001083 VisitedSet visited_set;
Dominik Röttsches0bbce332021-11-04 12:20:44 +02001084 colrv1_traverse_paint(canvas, palette, foregroundColor,
1085 ft_face, opaque_paint, &visited_set);
Dominik Röttsches691a7942021-02-11 21:08:44 +02001086 }
1087 return has_colrv1_layers;
1088}
Dominik Röttsches2fa273e2021-08-10 12:10:45 +03001089
1090bool colrv1_start_glyph_bounds(SkMatrix *ctm,
1091 SkRect* bounds,
1092 FT_Face ft_face,
1093 uint16_t glyph_id,
1094 FT_Color_Root_Transform root_transform);
1095
1096bool colrv1_traverse_paint_bounds(SkMatrix* ctm,
1097 SkRect* bounds,
1098 FT_Face face,
1099 FT_OpaquePaint opaque_paint,
1100 VisitedSet* visited_set) {
1101 // Cycle detection, see section "5.7.11.1.9 Color glyphs as a directed acyclic graph".
1102 if (visited_set->contains(opaque_paint)) {
1103 return false;
1104 }
1105
1106 visited_set->add(opaque_paint);
1107 SK_AT_SCOPE_EXIT(visited_set->remove(opaque_paint));
1108
1109 FT_COLR_Paint paint;
1110 if (!FT_Get_Paint(face, opaque_paint, &paint)) {
1111 return false;
1112 }
1113
1114 // Keep track of failures to retrieve the FT_COLR_Paint from FreeType in the
1115 // recursion, cancel recursion when a paint retrieval fails.
1116 bool traverse_result = true;
1117 SkMatrix restore_matrix = *ctm;
1118 SK_AT_SCOPE_EXIT(*ctm = restore_matrix);
1119
1120 switch (paint.format) {
1121 case FT_COLR_PAINTFORMAT_COLR_LAYERS: {
1122 FT_LayerIterator& layer_iterator = paint.u.colr_layers.layer_iterator;
1123 FT_OpaquePaint opaque_paint_fetch;
1124 opaque_paint_fetch.p = nullptr;
1125 while (FT_Get_Paint_Layers(face, &layer_iterator, &opaque_paint_fetch)) {
1126 colrv1_traverse_paint_bounds(ctm, bounds, face, opaque_paint_fetch, visited_set);
1127 }
1128 break;
1129 }
1130 case FT_COLR_PAINTFORMAT_GLYPH: {
1131 FT_UInt glyphID = paint.u.glyph.glyphID;
1132 SkPath path;
1133 if ((traverse_result = generateFacePathCOLRv1(face, glyphID, &path))) {
1134 path.transform(*ctm);
1135 bounds->join(path.getBounds());
1136 }
1137 break;
1138 }
1139 case FT_COLR_PAINTFORMAT_COLR_GLYPH:
1140 traverse_result = colrv1_start_glyph_bounds(
1141 ctm, bounds, face, paint.u.colr_glyph.glyphID, FT_COLOR_NO_ROOT_TRANSFORM);
1142 break;
1143
1144 case FT_COLR_PAINTFORMAT_TRANSFORM: {
1145 SkMatrix transform_matrix;
1146 colrv1_transform(face, paint, nullptr, &transform_matrix);
1147 ctm->preConcat(transform_matrix);
1148 traverse_result = colrv1_traverse_paint_bounds(
1149 ctm, bounds, face, paint.u.transform.paint, visited_set);
1150 break;
1151 }
1152 case FT_COLR_PAINTFORMAT_TRANSLATE: {
1153 SkMatrix transform_matrix;
1154 colrv1_transform(face, paint, nullptr, &transform_matrix);
1155 ctm->preConcat(transform_matrix);
1156 traverse_result = colrv1_traverse_paint_bounds(
1157 ctm, bounds, face, paint.u.translate.paint, visited_set);
1158 break;
1159 }
1160 case FT_COLR_PAINTFORMAT_SCALE: {
1161 SkMatrix transform_matrix;
1162 colrv1_transform(face, paint, nullptr, &transform_matrix);
1163 ctm->preConcat(transform_matrix);
1164 traverse_result = colrv1_traverse_paint_bounds(
1165 ctm, bounds, face, paint.u.scale.paint, visited_set);
1166 break;
1167 }
1168 case FT_COLR_PAINTFORMAT_ROTATE: {
1169 SkMatrix transform_matrix;
1170 colrv1_transform(face, paint, nullptr, &transform_matrix);
1171 ctm->preConcat(transform_matrix);
1172 traverse_result = colrv1_traverse_paint_bounds(
1173 ctm, bounds, face, paint.u.rotate.paint, visited_set);
1174 break;
1175 }
1176 case FT_COLR_PAINTFORMAT_SKEW: {
1177 SkMatrix transform_matrix;
1178 colrv1_transform(face, paint, nullptr, &transform_matrix);
1179 ctm->preConcat(transform_matrix);
1180 traverse_result = colrv1_traverse_paint_bounds(
1181 ctm, bounds, face, paint.u.skew.paint, visited_set);
1182 break;
1183 }
1184 case FT_COLR_PAINTFORMAT_COMPOSITE: {
1185 traverse_result = colrv1_traverse_paint_bounds(
1186 ctm, bounds, face, paint.u.composite.backdrop_paint, visited_set);
1187 traverse_result = colrv1_traverse_paint_bounds(
1188 ctm, bounds, face, paint.u.composite.source_paint, visited_set);
1189 break;
1190 }
1191 case FT_COLR_PAINTFORMAT_SOLID:
1192 case FT_COLR_PAINTFORMAT_LINEAR_GRADIENT:
1193 case FT_COLR_PAINTFORMAT_RADIAL_GRADIENT:
1194 case FT_COLR_PAINTFORMAT_SWEEP_GRADIENT: {
1195 break;
1196 }
1197 default:
1198 SkASSERT(false);
1199 break;
1200}
1201 return traverse_result;
1202}
1203
1204
1205bool colrv1_start_glyph_bounds(SkMatrix *ctm,
1206 SkRect* bounds,
1207 FT_Face ft_face,
1208 uint16_t glyph_id,
1209 FT_Color_Root_Transform root_transform) {
1210 FT_OpaquePaint opaque_paint;
1211 opaque_paint.p = nullptr;
1212 bool has_colrv1_layers = false;
1213 if (FT_Get_Color_Glyph_Paint(ft_face, glyph_id, root_transform, &opaque_paint)) {
1214 has_colrv1_layers = true;
1215 VisitedSet visited_set;
1216 colrv1_traverse_paint_bounds(ctm, bounds, ft_face, opaque_paint, &visited_set);
1217 }
1218 return has_colrv1_layers;
1219}
Dominik Röttsches691a7942021-02-11 21:08:44 +02001220#endif // TT_SUPPORT_COLRV1
1221
Ben Wagner2512db22016-11-10 16:14:52 -05001222} // namespace
1223
bungeman401ae2d2016-07-18 15:46:27 -07001224void SkScalerContext_FreeType_Base::generateGlyphImage(
1225 FT_Face face,
1226 const SkGlyph& glyph,
1227 const SkMatrix& bitmapTransform)
1228{
george@mozilla.comc59b5da2012-08-23 00:39:08 +00001229 const bool doBGR = SkToBool(fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag);
1230 const bool doVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag);
1231
1232 switch ( face->glyph->format ) {
1233 case FT_GLYPH_FORMAT_OUTLINE: {
1234 FT_Outline* outline = &face->glyph->outline;
george@mozilla.comc59b5da2012-08-23 00:39:08 +00001235
george@mozilla.comc59b5da2012-08-23 00:39:08 +00001236 int dx = 0, dy = 0;
Ben Wagneraa166bd2018-08-14 14:58:18 -04001237 if (this->isSubpixel()) {
george@mozilla.comc59b5da2012-08-23 00:39:08 +00001238 dx = SkFixedToFDot6(glyph.getSubXFixed());
1239 dy = SkFixedToFDot6(glyph.getSubYFixed());
1240 // negate dy since freetype-y-goes-up and skia-y-goes-down
1241 dy = -dy;
1242 }
Ben Wagner6cdb5f22017-06-15 10:43:17 -04001243
1244 memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight);
george@mozilla.comc59b5da2012-08-23 00:39:08 +00001245
Bruce Wangf3ca1c62018-07-09 10:08:36 -04001246#ifdef FT_COLOR_H
1247 if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
1248 SkBitmap dstBitmap;
1249 // TODO: mark this as sRGB when the blits will be sRGB.
1250 dstBitmap.setInfo(SkImageInfo::Make(glyph.fWidth, glyph.fHeight,
1251 kN32_SkColorType,
1252 kPremul_SkAlphaType),
1253 glyph.rowBytes());
1254 dstBitmap.setPixels(glyph.fImage);
1255
1256 // Scale unscaledBitmap into dstBitmap.
1257 SkCanvas canvas(dstBitmap);
1258#ifdef SK_SHOW_TEXT_BLIT_COVERAGE
1259 canvas.clear(0x33FF0000);
1260#else
1261 canvas.clear(SK_ColorTRANSPARENT);
1262#endif
1263 canvas.translate(-glyph.fLeft, -glyph.fTop);
1264
Bruce Wangba6f5332018-08-13 13:40:55 -04001265 if (this->isSubpixel()) {
1266 canvas.translate(SkFixedToScalar(glyph.getSubXFixed()),
1267 SkFixedToScalar(glyph.getSubYFixed()));
1268 }
1269
Bruce Wangf3ca1c62018-07-09 10:08:36 -04001270 SkPaint paint;
1271 paint.setAntiAlias(true);
1272
1273 FT_Color *palette;
Dominik Röttsches4be20572021-10-27 17:58:29 +03001274 FT_Palette_Data palette_data;
1275
1276 FT_Error err = FT_Palette_Data_Get(face, &palette_data);
1277 if (err) {
1278 SK_TRACEFTR(err, "Could not get palette data from %s fontFace.", face->family_name);
1279 return;
1280 }
1281
1282 err = FT_Palette_Select(face, 0, &palette);
Bruce Wangf3ca1c62018-07-09 10:08:36 -04001283 if (err) {
1284 SK_TRACEFTR(err, "Could not get palette from %s fontFace.", face->family_name);
1285 return;
1286 }
Bruce Wangf3ca1c62018-07-09 10:08:36 -04001287
Dominik Röttsches4be20572021-10-27 17:58:29 +03001288 SkSpan<FT_Color> paletteSpan(palette, palette_data.num_palette_entries);
1289
Dominik Röttsches691a7942021-02-11 21:08:44 +02001290 FT_Bool haveLayers = false;
1291
1292#ifdef TT_SUPPORT_COLRV1
1293 // Only attempt to draw COLRv1 glyph is FreeType is new enough
1294 // to have the COLRv1 additions, as indicated by the
1295 // TT_SUPPORT_COLRV1 flag defined by the FreeType headers in
1296 // that case.
1297
Dominik Röttsches0bbce332021-11-04 12:20:44 +02001298 haveLayers = colrv1_start_glyph(&canvas, paletteSpan,
1299 fRec.fForegroundColor,
1300 face, glyph.getGlyphID(),
Dominik Röttsches691a7942021-02-11 21:08:44 +02001301 FT_COLOR_INCLUDE_ROOT_TRANSFORM);
1302#else
1303 haveLayers = false;
1304#endif
1305 if (!haveLayers) {
1306 // If we didn't have colr v1 layers, try v0 layers.
1307 FT_LayerIterator layerIterator;
1308 layerIterator.p = NULL;
1309 FT_UInt layerGlyphIndex = 0;
1310 FT_UInt layerColorIndex = 0;
1311 while (FT_Get_Color_Glyph_Layer(face, glyph.getGlyphID(), &layerGlyphIndex,
1312 &layerColorIndex, &layerIterator)) {
1313 haveLayers = true;
1314 if (layerColorIndex == 0xFFFF) {
Dominik Röttsches0bbce332021-11-04 12:20:44 +02001315 paint.setColor(fRec.fForegroundColor);
Dominik Röttsches691a7942021-02-11 21:08:44 +02001316 } else {
1317 SkColor color = SkColorSetARGB(palette[layerColorIndex].alpha,
1318 palette[layerColorIndex].red,
1319 palette[layerColorIndex].green,
1320 palette[layerColorIndex].blue);
1321 paint.setColor(color);
1322 }
1323 SkPath path;
1324 if (this->generateFacePath(face, layerGlyphIndex, &path)) {
1325 canvas.drawPath(path, paint);
1326 }
Bruce Wangf3ca1c62018-07-09 10:08:36 -04001327 }
1328 }
1329
1330 if (!haveLayers) {
Dominik Röttsches691a7942021-02-11 21:08:44 +02001331 SK_TRACEFTR(err, "Could not get layers (neither v0, nor v1) from %s fontFace.",
1332 face->family_name);
Bruce Wangf3ca1c62018-07-09 10:08:36 -04001333 return;
1334 }
1335 } else
1336#endif
george@mozilla.comc59b5da2012-08-23 00:39:08 +00001337 if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
Ben Wagner6cdb5f22017-06-15 10:43:17 -04001338 FT_Outline_Translate(outline, dx, dy);
Ben Wagner2512db22016-11-10 16:14:52 -05001339 FT_Error err = FT_Render_Glyph(face->glyph, doVert ? FT_RENDER_MODE_LCD_V :
1340 FT_RENDER_MODE_LCD);
1341 if (err) {
John Stiles7bf79992021-06-25 11:05:20 -04001342 SK_TRACEFTR(err, "Could not render glyph %p.", face->glyph);
Ben Wagner2512db22016-11-10 16:14:52 -05001343 return;
1344 }
Ben Wagner6cdb5f22017-06-15 10:43:17 -04001345
Herb Derby990bfc72019-04-12 15:48:56 -04001346 SkMask mask = glyph.mask();
Ben Wagner6cdb5f22017-06-15 10:43:17 -04001347#ifdef SK_SHOW_TEXT_BLIT_COVERAGE
1348 memset(mask.fImage, 0x80, mask.fBounds.height() * mask.fRowBytes);
1349#endif
1350 FT_GlyphSlotRec& ftGlyph = *face->glyph;
1351
1352 if (!SkIRect::Intersects(mask.fBounds,
1353 SkIRect::MakeXYWH( ftGlyph.bitmap_left,
1354 -ftGlyph.bitmap_top,
1355 ftGlyph.bitmap.width,
1356 ftGlyph.bitmap.rows)))
1357 {
1358 return;
1359 }
1360
1361 // If the FT_Bitmap extent is larger, discard bits of the bitmap outside the mask.
1362 // If the SkMask extent is larger, shrink mask to fit bitmap (clearing discarded).
1363 unsigned char* origBuffer = ftGlyph.bitmap.buffer;
1364 // First align the top left (origin).
1365 if (-ftGlyph.bitmap_top < mask.fBounds.fTop) {
1366 int32_t topDiff = mask.fBounds.fTop - (-ftGlyph.bitmap_top);
1367 ftGlyph.bitmap.buffer += ftGlyph.bitmap.pitch * topDiff;
1368 ftGlyph.bitmap.rows -= topDiff;
1369 ftGlyph.bitmap_top = -mask.fBounds.fTop;
1370 }
1371 if (ftGlyph.bitmap_left < mask.fBounds.fLeft) {
1372 int32_t leftDiff = mask.fBounds.fLeft - ftGlyph.bitmap_left;
1373 ftGlyph.bitmap.buffer += leftDiff;
1374 ftGlyph.bitmap.width -= leftDiff;
1375 ftGlyph.bitmap_left = mask.fBounds.fLeft;
1376 }
1377 if (mask.fBounds.fTop < -ftGlyph.bitmap_top) {
1378 mask.fImage += mask.fRowBytes * (-ftGlyph.bitmap_top - mask.fBounds.fTop);
1379 mask.fBounds.fTop = -ftGlyph.bitmap_top;
1380 }
1381 if (mask.fBounds.fLeft < ftGlyph.bitmap_left) {
1382 mask.fImage += sizeof(uint16_t) * (ftGlyph.bitmap_left - mask.fBounds.fLeft);
1383 mask.fBounds.fLeft = ftGlyph.bitmap_left;
1384 }
1385 // Origins aligned, clean up the width and height.
1386 int ftVertScale = (doVert ? 3 : 1);
1387 int ftHoriScale = (doVert ? 1 : 3);
1388 if (mask.fBounds.height() * ftVertScale < SkToInt(ftGlyph.bitmap.rows)) {
1389 ftGlyph.bitmap.rows = mask.fBounds.height() * ftVertScale;
1390 }
1391 if (mask.fBounds.width() * ftHoriScale < SkToInt(ftGlyph.bitmap.width)) {
1392 ftGlyph.bitmap.width = mask.fBounds.width() * ftHoriScale;
1393 }
1394 if (SkToInt(ftGlyph.bitmap.rows) < mask.fBounds.height() * ftVertScale) {
1395 mask.fBounds.fBottom = mask.fBounds.fTop + ftGlyph.bitmap.rows / ftVertScale;
1396 }
1397 if (SkToInt(ftGlyph.bitmap.width) < mask.fBounds.width() * ftHoriScale) {
1398 mask.fBounds.fRight = mask.fBounds.fLeft + ftGlyph.bitmap.width / ftHoriScale;
1399 }
bungeman@google.coma76de722012-10-26 19:35:54 +00001400 if (fPreBlend.isApplicable()) {
Ben Wagner6cdb5f22017-06-15 10:43:17 -04001401 copyFT2LCD16<true>(ftGlyph.bitmap, mask, doBGR,
bungeman@google.coma76de722012-10-26 19:35:54 +00001402 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
george@mozilla.comc59b5da2012-08-23 00:39:08 +00001403 } else {
Ben Wagner6cdb5f22017-06-15 10:43:17 -04001404 copyFT2LCD16<false>(ftGlyph.bitmap, mask, doBGR,
bungeman@google.coma76de722012-10-26 19:35:54 +00001405 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
george@mozilla.comc59b5da2012-08-23 00:39:08 +00001406 }
Ben Wagner6cdb5f22017-06-15 10:43:17 -04001407 // Restore the buffer pointer so FreeType can properly free it.
1408 ftGlyph.bitmap.buffer = origBuffer;
george@mozilla.comc59b5da2012-08-23 00:39:08 +00001409 } else {
Ben Wagner6cdb5f22017-06-15 10:43:17 -04001410 FT_BBox bbox;
1411 FT_Bitmap target;
1412 FT_Outline_Get_CBox(outline, &bbox);
1413 /*
1414 what we really want to do for subpixel is
1415 offset(dx, dy)
1416 compute_bounds
1417 offset(bbox & !63)
1418 but that is two calls to offset, so we do the following, which
1419 achieves the same thing with only one offset call.
1420 */
1421 FT_Outline_Translate(outline, dx - ((bbox.xMin + dx) & ~63),
1422 dy - ((bbox.yMin + dy) & ~63));
1423
george@mozilla.comc59b5da2012-08-23 00:39:08 +00001424 target.width = glyph.fWidth;
1425 target.rows = glyph.fHeight;
1426 target.pitch = glyph.rowBytes();
1427 target.buffer = reinterpret_cast<uint8_t*>(glyph.fImage);
Herb Derby0fc077e2020-11-11 10:37:35 -05001428 target.pixel_mode = compute_pixel_mode(glyph.fMaskFormat);
george@mozilla.comc59b5da2012-08-23 00:39:08 +00001429 target.num_grays = 256;
1430
george@mozilla.comc59b5da2012-08-23 00:39:08 +00001431 FT_Outline_Get_Bitmap(face->glyph->library, outline, &target);
Ben Wagner6cdb5f22017-06-15 10:43:17 -04001432#ifdef SK_SHOW_TEXT_BLIT_COVERAGE
1433 for (int y = 0; y < glyph.fHeight; ++y) {
1434 for (int x = 0; x < glyph.fWidth; ++x) {
1435 uint8_t& a = ((uint8_t*)glyph.fImage)[(glyph.rowBytes() * y) + x];
Brian Osman788b9162020-02-07 10:36:46 -05001436 a = std::max<uint8_t>(a, 0x20);
Ben Wagner6cdb5f22017-06-15 10:43:17 -04001437 }
1438 }
1439#endif
george@mozilla.comc59b5da2012-08-23 00:39:08 +00001440 }
1441 } break;
1442
1443 case FT_GLYPH_FORMAT_BITMAP: {
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001444 FT_Pixel_Mode pixel_mode = static_cast<FT_Pixel_Mode>(face->glyph->bitmap.pixel_mode);
1445 SkMask::Format maskFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
1446
1447 // Assume that the other formats do not exist.
1448 SkASSERT(FT_PIXEL_MODE_MONO == pixel_mode ||
1449 FT_PIXEL_MODE_GRAY == pixel_mode ||
1450 FT_PIXEL_MODE_BGRA == pixel_mode);
1451
1452 // These are the only formats this ScalerContext should request.
1453 SkASSERT(SkMask::kBW_Format == maskFormat ||
1454 SkMask::kA8_Format == maskFormat ||
1455 SkMask::kARGB32_Format == maskFormat ||
1456 SkMask::kLCD16_Format == maskFormat);
1457
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001458 // If no scaling needed, directly copy glyph bitmap.
bungeman401ae2d2016-07-18 15:46:27 -07001459 if (bitmapTransform.isIdentity()) {
Herb Derby990bfc72019-04-12 15:48:56 -04001460 SkMask dstMask = glyph.mask();
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001461 copyFTBitmap(face->glyph->bitmap, dstMask);
1462 break;
1463 }
george@mozilla.comc59b5da2012-08-23 00:39:08 +00001464
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001465 // Otherwise, scale the bitmap.
george@mozilla.comc59b5da2012-08-23 00:39:08 +00001466
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001467 // Copy the FT_Bitmap into an SkBitmap (either A8 or ARGB)
1468 SkBitmap unscaledBitmap;
bungeman401ae2d2016-07-18 15:46:27 -07001469 // TODO: mark this as sRGB when the blits will be sRGB.
reed6c225732014-06-09 19:52:07 -07001470 unscaledBitmap.allocPixels(SkImageInfo::Make(face->glyph->bitmap.width,
1471 face->glyph->bitmap.rows,
1472 SkColorType_for_FTPixelMode(pixel_mode),
1473 kPremul_SkAlphaType));
george@mozilla.comc59b5da2012-08-23 00:39:08 +00001474
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001475 SkMask unscaledBitmapAlias;
1476 unscaledBitmapAlias.fImage = reinterpret_cast<uint8_t*>(unscaledBitmap.getPixels());
Mike Reed92b33352019-08-24 19:39:13 -04001477 unscaledBitmapAlias.fBounds.setWH(unscaledBitmap.width(), unscaledBitmap.height());
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001478 unscaledBitmapAlias.fRowBytes = unscaledBitmap.rowBytes();
reed6c225732014-06-09 19:52:07 -07001479 unscaledBitmapAlias.fFormat = SkMaskFormat_for_SkColorType(unscaledBitmap.colorType());
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001480 copyFTBitmap(face->glyph->bitmap, unscaledBitmapAlias);
george@mozilla.comc59b5da2012-08-23 00:39:08 +00001481
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001482 // Wrap the glyph's mask in a bitmap, unless the glyph's mask is BW or LCD.
1483 // BW requires an A8 target for resizing, which can then be down sampled.
1484 // LCD should use a 4x A8 target, which will then be down sampled.
1485 // For simplicity, LCD uses A8 and is replicated.
1486 int bitmapRowBytes = 0;
1487 if (SkMask::kBW_Format != maskFormat && SkMask::kLCD16_Format != maskFormat) {
1488 bitmapRowBytes = glyph.rowBytes();
1489 }
1490 SkBitmap dstBitmap;
bungeman401ae2d2016-07-18 15:46:27 -07001491 // TODO: mark this as sRGB when the blits will be sRGB.
reed6c225732014-06-09 19:52:07 -07001492 dstBitmap.setInfo(SkImageInfo::Make(glyph.fWidth, glyph.fHeight,
1493 SkColorType_for_SkMaskFormat(maskFormat),
1494 kPremul_SkAlphaType),
1495 bitmapRowBytes);
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001496 if (SkMask::kBW_Format == maskFormat || SkMask::kLCD16_Format == maskFormat) {
1497 dstBitmap.allocPixels();
george@mozilla.comc59b5da2012-08-23 00:39:08 +00001498 } else {
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001499 dstBitmap.setPixels(glyph.fImage);
george@mozilla.comc59b5da2012-08-23 00:39:08 +00001500 }
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001501
1502 // Scale unscaledBitmap into dstBitmap.
1503 SkCanvas canvas(dstBitmap);
bungeman401ae2d2016-07-18 15:46:27 -07001504#ifdef SK_SHOW_TEXT_BLIT_COVERAGE
1505 canvas.clear(0x33FF0000);
1506#else
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001507 canvas.clear(SK_ColorTRANSPARENT);
bungeman401ae2d2016-07-18 15:46:27 -07001508#endif
1509 canvas.translate(-glyph.fLeft, -glyph.fTop);
1510 canvas.concat(bitmapTransform);
1511 canvas.translate(face->glyph->bitmap_left, -face->glyph->bitmap_top);
1512
Mike Reed7281cb12021-01-03 12:11:29 -05001513 SkSamplingOptions sampling(SkFilterMode::kLinear, SkMipmapMode::kNearest);
1514 canvas.drawImage(unscaledBitmap.asImage().get(), 0, 0, sampling, nullptr);
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001515
1516 // If the destination is BW or LCD, convert from A8.
1517 if (SkMask::kBW_Format == maskFormat) {
1518 // Copy the A8 dstBitmap into the A1 glyph.fImage.
Herb Derby990bfc72019-04-12 15:48:56 -04001519 SkMask dstMask = glyph.mask();
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001520 packA8ToA1(dstMask, dstBitmap.getAddr8(0, 0), dstBitmap.rowBytes());
1521 } else if (SkMask::kLCD16_Format == maskFormat) {
1522 // Copy the A8 dstBitmap into the LCD16 glyph.fImage.
1523 uint8_t* src = dstBitmap.getAddr8(0, 0);
1524 uint16_t* dst = reinterpret_cast<uint16_t*>(glyph.fImage);
1525 for (int y = dstBitmap.height(); y --> 0;) {
1526 for (int x = 0; x < dstBitmap.width(); ++x) {
1527 dst[x] = grayToRGB16(src[x]);
1528 }
1529 dst = (uint16_t*)((char*)dst + glyph.rowBytes());
1530 src += dstBitmap.rowBytes();
1531 }
1532 }
1533
george@mozilla.comc59b5da2012-08-23 00:39:08 +00001534 } break;
1535
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001536 default:
1537 SkDEBUGFAIL("unknown glyph format");
1538 memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight);
1539 return;
george@mozilla.comc59b5da2012-08-23 00:39:08 +00001540 }
1541
1542// We used to always do this pre-USE_COLOR_LUMINANCE, but with colorlum,
1543// it is optional
1544#if defined(SK_GAMMA_APPLY_TO_A8)
bungeman@google.coma76de722012-10-26 19:35:54 +00001545 if (SkMask::kA8_Format == glyph.fMaskFormat && fPreBlend.isApplicable()) {
george@mozilla.comc59b5da2012-08-23 00:39:08 +00001546 uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
1547 unsigned rowBytes = glyph.rowBytes();
rmistry@google.comd6176b02012-08-23 18:14:13 +00001548
george@mozilla.comc59b5da2012-08-23 00:39:08 +00001549 for (int y = glyph.fHeight - 1; y >= 0; --y) {
1550 for (int x = glyph.fWidth - 1; x >= 0; --x) {
bungeman@google.coma76de722012-10-26 19:35:54 +00001551 dst[x] = fPreBlend.fG[dst[x]];
george@mozilla.comc59b5da2012-08-23 00:39:08 +00001552 }
1553 dst += rowBytes;
1554 }
1555 }
1556#endif
1557}
1558
bungeman@google.com8ff8a192012-09-25 20:38:28 +00001559///////////////////////////////////////////////////////////////////////////////
1560
Ben Wagner2512db22016-11-10 16:14:52 -05001561namespace {
1562
Ben Wagnerd38f00a2020-01-27 17:43:41 -05001563class SkFTGeometrySink {
1564 SkPath* fPath;
1565 bool fStarted;
1566 FT_Vector fCurrent;
bungeman@google.com8ff8a192012-09-25 20:38:28 +00001567
Ben Wagnerd38f00a2020-01-27 17:43:41 -05001568 void goingTo(const FT_Vector* pt) {
1569 if (!fStarted) {
1570 fStarted = true;
1571 fPath->moveTo(SkFDot6ToScalar(fCurrent.x), -SkFDot6ToScalar(fCurrent.y));
1572 }
1573 fCurrent = *pt;
1574 }
bungeman@google.com8ff8a192012-09-25 20:38:28 +00001575
Ben Wagnerd38f00a2020-01-27 17:43:41 -05001576 bool currentIsNot(const FT_Vector* pt) {
1577 return fCurrent.x != pt->x || fCurrent.y != pt->y;
1578 }
bungeman@google.com8ff8a192012-09-25 20:38:28 +00001579
Ben Wagnerd38f00a2020-01-27 17:43:41 -05001580 static int Move(const FT_Vector* pt, void* ctx) {
1581 SkFTGeometrySink& self = *(SkFTGeometrySink*)ctx;
1582 if (self.fStarted) {
1583 self.fPath->close();
1584 self.fStarted = false;
1585 }
1586 self.fCurrent = *pt;
1587 return 0;
1588 }
1589
1590 static int Line(const FT_Vector* pt, void* ctx) {
1591 SkFTGeometrySink& self = *(SkFTGeometrySink*)ctx;
1592 if (self.currentIsNot(pt)) {
1593 self.goingTo(pt);
1594 self.fPath->lineTo(SkFDot6ToScalar(pt->x), -SkFDot6ToScalar(pt->y));
1595 }
1596 return 0;
1597 }
1598
1599 static int Quad(const FT_Vector* pt0, const FT_Vector* pt1, void* ctx) {
1600 SkFTGeometrySink& self = *(SkFTGeometrySink*)ctx;
1601 if (self.currentIsNot(pt0) || self.currentIsNot(pt1)) {
1602 self.goingTo(pt1);
1603 self.fPath->quadTo(SkFDot6ToScalar(pt0->x), -SkFDot6ToScalar(pt0->y),
1604 SkFDot6ToScalar(pt1->x), -SkFDot6ToScalar(pt1->y));
1605 }
1606 return 0;
1607 }
1608
1609 static int Cubic(const FT_Vector* pt0, const FT_Vector* pt1, const FT_Vector* pt2, void* ctx) {
1610 SkFTGeometrySink& self = *(SkFTGeometrySink*)ctx;
1611 if (self.currentIsNot(pt0) || self.currentIsNot(pt1) || self.currentIsNot(pt2)) {
1612 self.goingTo(pt2);
1613 self.fPath->cubicTo(SkFDot6ToScalar(pt0->x), -SkFDot6ToScalar(pt0->y),
1614 SkFDot6ToScalar(pt1->x), -SkFDot6ToScalar(pt1->y),
1615 SkFDot6ToScalar(pt2->x), -SkFDot6ToScalar(pt2->y));
1616 }
1617 return 0;
1618 }
1619
1620public:
1621 SkFTGeometrySink(SkPath* path) : fPath{path}, fStarted{false}, fCurrent{0,0} {}
1622
Brian Salomon9fa47cc2021-10-08 18:48:26 -04001623 inline static constexpr const FT_Outline_Funcs Funcs{
Ben Wagnerd38f00a2020-01-27 17:43:41 -05001624 /*move_to =*/ SkFTGeometrySink::Move,
1625 /*line_to =*/ SkFTGeometrySink::Line,
1626 /*conic_to =*/ SkFTGeometrySink::Quad,
1627 /*cubic_to =*/ SkFTGeometrySink::Cubic,
1628 /*shift = */ 0,
1629 /*delta =*/ 0,
1630 };
1631};
bungeman@google.com8ff8a192012-09-25 20:38:28 +00001632
Dominik Röttsches691a7942021-02-11 21:08:44 +02001633bool generateGlyphPathStatic(FT_Face face, SkPath* path) {
Ben Wagnerd38f00a2020-01-27 17:43:41 -05001634 SkFTGeometrySink sink{path};
1635 FT_Error err = FT_Outline_Decompose(&face->glyph->outline, &SkFTGeometrySink::Funcs, &sink);
george@mozilla.comc59b5da2012-08-23 00:39:08 +00001636
1637 if (err != 0) {
1638 path->reset();
Ben Wagner5ddb3082018-03-29 11:18:06 -04001639 return false;
george@mozilla.comc59b5da2012-08-23 00:39:08 +00001640 }
1641
1642 path->close();
Ben Wagner5ddb3082018-03-29 11:18:06 -04001643 return true;
george@mozilla.comc59b5da2012-08-23 00:39:08 +00001644}
Bruce Wangf3ca1c62018-07-09 10:08:36 -04001645
Dominik Röttsches691a7942021-02-11 21:08:44 +02001646bool generateFacePathStatic(FT_Face face, SkGlyphID glyphID, SkPath* path) {
Bruce Wangf3ca1c62018-07-09 10:08:36 -04001647 uint32_t flags = 0; //fLoadGlyphFlags;
1648 flags |= FT_LOAD_NO_BITMAP; // ignore embedded bitmaps so we're sure to get the outline
1649 flags &= ~FT_LOAD_RENDER; // don't scan convert (we just want the outline)
1650
1651 FT_Error err = FT_Load_Glyph(face, glyphID, flags);
1652 if (err != 0) {
1653 path->reset();
1654 return false;
1655 }
1656
Dominik Röttsches691a7942021-02-11 21:08:44 +02001657 if (!generateGlyphPathStatic(face, path)) {
Bruce Wangf3ca1c62018-07-09 10:08:36 -04001658 path->reset();
1659 return false;
1660 }
1661 return true;
1662}
Dominik Röttsches691a7942021-02-11 21:08:44 +02001663
1664#ifdef TT_SUPPORT_COLRV1
1665bool generateFacePathCOLRv1(FT_Face face, SkGlyphID glyphID, SkPath* path) {
1666 uint32_t flags = 0;
1667 flags |= FT_LOAD_NO_BITMAP; // ignore embedded bitmaps so we're sure to get the outline
1668 flags &= ~FT_LOAD_RENDER; // don't scan convert (we just want the outline)
1669
1670 flags |= FT_LOAD_IGNORE_TRANSFORM;
1671
1672
1673 using DoneFTSize = SkFunctionWrapper<decltype(FT_Done_Size), FT_Done_Size>;
1674 std::unique_ptr<std::remove_pointer_t<FT_Size>, DoneFTSize> unscaledFtSize([face]() -> FT_Size {
1675 FT_Size size;
1676 FT_Error err = FT_New_Size(face, &size);
1677 if (err != 0) {
1678 SK_TRACEFTR(err, "FT_New_Size(%s) failed in generateFacePathStaticCOLRv1.", face->family_name);
1679 return nullptr;
1680 }
1681 return size;
1682 }());
1683
1684 if (!unscaledFtSize) {
1685 return false;
1686 }
1687
1688 FT_Size oldSize = face->size;
1689
1690 auto try_generate_path = [face, &unscaledFtSize, glyphID, flags, path]() {
1691 FT_Error err = 0;
1692
1693 err = FT_Activate_Size(unscaledFtSize.get());
1694 if (err != 0) {
1695 return false;
1696 }
1697
1698 err = FT_Set_Char_Size(face, SkIntToFDot6(face->units_per_EM),
1699 SkIntToFDot6(face->units_per_EM), 72, 72);
1700 if (err != 0) {
1701 return false;
1702 }
1703
1704 err = FT_Load_Glyph(face, glyphID, flags);
1705 if (err != 0) {
1706 path->reset();
1707 return false;
1708 }
1709
1710 if (!generateGlyphPathStatic(face, path)) {
1711 path->reset();
1712 return false;
1713 }
1714
1715 return true;
1716 };
1717
1718 bool path_generation_result = try_generate_path();
1719
1720 FT_Activate_Size(oldSize);
1721
1722 return path_generation_result;
1723}
1724#endif
1725
1726} // namespace
1727
1728bool SkScalerContext_FreeType_Base::generateGlyphPath(FT_Face face, SkPath* path) {
1729 return generateGlyphPathStatic(face, path);
1730}
1731
1732bool SkScalerContext_FreeType_Base::generateFacePath(FT_Face face,
1733 SkGlyphID glyphID,
1734 SkPath* path) {
1735 return generateFacePathStatic(face, glyphID, path);
1736}
Dominik Röttsches2fa273e2021-08-10 12:10:45 +03001737
1738bool SkScalerContext_FreeType_Base::computeColrV1GlyphBoundingBox(FT_Face face,
1739 SkGlyphID glyphID,
1740 FT_BBox* boundingBox) {
1741#ifdef TT_SUPPORT_COLRV1
1742 SkMatrix ctm;
1743 SkRect bounds = SkRect::MakeEmpty();
1744 if (!colrv1_start_glyph_bounds(&ctm, &bounds, face, glyphID, FT_COLOR_INCLUDE_ROOT_TRANSFORM)) {
1745 return false;
1746 }
1747
1748 /* Convert back to FT_BBox as caller needs it in this format. */
1749 bounds.sort();
1750 boundingBox->xMin = SkScalarToFDot6(bounds.left());
1751 boundingBox->xMax = SkScalarToFDot6(bounds.right());
1752 boundingBox->yMin = SkScalarToFDot6(-bounds.bottom());
1753 boundingBox->yMax = SkScalarToFDot6(-bounds.top());
1754
1755 return true;
1756#else
1757 SkASSERT(false);
1758 return false;
1759#endif
1760}