--- /dev/null
+/*
+ * Copyright 2011 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 "SkColorFilter.h"
+#include "SkColorPriv.h"
+#include "SkShader.h"
+#include "SkTestImageFilters.h"
+
+#define FILTER_WIDTH SkIntToScalar(150)
+#define FILTER_HEIGHT SkIntToScalar(200)
+
+static SkImageFilter* make0() { return new SkDownSampleImageFilter(SK_Scalar1 / 5); }
+static SkImageFilter* make1() { return new SkOffsetImageFilter(SkIntToScalar(16), SkIntToScalar(16)); }
+static SkImageFilter* make2() {
+ SkColorFilter* cf = SkColorFilter::CreateModeFilter(SK_ColorBLUE,
+ SkXfermode::kSrcIn_Mode);
+ SkAutoUnref aur(cf);
+ return new SkColorFilterImageFilter(cf);
+}
+static SkImageFilter* make3() {
+ SkImageFilter* outer = new SkOffsetImageFilter(SkIntToScalar(16), SkIntToScalar(16));
+ SkImageFilter* inner = new SkDownSampleImageFilter(SK_Scalar1 / 5);
+ SkAutoUnref aur0(outer);
+ SkAutoUnref aur1(inner);
+ return new SkComposeImageFilter(outer, inner);
+}
+static SkImageFilter* make4() {
+ SkImageFilter* first = new SkOffsetImageFilter(SkIntToScalar(16), SkIntToScalar(16));
+ SkImageFilter* second = new SkDownSampleImageFilter(SK_Scalar1 / 5);
+ SkAutoUnref aur0(first);
+ SkAutoUnref aur1(second);
+ return new SkMergeImageFilter(first, second);
+}
+
+static SkImageFilter* make5() {
+ SkImageFilter* outer = new SkOffsetImageFilter(SkIntToScalar(16), SkIntToScalar(16));
+ SkImageFilter* inner = new SkDownSampleImageFilter(SK_Scalar1 / 5);
+ SkAutoUnref aur0(outer);
+ SkAutoUnref aur1(inner);
+ SkImageFilter* compose = new SkComposeImageFilter(outer, inner);
+ SkAutoUnref aur2(compose);
+
+ SkColorFilter* cf = SkColorFilter::CreateModeFilter(0x880000FF,
+ SkXfermode::kSrcIn_Mode);
+ SkAutoUnref aur3(cf);
+ SkImageFilter* blue = new SkColorFilterImageFilter(cf);
+ SkAutoUnref aur4(blue);
+
+ return new SkMergeImageFilter(compose, blue);
+}
+
+static void draw0(SkCanvas* canvas) {
+ SkPaint p;
+ p.setAntiAlias(true);
+ SkRect r = SkRect::MakeWH(FILTER_WIDTH, FILTER_HEIGHT);
+ r.inset(SK_Scalar1, SK_Scalar1);
+ p.setColor(SK_ColorRED);
+ canvas->drawOval(r, p);
+}
+
+class TestImageFiltersGM : public skiagm::GM {
+public:
+ TestImageFiltersGM () {}
+
+protected:
+
+ virtual SkString onShortName() {
+ return SkString("testimagefilters");
+ }
+
+ virtual SkISize onISize() { return SkISize::Make(640, 480); }
+
+ virtual void onDraw(SkCanvas* canvas) {
+ static SkImageFilter* (*gFilterProc[])() = {
+ make0, make1, make2, make3, make4, make5
+ };
+
+ const SkRect bounds = SkRect::MakeWH(FILTER_WIDTH, FILTER_HEIGHT);
+
+ const SkScalar dx = bounds.width() * 8 / 7;
+ const SkScalar dy = bounds.height() * 8 / 7;
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gFilterProc); ++i) {
+ int ix = i % 3;
+ int iy = i / 3;
+
+ SkPaint paint;
+ paint.setImageFilter(gFilterProc[i]())->unref();
+
+ SkAutoCanvasRestore acr(canvas, true);
+ canvas->translate(ix * dx, iy * dy);
+
+ SkPaint p;
+ p.setColor(0xFFCCCCCC);
+ canvas->drawRect(bounds, p);
+
+ canvas->saveLayer(&bounds, &paint);
+ draw0(canvas);
+ }
+ }
+
+private:
+ typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static skiagm::GM* MyFactory(void*) { return new TestImageFiltersGM; }
+static skiagm::GMRegistry reg(MyFactory);
+
+
'../src/effects/SkPorterDuff.cpp',
'../src/effects/SkRadialGradient_Table.h',
'../src/effects/SkRectShape.cpp',
+ '../src/effects/SkTestImageFilters.cpp',
'../src/effects/SkTransparentShader.cpp',
],
'direct_dependent_settings': {
'../gm/shapes.cpp',
'../gm/strokerects.cpp',
'../gm/strokes.cpp',
+ '../gm/testimagefilters.cpp',
'../gm/texdata.cpp',
'../gm/tilemodes.cpp',
'../gm/tinybitmap.cpp',
* then be handed to the imagefilter, who in turn creates a new bitmap which
* is what will finally be drawn to the device (using the original xfermode).
*
- * If the imagefilter returns false, nothing is drawn.
+ * THIS SIGNATURE IS TEMPORARY
+ *
+ * There are several weaknesses in this function signature:
+ * 1. Does not expose the destination/target device, so filters that can draw
+ * directly to it are unable to take advantage of that optimization.
+ * 2. Does not expose a way to create a "compabitible" image (i.e. gpu -> gpu)
+ * 3. As with #1, the filter is unable to "read" the dest (which would be slow)
+ *
+ * Therefore, we should not create any real dependencies on this API yet -- it
+ * is being checked in as a check-point so we can explore these and other
+ * considerations.
*/
class SK_API SkImageFilter : public SkFlattenable {
public:
* If the result image cannot be created, return false, in which case both
* the result and offset parameters will be ignored by the caller.
*/
- bool filterImage(const SkBitmap& src, const SkMatrix&,
+ bool filterImage(const SkBitmap& src, const SkMatrix& ctm,
SkBitmap* result, SkIPoint* offset);
/**
+ * Given the src bounds of an image, this returns the bounds of the result
+ * image after the filter has been applied.
+ */
+ bool filterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst);
+
+ /**
* Experimental.
*
* If the filter can be expressed as a gaussian-blur, return true and
protected:
SkImageFilter() {}
explicit SkImageFilter(SkFlattenableReadBuffer& rb) : INHERITED(rb) {}
+
+ // Default impl returns false
virtual bool onFilterImage(const SkBitmap& src, const SkMatrix&,
SkBitmap* result, SkIPoint* offset);
+ // Default impl copies src into dst and returns true
+ virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*);
private:
typedef SkFlattenable INHERITED;
--- /dev/null
+
+#ifndef _SkTestImageFilters_h
+#define _SkTestImageFilters_h
+
+#include "SkImageFilter.h"
+
+class SkOffsetImageFilter : public SkImageFilter {
+public:
+ SkOffsetImageFilter(SkScalar dx, SkScalar dy) {
+ fOffset.set(dx, dy);
+ }
+
+ static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+ return SkNEW_ARGS(SkOffsetImageFilter, (buffer));
+ }
+
+protected:
+ SkOffsetImageFilter(SkFlattenableReadBuffer& buffer);
+
+ virtual bool onFilterImage(const SkBitmap& src, const SkMatrix&,
+ SkBitmap* result, SkIPoint* loc) SK_OVERRIDE;
+ virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) SK_OVERRIDE;
+ // overrides from SkFlattenable
+ virtual void flatten(SkFlattenableWriteBuffer&) SK_OVERRIDE;
+ virtual Factory getFactory() SK_OVERRIDE;
+
+private:
+ SkVector fOffset;
+
+ typedef SkImageFilter INHERITED;
+};
+
+class SkComposeImageFilter : public SkImageFilter {
+public:
+ SkComposeImageFilter(SkImageFilter* outer, SkImageFilter* inner) {
+ fOuter = outer;
+ fInner = inner;
+ SkSafeRef(outer);
+ SkSafeRef(inner);
+ }
+ virtual ~SkComposeImageFilter();
+
+ static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+ return SkNEW_ARGS(SkComposeImageFilter, (buffer));
+ }
+
+protected:
+ SkComposeImageFilter(SkFlattenableReadBuffer& buffer);
+
+ virtual bool onFilterImage(const SkBitmap& src, const SkMatrix&,
+ SkBitmap* result, SkIPoint* loc) SK_OVERRIDE;
+ virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) SK_OVERRIDE;
+ // overrides from SkFlattenable
+ virtual void flatten(SkFlattenableWriteBuffer&) SK_OVERRIDE;
+ virtual Factory getFactory() SK_OVERRIDE;
+
+private:
+ SkImageFilter* fOuter;
+ SkImageFilter* fInner;
+
+ typedef SkImageFilter INHERITED;
+};
+
+#include "SkXfermode.h"
+
+class SkMergeImageFilter : public SkImageFilter {
+public:
+ SkMergeImageFilter(SkImageFilter* first, SkImageFilter* second,
+ SkXfermode::Mode = SkXfermode::kSrcOver_Mode);
+ SkMergeImageFilter(SkImageFilter* const filters[], int count,
+ const SkXfermode::Mode modes[] = NULL);
+ virtual ~SkMergeImageFilter();
+
+ static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+ return SkNEW_ARGS(SkMergeImageFilter, (buffer));
+ }
+
+protected:
+ SkMergeImageFilter(SkFlattenableReadBuffer& buffer);
+
+ virtual bool onFilterImage(const SkBitmap& src, const SkMatrix&,
+ SkBitmap* result, SkIPoint* loc) SK_OVERRIDE;
+ virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) SK_OVERRIDE;
+ // overrides from SkFlattenable
+ virtual void flatten(SkFlattenableWriteBuffer&) SK_OVERRIDE;
+ virtual Factory getFactory() SK_OVERRIDE;
+
+private:
+ SkImageFilter** fFilters;
+ uint8_t* fModes; // SkXfermode::Mode
+ int fCount;
+
+ // private storage, to avoid dynamically allocating storage for our copy
+ // of the filters and modes (unless fCount is so large we can't fit).
+ intptr_t fStorage[16];
+
+ void initAlloc(int count, bool hasModes);
+ void init(SkImageFilter* const [], int count, const SkXfermode::Mode []);
+
+ typedef SkImageFilter INHERITED;
+};
+
+class SkColorFilter;
+
+class SkColorFilterImageFilter : public SkImageFilter {
+public:
+ SkColorFilterImageFilter(SkColorFilter* cf) : fColorFilter(cf) {
+ SkSafeRef(cf);
+ }
+ virtual ~SkColorFilterImageFilter();
+
+ static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+ return SkNEW_ARGS(SkColorFilterImageFilter, (buffer));
+ }
+
+protected:
+ SkColorFilterImageFilter(SkFlattenableReadBuffer& buffer);
+
+ virtual bool onFilterImage(const SkBitmap& src, const SkMatrix&,
+ SkBitmap* result, SkIPoint* loc) SK_OVERRIDE;
+ // overrides from SkFlattenable
+ virtual void flatten(SkFlattenableWriteBuffer&) SK_OVERRIDE;
+ virtual Factory getFactory() SK_OVERRIDE;
+
+private:
+ SkColorFilter* fColorFilter;
+
+ typedef SkImageFilter INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Fun mode that scales down (only) and then scales back up to look pixelated
+class SkDownSampleImageFilter : public SkImageFilter {
+public:
+ SkDownSampleImageFilter(SkScalar scale) : fScale(scale) {}
+
+ static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+ return SkNEW_ARGS(SkDownSampleImageFilter, (buffer));
+ }
+
+protected:
+ SkDownSampleImageFilter(SkFlattenableReadBuffer& buffer);
+
+ virtual bool onFilterImage(const SkBitmap& src, const SkMatrix&,
+ SkBitmap* result, SkIPoint* loc) SK_OVERRIDE;
+ // overrides from SkFlattenable
+ virtual void flatten(SkFlattenableWriteBuffer&) SK_OVERRIDE;
+ virtual Factory getFactory() SK_OVERRIDE;
+
+private:
+ SkScalar fScale;
+
+ typedef SkImageFilter INHERITED;
+};
+
+#endif
*/
#include "SkDevice.h"
#include "SkDraw.h"
+#include "SkImageFilter.h"
#include "SkMetaData.h"
#include "SkRect.h"
void SkDevice::drawDevice(const SkDraw& draw, SkDevice* device,
int x, int y, const SkPaint& paint) {
- draw.drawSprite(device->accessBitmap(false), x, y, paint);
+ SkBitmap output;
+ const SkBitmap* src = &device->accessBitmap(false);
+ SkImageFilter* filter = paint.getImageFilter();
+
+ if (filter) {
+ SkIPoint loc;
+ loc.set(x, y);
+ if (filter->filterImage(*src, *draw.fMatrix, &output, &loc)) {
+ src = &output;
+ x = loc.fX;
+ y = loc.fY;
+ }
+ }
+ draw.drawSprite(*src, x, y, paint);
}
///////////////////////////////////////////////////////////////////////////////
//////////// Move these to their own file soon.
bool SkImageFilter::filterImage(const SkBitmap& src, const SkMatrix& matrix,
- SkBitmap* result, SkIPoint* offset) {
+ SkBitmap* result, SkIPoint* loc) {
SkASSERT(result);
- SkASSERT(offset);
- return this->onFilterImage(src, matrix, result, offset);
+ SkASSERT(loc);
+ return this->onFilterImage(src, matrix, result, loc);
+}
+
+bool SkImageFilter::filterBounds(const SkIRect& src, const SkMatrix& ctm,
+ SkIRect* dst) {
+ SkASSERT(&src);
+ SkASSERT(dst);
+ return this->onFilterBounds(src, ctm, dst);
}
bool SkImageFilter::onFilterImage(const SkBitmap& src, const SkMatrix&,
- SkBitmap* result, SkIPoint* offset) {
+ SkBitmap*, SkIPoint*) {
return false;
}
+bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
+ SkIRect* dst) {
+ *dst = src;
+ return true;
+}
+
bool SkImageFilter::asABlur(SkSize* sigma) const {
return false;
}
--- /dev/null
+#include "SkTestImageFilters.h"
+#include "SkCanvas.h"
+
+bool SkOffsetImageFilter::onFilterImage(const SkBitmap& src,
+ const SkMatrix& matrix,
+ SkBitmap* result,
+ SkIPoint* loc) {
+ SkVector vec;
+ matrix.mapVectors(&vec, &fOffset, 1);
+
+ loc->fX += SkScalarRoundToInt(vec.fX);
+ loc->fY += SkScalarRoundToInt(vec.fY);
+ *result = src;
+ return true;
+}
+
+bool SkOffsetImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
+ SkIRect* dst) {
+ SkVector vec;
+ ctm.mapVectors(&vec, &fOffset, 1);
+
+ *dst = src;
+ dst->offset(SkScalarRoundToInt(vec.fX), SkScalarRoundToInt(vec.fY));
+ return true;
+}
+
+void SkOffsetImageFilter::flatten(SkFlattenableWriteBuffer& buffer) {
+ this->INHERITED::flatten(buffer);
+ buffer.writeScalar(fOffset.x());
+ buffer.writeScalar(fOffset.y());
+}
+
+SkOffsetImageFilter::SkOffsetImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+ fOffset.fX = buffer.readScalar();
+ fOffset.fY = buffer.readScalar();
+}
+
+SkFlattenable::Factory SkOffsetImageFilter::getFactory() {
+ return CreateProc;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkComposeImageFilter::~SkComposeImageFilter() {
+ SkSafeUnref(fInner);
+ SkSafeUnref(fOuter);
+}
+
+bool SkComposeImageFilter::onFilterImage(const SkBitmap& src,
+ const SkMatrix& ctm,
+ SkBitmap* result,
+ SkIPoint* loc) {
+ if (!fOuter && !fInner) {
+ return false;
+ }
+
+ if (!fOuter || !fInner) {
+ return (fOuter ? fOuter : fInner)->filterImage(src, ctm, result, loc);
+ }
+
+ SkBitmap tmp;
+ return fInner->filterImage(src, ctm, &tmp, loc) &&
+ fOuter->filterImage(tmp, ctm, result, loc);
+}
+
+bool SkComposeImageFilter::onFilterBounds(const SkIRect& src,
+ const SkMatrix& ctm,
+ SkIRect* dst) {
+ if (!fOuter && !fInner) {
+ return false;
+ }
+
+ if (!fOuter || !fInner) {
+ return (fOuter ? fOuter : fInner)->filterBounds(src, ctm, dst);
+ }
+
+ SkIRect tmp;
+ return fInner->filterBounds(src, ctm, &tmp) &&
+ fOuter->filterBounds(tmp, ctm, dst);
+}
+
+void SkComposeImageFilter::flatten(SkFlattenableWriteBuffer& buffer) {
+ this->INHERITED::flatten(buffer);
+
+ buffer.writeFlattenable(fOuter);
+ buffer.writeFlattenable(fInner);
+}
+
+SkComposeImageFilter::SkComposeImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+ fOuter = (SkImageFilter*)buffer.readFlattenable();
+ fInner = (SkImageFilter*)buffer.readFlattenable();
+}
+
+SkFlattenable::Factory SkComposeImageFilter::getFactory() {
+ return CreateProc;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+template <typename T> T* SkSafeRefReturn(T* obj) {
+ SkSafeRef(obj);
+ return obj;
+}
+
+void SkMergeImageFilter::initAlloc(int count, bool hasModes) {
+ if (count < 1) {
+ fFilters = NULL;
+ fModes = NULL;
+ fCount = 0;
+ } else {
+ int modeCount = hasModes ? count : 0;
+ size_t size = sizeof(SkImageFilter*) * count + sizeof(uint8_t) * modeCount;
+ if (size <= sizeof(fStorage)) {
+ fFilters = SkTCast<SkImageFilter**>(fStorage);
+ } else {
+ fFilters = SkTCast<SkImageFilter**>(sk_malloc_throw(size));
+ }
+ fModes = hasModes ? SkTCast<uint8_t*>(fFilters + count) : NULL;
+ fCount = count;
+ }
+}
+
+void SkMergeImageFilter::init(SkImageFilter* const filters[], int count,
+ const SkXfermode::Mode modes[]) {
+ this->initAlloc(count, !!modes);
+ for (int i = 0; i < count; ++i) {
+ fFilters[i] = SkSafeRefReturn(filters[i]);
+ if (modes) {
+ fModes[i] = SkToU8(modes[i]);
+ }
+ }
+}
+
+SkMergeImageFilter::SkMergeImageFilter(SkImageFilter* first, SkImageFilter* second,
+ SkXfermode::Mode mode) {
+ SkImageFilter* filters[] = { first, second };
+ SkXfermode::Mode modes[] = { mode, mode };
+ this->init(filters, 2, SkXfermode::kSrcOver_Mode == mode ? NULL : modes);
+}
+
+SkMergeImageFilter::SkMergeImageFilter(SkImageFilter* const filters[], int count,
+ const SkXfermode::Mode modes[]) {
+ this->init(filters, count, modes);
+}
+
+SkMergeImageFilter::~SkMergeImageFilter() {
+ for (int i = 0; i < fCount; ++i) {
+ SkSafeUnref(fFilters[i]);
+ }
+
+ if (fFilters != SkTCast<SkImageFilter**>(fStorage)) {
+ sk_free(fFilters);
+ // fModes is allocated in the same block as fFilters, so no need to
+ // separately free it.
+ }
+}
+
+bool SkMergeImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
+ SkIRect* dst) {
+ if (fCount < 1) {
+ return false;
+ }
+
+ SkIRect totalBounds;
+
+ for (int i = 0; i < fCount; ++i) {
+ SkImageFilter* filter = fFilters[i];
+ SkIRect r;
+ if (filter) {
+ if (!filter->filterBounds(src, ctm, &r)) {
+ return false;
+ }
+ } else {
+ r = src;
+ }
+ if (0 == i) {
+ totalBounds = r;
+ } else {
+ totalBounds.join(r);
+ }
+ }
+
+ // don't modify dst until now, so we don't accidentally change it in the
+ // loop, but then return false on the next filter.
+ *dst = totalBounds;
+ return true;
+}
+
+bool SkMergeImageFilter::onFilterImage(const SkBitmap& src, const SkMatrix& ctm,
+ SkBitmap* result, SkIPoint* loc) {
+ if (fCount < 1) {
+ return false;
+ }
+
+ const SkIRect srcBounds = SkIRect::MakeXYWH(loc->x(), loc->y(),
+ src.width(), src.height());
+ SkIRect bounds;
+ if (!this->filterBounds(srcBounds, ctm, &bounds)) {
+ return false;
+ }
+
+ const int x0 = bounds.left();
+ const int y0 = bounds.top();
+
+ SkBitmap dst;
+ dst.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(), bounds.height());
+ dst.allocPixels();
+ dst.eraseColor(0);
+
+ SkCanvas canvas(dst);
+ SkPaint paint;
+
+ for (int i = 0; i < fCount; ++i) {
+ SkBitmap tmp;
+ const SkBitmap* srcPtr;
+ SkIPoint pos = *loc;
+ SkImageFilter* filter = fFilters[i];
+ if (filter) {
+ if (!filter->filterImage(src, ctm, &tmp, &pos)) {
+ return false;
+ }
+ srcPtr = &tmp;
+ } else {
+ srcPtr = &src;
+ }
+
+ if (fModes) {
+ paint.setXfermodeMode((SkXfermode::Mode)fModes[i]);
+ } else {
+ paint.setXfermode(NULL);
+ }
+ canvas.drawSprite(*srcPtr, pos.x() - x0, pos.y() - y0, &paint);
+ }
+
+ loc->set(bounds.left(), bounds.top());
+ result->swap(dst);
+ return true;
+}
+
+void SkMergeImageFilter::flatten(SkFlattenableWriteBuffer& buffer) {
+ this->INHERITED::flatten(buffer);
+
+ int storedCount = fCount;
+ if (fModes) {
+ // negative count signals we have modes
+ storedCount = -storedCount;
+ }
+ buffer.write32(storedCount);
+
+ if (fCount) {
+ for (int i = 0; i < fCount; ++i) {
+ buffer.writeFlattenable(fFilters[i]);
+ }
+ if (fModes) {
+ buffer.write(fModes, fCount * sizeof(fModes[0]));
+ }
+ }
+}
+
+SkMergeImageFilter::SkMergeImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+ int storedCount = buffer.readS32();
+ this->initAlloc(SkAbs32(storedCount), storedCount < 0);
+
+ for (int i = 0; i < fCount; ++i) {
+ fFilters[i] = (SkImageFilter*)buffer.readFlattenable();
+ }
+
+ if (fModes) {
+ SkASSERT(storedCount < 0);
+ buffer.read(fModes, fCount * sizeof(fModes[0]));
+ } else {
+ SkASSERT(storedCount >= 0);
+ }
+}
+
+SkFlattenable::Factory SkMergeImageFilter::getFactory() {
+ return CreateProc;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkColorFilter.h"
+
+SkColorFilterImageFilter::~SkColorFilterImageFilter() {
+ SkSafeUnref(fColorFilter);
+}
+
+bool SkColorFilterImageFilter::onFilterImage(const SkBitmap& src,
+ const SkMatrix& matrix,
+ SkBitmap* result,
+ SkIPoint* loc) {
+ if (SkBitmap::kARGB_8888_Config != src.config()) {
+ return false;
+ }
+
+ SkColorFilter* cf = fColorFilter;
+ if (NULL == cf) {
+ *result = src;
+ return true;
+ }
+
+ SkAutoLockPixels alpsrc(src);
+ if (!src.readyToDraw()) {
+ return false;
+ }
+
+ SkBitmap dst(src);
+ dst.allocPixels();
+ if (!dst.readyToDraw()) {
+ return false;
+ }
+
+ for (int y = 0; y < src.height(); ++y) {
+ cf->filterSpan(src.getAddr32(0, y), src.width(), dst.getAddr32(0, y));
+ }
+
+ result->swap(dst);
+ return true;
+}
+
+void SkColorFilterImageFilter::flatten(SkFlattenableWriteBuffer& buffer) {
+ this->INHERITED::flatten(buffer);
+
+ buffer.writeFlattenable(fColorFilter);
+}
+
+SkColorFilterImageFilter::SkColorFilterImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+ fColorFilter = (SkColorFilter*)buffer.readFlattenable();
+}
+
+SkFlattenable::Factory SkColorFilterImageFilter::getFactory() {
+ return CreateProc;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkDownSampleImageFilter::onFilterImage(const SkBitmap& src,
+ const SkMatrix& matrix,
+ SkBitmap* result, SkIPoint*) {
+ SkScalar scale = fScale;
+ if (scale > SK_Scalar1 || scale <= 0) {
+ return false;
+ }
+
+ int dstW = SkScalarRoundToInt(src.width() * scale);
+ int dstH = SkScalarRoundToInt(src.height() * scale);
+ if (dstW < 1) {
+ dstW = 1;
+ }
+ if (dstH < 1) {
+ dstH = 1;
+ }
+
+ SkBitmap dst;
+ dst.setConfig(SkBitmap::kARGB_8888_Config, dstW, dstH);
+ dst.allocPixels();
+ dst.eraseColor(0);
+
+ // downsample
+ {
+ SkPaint paint;
+ paint.setFilterBitmap(true);
+
+ SkCanvas canvas(dst);
+ canvas.scale(scale, scale);
+ canvas.drawBitmap(src, 0, 0, &paint);
+ }
+
+ result->setConfig(SkBitmap::kARGB_8888_Config, src.width(), src.height());
+ result->allocPixels();
+ result->eraseColor(0);
+
+ // upscale
+ {
+ SkRect r = SkRect::MakeWH(SkIntToScalar(result->width()),
+ SkIntToScalar(result->height()));
+ SkCanvas canvas(*result);
+ canvas.drawBitmapRect(dst, NULL, r, NULL);
+ }
+ return true;
+}
+
+void SkDownSampleImageFilter::flatten(SkFlattenableWriteBuffer& buffer) {
+ this->INHERITED::flatten(buffer);
+
+ buffer.writeScalar(fScale);
+}
+
+SkDownSampleImageFilter::SkDownSampleImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+ fScale = buffer.readScalar();
+}
+
+SkFlattenable::Factory SkDownSampleImageFilter::getFactory() {
+ return CreateProc;
+}
+