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/SkMergeImageFilter.h" |
9 | |
10 | #include "include/core/SkCanvas.h" |
11 | #include "src/core/SkImageFilter_Base.h" |
12 | #include "src/core/SkReadBuffer.h" |
13 | #include "src/core/SkSpecialImage.h" |
14 | #include "src/core/SkSpecialSurface.h" |
15 | #include "src/core/SkValidationUtils.h" |
16 | #include "src/core/SkWriteBuffer.h" |
17 | |
18 | namespace { |
19 | |
20 | class SkMergeImageFilterImpl final : public SkImageFilter_Base { |
21 | public: |
22 | SkMergeImageFilterImpl(sk_sp<SkImageFilter>* const filters, int count, |
23 | const CropRect* cropRect) |
24 | : INHERITED(filters, count, cropRect) { |
25 | SkASSERT(count >= 0); |
26 | } |
27 | |
28 | protected: |
29 | sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const override; |
30 | bool onCanHandleComplexCTM() const override { return true; } |
31 | |
32 | private: |
33 | friend void SkMergeImageFilter::RegisterFlattenables(); |
34 | SK_FLATTENABLE_HOOKS(SkMergeImageFilterImpl) |
35 | |
36 | typedef SkImageFilter_Base INHERITED; |
37 | }; |
38 | |
39 | } // end namespace |
40 | |
41 | sk_sp<SkImageFilter> SkMergeImageFilter::Make(sk_sp<SkImageFilter>* const filters, int count, |
42 | const SkImageFilter::CropRect* cropRect) { |
43 | return sk_sp<SkImageFilter>(new SkMergeImageFilterImpl(filters, count, cropRect)); |
44 | } |
45 | |
46 | void SkMergeImageFilter::RegisterFlattenables() { |
47 | SK_REGISTER_FLATTENABLE(SkMergeImageFilterImpl); |
48 | // TODO (michaelludwig) - Remove after grace period for SKPs to stop using old name |
49 | SkFlattenable::Register("SkMergeImageFilter" , SkMergeImageFilterImpl::CreateProc); |
50 | } |
51 | |
52 | /////////////////////////////////////////////////////////////////////////////// |
53 | |
54 | sk_sp<SkFlattenable> SkMergeImageFilterImpl::CreateProc(SkReadBuffer& buffer) { |
55 | Common common; |
56 | if (!common.unflatten(buffer, -1) || !buffer.isValid()) { |
57 | return nullptr; |
58 | } |
59 | return SkMergeImageFilter::Make(common.inputs(), common.inputCount(), &common.cropRect()); |
60 | } |
61 | |
62 | sk_sp<SkSpecialImage> SkMergeImageFilterImpl::onFilterImage(const Context& ctx, |
63 | SkIPoint* offset) const { |
64 | int inputCount = this->countInputs(); |
65 | if (inputCount < 1) { |
66 | return nullptr; |
67 | } |
68 | |
69 | SkIRect bounds; |
70 | bounds.setEmpty(); |
71 | |
72 | std::unique_ptr<sk_sp<SkSpecialImage>[]> inputs(new sk_sp<SkSpecialImage>[inputCount]); |
73 | std::unique_ptr<SkIPoint[]> offsets(new SkIPoint[inputCount]); |
74 | |
75 | // Filter all of the inputs. |
76 | for (int i = 0; i < inputCount; ++i) { |
77 | offsets[i] = { 0, 0 }; |
78 | inputs[i] = this->filterInput(i, ctx, &offsets[i]); |
79 | if (!inputs[i]) { |
80 | continue; |
81 | } |
82 | const SkIRect inputBounds = SkIRect::MakeXYWH(offsets[i].fX, offsets[i].fY, |
83 | inputs[i]->width(), inputs[i]->height()); |
84 | bounds.join(inputBounds); |
85 | } |
86 | if (bounds.isEmpty()) { |
87 | return nullptr; |
88 | } |
89 | |
90 | // Apply the crop rect to the union of the inputs' bounds. |
91 | // Note that the crop rect can only reduce the bounds, since this |
92 | // filter does not affect transparent black. |
93 | bool embiggen = false; |
94 | this->getCropRect().applyTo(bounds, ctx.ctm(), embiggen, &bounds); |
95 | if (!bounds.intersect(ctx.clipBounds())) { |
96 | return nullptr; |
97 | } |
98 | |
99 | const int x0 = bounds.left(); |
100 | const int y0 = bounds.top(); |
101 | |
102 | sk_sp<SkSpecialSurface> surf(ctx.makeSurface(bounds.size())); |
103 | if (!surf) { |
104 | return nullptr; |
105 | } |
106 | |
107 | SkCanvas* canvas = surf->getCanvas(); |
108 | SkASSERT(canvas); |
109 | |
110 | canvas->clear(0x0); |
111 | |
112 | // Composite all of the filter inputs. |
113 | for (int i = 0; i < inputCount; ++i) { |
114 | if (!inputs[i]) { |
115 | continue; |
116 | } |
117 | |
118 | inputs[i]->draw(canvas, |
119 | SkIntToScalar(offsets[i].x() - x0), SkIntToScalar(offsets[i].y() - y0), |
120 | nullptr); |
121 | } |
122 | |
123 | offset->fX = bounds.left(); |
124 | offset->fY = bounds.top(); |
125 | return surf->makeImageSnapshot(); |
126 | } |
127 | |