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 "SkTileImageFilter.h"
9 #include "SkColorSpaceXformer.h"
10 #include "SkCanvas.h"
11 #include "SkImage.h"
12 #include "SkMatrix.h"
13 #include "SkOffsetImageFilter.h"
14 #include "SkPaint.h"
15 #include "SkReadBuffer.h"
16 #include "SkShader.h"
17 #include "SkSpecialImage.h"
18 #include "SkSpecialSurface.h"
19 #include "SkSurface.h"
20 #include "SkValidationUtils.h"
21 #include "SkWriteBuffer.h"
22
Make(const SkRect & srcRect,const SkRect & dstRect,sk_sp<SkImageFilter> input)23 sk_sp<SkImageFilter> SkTileImageFilter::Make(const SkRect& srcRect, const SkRect& dstRect,
24 sk_sp<SkImageFilter> input) {
25 if (!SkIsValidRect(srcRect) || !SkIsValidRect(dstRect)) {
26 return nullptr;
27 }
28 if (srcRect.width() == dstRect.width() && srcRect.height() == dstRect.height()) {
29 SkRect ir = dstRect;
30 if (!ir.intersect(srcRect)) {
31 return input;
32 }
33 CropRect cropRect(ir);
34 return SkOffsetImageFilter::Make(dstRect.x() - srcRect.x(),
35 dstRect.y() - srcRect.y(),
36 std::move(input),
37 &cropRect);
38 }
39 return sk_sp<SkImageFilter>(new SkTileImageFilter(srcRect, dstRect, std::move(input)));
40 }
41
onFilterImage(SkSpecialImage * source,const Context & ctx,SkIPoint * offset) const42 sk_sp<SkSpecialImage> SkTileImageFilter::onFilterImage(SkSpecialImage* source,
43 const Context& ctx,
44 SkIPoint* offset) const {
45 SkIPoint inputOffset = SkIPoint::Make(0, 0);
46 sk_sp<SkSpecialImage> input(this->filterInput(0, source, ctx, &inputOffset));
47 if (!input) {
48 return nullptr;
49 }
50
51 SkRect dstRect;
52 ctx.ctm().mapRect(&dstRect, fDstRect);
53 if (!dstRect.intersect(SkRect::Make(ctx.clipBounds()))) {
54 return nullptr;
55 }
56
57 const SkIRect dstIRect = dstRect.roundOut();
58 if (!fSrcRect.width() || !fSrcRect.height() || !dstIRect.width() || !dstIRect.height()) {
59 return nullptr;
60 }
61
62 SkRect srcRect;
63 ctx.ctm().mapRect(&srcRect, fSrcRect);
64 SkIRect srcIRect;
65 srcRect.roundOut(&srcIRect);
66 srcIRect.offset(-inputOffset);
67 const SkIRect inputBounds = SkIRect::MakeWH(input->width(), input->height());
68
69 if (!SkIRect::Intersects(srcIRect, inputBounds)) {
70 return nullptr;
71 }
72
73 // We create an SkImage here b.c. it needs to be a tight fit for the tiling
74 sk_sp<SkImage> subset;
75 if (inputBounds.contains(srcIRect)) {
76 subset = input->asImage(&srcIRect);
77 if (!subset) {
78 return nullptr;
79 }
80 } else {
81 sk_sp<SkSurface> surf(input->makeTightSurface(ctx.outputProperties(), srcIRect.size()));
82 if (!surf) {
83 return nullptr;
84 }
85
86 SkCanvas* canvas = surf->getCanvas();
87 SkASSERT(canvas);
88
89 SkPaint paint;
90 paint.setBlendMode(SkBlendMode::kSrc);
91
92 input->draw(canvas,
93 SkIntToScalar(inputOffset.x()), SkIntToScalar(inputOffset.y()),
94 &paint);
95
96 subset = surf->makeImageSnapshot();
97 }
98 SkASSERT(subset->width() == srcIRect.width());
99 SkASSERT(subset->height() == srcIRect.height());
100
101 sk_sp<SkSpecialSurface> surf(source->makeSurface(ctx.outputProperties(), dstIRect.size()));
102 if (!surf) {
103 return nullptr;
104 }
105
106 SkCanvas* canvas = surf->getCanvas();
107 SkASSERT(canvas);
108
109 SkPaint paint;
110 paint.setBlendMode(SkBlendMode::kSrc);
111 paint.setShader(subset->makeShader(SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode));
112 canvas->translate(-dstRect.fLeft, -dstRect.fTop);
113 canvas->drawRect(dstRect, paint);
114 offset->fX = dstIRect.fLeft;
115 offset->fY = dstIRect.fTop;
116 return surf->makeImageSnapshot();
117 }
118
onMakeColorSpace(SkColorSpaceXformer * xformer) const119 sk_sp<SkImageFilter> SkTileImageFilter::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
120 SkASSERT(1 == this->countInputs());
121
122 auto input = xformer->apply(this->getInput(0));
123 if (input.get() != this->getInput(0)) {
124 return SkTileImageFilter::Make(fSrcRect, fDstRect, std::move(input));
125 }
126 return this->refMe();
127 }
128
onFilterNodeBounds(const SkIRect & src,const SkMatrix & ctm,MapDirection direction) const129 SkIRect SkTileImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
130 MapDirection direction) const {
131 SkRect rect = kReverse_MapDirection == direction ? fSrcRect : fDstRect;
132 ctm.mapRect(&rect);
133 return rect.roundOut();
134 }
135
onFilterBounds(const SkIRect & src,const SkMatrix &,MapDirection) const136 SkIRect SkTileImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix&, MapDirection) const {
137 // Don't recurse into inputs.
138 return src;
139 }
140
computeFastBounds(const SkRect & src) const141 SkRect SkTileImageFilter::computeFastBounds(const SkRect& src) const {
142 return fDstRect;
143 }
144
CreateProc(SkReadBuffer & buffer)145 sk_sp<SkFlattenable> SkTileImageFilter::CreateProc(SkReadBuffer& buffer) {
146 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
147 SkRect src, dst;
148 buffer.readRect(&src);
149 buffer.readRect(&dst);
150 return Make(src, dst, common.getInput(0));
151 }
152
flatten(SkWriteBuffer & buffer) const153 void SkTileImageFilter::flatten(SkWriteBuffer& buffer) const {
154 this->INHERITED::flatten(buffer);
155 buffer.writeRect(fSrcRect);
156 buffer.writeRect(fDstRect);
157 }
158
159 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const160 void SkTileImageFilter::toString(SkString* str) const {
161 str->appendf("SkTileImageFilter: (");
162 str->appendf("src: %.2f %.2f %.2f %.2f",
163 fSrcRect.fLeft, fSrcRect.fTop, fSrcRect.fRight, fSrcRect.fBottom);
164 str->appendf(" dst: %.2f %.2f %.2f %.2f",
165 fDstRect.fLeft, fDstRect.fTop, fDstRect.fRight, fDstRect.fBottom);
166 if (this->getInput(0)) {
167 str->appendf("input: (");
168 this->getInput(0)->toString(str);
169 str->appendf(")");
170 }
171 str->append(")");
172 }
173 #endif
174