aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gm/colorfilterimagefilter.cpp134
-rw-r--r--gyp/gmslides.gypi1
-rw-r--r--include/core/SkImageFilter.h32
-rwxr-xr-xinclude/effects/SkColorFilterImageFilter.h2
-rw-r--r--src/core/SkImageFilter.cpp30
-rwxr-xr-xsrc/effects/SkColorFilterImageFilter.cpp79
-rwxr-xr-xsrc/effects/SkTestImageFilters.cpp22
7 files changed, 266 insertions, 34 deletions
diff --git a/gm/colorfilterimagefilter.cpp b/gm/colorfilterimagefilter.cpp
new file mode 100644
index 0000000000..7963c8af73
--- /dev/null
+++ b/gm/colorfilterimagefilter.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+#include "SkCanvas.h"
+#include "SkColorMatrixFilter.h"
+#include "SkColorPriv.h"
+#include "SkShader.h"
+
+#include "SkBlurImageFilter.h"
+#include "SkColorFilterImageFilter.h"
+
+#define FILTER_WIDTH SkIntToScalar(30)
+#define FILTER_HEIGHT SkIntToScalar(30)
+#define MARGIN SkIntToScalar(10)
+
+static SkImageFilter* make_blur(float amount, SkImageFilter* input = NULL) {
+ return new SkBlurImageFilter(amount, amount, input);
+}
+
+static SkImageFilter* make_brightness(float amount, SkImageFilter* input = NULL) {
+ SkScalar amount255 = SkScalarMul(SkFloatToScalar(amount), SkIntToScalar(255));
+ SkScalar matrix[20] = { 1, 0, 0, 0, amount255,
+ 0, 1, 0, 0, amount255,
+ 0, 0, 1, 0, amount255,
+ 0, 0, 0, 1, 0 };
+ SkAutoTUnref<SkColorFilter> filter(new SkColorMatrixFilter(matrix));
+ return new SkColorFilterImageFilter(filter, input);
+}
+
+static SkImageFilter* make_grayscale(SkImageFilter* input = NULL) {
+ SkScalar matrix[20];
+ memset(matrix, 0, 20 * sizeof(SkScalar));
+ matrix[0] = matrix[5] = matrix[10] = SkFloatToScalar(0.2126f);
+ matrix[1] = matrix[6] = matrix[11] = SkFloatToScalar(0.7152f);
+ matrix[2] = matrix[7] = matrix[12] = SkFloatToScalar(0.0722f);
+ matrix[18] = SkFloatToScalar(1.0f);
+ SkAutoTUnref<SkColorFilter> filter(new SkColorMatrixFilter(matrix));
+ return new SkColorFilterImageFilter(filter, input);
+}
+
+static SkImageFilter* make_mode_blue(SkImageFilter* input = NULL) {
+ SkAutoTUnref<SkColorFilter> filter(
+ SkColorFilter::CreateModeFilter(SK_ColorBLUE, SkXfermode::kSrcIn_Mode));
+ return new SkColorFilterImageFilter(filter, input);
+}
+
+class ColorFilterImageFilterGM : public skiagm::GM {
+public:
+ ColorFilterImageFilterGM () {}
+
+protected:
+
+ virtual SkString onShortName() {
+ return SkString("colorfilterimagefilter");
+ }
+
+ void drawClippedRect(SkCanvas* canvas, const SkRect& r, const SkPaint& paint, float outset = 0.0f) {
+ canvas->save();
+ SkRect clip(r);
+ clip.outset(SkFloatToScalar(outset), SkFloatToScalar(outset));
+ canvas->clipRect(clip);
+ canvas->drawRect(r, paint);
+ canvas->restore();
+ }
+
+ virtual SkISize onISize() { return SkISize::Make(400, 100); }
+
+ virtual void onDraw(SkCanvas* canvas) {
+
+ SkRect r = SkRect::MakeWH(FILTER_WIDTH, FILTER_HEIGHT);
+ SkPaint paint;
+ paint.setColor(SK_ColorRED);
+ canvas->save();
+ for (float brightness = -1.0f; brightness <= 1.0f; brightness += 0.2f) {
+ SkAutoTUnref<SkImageFilter> dim(make_brightness(-brightness));
+ SkAutoTUnref<SkImageFilter> bright(make_brightness(brightness, dim));
+ paint.setImageFilter(bright);
+ drawClippedRect(canvas, r, paint);
+ canvas->translate(FILTER_WIDTH + MARGIN, 0);
+ }
+ canvas->restore();
+ canvas->translate(0, FILTER_HEIGHT + MARGIN);
+ {
+ SkAutoTUnref<SkImageFilter> brightness(make_brightness(0.9f));
+ SkAutoTUnref<SkImageFilter> grayscale(make_grayscale(brightness));
+ paint.setImageFilter(grayscale);
+ drawClippedRect(canvas, r, paint);
+ canvas->translate(FILTER_WIDTH + MARGIN, 0);
+ }
+ {
+ SkAutoTUnref<SkImageFilter> grayscale(make_grayscale());
+ SkAutoTUnref<SkImageFilter> brightness(make_brightness(0.9f, grayscale));
+ paint.setImageFilter(brightness);
+ drawClippedRect(canvas, r, paint);
+ canvas->translate(FILTER_WIDTH + MARGIN, 0);
+ }
+ {
+ SkAutoTUnref<SkImageFilter> blue(make_mode_blue());
+ SkAutoTUnref<SkImageFilter> brightness(make_brightness(1.0f, blue));
+ paint.setImageFilter(brightness);
+ drawClippedRect(canvas, r, paint);
+ canvas->translate(FILTER_WIDTH + MARGIN, 0);
+ }
+ {
+ SkAutoTUnref<SkImageFilter> brightness(make_brightness(1.0f));
+ SkAutoTUnref<SkImageFilter> blue(make_mode_blue(brightness));
+ paint.setImageFilter(blue);
+ drawClippedRect(canvas, r, paint);
+ canvas->translate(FILTER_WIDTH + MARGIN, 0);
+ }
+ {
+ SkAutoTUnref<SkImageFilter> blur(make_blur(3.0f));
+ SkAutoTUnref<SkImageFilter> brightness(make_brightness(0.5f, blur));
+ paint.setImageFilter(brightness);
+ drawClippedRect(canvas, r, paint, 3);
+ canvas->translate(FILTER_WIDTH + MARGIN, 0);
+ }
+ }
+
+private:
+ typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static skiagm::GM* MyFactory(void*) { return new ColorFilterImageFilterGM; }
+static skiagm::GMRegistry reg(MyFactory);
+
+
diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi
index e922713e7f..313eaf609c 100644
--- a/gyp/gmslides.gypi
+++ b/gyp/gmslides.gypi
@@ -13,6 +13,7 @@
'../gm/blend.cpp',
'../gm/blurs.cpp',
'../gm/circles.cpp',
+ '../gm/colorfilterimagefilter.cpp',
'../gm/colormatrix.cpp',
'../gm/complexclip.cpp',
'../gm/complexclip2.cpp',
diff --git a/include/core/SkImageFilter.h b/include/core/SkImageFilter.h
index f035bf9d37..596da242ac 100644
--- a/include/core/SkImageFilter.h
+++ b/include/core/SkImageFilter.h
@@ -11,6 +11,7 @@
#include "SkFlattenable.h"
class SkBitmap;
+class SkColorFilter;
class SkDevice;
class SkMatrix;
struct SkIPoint;
@@ -108,13 +109,34 @@ public:
*/
virtual GrTexture* onFilterImageGPU(Proxy*, GrTexture* texture, const SkRect& rect);
+ /**
+ * Returns this image filter as a color filter if possible,
+ * NULL otherwise.
+ */
+ virtual SkColorFilter* asColorFilter() const;
+
+ /**
+ * Returns the number of inputs this filter will accept (some inputs can
+ * be NULL).
+ */
+ int countInputs() const { return fInputCount; }
+
+ /**
+ * Returns the input filter at a given index, or NULL if no input is
+ * connected. The indices used are filter-specific.
+ */
+ SkImageFilter* getInput(int i) const {
+ SkASSERT(i < fInputCount);
+ return fInputs[i];
+ }
+
protected:
- SkImageFilter(int numInputs, SkImageFilter** inputs);
+ SkImageFilter(int inputCount, SkImageFilter** inputs);
- // The ... represents numInputs SkImageFilter pointers, upon which this
+ // The ... represents inputCount SkImageFilter pointers, upon which this
// constructor will call SkSafeRef(). This is the same behaviour as
// the SkImageFilter(int, SkImageFilter**) constructor above.
- explicit SkImageFilter(int numInputs, ...);
+ explicit SkImageFilter(int inputCount, ...);
virtual ~SkImageFilter();
@@ -128,8 +150,6 @@ protected:
// Default impl copies src into dst and returns true
virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*);
- int numInputs() const { return fNumInputs; }
- SkImageFilter* getInput(int i) const { SkASSERT(i < fNumInputs); return fInputs[i]; }
// Return the result of processing the given input, or the source bitmap
// if we have no connected input at that index.
SkBitmap getInputResult(int index, Proxy*, const SkBitmap& src, const SkMatrix&,
@@ -137,7 +157,7 @@ protected:
private:
typedef SkFlattenable INHERITED;
- int fNumInputs;
+ int fInputCount;
SkImageFilter** fInputs;
};
diff --git a/include/effects/SkColorFilterImageFilter.h b/include/effects/SkColorFilterImageFilter.h
index f5192918f3..e9124f9123 100755
--- a/include/effects/SkColorFilterImageFilter.h
+++ b/include/effects/SkColorFilterImageFilter.h
@@ -26,6 +26,8 @@ protected:
virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
SkBitmap* result, SkIPoint* loc) SK_OVERRIDE;
+ virtual SkColorFilter* asColorFilter() const SK_OVERRIDE;
+
private:
SkColorFilter* fColorFilter;
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;