xref: /aosp_15_r20/external/skia/src/effects/imagefilters/SkBlendImageFilter.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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 
8 #include "include/effects/SkImageFilters.h"
9 
10 #include "include/core/SkBlendMode.h"
11 #include "include/core/SkBlender.h"
12 #include "include/core/SkColor.h"
13 #include "include/core/SkFlattenable.h"
14 #include "include/core/SkImageFilter.h"
15 #include "include/core/SkM44.h"
16 #include "include/core/SkRect.h"
17 #include "include/core/SkRefCnt.h"
18 #include "include/core/SkScalar.h"
19 #include "include/core/SkShader.h"
20 #include "include/core/SkTypes.h"
21 #include "include/effects/SkBlenders.h"
22 #include "include/private/base/SkSpan_impl.h"
23 #include "include/private/base/SkTo.h"
24 #include "src/core/SkBlendModePriv.h"
25 #include "src/core/SkBlenderBase.h"
26 #include "src/core/SkImageFilterTypes.h"
27 #include "src/core/SkImageFilter_Base.h"
28 #include "src/core/SkPicturePriv.h"
29 #include "src/core/SkReadBuffer.h"
30 #include "src/core/SkRectPriv.h"
31 #include "src/core/SkWriteBuffer.h"
32 
33 #include <cstdint>
34 #include <optional>
35 #include <utility>
36 
37 namespace {
38 
39 class SkBlendImageFilter : public SkImageFilter_Base {
40     // Input image filter indices
41     static constexpr int kBackground = 0;
42     static constexpr int kForeground = 1;
43 
44 public:
SkBlendImageFilter(sk_sp<SkBlender> blender,const std::optional<SkV4> & coefficients,bool enforcePremul,sk_sp<SkImageFilter> inputs[2])45     SkBlendImageFilter(sk_sp<SkBlender> blender,
46                        const std::optional<SkV4>& coefficients,
47                        bool enforcePremul,
48                        sk_sp<SkImageFilter> inputs[2])
49             : SkImageFilter_Base(inputs, 2)
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 
59 protected:
60     void flatten(SkWriteBuffer&) const override;
61 
62 private:
63     static constexpr uint32_t kArithmetic_SkBlendMode = kCustom_SkBlendMode + 1;
64 
65     friend void ::SkRegisterBlendImageFilterFlattenable();
66     SK_FLATTENABLE_HOOKS(SkBlendImageFilter)
67     static sk_sp<SkFlattenable> LegacyArithmeticCreateProc(SkReadBuffer& buffer);
68 
onGetCTMCapability() const69     MatrixCapability onGetCTMCapability() const override { return MatrixCapability::kComplex; }
70 
onAffectsTransparentBlack() const71     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 
80     skif::LayerSpace<SkIRect> onGetInputLayerBounds(
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 
make_blend(sk_sp<SkBlender> blender,sk_sp<SkImageFilter> background,sk_sp<SkImageFilter> foreground,const SkImageFilters::CropRect & cropRect,std::optional<SkV4> coefficients={},bool enforcePremul=false)99 sk_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) {
106         blender = SkBlender::Mode(SkBlendMode::kSrcOver);
107     }
108 
__anon60fa5f0c0202(sk_sp<SkImageFilter> filter) 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 
Blend(SkBlendMode mode,sk_sp<SkImageFilter> background,sk_sp<SkImageFilter> foreground,const CropRect & cropRect)134 sk_sp<SkImageFilter> SkImageFilters::Blend(SkBlendMode mode,
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 
Blend(sk_sp<SkBlender> blender,sk_sp<SkImageFilter> background,sk_sp<SkImageFilter> foreground,const CropRect & cropRect)144 sk_sp<SkImageFilter> SkImageFilters::Blend(sk_sp<SkBlender> blender,
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 
Arithmetic(SkScalar k1,SkScalar k2,SkScalar k3,SkScalar k4,bool enforcePMColor,sk_sp<SkImageFilter> background,sk_sp<SkImageFilter> foreground,const CropRect & cropRect)151 sk_sp<SkImageFilter> SkImageFilters::Arithmetic(SkScalar k1,
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 
SkRegisterBlendImageFilterFlattenable()174 void SkRegisterBlendImageFilterFlattenable() {
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 
LegacyArithmeticCreateProc(SkReadBuffer & buffer)185 sk_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 
192     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 2);
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 
CreateProc(SkReadBuffer & buffer)202 sk_sp<SkFlattenable> SkBlendImageFilter::CreateProc(SkReadBuffer& buffer) {
203     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 2);
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 
flatten(SkWriteBuffer & buffer) const241 void 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 {
255         buffer.write32(kCustom_SkBlendMode);
256         buffer.writeFlattenable(fBlender.get());
257     }
258 }
259 
260 ///////////////////////////////////////////////////////////////////////////////////////////////////
261 
makeBlendShader(sk_sp<SkShader> bg,sk_sp<SkShader> fg) const262 sk_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()) {
273             SkBlendModeCoeff src, dst;
274             if (SkBlendMode_AsCoeff(*bm, &src, &dst)) {
275                 if (bg && (dst == SkBlendModeCoeff::kOne ||
276                            dst == SkBlendModeCoeff::kISA ||
277                            dst == SkBlendModeCoeff::kISC)) {
278                     return bg;
279                 }
280                 if (fg && (src == SkBlendModeCoeff::kOne ||
281                            src == SkBlendModeCoeff::kIDA)) {
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 
onFilterImage(const skif::Context & ctx) const295 skif::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);
311     skif::FilterResult::Builder builder{ctx};
312     builder.add(this->getChildOutput(kBackground, inputCtx));
313     builder.add(this->getChildOutput(kForeground, inputCtx));
314     return builder.eval(
315             [&](SkSpan<sk_sp<SkShader>> inputs) -> sk_sp<SkShader> {
316                 return this->makeBlendShader(inputs[kBackground], inputs[kForeground]);
317             }, requiredInput);
318 }
319 
onGetInputLayerBounds(const skif::Mapping & mapping,const skif::LayerSpace<SkIRect> & desiredOutput,std::optional<skif::LayerSpace<SkIRect>> contentBounds) const320 skif::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
332             return skif::LayerSpace<SkIRect>::Empty();
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
341     skif::LayerSpace<SkIRect> bgInput =
342             this->getChildInputLayerBounds(kBackground, mapping, requiredInput, contentBounds);
343     skif::LayerSpace<SkIRect> fgInput =
344             this->getChildInputLayerBounds(kForeground, mapping, requiredInput, contentBounds);
345 
346     bgInput.join(fgInput);
347     return bgInput;
348 }
349 
onGetOutputLayerBounds(const skif::Mapping & mapping,std::optional<skif::LayerSpace<SkIRect>> contentBounds) const350 std::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
369         SkBlendModeCoeff src, dst;
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
375                                                                   || dst == SkBlendModeCoeff::kSC;
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
388             return skif::LayerSpace<SkIRect>::Unbounded();
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.
399         return skif::LayerSpace<SkIRect>::Unbounded();
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)) {
410                 return skif::LayerSpace<SkIRect>::Empty();
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 
computeFastBounds(const SkRect & bounds) const430 SkRect 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
437         SkBlendModeCoeff src, dst;
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
452             return SkRectPriv::MakeLargeS32();
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.
463         return SkRectPriv::MakeLargeS32();
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 }
486