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 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 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 31 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] = { 0, 0 }; 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 97 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 112 sk_sp<SkFlattenable> SkMergeImageFilter::CreateProc(SkReadBuffer& buffer) { 113 Common common; 114 if (!common.unflatten(buffer, -1) || !buffer.isValid()) { 115 return nullptr; 116 } 117 return Make(common.inputs(), common.inputCount(), &common.cropRect()); 118 } 119 120 void SkMergeImageFilter::flatten(SkWriteBuffer& buffer) const { 121 this->INHERITED::flatten(buffer); 122 } 123 124 #ifndef SK_IGNORE_TO_STRING 125 void SkMergeImageFilter::toString(SkString* str) const { 126 str->appendf("SkMergeImageFilter: ("); 127 128 for (int i = 0; i < this->countInputs(); ++i) { 129 SkImageFilter* filter = this->getInput(i); 130 str->appendf("%d: (", i); 131 filter->toString(str); 132 str->appendf(")"); 133 } 134 135 str->append(")"); 136 } 137 #endif 138