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/core/SkBlendMode.h"
9 #include "include/core/SkCanvas.h"
10 #include "include/core/SkColor.h"
11 #include "include/core/SkColorFilter.h"
12 #include "include/core/SkFlattenable.h"
13 #include "include/core/SkImageFilter.h"
14 #include "include/core/SkMatrix.h"
15 #include "include/core/SkPaint.h"
16 #include "include/core/SkPoint.h"
17 #include "include/core/SkRect.h"
18 #include "include/core/SkRefCnt.h"
19 #include "include/core/SkSamplingOptions.h"
20 #include "include/core/SkScalar.h"
21 #include "include/core/SkTypes.h"
22 #include "include/effects/SkImageFilters.h"
23 #include "include/private/base/SkTo.h"
24 #include "src/core/SkImageFilter_Base.h"
25 #include "src/core/SkReadBuffer.h"
26 #include "src/core/SkSpecialImage.h"
27 #include "src/core/SkSpecialSurface.h"
28 #include "src/core/SkWriteBuffer.h"
29
30 #include <utility>
31
32 namespace {
33
34 class SkDropShadowImageFilter final : public SkImageFilter_Base {
35 public:
SkDropShadowImageFilter(SkScalar dx,SkScalar dy,SkScalar sigmaX,SkScalar sigmaY,SkColor color,bool shadowOnly,sk_sp<SkImageFilter> input,const SkRect * cropRect)36 SkDropShadowImageFilter(SkScalar dx, SkScalar dy, SkScalar sigmaX, SkScalar sigmaY,
37 SkColor color, bool shadowOnly, sk_sp<SkImageFilter> input,
38 const SkRect* cropRect)
39 : INHERITED(&input, 1, cropRect)
40 , fDx(dx)
41 , fDy(dy)
42 , fSigmaX(sigmaX)
43 , fSigmaY(sigmaY)
44 , fColor(color)
45 , fShadowOnly(shadowOnly) {}
46
Make(SkScalar dx,SkScalar dy,SkScalar sigmaX,SkScalar sigmaY,SkColor color,bool shadowOnly,sk_sp<SkImageFilter> input,const SkRect * cropRect)47 static sk_sp<SkImageFilter> Make(SkScalar dx, SkScalar dy, SkScalar sigmaX, SkScalar sigmaY,
48 SkColor color, bool shadowOnly, sk_sp<SkImageFilter> input,
49 const SkRect* cropRect) {
50 return sk_sp<SkImageFilter>(new SkDropShadowImageFilter(
51 dx, dy, sigmaX, sigmaY, color, shadowOnly, std::move(input), cropRect));
52 }
53
54 SkRect computeFastBounds(const SkRect&) const override;
55
56 protected:
57 void flatten(SkWriteBuffer&) const override;
58 sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const override;
59 SkIRect onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
60 MapDirection, const SkIRect* inputRect) const override;
61
62 private:
63 friend void ::SkRegisterDropShadowImageFilterFlattenable();
64 SK_FLATTENABLE_HOOKS(SkDropShadowImageFilter)
65
66 SkScalar fDx, fDy, fSigmaX, fSigmaY;
67 SkColor fColor;
68 bool fShadowOnly;
69
70 using INHERITED = SkImageFilter_Base;
71 };
72
73 } // end namespace
74
DropShadow(SkScalar dx,SkScalar dy,SkScalar sigmaX,SkScalar sigmaY,SkColor color,sk_sp<SkImageFilter> input,const CropRect & cropRect)75 sk_sp<SkImageFilter> SkImageFilters::DropShadow(
76 SkScalar dx, SkScalar dy, SkScalar sigmaX, SkScalar sigmaY, SkColor color,
77 sk_sp<SkImageFilter> input, const CropRect& cropRect) {
78 return SkDropShadowImageFilter::Make(dx, dy, sigmaX, sigmaY, color, /* shadowOnly */ false,
79 std::move(input), cropRect);
80 }
81
DropShadowOnly(SkScalar dx,SkScalar dy,SkScalar sigmaX,SkScalar sigmaY,SkColor color,sk_sp<SkImageFilter> input,const CropRect & cropRect)82 sk_sp<SkImageFilter> SkImageFilters::DropShadowOnly(
83 SkScalar dx, SkScalar dy, SkScalar sigmaX, SkScalar sigmaY, SkColor color,
84 sk_sp<SkImageFilter> input, const CropRect& cropRect) {
85 return SkDropShadowImageFilter::Make(dx, dy, sigmaX, sigmaY, color, /* shadowOnly */ true,
86 std::move(input), cropRect);
87 }
88
SkRegisterDropShadowImageFilterFlattenable()89 void SkRegisterDropShadowImageFilterFlattenable() {
90 SK_REGISTER_FLATTENABLE(SkDropShadowImageFilter);
91 // TODO (michaelludwig) - Remove after grace period for SKPs to stop using old name
92 SkFlattenable::Register("SkDropShadowImageFilterImpl", SkDropShadowImageFilter::CreateProc);
93 }
94
CreateProc(SkReadBuffer & buffer)95 sk_sp<SkFlattenable> SkDropShadowImageFilter::CreateProc(SkReadBuffer& buffer) {
96 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
97 SkScalar dx = buffer.readScalar();
98 SkScalar dy = buffer.readScalar();
99 SkScalar sigmaX = buffer.readScalar();
100 SkScalar sigmaY = buffer.readScalar();
101 SkColor color = buffer.readColor();
102
103 // For backwards compatibility, the shadow mode had been saved as an enum cast to a 32LE int,
104 // where shadow-and-foreground was 0 and shadow-only was 1. Other than the number of bits, this
105 // is equivalent to the bool that SkDropShadowImageFilter now uses.
106 bool shadowOnly = SkToBool(buffer.read32LE(1));
107 return SkDropShadowImageFilter::Make(dx, dy, sigmaX, sigmaY, color, shadowOnly,
108 common.getInput(0), common.cropRect());
109 }
110
flatten(SkWriteBuffer & buffer) const111 void SkDropShadowImageFilter::flatten(SkWriteBuffer& buffer) const {
112 this->INHERITED::flatten(buffer);
113 buffer.writeScalar(fDx);
114 buffer.writeScalar(fDy);
115 buffer.writeScalar(fSigmaX);
116 buffer.writeScalar(fSigmaY);
117 buffer.writeColor(fColor);
118 // See CreateProc, but we save the bool as an int to match previous enum serialization.
119 buffer.writeInt(fShadowOnly);
120 }
121
122 ///////////////////////////////////////////////////////////////////////////////////////////////////
123
onFilterImage(const Context & ctx,SkIPoint * offset) const124 sk_sp<SkSpecialImage> SkDropShadowImageFilter::onFilterImage(const Context& ctx,
125 SkIPoint* offset) const {
126 SkIPoint inputOffset = SkIPoint::Make(0, 0);
127 sk_sp<SkSpecialImage> input(this->filterInput(0, ctx, &inputOffset));
128 if (!input) {
129 return nullptr;
130 }
131
132 const SkIRect inputBounds = SkIRect::MakeXYWH(inputOffset.x(), inputOffset.y(),
133 input->width(), input->height());
134 SkIRect bounds;
135 if (!this->applyCropRect(ctx, inputBounds, &bounds)) {
136 return nullptr;
137 }
138
139 sk_sp<SkSpecialSurface> surf(ctx.makeSurface(bounds.size()));
140 if (!surf) {
141 return nullptr;
142 }
143
144 SkCanvas* canvas = surf->getCanvas();
145 SkASSERT(canvas);
146
147 canvas->clear(0x0);
148
149 SkVector sigma = SkVector::Make(fSigmaX, fSigmaY);
150 ctx.ctm().mapVectors(&sigma, 1);
151 sigma.fX = SkScalarAbs(sigma.fX);
152 sigma.fY = SkScalarAbs(sigma.fY);
153
154 SkPaint paint;
155 paint.setAntiAlias(true);
156 paint.setImageFilter(SkImageFilters::Blur(sigma.fX, sigma.fY, nullptr));
157 paint.setColorFilter(SkColorFilters::Blend(fColor, SkBlendMode::kSrcIn));
158
159 SkVector offsetVec = SkVector::Make(fDx, fDy);
160 ctx.ctm().mapVectors(&offsetVec, 1);
161
162 canvas->translate(SkIntToScalar(inputOffset.fX) - SkIntToScalar(bounds.fLeft),
163 SkIntToScalar(inputOffset.fY) - SkIntToScalar(bounds.fTop));
164 input->draw(canvas, offsetVec.fX, offsetVec.fY, SkSamplingOptions(), &paint);
165
166 if (!fShadowOnly) {
167 input->draw(canvas, 0, 0);
168 }
169 offset->fX = bounds.fLeft;
170 offset->fY = bounds.fTop;
171 return surf->makeImageSnapshot();
172 }
173
computeFastBounds(const SkRect & src) const174 SkRect SkDropShadowImageFilter::computeFastBounds(const SkRect& src) const {
175 SkRect bounds = this->getInput(0) ? this->getInput(0)->computeFastBounds(src) : src;
176 SkRect shadowBounds = bounds;
177 shadowBounds.offset(fDx, fDy);
178 shadowBounds.outset(fSigmaX * 3, fSigmaY * 3);
179 if (!fShadowOnly) {
180 bounds.join(shadowBounds);
181 } else {
182 bounds = shadowBounds;
183 }
184 return bounds;
185 }
186
onFilterNodeBounds(const SkIRect & src,const SkMatrix & ctm,MapDirection dir,const SkIRect * inputRect) const187 SkIRect SkDropShadowImageFilter::onFilterNodeBounds(
188 const SkIRect& src, const SkMatrix& ctm, MapDirection dir, const SkIRect* inputRect) const {
189 SkVector offsetVec = SkVector::Make(fDx, fDy);
190 if (kReverse_MapDirection == dir) {
191 offsetVec.negate();
192 }
193 ctm.mapVectors(&offsetVec, 1);
194 SkIRect dst = src.makeOffset(SkScalarCeilToInt(offsetVec.x()),
195 SkScalarCeilToInt(offsetVec.y()));
196 SkVector sigma = SkVector::Make(fSigmaX, fSigmaY);
197 ctm.mapVectors(&sigma, 1);
198 dst.outset(
199 SkScalarCeilToInt(SkScalarAbs(sigma.x() * 3)),
200 SkScalarCeilToInt(SkScalarAbs(sigma.y() * 3)));
201 if (!fShadowOnly) {
202 dst.join(src);
203 }
204 return dst;
205 }
206