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
18namespace {
19
20class SkMergeImageFilterImpl final : public SkImageFilter_Base {
21public:
22 SkMergeImageFilterImpl(sk_sp<SkImageFilter>* const filters, int count,
23 const CropRect* cropRect)
24 : INHERITED(filters, count, cropRect) {
25 SkASSERT(count >= 0);
26 }
27
28protected:
29 sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const override;
30 bool onCanHandleComplexCTM() const override { return true; }
31
32private:
33 friend void SkMergeImageFilter::RegisterFlattenables();
34 SK_FLATTENABLE_HOOKS(SkMergeImageFilterImpl)
35
36 typedef SkImageFilter_Base INHERITED;
37};
38
39} // end namespace
40
41sk_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
46void 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
54sk_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
62sk_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