blob: 546d66a7971084855f02534a9e082f5cbaff42e8 [file] [log] [blame]
senorblanco@chromium.org4a9a6122012-12-04 14:18:50 +00001/*
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"
robertphillips393aa362016-03-10 04:44:20 -08009
senorblanco@chromium.org4a9a6122012-12-04 14:18:50 +000010#include "SkCanvas.h"
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000011#include "SkReadBuffer.h"
robertphillips393aa362016-03-10 04:44:20 -080012#include "SkSpecialImage.h"
13#include "SkSpecialSurface.h"
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000014#include "SkWriteBuffer.h"
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +000015#include "SkValidationUtils.h"
senorblanco@chromium.org4a9a6122012-12-04 14:18:50 +000016
robertphillips225db442016-04-17 14:27:05 -070017sk_sp<SkImageFilter> SkMergeImageFilter::Make(sk_sp<SkImageFilter> first,
18 sk_sp<SkImageFilter> second,
Mike Reed7d954ad2016-10-28 15:42:34 -040019 SkBlendMode mode,
robertphillips225db442016-04-17 14:27:05 -070020 const CropRect* cropRect) {
21 sk_sp<SkImageFilter> inputs[2] = { first, second };
Mike Reed7d954ad2016-10-28 15:42:34 -040022 SkBlendMode modes[2] = { mode, mode };
robertphillips225db442016-04-17 14:27:05 -070023 return sk_sp<SkImageFilter>(new SkMergeImageFilter(inputs, 2, modes, cropRect));
24}
25
Mike Reed7d954ad2016-10-28 15:42:34 -040026sk_sp<SkImageFilter> SkMergeImageFilter::MakeN(sk_sp<SkImageFilter> filters[], int count,
27 const SkBlendMode modes[],
28 const CropRect* cropRect) {
robertphillips225db442016-04-17 14:27:05 -070029 return sk_sp<SkImageFilter>(new SkMergeImageFilter(filters, count, modes, cropRect));
30}
31
senorblanco@chromium.org4a9a6122012-12-04 14:18:50 +000032///////////////////////////////////////////////////////////////////////////////
33
34void SkMergeImageFilter::initAllocModes() {
senorblanco4a243982015-11-25 07:06:55 -080035 int inputCount = this->countInputs();
senorblanco@chromium.org4a9a6122012-12-04 14:18:50 +000036 if (inputCount) {
37 size_t size = sizeof(uint8_t) * inputCount;
38 if (size <= sizeof(fStorage)) {
39 fModes = SkTCast<uint8_t*>(fStorage);
40 } else {
41 fModes = SkTCast<uint8_t*>(sk_malloc_throw(size));
42 }
43 } else {
halcanary96fcdcc2015-08-27 07:41:13 -070044 fModes = nullptr;
senorblanco@chromium.org4a9a6122012-12-04 14:18:50 +000045 }
46}
47
Mike Reed7d954ad2016-10-28 15:42:34 -040048void SkMergeImageFilter::initModes(const SkBlendMode modes[]) {
senorblanco@chromium.org4a9a6122012-12-04 14:18:50 +000049 if (modes) {
50 this->initAllocModes();
senorblanco4a243982015-11-25 07:06:55 -080051 int inputCount = this->countInputs();
senorblanco@chromium.org4a9a6122012-12-04 14:18:50 +000052 for (int i = 0; i < inputCount; ++i) {
Mike Reed7d954ad2016-10-28 15:42:34 -040053 fModes[i] = SkToU8((unsigned)modes[i]);
senorblanco@chromium.org4a9a6122012-12-04 14:18:50 +000054 }
55 } else {
halcanary96fcdcc2015-08-27 07:41:13 -070056 fModes = nullptr;
senorblanco@chromium.org4a9a6122012-12-04 14:18:50 +000057 }
58}
59
robertphillips2238c9d2016-03-30 13:34:16 -070060SkMergeImageFilter::SkMergeImageFilter(sk_sp<SkImageFilter> filters[], int count,
Mike Reed7d954ad2016-10-28 15:42:34 -040061 const SkBlendMode modes[],
senorblanco24e06d52015-03-18 12:11:33 -070062 const CropRect* cropRect)
robertphillips2238c9d2016-03-30 13:34:16 -070063 : INHERITED(filters, count, cropRect) {
commit-bot@chromium.orgc84728d2013-12-04 20:07:47 +000064 SkASSERT(count >= 0);
senorblanco@chromium.org4a9a6122012-12-04 14:18:50 +000065 this->initModes(modes);
66}
67
68SkMergeImageFilter::~SkMergeImageFilter() {
69
70 if (fModes != SkTCast<uint8_t*>(fStorage)) {
71 sk_free(fModes);
72 }
73}
74
robertphillips2302de92016-03-24 07:26:32 -070075sk_sp<SkSpecialImage> SkMergeImageFilter::onFilterImage(SkSpecialImage* source, const Context& ctx,
76 SkIPoint* offset) const {
senorblanco4a243982015-11-25 07:06:55 -080077 int inputCount = this->countInputs();
78 if (inputCount < 1) {
robertphillips393aa362016-03-10 04:44:20 -080079 return nullptr;
senorblanco@chromium.org4a9a6122012-12-04 14:18:50 +000080 }
81
senorblanco@chromium.org4a9a6122012-12-04 14:18:50 +000082 SkIRect bounds;
robertphillips393aa362016-03-10 04:44:20 -080083 bounds.setEmpty();
senorblanco4a243982015-11-25 07:06:55 -080084
Ben Wagner7ecc5962016-11-02 17:07:33 -040085 std::unique_ptr<sk_sp<SkSpecialImage>[]> inputs(new sk_sp<SkSpecialImage>[inputCount]);
86 std::unique_ptr<SkIPoint[]> offsets(new SkIPoint[inputCount]);
senorblanco4a243982015-11-25 07:06:55 -080087
88 // Filter all of the inputs.
89 for (int i = 0; i < inputCount; ++i) {
senorblanco4a243982015-11-25 07:06:55 -080090 offsets[i].setZero();
robertphillips2302de92016-03-24 07:26:32 -070091 inputs[i] = this->filterInput(i, source, ctx, &offsets[i]);
robertphillips393aa362016-03-10 04:44:20 -080092 if (!inputs[i]) {
senorblanco4a243982015-11-25 07:06:55 -080093 continue;
94 }
halcanary9d524f22016-03-29 09:03:52 -070095 const SkIRect inputBounds = SkIRect::MakeXYWH(offsets[i].fX, offsets[i].fY,
robertphillips393aa362016-03-10 04:44:20 -080096 inputs[i]->width(), inputs[i]->height());
97 bounds.join(inputBounds);
senorblanco4a243982015-11-25 07:06:55 -080098 }
robertphillips393aa362016-03-10 04:44:20 -080099 if (bounds.isEmpty()) {
100 return nullptr;
senorblanco4a243982015-11-25 07:06:55 -0800101 }
102
103 // Apply the crop rect to the union of the inputs' bounds.
senorblanco6db0a7b2016-04-01 16:41:10 -0700104 // Note that the crop rect can only reduce the bounds, since this
105 // filter does not affect transparent black.
106 bool embiggen = false;
107 this->getCropRect().applyTo(bounds, ctx.ctm(), embiggen, &bounds);
senorblancod8ff5b32016-01-28 08:23:02 -0800108 if (!bounds.intersect(ctx.clipBounds())) {
robertphillips393aa362016-03-10 04:44:20 -0800109 return nullptr;
senorblanco@chromium.org4a9a6122012-12-04 14:18:50 +0000110 }
111
112 const int x0 = bounds.left();
113 const int y0 = bounds.top();
114
brianosmaneed6b0e2016-09-23 13:04:05 -0700115 sk_sp<SkSpecialSurface> surf(source->makeSurface(ctx.outputProperties(), bounds.size()));
robertphillips393aa362016-03-10 04:44:20 -0800116 if (!surf) {
117 return nullptr;
senorblanco@chromium.org4a9a6122012-12-04 14:18:50 +0000118 }
robertphillips393aa362016-03-10 04:44:20 -0800119
120 SkCanvas* canvas = surf->getCanvas();
121 SkASSERT(canvas);
122
123 canvas->clear(0x0);
senorblanco@chromium.org4a9a6122012-12-04 14:18:50 +0000124
senorblanco4a243982015-11-25 07:06:55 -0800125 // Composite all of the filter inputs.
senorblanco@chromium.org4a9a6122012-12-04 14:18:50 +0000126 for (int i = 0; i < inputCount; ++i) {
robertphillips393aa362016-03-10 04:44:20 -0800127 if (!inputs[i]) {
128 continue;
129 }
130
senorblanco4a243982015-11-25 07:06:55 -0800131 SkPaint paint;
senorblanco@chromium.org4a9a6122012-12-04 14:18:50 +0000132 if (fModes) {
reed374772b2016-10-05 17:33:02 -0700133 paint.setBlendMode((SkBlendMode)fModes[i]);
senorblanco@chromium.org4a9a6122012-12-04 14:18:50 +0000134 }
robertphillips393aa362016-03-10 04:44:20 -0800135
136 inputs[i]->draw(canvas,
137 SkIntToScalar(offsets[i].x() - x0), SkIntToScalar(offsets[i].y() - y0),
138 &paint);
senorblanco@chromium.org4a9a6122012-12-04 14:18:50 +0000139 }
140
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000141 offset->fX = bounds.left();
142 offset->fY = bounds.top();
robertphillips2302de92016-03-24 07:26:32 -0700143 return surf->makeImageSnapshot();
senorblanco@chromium.org4a9a6122012-12-04 14:18:50 +0000144}
145
Matt Sarett6d72ed92017-04-10 16:35:33 -0400146sk_sp<SkImageFilter> SkMergeImageFilter::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
147 SkSTArray<5, sk_sp<SkImageFilter>> inputs(this->countInputs());
148 SkSTArray<5, SkBlendMode> modes(this->countInputs());
149 for (int i = 0; i < this->countInputs(); i++) {
150 inputs.push_back(this->getInput(i) ? this->getInput(i)->makeColorSpace(xformer) : nullptr);
151 modes.push_back((SkBlendMode) fModes[i]);
152 }
153
154 return SkMergeImageFilter::MakeN(inputs.begin(), this->countInputs(), modes.begin(),
155 this->getCropRectIfSet());
156}
157
reed60c9b582016-04-03 09:11:13 -0700158sk_sp<SkFlattenable> SkMergeImageFilter::CreateProc(SkReadBuffer& buffer) {
reed9fa60da2014-08-21 07:59:51 -0700159 Common common;
160 if (!common.unflatten(buffer, -1)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700161 return nullptr;
reed9fa60da2014-08-21 07:59:51 -0700162 }
163
164 const int count = common.inputCount();
165 bool hasModes = buffer.readBool();
166 if (hasModes) {
Mike Reed7d954ad2016-10-28 15:42:34 -0400167 SkAutoSTArray<4, SkBlendMode> modes(count);
reed9fa60da2014-08-21 07:59:51 -0700168 SkAutoSTArray<4, uint8_t> modes8(count);
169 if (!buffer.readByteArray(modes8.get(), count)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700170 return nullptr;
reed9fa60da2014-08-21 07:59:51 -0700171 }
172 for (int i = 0; i < count; ++i) {
Mike Reed7d954ad2016-10-28 15:42:34 -0400173 modes[i] = (SkBlendMode)modes8[i];
reed9fa60da2014-08-21 07:59:51 -0700174 buffer.validate(SkIsValidMode(modes[i]));
175 }
176 if (!buffer.isValid()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700177 return nullptr;
reed9fa60da2014-08-21 07:59:51 -0700178 }
Mike Reed7d954ad2016-10-28 15:42:34 -0400179 return MakeN(common.inputs(), count, modes.get(), &common.cropRect());
reed9fa60da2014-08-21 07:59:51 -0700180 }
Mike Reed7d954ad2016-10-28 15:42:34 -0400181 return MakeN(common.inputs(), count, nullptr, &common.cropRect());
reed9fa60da2014-08-21 07:59:51 -0700182}
183
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000184void SkMergeImageFilter::flatten(SkWriteBuffer& buffer) const {
senorblanco@chromium.org4a9a6122012-12-04 14:18:50 +0000185 this->INHERITED::flatten(buffer);
halcanary96fcdcc2015-08-27 07:41:13 -0700186 buffer.writeBool(fModes != nullptr);
senorblanco@chromium.org4a9a6122012-12-04 14:18:50 +0000187 if (fModes) {
senorblanco4a243982015-11-25 07:06:55 -0800188 buffer.writeByteArray(fModes, this->countInputs() * sizeof(fModes[0]));
senorblanco@chromium.org4a9a6122012-12-04 14:18:50 +0000189 }
190}
robertphillipsf3f5bad2014-12-19 13:49:15 -0800191
192#ifndef SK_IGNORE_TO_STRING
193void SkMergeImageFilter::toString(SkString* str) const {
194 str->appendf("SkMergeImageFilter: (");
halcanary9d524f22016-03-29 09:03:52 -0700195
robertphillipsf3f5bad2014-12-19 13:49:15 -0800196 for (int i = 0; i < this->countInputs(); ++i) {
197 SkImageFilter* filter = this->getInput(i);
198 str->appendf("%d: (", i);
199 filter->toString(str);
200 str->appendf(")");
201 }
202
203 str->append(")");
204}
205#endif