xref: /aosp_15_r20/external/skia/src/effects/imagefilters/SkDropShadowImageFilter.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2013 Google Inc.
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/SkColor.h"
12 #include "include/core/SkColorFilter.h"
13 #include "include/core/SkColorSpace.h"
14 #include "include/core/SkFlattenable.h"
15 #include "include/core/SkImageFilter.h"
16 #include "include/core/SkMatrix.h"
17 #include "include/core/SkPoint.h"
18 #include "include/core/SkRefCnt.h"
19 #include "include/core/SkSamplingOptions.h"
20 #include "include/core/SkScalar.h"
21 #include "include/core/SkSize.h"
22 #include "include/core/SkTypes.h"
23 #include "include/private/base/SkTo.h"
24 #include "src/core/SkImageFilter_Base.h"
25 #include "src/core/SkPicturePriv.h"
26 #include "src/core/SkReadBuffer.h"
27 
28 #include <optional>
29 #include <utility>
30 
31 struct SkRect;
32 
33 namespace {
34 
make_drop_shadow_graph(SkVector offset,SkSize sigma,SkColor4f color,sk_sp<SkColorSpace> colorSpace,bool shadowOnly,sk_sp<SkImageFilter> input,const std::optional<SkRect> & crop)35 static sk_sp<SkImageFilter> make_drop_shadow_graph(SkVector offset,
36                                                    SkSize sigma,
37                                                    SkColor4f color,
38                                                    sk_sp<SkColorSpace> colorSpace,
39                                                    bool shadowOnly,
40                                                    sk_sp<SkImageFilter> input,
41                                                    const std::optional<SkRect>& crop) {
42     // A drop shadow blurs the input, filters it to be the solid color + blurred
43     // alpha, and then offsets it. If it's not shadow-only, the input is then
44     // src-over blended on top. Finally it's cropped to the optional 'crop'.
45     sk_sp<SkImageFilter> filter = input;
46     filter = SkImageFilters::Blur(sigma.fWidth, sigma.fHeight, std::move(filter));
47     filter = SkImageFilters::ColorFilter(
48             SkColorFilters::Blend(color, std::move(colorSpace), SkBlendMode::kSrcIn),
49             std::move(filter));
50     // TODO: Offset should take SkSamplingOptions too, but kLinear filtering is needed to hide
51     // nearest-neighbor sampling artifacts from fractional offsets applied post-blur.
52     filter = SkImageFilters::MatrixTransform(SkMatrix::Translate(offset.fX, offset.fY),
53                                              SkFilterMode::kLinear,
54                                              std::move(filter));
55     if (!shadowOnly) {
56 #if defined(SK_LEGACY_BLEND_FOR_DROP_SHADOWS)
57         filter = SkImageFilters::Blend(
58                 SkBlendMode::kSrcOver, std::move(filter), std::move(input));
59 #else
60         // Merge is visually equivalent to Blend(kSrcOver) but draws each child independently,
61         // whereas Blend() fills the union of the child bounds with a single shader evaluation.
62         // Since we know the original and the offset blur will have somewhat disjoint bounds, a
63         // Blend() shader would force evaluating tile edge conditions for each, while merge lets us
64         // avoid that.
65         filter = SkImageFilters::Merge(std::move(filter), std::move(input));
66 #endif
67     }
68     if (crop) {
69         filter = SkImageFilters::Crop(*crop, std::move(filter));
70     }
71     return filter;
72 }
73 
legacy_drop_shadow_create_proc(SkReadBuffer & buffer)74 sk_sp<SkFlattenable> legacy_drop_shadow_create_proc(SkReadBuffer& buffer) {
75     if (!buffer.isVersionLT(SkPicturePriv::Version::kDropShadowImageFilterComposition)) {
76         // SKPs created with this version or newer just serialize the image filter composition that
77         // is equivalent to a drop-shadow, instead of a single dedicated flattenable for the effect.
78         return nullptr;
79     }
80 
81     auto [child, cropRect] = SkImageFilter_Base::Unflatten(buffer);
82 
83     SkScalar dx = buffer.readScalar();
84     SkScalar dy = buffer.readScalar();
85     SkScalar sigmaX = buffer.readScalar();
86     SkScalar sigmaY = buffer.readScalar();
87     SkColor4f color = SkColor4f::FromColor(buffer.readColor());
88 
89     // For backwards compatibility, the shadow mode had been saved as an enum cast to a 32LE int,
90     // where shadow-and-foreground was 0 and shadow-only was 1. Other than the number of bits, this
91     // is equivalent to the bool that SkDropShadowImageFilter now uses.
92     bool shadowOnly = SkToBool(buffer.read32LE(1));
93     return make_drop_shadow_graph({dx, dy}, {sigmaX, sigmaY}, color, /*colorSpace=*/nullptr,
94                                   shadowOnly, std::move(child), cropRect);
95 }
96 
97 } // anonymous namespace
98 
DropShadow(SkScalar dx,SkScalar dy,SkScalar sigmaX,SkScalar sigmaY,SkColor4f color,sk_sp<SkColorSpace> colorSpace,sk_sp<SkImageFilter> input,const CropRect & cropRect)99 sk_sp<SkImageFilter> SkImageFilters::DropShadow(
100         SkScalar dx, SkScalar dy, SkScalar sigmaX, SkScalar sigmaY, SkColor4f color,
101         sk_sp<SkColorSpace> colorSpace,
102         sk_sp<SkImageFilter> input, const CropRect& cropRect) {
103     return make_drop_shadow_graph({dx, dy}, {sigmaX, sigmaY}, color, std::move(colorSpace),
104                                   /*shadowOnly=*/false, std::move(input), cropRect);
105 }
106 
DropShadowOnly(SkScalar dx,SkScalar dy,SkScalar sigmaX,SkScalar sigmaY,SkColor4f color,sk_sp<SkColorSpace> colorSpace,sk_sp<SkImageFilter> input,const CropRect & cropRect)107 sk_sp<SkImageFilter> SkImageFilters::DropShadowOnly(
108         SkScalar dx, SkScalar dy, SkScalar sigmaX, SkScalar sigmaY, SkColor4f color,
109         sk_sp<SkColorSpace> colorSpace, sk_sp<SkImageFilter> input, const CropRect& cropRect) {
110     return make_drop_shadow_graph({dx, dy}, {sigmaX, sigmaY}, color, std::move(colorSpace),
111                                   /*shadowOnly=*/true, std::move(input), cropRect);
112 }
113 
114 // TODO (michaelludwig) - Remove after grace period for SKPs to stop using old create proc
SkRegisterLegacyDropShadowImageFilterFlattenable()115 void SkRegisterLegacyDropShadowImageFilterFlattenable() {
116     SkFlattenable::Register("SkDropShadowImageFilter", legacy_drop_shadow_create_proc);
117     SkFlattenable::Register("SkDropShadowImageFilterImpl", legacy_drop_shadow_create_proc);
118 }
119