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/SkFlattenable.h"
11 #include "include/core/SkImage.h"
12 #include "include/core/SkImageFilter.h"
13 #include "include/core/SkMatrix.h"
14 #include "include/core/SkPaint.h"
15 #include "include/core/SkPoint.h"
16 #include "include/core/SkRect.h"
17 #include "include/core/SkRefCnt.h"
18 #include "include/core/SkSamplingOptions.h"
19 #include "include/core/SkScalar.h"
20 #include "include/core/SkSurface.h"
21 #include "include/core/SkTileMode.h"
22 #include "include/core/SkTypes.h"
23 #include "include/effects/SkImageFilters.h"
24 #include "src/core/SkImageFilterTypes.h"
25 #include "src/core/SkImageFilter_Base.h"
26 #include "src/core/SkReadBuffer.h"
27 #include "src/core/SkSpecialImage.h"
28 #include "src/core/SkSpecialSurface.h"
29 #include "src/core/SkValidationUtils.h"
30 #include "src/core/SkWriteBuffer.h"
31
32 #include <utility>
33
34 namespace {
35
36 class SkTileImageFilter final : public SkImageFilter_Base {
37 public:
SkTileImageFilter(const SkRect & srcRect,const SkRect & dstRect,sk_sp<SkImageFilter> input)38 SkTileImageFilter(const SkRect& srcRect, const SkRect& dstRect, sk_sp<SkImageFilter> input)
39 : INHERITED(&input, 1, nullptr)
40 , fSrcRect(srcRect)
41 , fDstRect(dstRect) {}
42
43 SkIRect onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
44 MapDirection, const SkIRect* inputRect) const override;
45 SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix& ctm,
46 MapDirection, const SkIRect* inputRect) const override;
47 SkRect computeFastBounds(const SkRect& src) const override;
48
49 protected:
50 void flatten(SkWriteBuffer& buffer) const override;
51
52 sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const override;
53
54 private:
55 friend void ::SkRegisterTileImageFilterFlattenable();
56 SK_FLATTENABLE_HOOKS(SkTileImageFilter)
57
58 SkRect fSrcRect;
59 SkRect fDstRect;
60
61 using INHERITED = SkImageFilter_Base;
62 };
63
64 } // end namespace
65
66
Tile(const SkRect & src,const SkRect & dst,sk_sp<SkImageFilter> input)67 sk_sp<SkImageFilter> SkImageFilters::Tile(const SkRect& src,
68 const SkRect& dst,
69 sk_sp<SkImageFilter> input) {
70 if (!SkIsValidRect(src) || !SkIsValidRect(dst)) {
71 return nullptr;
72 }
73 if (src.width() == dst.width() && src.height() == dst.height()) {
74 SkRect ir = dst;
75 if (!ir.intersect(src)) {
76 return input;
77 }
78 return SkImageFilters::Offset(dst.x() - src.x(), dst.y() - src.y(),
79 std::move(input), &ir);
80 }
81 return sk_sp<SkImageFilter>(new SkTileImageFilter(src, dst, std::move(input)));
82 }
83
SkRegisterTileImageFilterFlattenable()84 void SkRegisterTileImageFilterFlattenable() {
85 SK_REGISTER_FLATTENABLE(SkTileImageFilter);
86 // TODO (michaelludwig) - Remove after grace period for SKPs to stop using old name
87 SkFlattenable::Register("SkTileImageFilterImpl", SkTileImageFilter::CreateProc);
88 }
89
CreateProc(SkReadBuffer & buffer)90 sk_sp<SkFlattenable> SkTileImageFilter::CreateProc(SkReadBuffer& buffer) {
91 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
92 SkRect src, dst;
93 buffer.readRect(&src);
94 buffer.readRect(&dst);
95 return SkImageFilters::Tile(src, dst, common.getInput(0));
96 }
97
flatten(SkWriteBuffer & buffer) const98 void SkTileImageFilter::flatten(SkWriteBuffer& buffer) const {
99 this->INHERITED::flatten(buffer);
100 buffer.writeRect(fSrcRect);
101 buffer.writeRect(fDstRect);
102 }
103
104 ///////////////////////////////////////////////////////////////////////////////////////////////////
105
onFilterImage(const Context & ctx,SkIPoint * offset) const106 sk_sp<SkSpecialImage> SkTileImageFilter::onFilterImage(const Context& ctx,
107 SkIPoint* offset) const {
108 SkIPoint inputOffset = SkIPoint::Make(0, 0);
109 sk_sp<SkSpecialImage> input(this->filterInput(0, ctx, &inputOffset));
110 if (!input) {
111 return nullptr;
112 }
113
114 SkRect dstRect;
115 ctx.ctm().mapRect(&dstRect, fDstRect);
116 if (!dstRect.intersect(SkRect::Make(ctx.clipBounds()))) {
117 return nullptr;
118 }
119
120 const SkIRect dstIRect = skif::RoundOut(dstRect);
121 if (!fSrcRect.width() || !fSrcRect.height() || !dstIRect.width() || !dstIRect.height()) {
122 return nullptr;
123 }
124
125 SkRect srcRect;
126 ctx.ctm().mapRect(&srcRect, fSrcRect);
127 SkIRect srcIRect = skif::RoundOut(srcRect);
128 srcIRect.offset(-inputOffset);
129 const SkIRect inputBounds = SkIRect::MakeWH(input->width(), input->height());
130
131 if (!SkIRect::Intersects(srcIRect, inputBounds)) {
132 return nullptr;
133 }
134
135 // We create an SkImage here b.c. it needs to be a tight fit for the tiling
136 sk_sp<SkImage> subset;
137 if (inputBounds.contains(srcIRect)) {
138 subset = input->asImage(&srcIRect);
139 } else {
140 sk_sp<SkSurface> surf(input->makeTightSurface(ctx.colorType(), ctx.colorSpace(),
141 srcIRect.size()));
142 if (!surf) {
143 return nullptr;
144 }
145
146 SkCanvas* canvas = surf->getCanvas();
147 SkASSERT(canvas);
148
149 SkPaint paint;
150 paint.setBlendMode(SkBlendMode::kSrc);
151
152 input->draw(canvas,
153 SkIntToScalar(inputOffset.x()), SkIntToScalar(inputOffset.y()),
154 SkSamplingOptions(), &paint);
155
156 subset = surf->makeImageSnapshot();
157 }
158 if (!subset) {
159 return nullptr;
160 }
161 SkASSERT(subset->width() == srcIRect.width());
162 SkASSERT(subset->height() == srcIRect.height());
163
164 sk_sp<SkSpecialSurface> surf(ctx.makeSurface(dstIRect.size()));
165 if (!surf) {
166 return nullptr;
167 }
168
169 SkCanvas* canvas = surf->getCanvas();
170 SkASSERT(canvas);
171
172 SkPaint paint;
173 paint.setBlendMode(SkBlendMode::kSrc);
174 paint.setShader(subset->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat,
175 SkSamplingOptions()));
176 canvas->translate(-dstRect.fLeft, -dstRect.fTop);
177 canvas->drawRect(dstRect, paint);
178 offset->fX = dstIRect.fLeft;
179 offset->fY = dstIRect.fTop;
180 return surf->makeImageSnapshot();
181 }
182
onFilterNodeBounds(const SkIRect & src,const SkMatrix & ctm,MapDirection dir,const SkIRect * inputRect) const183 SkIRect SkTileImageFilter::onFilterNodeBounds(
184 const SkIRect& src, const SkMatrix& ctm, MapDirection dir, const SkIRect* inputRect) const {
185 SkRect rect = kReverse_MapDirection == dir ? fSrcRect : fDstRect;
186 ctm.mapRect(&rect);
187 return rect.roundOut();
188 }
189
onFilterBounds(const SkIRect & src,const SkMatrix &,MapDirection,const SkIRect * inputRect) const190 SkIRect SkTileImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix&,
191 MapDirection, const SkIRect* inputRect) const {
192 // Don't recurse into inputs.
193 return src;
194 }
195
computeFastBounds(const SkRect & src) const196 SkRect SkTileImageFilter::computeFastBounds(const SkRect& src) const {
197 return fDstRect;
198 }
199