Home | History | Annotate | Download | only in effects
      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 #include "SkCanvas.h"
     10 #include "SkDevice.h"
     11 #include "SkReadBuffer.h"
     12 #include "SkWriteBuffer.h"
     13 #include "SkValidationUtils.h"
     14 
     15 ///////////////////////////////////////////////////////////////////////////////
     16 
     17 void SkMergeImageFilter::initAllocModes() {
     18     int inputCount = this->countInputs();
     19     if (inputCount) {
     20         size_t size = sizeof(uint8_t) * inputCount;
     21         if (size <= sizeof(fStorage)) {
     22             fModes = SkTCast<uint8_t*>(fStorage);
     23         } else {
     24             fModes = SkTCast<uint8_t*>(sk_malloc_throw(size));
     25         }
     26     } else {
     27         fModes = nullptr;
     28     }
     29 }
     30 
     31 void SkMergeImageFilter::initModes(const SkXfermode::Mode modes[]) {
     32     if (modes) {
     33         this->initAllocModes();
     34         int inputCount = this->countInputs();
     35         for (int i = 0; i < inputCount; ++i) {
     36             fModes[i] = SkToU8(modes[i]);
     37         }
     38     } else {
     39         fModes = nullptr;
     40     }
     41 }
     42 
     43 SkMergeImageFilter::SkMergeImageFilter(SkImageFilter* filters[], int count,
     44                                        const SkXfermode::Mode modes[],
     45                                        const CropRect* cropRect)
     46   : INHERITED(count, filters, cropRect) {
     47     SkASSERT(count >= 0);
     48     this->initModes(modes);
     49 }
     50 
     51 SkMergeImageFilter::~SkMergeImageFilter() {
     52 
     53     if (fModes != SkTCast<uint8_t*>(fStorage)) {
     54         sk_free(fModes);
     55     }
     56 }
     57 
     58 bool SkMergeImageFilter::onFilterImageDeprecated(Proxy* proxy, const SkBitmap& src,
     59                                                  const Context& ctx,
     60                                                  SkBitmap* result, SkIPoint* offset) const {
     61     int inputCount = this->countInputs();
     62     if (inputCount < 1) {
     63         return false;
     64     }
     65 
     66     SkIRect bounds;
     67 
     68     SkAutoTDeleteArray<SkBitmap> inputs(new SkBitmap[inputCount]);
     69     SkAutoTDeleteArray<SkIPoint> offsets(new SkIPoint[inputCount]);
     70     bool didProduceResult = false;
     71 
     72     // Filter all of the inputs.
     73     for (int i = 0; i < inputCount; ++i) {
     74         inputs[i] = src;
     75         offsets[i].setZero();
     76         if (!this->filterInputDeprecated(i, proxy, src, ctx, &inputs[i], &offsets[i])) {
     77             inputs[i].reset();
     78             continue;
     79         }
     80         SkIRect srcBounds;
     81         inputs[i].getBounds(&srcBounds);
     82         srcBounds.offset(offsets[i]);
     83         if (!didProduceResult) {
     84             bounds = srcBounds;
     85             didProduceResult = true;
     86         } else {
     87             bounds.join(srcBounds);
     88         }
     89     }
     90     if (!didProduceResult) {
     91         return false;
     92     }
     93 
     94     // Apply the crop rect to the union of the inputs' bounds.
     95     this->getCropRect().applyTo(bounds, ctx.ctm(), &bounds);
     96     if (!bounds.intersect(ctx.clipBounds())) {
     97         return false;
     98     }
     99 
    100     const int x0 = bounds.left();
    101     const int y0 = bounds.top();
    102 
    103     // Allocate the destination buffer.
    104     SkAutoTUnref<SkBaseDevice> dst(proxy->createDevice(bounds.width(), bounds.height()));
    105     if (nullptr == dst) {
    106         return false;
    107     }
    108     SkCanvas canvas(dst);
    109 
    110     // Composite all of the filter inputs.
    111     for (int i = 0; i < inputCount; ++i) {
    112         SkPaint paint;
    113         if (fModes) {
    114             paint.setXfermodeMode((SkXfermode::Mode)fModes[i]);
    115         }
    116         canvas.drawBitmap(inputs[i], SkIntToScalar(offsets[i].x() - x0),
    117                                      SkIntToScalar(offsets[i].y() - y0), &paint);
    118     }
    119 
    120     offset->fX = bounds.left();
    121     offset->fY = bounds.top();
    122     *result = dst->accessBitmap(false);
    123     return true;
    124 }
    125 
    126 SkFlattenable* SkMergeImageFilter::CreateProc(SkReadBuffer& buffer) {
    127     Common common;
    128     if (!common.unflatten(buffer, -1)) {
    129         return nullptr;
    130     }
    131 
    132     const int count = common.inputCount();
    133     bool hasModes = buffer.readBool();
    134     if (hasModes) {
    135         SkAutoSTArray<4, SkXfermode::Mode> modes(count);
    136         SkAutoSTArray<4, uint8_t> modes8(count);
    137         if (!buffer.readByteArray(modes8.get(), count)) {
    138             return nullptr;
    139         }
    140         for (int i = 0; i < count; ++i) {
    141             modes[i] = (SkXfermode::Mode)modes8[i];
    142             buffer.validate(SkIsValidMode(modes[i]));
    143         }
    144         if (!buffer.isValid()) {
    145             return nullptr;
    146         }
    147         return Create(common.inputs(), count, modes.get(), &common.cropRect());
    148     }
    149     return Create(common.inputs(), count, nullptr, &common.cropRect());
    150 }
    151 
    152 void SkMergeImageFilter::flatten(SkWriteBuffer& buffer) const {
    153     this->INHERITED::flatten(buffer);
    154     buffer.writeBool(fModes != nullptr);
    155     if (fModes) {
    156         buffer.writeByteArray(fModes, this->countInputs() * sizeof(fModes[0]));
    157     }
    158 }
    159 
    160 #ifndef SK_IGNORE_TO_STRING
    161 void SkMergeImageFilter::toString(SkString* str) const {
    162     str->appendf("SkMergeImageFilter: (");
    163 
    164     for (int i = 0; i < this->countInputs(); ++i) {
    165         SkImageFilter* filter = this->getInput(i);
    166         str->appendf("%d: (", i);
    167         filter->toString(str);
    168         str->appendf(")");
    169     }
    170 
    171     str->append(")");
    172 }
    173 #endif
    174