Bug Summary

File:root/firefox-clang/gfx/skia/skia/src/ports/SkFontHost_FreeType_common.cpp
Warning:line 384, column 40
Dereference of null pointer

Annotated Source Code

Press '?' to see keyboard shortcuts

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