senorblanco@chromium.org | 4a9a612 | 2012-12-04 14:18:50 +0000 | [diff] [blame] | 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" |
robertphillips | 393aa36 | 2016-03-10 04:44:20 -0800 | [diff] [blame] | 9 | |
senorblanco@chromium.org | 4a9a612 | 2012-12-04 14:18:50 +0000 | [diff] [blame] | 10 | #include "SkCanvas.h" |
commit-bot@chromium.org | 8b0e8ac | 2014-01-30 18:58:24 +0000 | [diff] [blame] | 11 | #include "SkReadBuffer.h" |
robertphillips | 393aa36 | 2016-03-10 04:44:20 -0800 | [diff] [blame] | 12 | #include "SkSpecialImage.h" |
| 13 | #include "SkSpecialSurface.h" |
commit-bot@chromium.org | 8b0e8ac | 2014-01-30 18:58:24 +0000 | [diff] [blame] | 14 | #include "SkWriteBuffer.h" |
commit-bot@chromium.org | c0b7e10 | 2013-10-23 17:06:21 +0000 | [diff] [blame] | 15 | #include "SkValidationUtils.h" |
senorblanco@chromium.org | 4a9a612 | 2012-12-04 14:18:50 +0000 | [diff] [blame] | 16 | |
robertphillips | 225db44 | 2016-04-17 14:27:05 -0700 | [diff] [blame] | 17 | sk_sp<SkImageFilter> SkMergeImageFilter::Make(sk_sp<SkImageFilter> first, |
| 18 | sk_sp<SkImageFilter> second, |
Mike Reed | 7d954ad | 2016-10-28 15:42:34 -0400 | [diff] [blame] | 19 | SkBlendMode mode, |
robertphillips | 225db44 | 2016-04-17 14:27:05 -0700 | [diff] [blame] | 20 | const CropRect* cropRect) { |
| 21 | sk_sp<SkImageFilter> inputs[2] = { first, second }; |
Mike Reed | 7d954ad | 2016-10-28 15:42:34 -0400 | [diff] [blame] | 22 | SkBlendMode modes[2] = { mode, mode }; |
robertphillips | 225db44 | 2016-04-17 14:27:05 -0700 | [diff] [blame] | 23 | return sk_sp<SkImageFilter>(new SkMergeImageFilter(inputs, 2, modes, cropRect)); |
| 24 | } |
| 25 | |
Mike Reed | 7d954ad | 2016-10-28 15:42:34 -0400 | [diff] [blame] | 26 | sk_sp<SkImageFilter> SkMergeImageFilter::MakeN(sk_sp<SkImageFilter> filters[], int count, |
| 27 | const SkBlendMode modes[], |
| 28 | const CropRect* cropRect) { |
robertphillips | 225db44 | 2016-04-17 14:27:05 -0700 | [diff] [blame] | 29 | return sk_sp<SkImageFilter>(new SkMergeImageFilter(filters, count, modes, cropRect)); |
| 30 | } |
| 31 | |
senorblanco@chromium.org | 4a9a612 | 2012-12-04 14:18:50 +0000 | [diff] [blame] | 32 | /////////////////////////////////////////////////////////////////////////////// |
| 33 | |
| 34 | void SkMergeImageFilter::initAllocModes() { |
senorblanco | 4a24398 | 2015-11-25 07:06:55 -0800 | [diff] [blame] | 35 | int inputCount = this->countInputs(); |
senorblanco@chromium.org | 4a9a612 | 2012-12-04 14:18:50 +0000 | [diff] [blame] | 36 | 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 { |
halcanary | 96fcdcc | 2015-08-27 07:41:13 -0700 | [diff] [blame] | 44 | fModes = nullptr; |
senorblanco@chromium.org | 4a9a612 | 2012-12-04 14:18:50 +0000 | [diff] [blame] | 45 | } |
| 46 | } |
| 47 | |
Mike Reed | 7d954ad | 2016-10-28 15:42:34 -0400 | [diff] [blame] | 48 | void SkMergeImageFilter::initModes(const SkBlendMode modes[]) { |
senorblanco@chromium.org | 4a9a612 | 2012-12-04 14:18:50 +0000 | [diff] [blame] | 49 | if (modes) { |
| 50 | this->initAllocModes(); |
senorblanco | 4a24398 | 2015-11-25 07:06:55 -0800 | [diff] [blame] | 51 | int inputCount = this->countInputs(); |
senorblanco@chromium.org | 4a9a612 | 2012-12-04 14:18:50 +0000 | [diff] [blame] | 52 | for (int i = 0; i < inputCount; ++i) { |
Mike Reed | 7d954ad | 2016-10-28 15:42:34 -0400 | [diff] [blame] | 53 | fModes[i] = SkToU8((unsigned)modes[i]); |
senorblanco@chromium.org | 4a9a612 | 2012-12-04 14:18:50 +0000 | [diff] [blame] | 54 | } |
| 55 | } else { |
halcanary | 96fcdcc | 2015-08-27 07:41:13 -0700 | [diff] [blame] | 56 | fModes = nullptr; |
senorblanco@chromium.org | 4a9a612 | 2012-12-04 14:18:50 +0000 | [diff] [blame] | 57 | } |
| 58 | } |
| 59 | |
robertphillips | 2238c9d | 2016-03-30 13:34:16 -0700 | [diff] [blame] | 60 | SkMergeImageFilter::SkMergeImageFilter(sk_sp<SkImageFilter> filters[], int count, |
Mike Reed | 7d954ad | 2016-10-28 15:42:34 -0400 | [diff] [blame] | 61 | const SkBlendMode modes[], |
senorblanco | 24e06d5 | 2015-03-18 12:11:33 -0700 | [diff] [blame] | 62 | const CropRect* cropRect) |
robertphillips | 2238c9d | 2016-03-30 13:34:16 -0700 | [diff] [blame] | 63 | : INHERITED(filters, count, cropRect) { |
commit-bot@chromium.org | c84728d | 2013-12-04 20:07:47 +0000 | [diff] [blame] | 64 | SkASSERT(count >= 0); |
senorblanco@chromium.org | 4a9a612 | 2012-12-04 14:18:50 +0000 | [diff] [blame] | 65 | this->initModes(modes); |
| 66 | } |
| 67 | |
| 68 | SkMergeImageFilter::~SkMergeImageFilter() { |
| 69 | |
| 70 | if (fModes != SkTCast<uint8_t*>(fStorage)) { |
| 71 | sk_free(fModes); |
| 72 | } |
| 73 | } |
| 74 | |
robertphillips | 2302de9 | 2016-03-24 07:26:32 -0700 | [diff] [blame] | 75 | sk_sp<SkSpecialImage> SkMergeImageFilter::onFilterImage(SkSpecialImage* source, const Context& ctx, |
| 76 | SkIPoint* offset) const { |
senorblanco | 4a24398 | 2015-11-25 07:06:55 -0800 | [diff] [blame] | 77 | int inputCount = this->countInputs(); |
| 78 | if (inputCount < 1) { |
robertphillips | 393aa36 | 2016-03-10 04:44:20 -0800 | [diff] [blame] | 79 | return nullptr; |
senorblanco@chromium.org | 4a9a612 | 2012-12-04 14:18:50 +0000 | [diff] [blame] | 80 | } |
| 81 | |
senorblanco@chromium.org | 4a9a612 | 2012-12-04 14:18:50 +0000 | [diff] [blame] | 82 | SkIRect bounds; |
robertphillips | 393aa36 | 2016-03-10 04:44:20 -0800 | [diff] [blame] | 83 | bounds.setEmpty(); |
senorblanco | 4a24398 | 2015-11-25 07:06:55 -0800 | [diff] [blame] | 84 | |
Ben Wagner | 7ecc596 | 2016-11-02 17:07:33 -0400 | [diff] [blame] | 85 | std::unique_ptr<sk_sp<SkSpecialImage>[]> inputs(new sk_sp<SkSpecialImage>[inputCount]); |
| 86 | std::unique_ptr<SkIPoint[]> offsets(new SkIPoint[inputCount]); |
senorblanco | 4a24398 | 2015-11-25 07:06:55 -0800 | [diff] [blame] | 87 | |
| 88 | // Filter all of the inputs. |
| 89 | for (int i = 0; i < inputCount; ++i) { |
senorblanco | 4a24398 | 2015-11-25 07:06:55 -0800 | [diff] [blame] | 90 | offsets[i].setZero(); |
robertphillips | 2302de9 | 2016-03-24 07:26:32 -0700 | [diff] [blame] | 91 | inputs[i] = this->filterInput(i, source, ctx, &offsets[i]); |
robertphillips | 393aa36 | 2016-03-10 04:44:20 -0800 | [diff] [blame] | 92 | if (!inputs[i]) { |
senorblanco | 4a24398 | 2015-11-25 07:06:55 -0800 | [diff] [blame] | 93 | continue; |
| 94 | } |
halcanary | 9d524f2 | 2016-03-29 09:03:52 -0700 | [diff] [blame] | 95 | const SkIRect inputBounds = SkIRect::MakeXYWH(offsets[i].fX, offsets[i].fY, |
robertphillips | 393aa36 | 2016-03-10 04:44:20 -0800 | [diff] [blame] | 96 | inputs[i]->width(), inputs[i]->height()); |
| 97 | bounds.join(inputBounds); |
senorblanco | 4a24398 | 2015-11-25 07:06:55 -0800 | [diff] [blame] | 98 | } |
robertphillips | 393aa36 | 2016-03-10 04:44:20 -0800 | [diff] [blame] | 99 | if (bounds.isEmpty()) { |
| 100 | return nullptr; |
senorblanco | 4a24398 | 2015-11-25 07:06:55 -0800 | [diff] [blame] | 101 | } |
| 102 | |
| 103 | // Apply the crop rect to the union of the inputs' bounds. |
senorblanco | 6db0a7b | 2016-04-01 16:41:10 -0700 | [diff] [blame] | 104 | // 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); |
senorblanco | d8ff5b3 | 2016-01-28 08:23:02 -0800 | [diff] [blame] | 108 | if (!bounds.intersect(ctx.clipBounds())) { |
robertphillips | 393aa36 | 2016-03-10 04:44:20 -0800 | [diff] [blame] | 109 | return nullptr; |
senorblanco@chromium.org | 4a9a612 | 2012-12-04 14:18:50 +0000 | [diff] [blame] | 110 | } |
| 111 | |
| 112 | const int x0 = bounds.left(); |
| 113 | const int y0 = bounds.top(); |
| 114 | |
brianosman | eed6b0e | 2016-09-23 13:04:05 -0700 | [diff] [blame] | 115 | sk_sp<SkSpecialSurface> surf(source->makeSurface(ctx.outputProperties(), bounds.size())); |
robertphillips | 393aa36 | 2016-03-10 04:44:20 -0800 | [diff] [blame] | 116 | if (!surf) { |
| 117 | return nullptr; |
senorblanco@chromium.org | 4a9a612 | 2012-12-04 14:18:50 +0000 | [diff] [blame] | 118 | } |
robertphillips | 393aa36 | 2016-03-10 04:44:20 -0800 | [diff] [blame] | 119 | |
| 120 | SkCanvas* canvas = surf->getCanvas(); |
| 121 | SkASSERT(canvas); |
| 122 | |
| 123 | canvas->clear(0x0); |
senorblanco@chromium.org | 4a9a612 | 2012-12-04 14:18:50 +0000 | [diff] [blame] | 124 | |
senorblanco | 4a24398 | 2015-11-25 07:06:55 -0800 | [diff] [blame] | 125 | // Composite all of the filter inputs. |
senorblanco@chromium.org | 4a9a612 | 2012-12-04 14:18:50 +0000 | [diff] [blame] | 126 | for (int i = 0; i < inputCount; ++i) { |
robertphillips | 393aa36 | 2016-03-10 04:44:20 -0800 | [diff] [blame] | 127 | if (!inputs[i]) { |
| 128 | continue; |
| 129 | } |
| 130 | |
senorblanco | 4a24398 | 2015-11-25 07:06:55 -0800 | [diff] [blame] | 131 | SkPaint paint; |
senorblanco@chromium.org | 4a9a612 | 2012-12-04 14:18:50 +0000 | [diff] [blame] | 132 | if (fModes) { |
reed | 374772b | 2016-10-05 17:33:02 -0700 | [diff] [blame] | 133 | paint.setBlendMode((SkBlendMode)fModes[i]); |
senorblanco@chromium.org | 4a9a612 | 2012-12-04 14:18:50 +0000 | [diff] [blame] | 134 | } |
robertphillips | 393aa36 | 2016-03-10 04:44:20 -0800 | [diff] [blame] | 135 | |
| 136 | inputs[i]->draw(canvas, |
| 137 | SkIntToScalar(offsets[i].x() - x0), SkIntToScalar(offsets[i].y() - y0), |
| 138 | &paint); |
senorblanco@chromium.org | 4a9a612 | 2012-12-04 14:18:50 +0000 | [diff] [blame] | 139 | } |
| 140 | |
senorblanco@chromium.org | 6776b82 | 2014-01-03 21:48:22 +0000 | [diff] [blame] | 141 | offset->fX = bounds.left(); |
| 142 | offset->fY = bounds.top(); |
robertphillips | 2302de9 | 2016-03-24 07:26:32 -0700 | [diff] [blame] | 143 | return surf->makeImageSnapshot(); |
senorblanco@chromium.org | 4a9a612 | 2012-12-04 14:18:50 +0000 | [diff] [blame] | 144 | } |
| 145 | |
Matt Sarett | 6d72ed9 | 2017-04-10 16:35:33 -0400 | [diff] [blame^] | 146 | sk_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 | |
reed | 60c9b58 | 2016-04-03 09:11:13 -0700 | [diff] [blame] | 158 | sk_sp<SkFlattenable> SkMergeImageFilter::CreateProc(SkReadBuffer& buffer) { |
reed | 9fa60da | 2014-08-21 07:59:51 -0700 | [diff] [blame] | 159 | Common common; |
| 160 | if (!common.unflatten(buffer, -1)) { |
halcanary | 96fcdcc | 2015-08-27 07:41:13 -0700 | [diff] [blame] | 161 | return nullptr; |
reed | 9fa60da | 2014-08-21 07:59:51 -0700 | [diff] [blame] | 162 | } |
| 163 | |
| 164 | const int count = common.inputCount(); |
| 165 | bool hasModes = buffer.readBool(); |
| 166 | if (hasModes) { |
Mike Reed | 7d954ad | 2016-10-28 15:42:34 -0400 | [diff] [blame] | 167 | SkAutoSTArray<4, SkBlendMode> modes(count); |
reed | 9fa60da | 2014-08-21 07:59:51 -0700 | [diff] [blame] | 168 | SkAutoSTArray<4, uint8_t> modes8(count); |
| 169 | if (!buffer.readByteArray(modes8.get(), count)) { |
halcanary | 96fcdcc | 2015-08-27 07:41:13 -0700 | [diff] [blame] | 170 | return nullptr; |
reed | 9fa60da | 2014-08-21 07:59:51 -0700 | [diff] [blame] | 171 | } |
| 172 | for (int i = 0; i < count; ++i) { |
Mike Reed | 7d954ad | 2016-10-28 15:42:34 -0400 | [diff] [blame] | 173 | modes[i] = (SkBlendMode)modes8[i]; |
reed | 9fa60da | 2014-08-21 07:59:51 -0700 | [diff] [blame] | 174 | buffer.validate(SkIsValidMode(modes[i])); |
| 175 | } |
| 176 | if (!buffer.isValid()) { |
halcanary | 96fcdcc | 2015-08-27 07:41:13 -0700 | [diff] [blame] | 177 | return nullptr; |
reed | 9fa60da | 2014-08-21 07:59:51 -0700 | [diff] [blame] | 178 | } |
Mike Reed | 7d954ad | 2016-10-28 15:42:34 -0400 | [diff] [blame] | 179 | return MakeN(common.inputs(), count, modes.get(), &common.cropRect()); |
reed | 9fa60da | 2014-08-21 07:59:51 -0700 | [diff] [blame] | 180 | } |
Mike Reed | 7d954ad | 2016-10-28 15:42:34 -0400 | [diff] [blame] | 181 | return MakeN(common.inputs(), count, nullptr, &common.cropRect()); |
reed | 9fa60da | 2014-08-21 07:59:51 -0700 | [diff] [blame] | 182 | } |
| 183 | |
commit-bot@chromium.org | 8b0e8ac | 2014-01-30 18:58:24 +0000 | [diff] [blame] | 184 | void SkMergeImageFilter::flatten(SkWriteBuffer& buffer) const { |
senorblanco@chromium.org | 4a9a612 | 2012-12-04 14:18:50 +0000 | [diff] [blame] | 185 | this->INHERITED::flatten(buffer); |
halcanary | 96fcdcc | 2015-08-27 07:41:13 -0700 | [diff] [blame] | 186 | buffer.writeBool(fModes != nullptr); |
senorblanco@chromium.org | 4a9a612 | 2012-12-04 14:18:50 +0000 | [diff] [blame] | 187 | if (fModes) { |
senorblanco | 4a24398 | 2015-11-25 07:06:55 -0800 | [diff] [blame] | 188 | buffer.writeByteArray(fModes, this->countInputs() * sizeof(fModes[0])); |
senorblanco@chromium.org | 4a9a612 | 2012-12-04 14:18:50 +0000 | [diff] [blame] | 189 | } |
| 190 | } |
robertphillips | f3f5bad | 2014-12-19 13:49:15 -0800 | [diff] [blame] | 191 | |
| 192 | #ifndef SK_IGNORE_TO_STRING |
| 193 | void SkMergeImageFilter::toString(SkString* str) const { |
| 194 | str->appendf("SkMergeImageFilter: ("); |
halcanary | 9d524f2 | 2016-03-29 09:03:52 -0700 | [diff] [blame] | 195 | |
robertphillips | f3f5bad | 2014-12-19 13:49:15 -0800 | [diff] [blame] | 196 | 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 |