diff options
author | 2012-10-12 19:14:06 +0000 | |
---|---|---|
committer | 2012-10-12 19:14:06 +0000 | |
commit | 8d21f6c7a9d0cf4f87d77c235c6da7203620c7e5 (patch) | |
tree | df28bf3f40b4f9327736cc0166d0d964c32fe463 /src | |
parent | 3bafe74a29c37761082980ed4ee9b831256bd27e (diff) |
When two or more color matrix image filters are connected together, and the non-leaf matrices do not require clamping, we can concatenate their matrices and apply them together.
Review URL: https://codereview.appspot.com/6489054
git-svn-id: http://skia.googlecode.com/svn/trunk@5931 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src')
-rw-r--r-- | src/core/SkImageFilter.cpp | 30 | ||||
-rwxr-xr-x | src/effects/SkColorFilterImageFilter.cpp | 79 | ||||
-rwxr-xr-x | src/effects/SkTestImageFilters.cpp | 22 |
3 files changed, 103 insertions, 28 deletions
diff --git a/src/core/SkImageFilter.cpp b/src/core/SkImageFilter.cpp index c688310111..45ad024d45 100644 --- a/src/core/SkImageFilter.cpp +++ b/src/core/SkImageFilter.cpp @@ -14,19 +14,19 @@ SK_DEFINE_INST_COUNT(SkImageFilter) -SkImageFilter::SkImageFilter(int numInputs, SkImageFilter** inputs) - : fNumInputs(numInputs), fInputs(new SkImageFilter*[numInputs]) { - for (int i = 0; i < numInputs; ++i) { +SkImageFilter::SkImageFilter(int inputCount, SkImageFilter** inputs) + : fInputCount(inputCount), fInputs(new SkImageFilter*[inputCount]) { + for (int i = 0; i < inputCount; ++i) { fInputs[i] = inputs[i]; SkSafeRef(fInputs[i]); } } -SkImageFilter::SkImageFilter(int numInputs, ...) - : fNumInputs(numInputs), fInputs(new SkImageFilter*[numInputs]) { +SkImageFilter::SkImageFilter(int inputCount, ...) + : fInputCount(inputCount), fInputs(new SkImageFilter*[inputCount]) { va_list ap; - va_start(ap, numInputs); - for (int i = 0; i < numInputs; ++i) { + va_start(ap, inputCount); + for (int i = 0; i < inputCount; ++i) { fInputs[i] = va_arg(ap, SkImageFilter*); SkSafeRef(fInputs[i]); } @@ -34,15 +34,15 @@ SkImageFilter::SkImageFilter(int numInputs, ...) } SkImageFilter::~SkImageFilter() { - for (int i = 0; i < fNumInputs; i++) { + for (int i = 0; i < fInputCount; i++) { SkSafeUnref(fInputs[i]); } delete[] fInputs; } SkImageFilter::SkImageFilter(SkFlattenableReadBuffer& buffer) - : fNumInputs(buffer.readInt()), fInputs(new SkImageFilter*[fNumInputs]) { - for (int i = 0; i < fNumInputs; i++) { + : fInputCount(buffer.readInt()), fInputs(new SkImageFilter*[fInputCount]) { + for (int i = 0; i < fInputCount; i++) { if (buffer.readBool()) { fInputs[i] = static_cast<SkImageFilter*>(buffer.readFlattenable()); } else { @@ -52,8 +52,8 @@ SkImageFilter::SkImageFilter(SkFlattenableReadBuffer& buffer) } void SkImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const { - buffer.writeInt(fNumInputs); - for (int i = 0; i < fNumInputs; i++) { + buffer.writeInt(fInputCount); + for (int i = 0; i < fInputCount; i++) { SkImageFilter* input = getInput(i); buffer.writeBool(input != NULL); if (input != NULL) { @@ -65,7 +65,7 @@ void SkImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const { SkBitmap SkImageFilter::getInputResult(int index, Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm, SkIPoint* loc) { - SkASSERT(index < fNumInputs); + SkASSERT(index < fInputCount); SkImageFilter* input = getInput(index); SkBitmap result; if (input && input->filterImage(proxy, src, ctm, &result, loc)) { @@ -118,3 +118,7 @@ bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, bool SkImageFilter::asNewCustomStage(GrCustomStage**, GrTexture*) const { return false; } + +SkColorFilter* SkImageFilter::asColorFilter() const { + return NULL; +} diff --git a/src/effects/SkColorFilterImageFilter.cpp b/src/effects/SkColorFilterImageFilter.cpp index f9c8cef5c1..f8df51fbd0 100755 --- a/src/effects/SkColorFilterImageFilter.cpp +++ b/src/effects/SkColorFilterImageFilter.cpp @@ -8,11 +8,56 @@ #include "SkColorFilterImageFilter.h" #include "SkBitmap.h" #include "SkCanvas.h" +#include "SkColorMatrixFilter.h" #include "SkDevice.h" #include "SkColorFilter.h" #include "SkFlattenableBuffers.h" +namespace { + +void mult_color_matrix(SkScalar a[20], SkScalar b[20], SkScalar out[20]) { + for (int j = 0; j < 4; ++j) { + for (int i = 0; i < 5; ++i) { + out[i+j*5] = 4 == i ? a[4+j*5] : 0; + for (int k = 0; k < 4; ++k) + out[i+j*5] += SkScalarMul(a[k+j*5], b[i+k*5]); + } + } +} + +// To detect if we need to apply clamping after applying a matrix, we check if +// any output component might go outside of [0, 255] for any combination of +// input components in [0..255]. +// Each output component is an affine transformation of the input component, so +// the minimum and maximum values are for any combination of minimum or maximum +// values of input components (i.e. 0 or 255). +// E.g. if R' = x*R + y*G + z*B + w*A + t +// Then the maximum value will be for R=255 if x>0 or R=0 if x<0, and the +// minimum value will be for R=0 if x>0 or R=255 if x<0. +// Same goes for all components. +bool component_needs_clamping(SkScalar row[5]) { + SkScalar maxValue = row[4] / 255; + SkScalar minValue = row[4] / 255; + for (int i = 0; i < 4; ++i) { + if (row[i] > 0) + maxValue += row[i]; + else + minValue += row[i]; + } + return (maxValue > 1) || (minValue < 0); +} + +bool matrix_needs_clamping(SkScalar matrix[20]) { + return component_needs_clamping(matrix) + || component_needs_clamping(matrix+5) + || component_needs_clamping(matrix+10) + || component_needs_clamping(matrix+15); +} + +}; + SkColorFilterImageFilter::SkColorFilterImageFilter(SkColorFilter* cf, SkImageFilter* input) : INHERITED(input), fColorFilter(cf) { + SkASSERT(cf); SkSafeRef(cf); } @@ -34,11 +79,29 @@ bool SkColorFilterImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& sourc const SkMatrix& matrix, SkBitmap* result, SkIPoint* loc) { - SkBitmap src = this->getInputResult(proxy, source, matrix, loc); - SkColorFilter* cf = fColorFilter; - if (NULL == cf) { - *result = src; - return true; + SkImageFilter* parent = getInput(0); + SkScalar colorMatrix[20]; + SkBitmap src; + SkColorFilter* cf; + if (parent && fColorFilter->asColorMatrix(colorMatrix)) { + SkColorFilter* parentColorFilter; + SkScalar parentMatrix[20]; + while (parent && (parentColorFilter = parent->asColorFilter()) + && parentColorFilter->asColorMatrix(parentMatrix) + && !matrix_needs_clamping(parentMatrix)) { + SkScalar combinedMatrix[20]; + mult_color_matrix(parentMatrix, colorMatrix, combinedMatrix); + memcpy(colorMatrix, combinedMatrix, 20 * sizeof(SkScalar)); + parent = parent->getInput(0); + } + if (!parent || !parent->filterImage(proxy, source, matrix, &src, loc)) { + src = source; + } + cf = SkNEW_ARGS(SkColorMatrixFilter, (colorMatrix)); + } else { + src = this->getInputResult(proxy, source, matrix, loc); + cf = fColorFilter; + cf->ref(); } SkAutoTUnref<SkDevice> device(proxy->createDevice(src.width(), src.height())); @@ -46,9 +109,13 @@ bool SkColorFilterImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& sourc SkPaint paint; paint.setXfermodeMode(SkXfermode::kSrc_Mode); - paint.setColorFilter(fColorFilter); + paint.setColorFilter(cf)->unref(); canvas.drawSprite(src, 0, 0, &paint); *result = device.get()->accessBitmap(false); return true; } + +SkColorFilter* SkColorFilterImageFilter::asColorFilter() const { + return fColorFilter; +} diff --git a/src/effects/SkTestImageFilters.cpp b/src/effects/SkTestImageFilters.cpp index a51149d6a8..a672c337f7 100755 --- a/src/effects/SkTestImageFilters.cpp +++ b/src/effects/SkTestImageFilters.cpp @@ -101,8 +101,9 @@ SkComposeImageFilter::SkComposeImageFilter(SkFlattenableReadBuffer& buffer) : IN /////////////////////////////////////////////////////////////////////////////// void SkMergeImageFilter::initAllocModes() { - if (numInputs()) { - size_t size = sizeof(uint8_t) * numInputs(); + int inputCount = countInputs(); + if (inputCount) { + size_t size = sizeof(uint8_t) * inputCount; if (size <= sizeof(fStorage)) { fModes = SkTCast<uint8_t*>(fStorage); } else { @@ -116,7 +117,8 @@ void SkMergeImageFilter::initAllocModes() { void SkMergeImageFilter::initModes(const SkXfermode::Mode modes[]) { if (modes) { this->initAllocModes(); - for (int i = 0; i < numInputs(); ++i) { + int inputCount = countInputs(); + for (int i = 0; i < inputCount; ++i) { fModes[i] = SkToU8(modes[i]); } } else { @@ -148,13 +150,14 @@ SkMergeImageFilter::~SkMergeImageFilter() { bool SkMergeImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) { - if (numInputs() < 1) { + if (countInputs() < 1) { return false; } SkIRect totalBounds; - for (int i = 0; i < numInputs(); ++i) { + int inputCount = countInputs(); + for (int i = 0; i < inputCount; ++i) { SkImageFilter* filter = getInput(i); SkIRect r; if (filter) { @@ -180,7 +183,7 @@ bool SkMergeImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, bool SkMergeImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm, SkBitmap* result, SkIPoint* loc) { - if (numInputs() < 1) { + if (countInputs() < 1) { return false; } @@ -201,7 +204,8 @@ bool SkMergeImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src, OwnDeviceCanvas canvas(dst); SkPaint paint; - for (int i = 0; i < numInputs(); ++i) { + int inputCount = countInputs(); + for (int i = 0; i < inputCount; ++i) { SkBitmap tmp; const SkBitmap* srcPtr; SkIPoint pos = *loc; @@ -233,7 +237,7 @@ void SkMergeImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const { buffer.writeBool(fModes != NULL); if (fModes) { - buffer.writeByteArray(fModes, numInputs() * sizeof(fModes[0])); + buffer.writeByteArray(fModes, countInputs() * sizeof(fModes[0])); } } @@ -241,7 +245,7 @@ SkMergeImageFilter::SkMergeImageFilter(SkFlattenableReadBuffer& buffer) : INHERI bool hasModes = buffer.readBool(); if (hasModes) { this->initAllocModes(); - SkASSERT(buffer.getArrayCount() == numInputs() * sizeof(fModes[0])); + SkASSERT(buffer.getArrayCount() == countInputs() * sizeof(fModes[0])); buffer.readByteArray(fModes); } else { fModes = 0; |