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 "SkMergeImageFilter.h"
9
10 #include "SkCanvas.h"
11 #include "SkColorSpaceXformer.h"
12 #include "SkReadBuffer.h"
13 #include "SkSpecialImage.h"
14 #include "SkSpecialSurface.h"
15 #include "SkWriteBuffer.h"
16 #include "SkValidationUtils.h"
17
Make(sk_sp<SkImageFilter> * const filters,int count,const CropRect * cropRect)18 sk_sp<SkImageFilter> SkMergeImageFilter::Make(sk_sp<SkImageFilter>* const filters, int count,
19 const CropRect* cropRect) {
20 return sk_sp<SkImageFilter>(new SkMergeImageFilter(filters, count, cropRect));
21 }
22
23 ///////////////////////////////////////////////////////////////////////////////
24
SkMergeImageFilter(sk_sp<SkImageFilter> * const filters,int count,const CropRect * cropRect)25 SkMergeImageFilter::SkMergeImageFilter(sk_sp<SkImageFilter>* const filters, int count,
26 const CropRect* cropRect)
27 : INHERITED(filters, count, cropRect) {
28 SkASSERT(count >= 0);
29 }
30
onFilterImage(SkSpecialImage * source,const Context & ctx,SkIPoint * offset) const31 sk_sp<SkSpecialImage> SkMergeImageFilter::onFilterImage(SkSpecialImage* source, const Context& ctx,
32 SkIPoint* offset) const {
33 int inputCount = this->countInputs();
34 if (inputCount < 1) {
35 return nullptr;
36 }
37
38 SkIRect bounds;
39 bounds.setEmpty();
40
41 std::unique_ptr<sk_sp<SkSpecialImage>[]> inputs(new sk_sp<SkSpecialImage>[inputCount]);
42 std::unique_ptr<SkIPoint[]> offsets(new SkIPoint[inputCount]);
43
44 // Filter all of the inputs.
45 for (int i = 0; i < inputCount; ++i) {
46 offsets[i].setZero();
47 inputs[i] = this->filterInput(i, source, ctx, &offsets[i]);
48 if (!inputs[i]) {
49 continue;
50 }
51 const SkIRect inputBounds = SkIRect::MakeXYWH(offsets[i].fX, offsets[i].fY,
52 inputs[i]->width(), inputs[i]->height());
53 bounds.join(inputBounds);
54 }
55 if (bounds.isEmpty()) {
56 return nullptr;
57 }
58
59 // Apply the crop rect to the union of the inputs' bounds.
60 // Note that the crop rect can only reduce the bounds, since this
61 // filter does not affect transparent black.
62 bool embiggen = false;
63 this->getCropRect().applyTo(bounds, ctx.ctm(), embiggen, &bounds);
64 if (!bounds.intersect(ctx.clipBounds())) {
65 return nullptr;
66 }
67
68 const int x0 = bounds.left();
69 const int y0 = bounds.top();
70
71 sk_sp<SkSpecialSurface> surf(source->makeSurface(ctx.outputProperties(), bounds.size()));
72 if (!surf) {
73 return nullptr;
74 }
75
76 SkCanvas* canvas = surf->getCanvas();
77 SkASSERT(canvas);
78
79 canvas->clear(0x0);
80
81 // Composite all of the filter inputs.
82 for (int i = 0; i < inputCount; ++i) {
83 if (!inputs[i]) {
84 continue;
85 }
86
87 inputs[i]->draw(canvas,
88 SkIntToScalar(offsets[i].x() - x0), SkIntToScalar(offsets[i].y() - y0),
89 nullptr);
90 }
91
92 offset->fX = bounds.left();
93 offset->fY = bounds.top();
94 return surf->makeImageSnapshot();
95 }
96
onMakeColorSpace(SkColorSpaceXformer * xformer) const97 sk_sp<SkImageFilter> SkMergeImageFilter::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
98 SkSTArray<5, sk_sp<SkImageFilter>> inputs(this->countInputs());
99 bool changed = false;
100 for (int i = 0; i < this->countInputs(); i++) {
101 inputs.push_back(xformer->apply(this->getInput(i)));
102 changed |= (inputs[i].get() != this->getInput(i));
103 }
104
105 if (changed) {
106 return SkMergeImageFilter::Make(inputs.begin(), this->countInputs(),
107 this->getCropRectIfSet());
108 }
109 return this->refMe();
110 }
111
CreateProc(SkReadBuffer & buffer)112 sk_sp<SkFlattenable> SkMergeImageFilter::CreateProc(SkReadBuffer& buffer) {
113 Common common;
114 if (!common.unflatten(buffer, -1)) {
115 return nullptr;
116 }
117
118 const int count = common.inputCount();
119 if (buffer.isVersionLT(SkReadBuffer::kNoModesInMergeImageFilter_Verison)) {
120 bool hasModes = buffer.readBool();
121 if (hasModes) {
122 // Older pictures may have stored blendmodes, but by inspection we think these were
123 // all src-over, so we have removed support for storing these.
124 SkAutoSTArray<4, uint8_t> modes8(count);
125 if (!buffer.readByteArray(modes8.get(), count)) {
126 return nullptr;
127 }
128 if (!buffer.isValid()) {
129 return nullptr;
130 }
131 }
132 }
133 return Make(common.inputs(), count, &common.cropRect());
134 }
135
flatten(SkWriteBuffer & buffer) const136 void SkMergeImageFilter::flatten(SkWriteBuffer& buffer) const {
137 this->INHERITED::flatten(buffer);
138 }
139
140 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const141 void SkMergeImageFilter::toString(SkString* str) const {
142 str->appendf("SkMergeImageFilter: (");
143
144 for (int i = 0; i < this->countInputs(); ++i) {
145 SkImageFilter* filter = this->getInput(i);
146 str->appendf("%d: (", i);
147 filter->toString(str);
148 str->appendf(")");
149 }
150
151 str->append(")");
152 }
153 #endif
154