diff options
-rw-r--r-- | gm/colorfilterimagefilter.cpp | 134 | ||||
-rw-r--r-- | gyp/gmslides.gypi | 1 | ||||
-rw-r--r-- | include/core/SkImageFilter.h | 32 | ||||
-rwxr-xr-x | include/effects/SkColorFilterImageFilter.h | 2 | ||||
-rw-r--r-- | src/core/SkImageFilter.cpp | 30 | ||||
-rwxr-xr-x | src/effects/SkColorFilterImageFilter.cpp | 79 | ||||
-rwxr-xr-x | src/effects/SkTestImageFilters.cpp | 22 |
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; |