Flutter Engine
The Flutter Engine
SkBlendImageFilter.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2013 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
9
15#include "include/core/SkM44.h"
16#include "include/core/SkRect.h"
30#include "src/core/SkRectPriv.h"
32
33#include <cstdint>
34#include <optional>
35#include <utility>
36
37namespace {
38
39class SkBlendImageFilter : public SkImageFilter_Base {
40 // Input image filter indices
41 static constexpr int kBackground = 0;
42 static constexpr int kForeground = 1;
43
44public:
45 SkBlendImageFilter(sk_sp<SkBlender> blender,
46 const std::optional<SkV4>& coefficients,
47 bool enforcePremul,
50 , fBlender(std::move(blender))
51 , fArithmeticCoefficients(coefficients)
52 , fEnforcePremul(enforcePremul) {
53 // A null blender represents src-over, which should have been filled in by the factory
54 SkASSERT(fBlender);
55 }
56
57 SkRect computeFastBounds(const SkRect& bounds) const override;
58
59protected:
60 void flatten(SkWriteBuffer&) const override;
61
62private:
63 static constexpr uint32_t kArithmetic_SkBlendMode = kCustom_SkBlendMode + 1;
64
66 SK_FLATTENABLE_HOOKS(SkBlendImageFilter)
67 static sk_sp<SkFlattenable> LegacyArithmeticCreateProc(SkReadBuffer& buffer);
68
70
71 bool onAffectsTransparentBlack() const override {
72 // An arbitrary runtime blender or an arithmetic runtime blender with k3 != 0 affects
73 // transparent black.
74 return !as_BB(fBlender)->asBlendMode().has_value() &&
75 (!fArithmeticCoefficients.has_value() || (*fArithmeticCoefficients)[3] != 0.f);
76 }
77
78 skif::FilterResult onFilterImage(const skif::Context&) const override;
79
81 const skif::Mapping& mapping,
82 const skif::LayerSpace<SkIRect>& desiredOutput,
83 std::optional<skif::LayerSpace<SkIRect>> contentBounds) const override;
84
85 std::optional<skif::LayerSpace<SkIRect>> onGetOutputLayerBounds(
86 const skif::Mapping& mapping,
87 std::optional<skif::LayerSpace<SkIRect>> contentBounds) const override;
88
89 sk_sp<SkShader> makeBlendShader(sk_sp<SkShader> bg, sk_sp<SkShader> fg) const;
90
91 sk_sp<SkBlender> fBlender;
92
93 // Normally runtime SkBlenders are pessimistic about the bounds they affect. For Arithmetic,
94 // we remember the coefficients so that bounds can be reasoned about.
95 std::optional<SkV4> fArithmeticCoefficients;
96 bool fEnforcePremul; // Remembered to serialize the Arithmetic variant correctly
97};
98
99sk_sp<SkImageFilter> make_blend(sk_sp<SkBlender> blender,
100 sk_sp<SkImageFilter> background,
101 sk_sp<SkImageFilter> foreground,
102 const SkImageFilters::CropRect& cropRect,
103 std::optional<SkV4> coefficients = {},
104 bool enforcePremul = false) {
105 if (!blender) {
107 }
108
109 auto cropped = [cropRect](sk_sp<SkImageFilter> filter) {
110 if (cropRect) {
111 filter = SkImageFilters::Crop(*cropRect, std::move(filter));
112 }
113 return filter;
114 };
115
116 if (auto bm = as_BB(blender)->asBlendMode()) {
117 if (bm == SkBlendMode::kSrc) {
118 return cropped(std::move(foreground));
119 } else if (bm == SkBlendMode::kDst) {
120 return cropped(std::move(background));
121 } else if (bm == SkBlendMode::kClear) {
122 return SkImageFilters::Empty();
123 }
124 }
125
126 sk_sp<SkImageFilter> inputs[2] = { std::move(background), std::move(foreground) };
127 sk_sp<SkImageFilter> filter{new SkBlendImageFilter(blender, coefficients,
128 enforcePremul, inputs)};
129 return cropped(std::move(filter));
130}
131
132} // anonymous namespace
133
135 sk_sp<SkImageFilter> background,
136 sk_sp<SkImageFilter> foreground,
137 const CropRect& cropRect) {
138 return make_blend(SkBlender::Mode(mode),
139 std::move(background),
140 std::move(foreground),
141 cropRect);
142}
143
145 sk_sp<SkImageFilter> background,
146 sk_sp<SkImageFilter> foreground,
147 const CropRect& cropRect) {
148 return make_blend(std::move(blender), std::move(background), std::move(foreground), cropRect);
149}
150
152 SkScalar k2,
153 SkScalar k3,
154 SkScalar k4,
155 bool enforcePMColor,
156 sk_sp<SkImageFilter> background,
157 sk_sp<SkImageFilter> foreground,
158 const CropRect& cropRect) {
159 auto blender = SkBlenders::Arithmetic(k1, k2, k3, k4, enforcePMColor);
160 if (!blender) {
161 // Arithmetic() returns null on an error, not to optimize src-over
162 return nullptr;
163 }
164 return make_blend(std::move(blender),
165 std::move(background),
166 std::move(foreground),
167 cropRect,
168 // Carry arithmetic coefficients and premul behavior into image filter for
169 // serialization and bounds analysis
170 SkV4{k1, k2, k3, k4},
171 enforcePMColor);
172}
173
175 SK_REGISTER_FLATTENABLE(SkBlendImageFilter);
176 // TODO (michaelludwig) - Remove after grace period for SKPs to stop using old name
177 SkFlattenable::Register("SkXfermodeImageFilter_Base", SkBlendImageFilter::CreateProc);
178 SkFlattenable::Register("SkXfermodeImageFilterImpl", SkBlendImageFilter::CreateProc);
179 SkFlattenable::Register("ArithmeticImageFilterImpl",
180 SkBlendImageFilter::LegacyArithmeticCreateProc);
181 SkFlattenable::Register("SkArithmeticImageFilter",
182 SkBlendImageFilter::LegacyArithmeticCreateProc);
183}
184
185sk_sp<SkFlattenable> SkBlendImageFilter::LegacyArithmeticCreateProc(SkReadBuffer& buffer) {
186 // Newer SKPs should be using the updated Blend CreateProc.
187 if (!buffer.validate(buffer.isVersionLT(SkPicturePriv::kCombineBlendArithmeticFilters))) {
188 SkASSERT(false); // debug-only, so release will just see a failed deserialization
189 return nullptr;
190 }
191
193 float k[4];
194 for (int i = 0; i < 4; ++i) {
195 k[i] = buffer.readScalar();
196 }
197 const bool enforcePremul = buffer.readBool();
198 return SkImageFilters::Arithmetic(k[0], k[1], k[2], k[3], enforcePremul,
199 common.getInput(0), common.getInput(1), common.cropRect());
200}
201
202sk_sp<SkFlattenable> SkBlendImageFilter::CreateProc(SkReadBuffer& buffer) {
204
205 sk_sp<SkBlender> blender;
206 std::optional<SkV4> coefficients;
207 bool enforcePremul = false;
208
209 const uint32_t mode = buffer.read32();
210 if (mode == kArithmetic_SkBlendMode) {
211 // Should only see this sentinel value in newer SKPs
212 if (buffer.validate(!buffer.isVersionLT(SkPicturePriv::kCombineBlendArithmeticFilters))) {
213 SkV4 k;
214 for (int i = 0; i < 4; ++i) {
215 k[i] = buffer.readScalar();
216 }
217 coefficients = k;
218 enforcePremul = buffer.readBool();
219 blender = SkBlenders::Arithmetic(k.x, k.y, k.z, k.w, enforcePremul);
220 if (!buffer.validate(SkToBool(blender))) {
221 return nullptr; // A null arithmetic blender is an error condition
222 }
223 }
224 } else if (mode == kCustom_SkBlendMode) {
225 blender = buffer.readBlender();
226 } else {
227 if (!buffer.validate(mode <= (unsigned) SkBlendMode::kLastMode)) {
228 return nullptr;
229 }
230 blender = SkBlender::Mode((SkBlendMode)mode);
231 }
232
233 return make_blend(std::move(blender),
234 common.getInput(kBackground),
235 common.getInput(kForeground),
236 common.cropRect(),
237 coefficients,
238 enforcePremul);
239}
240
241void SkBlendImageFilter::flatten(SkWriteBuffer& buffer) const {
242 this->SkImageFilter_Base::flatten(buffer);
243 if (fArithmeticCoefficients.has_value()) {
244 buffer.write32(kArithmetic_SkBlendMode);
245
246 const SkV4& k = *fArithmeticCoefficients;
247 buffer.writeScalar(k[0]);
248 buffer.writeScalar(k[1]);
249 buffer.writeScalar(k[2]);
250 buffer.writeScalar(k[3]);
251 buffer.writeBool(fEnforcePremul);
252 } else if (auto bm = as_BB(fBlender)->asBlendMode()) {
253 buffer.write32((unsigned)bm.value());
254 } else {
256 buffer.writeFlattenable(fBlender.get());
257 }
258}
259
260///////////////////////////////////////////////////////////////////////////////////////////////////
261
262sk_sp<SkShader> SkBlendImageFilter::makeBlendShader(sk_sp<SkShader> bg, sk_sp<SkShader> fg) const {
263 // A null input shader signifies transparent black when image filtering, but SkShaders::Blend
264 // expects non-null shaders. So we have to do some clean up.
265 if (!bg || !fg) {
266 // If we don't affect transparent black and both inputs are null, then return a null
267 // shader to skip any evaluation.
268 if (!this->onAffectsTransparentBlack() && !bg && !fg) {
269 return nullptr;
270 }
271 // Otherwise if only one input is null, we might be able to just return that one.
272 if (auto bm = as_BB(fBlender)->asBlendMode()) {
274 if (SkBlendMode_AsCoeff(*bm, &src, &dst)) {
275 if (bg && (dst == SkBlendModeCoeff::kOne ||
278 return bg;
279 }
280 if (fg && (src == SkBlendModeCoeff::kOne ||
282 return fg;
283 }
284 }
285 }
286 // If we made it this far, the blend has non-trivial behavior even when one of the
287 // inputs is transparent black, so replace the null shaders with that color.
288 if (!bg) { bg = SkShaders::Color(SK_ColorTRANSPARENT); }
289 if (!fg) { fg = SkShaders::Color(SK_ColorTRANSPARENT); }
290 }
291
292 return SkShaders::Blend(fBlender, std::move(bg), std::move(fg));
293}
294
295skif::FilterResult SkBlendImageFilter::onFilterImage(const skif::Context& ctx) const {
296 // We could just request 'desiredOutput' for the blend's required input size, since that's what
297 // it is expected to fill. However, some blend modes restrict the output to something other
298 // than the union of the foreground and background. To make this restriction available to both
299 // children before evaluating them, we determine the maximum possible output the blend can
300 // produce from the contentBounds and require that for both children to produce.
301 auto requiredInput = this->onGetOutputLayerBounds(ctx.mapping(), ctx.source().layerBounds());
302 if (requiredInput) {
303 if (!requiredInput->intersect(ctx.desiredOutput())) {
304 return {};
305 }
306 } else {
307 requiredInput = ctx.desiredOutput();
308 }
309
310 skif::Context inputCtx = ctx.withNewDesiredOutput(*requiredInput);
312 builder.add(this->getChildOutput(kBackground, inputCtx));
313 builder.add(this->getChildOutput(kForeground, inputCtx));
314 return builder.eval(
316 return this->makeBlendShader(inputs[kBackground], inputs[kForeground]);
317 }, requiredInput);
318}
319
320skif::LayerSpace<SkIRect> SkBlendImageFilter::onGetInputLayerBounds(
321 const skif::Mapping& mapping,
322 const skif::LayerSpace<SkIRect>& desiredOutput,
323 std::optional<skif::LayerSpace<SkIRect>> contentBounds) const {
324
325 skif::LayerSpace<SkIRect> requiredInput;
326 std::optional<skif::LayerSpace<SkIRect>> maxOutput;
327 if (contentBounds && (maxOutput = this->onGetOutputLayerBounds(mapping, *contentBounds))) {
328 // See comment in onFilterImage().
329 requiredInput = *maxOutput;
330 if (!requiredInput.intersect(desiredOutput)) {
331 // Don't bother recursing if we know the blend will discard everything
333 }
334 } else {
335 // The content and/or the output of the child are unbounded so the intersection with the
336 // desired output is simply the desired output.
337 requiredInput = desiredOutput;
338 }
339
340 // Return the union of both FG and BG required inputs to ensure both have all necessary pixels
342 this->getChildInputLayerBounds(kBackground, mapping, requiredInput, contentBounds);
344 this->getChildInputLayerBounds(kForeground, mapping, requiredInput, contentBounds);
345
346 bgInput.join(fgInput);
347 return bgInput;
348}
349
350std::optional<skif::LayerSpace<SkIRect>> SkBlendImageFilter::onGetOutputLayerBounds(
351 const skif::Mapping& mapping,
352 std::optional<skif::LayerSpace<SkIRect>> contentBounds) const {
353 // Blending is (k0*FG*BG + k1*FG + k2*BG + k3) for arithmetic blenders OR
354 // ( 0*FG*BG + srcCoeff*FG + dstCoeff*BG + 0 ) for Porter-Duff blend modes OR
355 // un-inspectable(FG, BG) for advanced blend modes and other runtime blenders.
356 //
357 // There are six possible output bounds that can be produced:
358 // 1. No output: K = (0,0,0,0) or (srcCoeff,dstCoeff) = (kZero,kZero)
359 // 2. intersect(FG,BG): K = (non-zero, 0,0,0) or (srcCoeff,dstCoeff) = (kZero|kDA, kZero|kSA)
360 // 3. FG-only: K = (0, non-zero, 0,0) or (srcCoeff,dstCoeff) = (!kZero&!kDA, kZero|kSA)
361 // 4. BG-only: K = (0,0, non-zero, 0) or (srcCoeff,dstCoeff) = (kZero|kDA, !kZero&!kSA)
362 // 5. union(FG,BG): K = (*,*,*,0) or (srcCoeff,dstCoeff) = (!kZero&!kDA, !kZero&!kSA)
363 // or an advanced blend mode.
364 // 6. infinite: K = (*,*,*, non-zero) or a runtime blender other than SkBlenders::Arithmetic.
365 bool transparentOutsideFG = false;
366 bool transparentOutsideBG = false;
367 if (auto bm = as_BB(fBlender)->asBlendMode()) {
368 SkASSERT(*bm != SkBlendMode::kClear); // Should have been caught at creation time
370 if (SkBlendMode_AsCoeff(*bm, &src, &dst)) {
371 // If dst's coefficient is 0 then nothing can produce non-transparent content outside
372 // of the foreground. When dst coefficient is SA, it will always be 0 outside the FG.
373 // For purposes of transparency analysis, SC == SA.
374 transparentOutsideFG = dst == SkBlendModeCoeff::kZero || dst == SkBlendModeCoeff::kSA
376 // And the reverse is true for src and the background content.
377 transparentOutsideBG = src == SkBlendModeCoeff::kZero || src == SkBlendModeCoeff::kDA;
378 }
379 // NOTE: advanced blends use src-over for their alpha channel, which should produce the
380 // union of FG and BG. That is the outcome if we leave transparentOutsideFG/BG false.
381 } else if (fArithmeticCoefficients.has_value()) {
382 [[maybe_unused]] static constexpr SkV4 kClearCoeff = {0.f, 0.f, 0.f, 0.f};
383 const SkV4& k = *fArithmeticCoefficients;
384 SkASSERT(k != kClearCoeff); // Should have been converted to an empty filter
385
386 if (k[3] != 0.f) {
387 // The arithmetic equation produces non-transparent black everywhere
389 } else {
390 // Given the earlier assert and if, then (k[1] == k[2] == 0) implies k[0] != 0. If only
391 // one of k[1] or k[2] are non-zero then, regardless of k[0], then only that bounds
392 // has non-transparent content.
393 transparentOutsideFG = k[2] == 0.f;
394 transparentOutsideBG = k[1] == 0.f;
395 }
396 } else {
397 // A non-arithmetic runtime blender, so pessimistically assume it can return non-transparent
398 // black anywhere.
400 }
401
402 auto foregroundBounds = this->getChildOutputLayerBounds(kForeground, mapping, contentBounds);
403 auto backgroundBounds = this->getChildOutputLayerBounds(kBackground, mapping, contentBounds);
404 if (transparentOutsideFG) {
405 if (transparentOutsideBG) {
406 // Output is the intersection of both
407 if (!foregroundBounds && backgroundBounds) {
408 foregroundBounds = *backgroundBounds;
409 } else if (backgroundBounds && !foregroundBounds->intersect(*backgroundBounds)) {
411 }
412 // When both fore and background are infinite, foregroundBounds remains uninstantiated.
413 // When only foreground is provided, it's left unmodified, which is the correct result.
414 }
415 return foregroundBounds;
416 } else {
417 if (!transparentOutsideBG) {
418 // Output is the union of both (infinite blend-induced bounds were detected earlier).
419 if (foregroundBounds && backgroundBounds) {
420 backgroundBounds->join(*foregroundBounds);
421 } else {
422 // At least one of the union arguments is unbounded, so the union is infinite
423 backgroundBounds.reset();
424 }
425 }
426 return backgroundBounds;
427 }
428}
429
430SkRect SkBlendImageFilter::computeFastBounds(const SkRect& bounds) const {
431 // TODO: This is a prime example of why computeFastBounds() and onGetOutputLayerBounds() should
432 // be combined into the same function.
433 bool transparentOutsideFG = false;
434 bool transparentOutsideBG = false;
435 if (auto bm = as_BB(fBlender)->asBlendMode()) {
436 SkASSERT(*bm != SkBlendMode::kClear); // Should have been caught at creation time
438 if (SkBlendMode_AsCoeff(*bm, &src, &dst)) {
439 // If dst's coefficient is 0 then nothing can produce non-transparent content outside
440 // of the foreground. When dst coefficient is SA, it will always be 0 outside the FG.
441 transparentOutsideFG = dst == SkBlendModeCoeff::kZero || dst == SkBlendModeCoeff::kSA;
442 // And the reverse is true for src and the background content.
443 transparentOutsideBG = src == SkBlendModeCoeff::kZero || src == SkBlendModeCoeff::kDA;
444 }
445 } else if (fArithmeticCoefficients.has_value()) {
446 [[maybe_unused]] static constexpr SkV4 kClearCoeff = {0.f, 0.f, 0.f, 0.f};
447 const SkV4& k = *fArithmeticCoefficients;
448 SkASSERT(k != kClearCoeff); // Should have been converted to an empty image filter
449
450 if (k[3] != 0.f) {
451 // The arithmetic equation produces non-transparent black everywhere
453 } else {
454 // Given the earlier assert and if, then (k[1] == k[2] == 0) implies k[0] != 0. If only
455 // one of k[1] or k[2] are non-zero then, regardless of k[0], then only that bounds
456 // has non-transparent content.
457 transparentOutsideFG = k[2] == 0.f;
458 transparentOutsideBG = k[1] == 0.f;
459 }
460 } else {
461 // A non-arithmetic runtime blender, so pessimistically assume it can return non-transparent
462 // black anywhere.
464 }
465
466 SkRect foregroundBounds = this->getInput(kForeground) ?
467 this->getInput(kForeground)->computeFastBounds(bounds) : bounds;
468 SkRect backgroundBounds = this->getInput(kBackground) ?
469 this->getInput(kBackground)->computeFastBounds(bounds) : bounds;
470 if (transparentOutsideFG) {
471 if (transparentOutsideBG) {
472 // Output is the intersection of both
473 if (!foregroundBounds.intersect(backgroundBounds)) {
474 return SkRect::MakeEmpty();
475 }
476 }
477 return foregroundBounds;
478 } else {
479 if (!transparentOutsideBG) {
480 // Output is the union of both (infinite bounds were detected earlier).
481 backgroundBounds.join(foregroundBounds);
482 }
483 return backgroundBounds;
484 }
485}
#define SkASSERT(cond)
Definition: SkAssert.h:116
void SkRegisterBlendImageFilterFlattenable()
constexpr uint8_t kCustom_SkBlendMode
SK_API bool SkBlendMode_AsCoeff(SkBlendMode mode, SkBlendModeCoeff *src, SkBlendModeCoeff *dst)
Definition: SkBlendMode.cpp:57
SkBlendMode
Definition: SkBlendMode.h:38
@ kSrcOver
r = s + (1-sa)*d
@ kLastMode
last valid value
@ kClear
r = 0
SkBlendModeCoeff
Definition: SkBlendMode.h:84
SkBlenderBase * as_BB(SkBlender *blend)
Definition: SkBlenderBase.h:69
constexpr SkColor SK_ColorTRANSPARENT
Definition: SkColor.h:99
#define SK_FLATTENABLE_HOOKS(type)
#define SK_REGISTER_FLATTENABLE(type)
#define SK_IMAGEFILTER_UNFLATTEN_COMMON(localVar, expectedCount)
static constexpr bool SkToBool(const T &x)
Definition: SkTo.h:35
virtual std::optional< SkBlendMode > asBlendMode() const
Definition: SkBlenderBase.h:45
static sk_sp< SkBlender > Mode(SkBlendMode mode)
static sk_sp< SkBlender > Arithmetic(float k1, float k2, float k3, float k4, bool enforcePremul)
Definition: SkBlenders.cpp:20
static void Register(const char name[], Factory)
virtual skif::LayerSpace< SkIRect > onGetInputLayerBounds(const skif::Mapping &mapping, const skif::LayerSpace< SkIRect > &desiredOutput, std::optional< skif::LayerSpace< SkIRect > > contentBounds) const =0
virtual bool onAffectsTransparentBlack() const
void flatten(SkWriteBuffer &) const override
virtual std::optional< skif::LayerSpace< SkIRect > > onGetOutputLayerBounds(const skif::Mapping &mapping, std::optional< skif::LayerSpace< SkIRect > > contentBounds) const =0
virtual skif::FilterResult onFilterImage(const skif::Context &context) const =0
virtual MatrixCapability onGetCTMCapability() const
virtual SkRect computeFastBounds(const SkRect &bounds) const
static sk_sp< SkImageFilter > Arithmetic(SkScalar k1, SkScalar k2, SkScalar k3, SkScalar k4, bool enforcePMColor, sk_sp< SkImageFilter > background, sk_sp< SkImageFilter > foreground, const CropRect &cropRect={})
static sk_sp< SkImageFilter > Empty()
static sk_sp< SkImageFilter > Crop(const SkRect &rect, SkTileMode tileMode, sk_sp< SkImageFilter > input)
static sk_sp< SkImageFilter > Blend(SkBlendMode mode, sk_sp< SkImageFilter > background, sk_sp< SkImageFilter > foreground=nullptr, const CropRect &cropRect={})
@ kCombineBlendArithmeticFilters
static SkRect MakeLargeS32()
Definition: SkRectPriv.h:33
const LayerSpace< SkIRect > & desiredOutput() const
const FilterResult & source() const
Context withNewDesiredOutput(const LayerSpace< SkIRect > &desiredOutput) const
const Mapping & mapping() const
LayerSpace< SkIRect > layerBounds() const
bool intersect(const LayerSpace< SkIRect > &r)
void join(const LayerSpace< SkIRect > &r)
@ kBackground
Suitable for threads that shouldn't disrupt high priority work.
Definition: embedder.h:260
float SkScalar
Definition: extension.cpp:12
Optional< SkRect > bounds
Definition: SkRecords.h:189
SK_API sk_sp< SkShader > Blend(SkBlendMode mode, sk_sp< SkShader > dst, sk_sp< SkShader > src)
SK_API sk_sp< SkShader > Color(SkColor)
SK_API sk_sp< SkShader > Empty()
Definition: common.py:1
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive mode
Definition: switches.h:228
dst
Definition: cp.py:12
Definition: ref_ptr.h:256
static constexpr SkRect MakeEmpty()
Definition: SkRect.h:595
bool intersect(const SkRect &r)
Definition: SkRect.cpp:114
void join(const SkRect &r)
Definition: SkRect.cpp:126
Definition: SkM44.h:98
float w
Definition: SkM44.h:99
float y
Definition: SkM44.h:99
float x
Definition: SkM44.h:99
float z
Definition: SkM44.h:99