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