Coverage Report

Created: 2021-08-22 09:07

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