1 /*
2 * Copyright 2012 The Android Open Source Project
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/SkCanvas.h"
9 #include "include/core/SkMatrix.h"
10 #include "include/core/SkPaint.h"
11 #include "include/effects/SkImageFilters.h"
12 #include "src/core/SkImageFilter_Base.h"
13 #include "src/core/SkPointPriv.h"
14 #include "src/core/SkReadBuffer.h"
15 #include "src/core/SkSpecialImage.h"
16 #include "src/core/SkSpecialSurface.h"
17 #include "src/core/SkWriteBuffer.h"
18
19 namespace {
20
21 class SkOffsetImageFilter final : public SkImageFilter_Base {
22 public:
SkOffsetImageFilter(SkScalar dx,SkScalar dy,sk_sp<SkImageFilter> input,const SkRect * cropRect)23 SkOffsetImageFilter(SkScalar dx, SkScalar dy, sk_sp<SkImageFilter> input,
24 const SkRect* cropRect)
25 : INHERITED(&input, 1, cropRect) {
26 fOffset.set(dx, dy);
27 }
28
29 SkRect computeFastBounds(const SkRect& src) const override;
30
31 protected:
32 void flatten(SkWriteBuffer&) const override;
33 sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const override;
34 SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix& ctm,
35 MapDirection, const SkIRect* inputRect) const override;
36
37 private:
38 friend void ::SkRegisterOffsetImageFilterFlattenable();
39 SK_FLATTENABLE_HOOKS(SkOffsetImageFilter)
40
41 SkVector fOffset;
42
43 using INHERITED = SkImageFilter_Base;
44 };
45
46 } // end namespace
47
Offset(SkScalar dx,SkScalar dy,sk_sp<SkImageFilter> input,const CropRect & cropRect)48 sk_sp<SkImageFilter> SkImageFilters::Offset(SkScalar dx, SkScalar dy,
49 sk_sp<SkImageFilter> input,
50 const CropRect& cropRect) {
51 if (!SkScalarIsFinite(dx) || !SkScalarIsFinite(dy)) {
52 return nullptr;
53 }
54
55 return sk_sp<SkImageFilter>(new SkOffsetImageFilter(dx, dy, std::move(input), cropRect));
56 }
57
SkRegisterOffsetImageFilterFlattenable()58 void SkRegisterOffsetImageFilterFlattenable() {
59 SK_REGISTER_FLATTENABLE(SkOffsetImageFilter);
60 // TODO (michaelludwig) - Remove after grace period for SKPs to stop using old name
61 SkFlattenable::Register("SkOffsetImageFilterImpl", SkOffsetImageFilter::CreateProc);
62 }
63
CreateProc(SkReadBuffer & buffer)64 sk_sp<SkFlattenable> SkOffsetImageFilter::CreateProc(SkReadBuffer& buffer) {
65 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
66 SkPoint offset;
67 buffer.readPoint(&offset);
68 return SkImageFilters::Offset(offset.x(), offset.y(), common.getInput(0), common.cropRect());
69 }
70
flatten(SkWriteBuffer & buffer) const71 void SkOffsetImageFilter::flatten(SkWriteBuffer& buffer) const {
72 this->INHERITED::flatten(buffer);
73 buffer.writePoint(fOffset);
74 }
75
76 ///////////////////////////////////////////////////////////////////////////////////////////////////
77
map_offset_vector(const SkMatrix & ctm,const SkVector & offset)78 static SkIPoint map_offset_vector(const SkMatrix& ctm, const SkVector& offset) {
79 SkVector vec = ctm.mapVector(offset.fX, offset.fY);
80 return SkIPoint::Make(SkScalarRoundToInt(vec.fX), SkScalarRoundToInt(vec.fY));
81 }
82
onFilterImage(const Context & ctx,SkIPoint * offset) const83 sk_sp<SkSpecialImage> SkOffsetImageFilter::onFilterImage(const Context& ctx,
84 SkIPoint* offset) const {
85 SkIPoint srcOffset = SkIPoint::Make(0, 0);
86 sk_sp<SkSpecialImage> input(this->filterInput(0, ctx, &srcOffset));
87 if (!input) {
88 return nullptr;
89 }
90
91 SkIPoint vec = map_offset_vector(ctx.ctm(), fOffset);
92
93 if (!this->cropRectIsSet()) {
94 offset->fX = Sk32_sat_add(srcOffset.fX, vec.fX);
95 offset->fY = Sk32_sat_add(srcOffset.fY, vec.fY);
96 return input;
97 } else {
98 SkIRect bounds;
99 const SkIRect srcBounds = SkIRect::MakeXYWH(srcOffset.fX, srcOffset.fY,
100 input->width(), input->height());
101 if (!this->applyCropRect(ctx, srcBounds, &bounds)) {
102 return nullptr;
103 }
104
105 sk_sp<SkSpecialSurface> surf(ctx.makeSurface(bounds.size()));
106 if (!surf) {
107 return nullptr;
108 }
109
110 SkCanvas* canvas = surf->getCanvas();
111 SkASSERT(canvas);
112
113 // TODO: it seems like this clear shouldn't be necessary (see skbug.com/5075)
114 canvas->clear(0x0);
115
116 SkPaint paint;
117 paint.setBlendMode(SkBlendMode::kSrc);
118 canvas->translate(SkIntToScalar(Sk32_sat_sub(srcOffset.fX, bounds.fLeft)),
119 SkIntToScalar(Sk32_sat_sub(srcOffset.fY, bounds.fTop)));
120
121 input->draw(canvas, vec.fX, vec.fY, SkSamplingOptions(), &paint);
122
123 offset->fX = bounds.fLeft;
124 offset->fY = bounds.fTop;
125 return surf->makeImageSnapshot();
126 }
127 }
128
computeFastBounds(const SkRect & src) const129 SkRect SkOffsetImageFilter::computeFastBounds(const SkRect& src) const {
130 SkRect bounds = this->getInput(0) ? this->getInput(0)->computeFastBounds(src) : src;
131 bounds.offset(fOffset.fX, fOffset.fY);
132 return bounds;
133 }
134
onFilterNodeBounds(const SkIRect & src,const SkMatrix & ctm,MapDirection dir,const SkIRect * inputRect) const135 SkIRect SkOffsetImageFilter::onFilterNodeBounds(
136 const SkIRect& src, const SkMatrix& ctm, MapDirection dir, const SkIRect* inputRect) const {
137 SkIPoint vec = map_offset_vector(ctm, fOffset);
138 if (kReverse_MapDirection == dir) {
139 SkPointPriv::Negate(vec);
140 }
141
142 return src.makeOffset(vec);
143 }
144