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 "SkReadBuffer.h" 12#include "SkSpecialImage.h" 13#include "SkSpecialSurface.h" 14#include "SkWriteBuffer.h" 15#include "SkValidationUtils.h" 16 17sk_sp<SkImageFilter> SkMergeImageFilter::Make(sk_sp<SkImageFilter> first, 18 sk_sp<SkImageFilter> second, 19 SkBlendMode mode, 20 const CropRect* cropRect) { 21 sk_sp<SkImageFilter> inputs[2] = { first, second }; 22 SkBlendMode modes[2] = { mode, mode }; 23 return sk_sp<SkImageFilter>(new SkMergeImageFilter(inputs, 2, modes, cropRect)); 24} 25 26sk_sp<SkImageFilter> SkMergeImageFilter::MakeN(sk_sp<SkImageFilter> filters[], int count, 27 const SkBlendMode modes[], 28 const CropRect* cropRect) { 29 return sk_sp<SkImageFilter>(new SkMergeImageFilter(filters, count, modes, cropRect)); 30} 31 32/////////////////////////////////////////////////////////////////////////////// 33 34void SkMergeImageFilter::initAllocModes() { 35 int inputCount = this->countInputs(); 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 { 44 fModes = nullptr; 45 } 46} 47 48void SkMergeImageFilter::initModes(const SkBlendMode modes[]) { 49 if (modes) { 50 this->initAllocModes(); 51 int inputCount = this->countInputs(); 52 for (int i = 0; i < inputCount; ++i) { 53 fModes[i] = SkToU8((unsigned)modes[i]); 54 } 55 } else { 56 fModes = nullptr; 57 } 58} 59 60SkMergeImageFilter::SkMergeImageFilter(sk_sp<SkImageFilter> filters[], int count, 61 const SkBlendMode modes[], 62 const CropRect* cropRect) 63 : INHERITED(filters, count, cropRect) { 64 SkASSERT(count >= 0); 65 this->initModes(modes); 66} 67 68SkMergeImageFilter::~SkMergeImageFilter() { 69 70 if (fModes != SkTCast<uint8_t*>(fStorage)) { 71 sk_free(fModes); 72 } 73} 74 75sk_sp<SkSpecialImage> SkMergeImageFilter::onFilterImage(SkSpecialImage* source, const Context& ctx, 76 SkIPoint* offset) const { 77 int inputCount = this->countInputs(); 78 if (inputCount < 1) { 79 return nullptr; 80 } 81 82 SkIRect bounds; 83 bounds.setEmpty(); 84 85 std::unique_ptr<sk_sp<SkSpecialImage>[]> inputs(new sk_sp<SkSpecialImage>[inputCount]); 86 std::unique_ptr<SkIPoint[]> offsets(new SkIPoint[inputCount]); 87 88 // Filter all of the inputs. 89 for (int i = 0; i < inputCount; ++i) { 90 offsets[i].setZero(); 91 inputs[i] = this->filterInput(i, source, ctx, &offsets[i]); 92 if (!inputs[i]) { 93 continue; 94 } 95 const SkIRect inputBounds = SkIRect::MakeXYWH(offsets[i].fX, offsets[i].fY, 96 inputs[i]->width(), inputs[i]->height()); 97 bounds.join(inputBounds); 98 } 99 if (bounds.isEmpty()) { 100 return nullptr; 101 } 102 103 // Apply the crop rect to the union of the inputs' bounds. 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); 108 if (!bounds.intersect(ctx.clipBounds())) { 109 return nullptr; 110 } 111 112 const int x0 = bounds.left(); 113 const int y0 = bounds.top(); 114 115 sk_sp<SkSpecialSurface> surf(source->makeSurface(ctx.outputProperties(), bounds.size())); 116 if (!surf) { 117 return nullptr; 118 } 119 120 SkCanvas* canvas = surf->getCanvas(); 121 SkASSERT(canvas); 122 123 canvas->clear(0x0); 124 125 // Composite all of the filter inputs. 126 for (int i = 0; i < inputCount; ++i) { 127 if (!inputs[i]) { 128 continue; 129 } 130 131 SkPaint paint; 132 if (fModes) { 133 paint.setBlendMode((SkBlendMode)fModes[i]); 134 } 135 136 inputs[i]->draw(canvas, 137 SkIntToScalar(offsets[i].x() - x0), SkIntToScalar(offsets[i].y() - y0), 138 &paint); 139 } 140 141 offset->fX = bounds.left(); 142 offset->fY = bounds.top(); 143 return surf->makeImageSnapshot(); 144} 145 146sk_sp<SkFlattenable> SkMergeImageFilter::CreateProc(SkReadBuffer& buffer) { 147 Common common; 148 if (!common.unflatten(buffer, -1)) { 149 return nullptr; 150 } 151 152 const int count = common.inputCount(); 153 bool hasModes = buffer.readBool(); 154 if (hasModes) { 155 SkAutoSTArray<4, SkBlendMode> modes(count); 156 SkAutoSTArray<4, uint8_t> modes8(count); 157 if (!buffer.readByteArray(modes8.get(), count)) { 158 return nullptr; 159 } 160 for (int i = 0; i < count; ++i) { 161 modes[i] = (SkBlendMode)modes8[i]; 162 buffer.validate(SkIsValidMode(modes[i])); 163 } 164 if (!buffer.isValid()) { 165 return nullptr; 166 } 167 return MakeN(common.inputs(), count, modes.get(), &common.cropRect()); 168 } 169 return MakeN(common.inputs(), count, nullptr, &common.cropRect()); 170} 171 172void SkMergeImageFilter::flatten(SkWriteBuffer& buffer) const { 173 this->INHERITED::flatten(buffer); 174 buffer.writeBool(fModes != nullptr); 175 if (fModes) { 176 buffer.writeByteArray(fModes, this->countInputs() * sizeof(fModes[0])); 177 } 178} 179 180#ifndef SK_IGNORE_TO_STRING 181void SkMergeImageFilter::toString(SkString* str) const { 182 str->appendf("SkMergeImageFilter: ("); 183 184 for (int i = 0; i < this->countInputs(); ++i) { 185 SkImageFilter* filter = this->getInput(i); 186 str->appendf("%d: (", i); 187 filter->toString(str); 188 str->appendf(")"); 189 } 190 191 str->append(")"); 192} 193#endif 194