aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar senorblanco@chromium.org <senorblanco@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-10-12 19:14:06 +0000
committerGravatar senorblanco@chromium.org <senorblanco@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-10-12 19:14:06 +0000
commit8d21f6c7a9d0cf4f87d77c235c6da7203620c7e5 (patch)
treedf28bf3f40b4f9327736cc0166d0d964c32fe463 /src
parent3bafe74a29c37761082980ed4ee9b831256bd27e (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.cpp30
-rwxr-xr-xsrc/effects/SkColorFilterImageFilter.cpp79
-rwxr-xr-xsrc/effects/SkTestImageFilters.cpp22
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;