From 32d25b6f5f4355d4c5281694034ba3a5aa2cf571 Mon Sep 17 00:00:00 2001 From: "reed@google.com" Date: Tue, 20 Dec 2011 16:19:00 +0000 Subject: [PATCH] initial impl of SkImageFilters : virtual signature will change! Do not invest too much in other subclasses until this API solidifies. git-svn-id: http://skia.googlecode.com/svn/trunk@2903 2bbb7eff-a529-9590-31e7-b0007b416f81 --- gm/testimagefilters.cpp | 117 +++++++++++ gyp/effects.gyp | 1 + gyp/gmslides.gypi | 1 + include/core/SkImageFilter.h | 24 ++- include/effects/SkTestImageFilters.h | 157 ++++++++++++++ src/core/SkDevice.cpp | 16 +- src/core/SkPaint.cpp | 21 +- src/effects/SkTestImageFilters.cpp | 396 +++++++++++++++++++++++++++++++++++ 8 files changed, 726 insertions(+), 7 deletions(-) create mode 100644 gm/testimagefilters.cpp create mode 100755 include/effects/SkTestImageFilters.h create mode 100755 src/effects/SkTestImageFilters.cpp diff --git a/gm/testimagefilters.cpp b/gm/testimagefilters.cpp new file mode 100644 index 0000000..e484e1b --- /dev/null +++ b/gm/testimagefilters.cpp @@ -0,0 +1,117 @@ +/* + * 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); + + diff --git a/gyp/effects.gyp b/gyp/effects.gyp index 4549439..39d9c8c 100644 --- a/gyp/effects.gyp +++ b/gyp/effects.gyp @@ -69,6 +69,7 @@ '../src/effects/SkPorterDuff.cpp', '../src/effects/SkRadialGradient_Table.h', '../src/effects/SkRectShape.cpp', + '../src/effects/SkTestImageFilters.cpp', '../src/effects/SkTransparentShader.cpp', ], 'direct_dependent_settings': { diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi index d20cf73..aee917d 100644 --- a/gyp/gmslides.gypi +++ b/gyp/gmslides.gypi @@ -36,6 +36,7 @@ '../gm/shapes.cpp', '../gm/strokerects.cpp', '../gm/strokes.cpp', + '../gm/testimagefilters.cpp', '../gm/texdata.cpp', '../gm/tilemodes.cpp', '../gm/tinybitmap.cpp', diff --git a/include/core/SkImageFilter.h b/include/core/SkImageFilter.h index bd4e8f5..1858791 100644 --- a/include/core/SkImageFilter.h +++ b/include/core/SkImageFilter.h @@ -23,7 +23,17 @@ struct SkPoint; * 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: @@ -41,10 +51,16 @@ 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 @@ -55,8 +71,12 @@ public: 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; diff --git a/include/effects/SkTestImageFilters.h b/include/effects/SkTestImageFilters.h new file mode 100755 index 0000000..4ec697e --- /dev/null +++ b/include/effects/SkTestImageFilters.h @@ -0,0 +1,157 @@ + +#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 diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp index 1865212..4cad466 100644 --- a/src/core/SkDevice.cpp +++ b/src/core/SkDevice.cpp @@ -7,6 +7,7 @@ */ #include "SkDevice.h" #include "SkDraw.h" +#include "SkImageFilter.h" #include "SkMetaData.h" #include "SkRect.h" @@ -353,7 +354,20 @@ void SkDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode, 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); } /////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp index ba99348..c0880b2 100644 --- a/src/core/SkPaint.cpp +++ b/src/core/SkPaint.cpp @@ -1976,17 +1976,30 @@ bool SkPaint::nothingToDraw() const { //////////// 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; } diff --git a/src/effects/SkTestImageFilters.cpp b/src/effects/SkTestImageFilters.cpp new file mode 100755 index 0000000..c974365 --- /dev/null +++ b/src/effects/SkTestImageFilters.cpp @@ -0,0 +1,396 @@ +#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 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(fStorage); + } else { + fFilters = SkTCast(sk_malloc_throw(size)); + } + fModes = hasModes ? SkTCast(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(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; +} + -- 2.7.4