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