Implementation of the displacement effect (both CPU and GPU)
authorsugoi@google.com <sugoi@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 15 Jan 2013 15:40:19 +0000 (15:40 +0000)
committersugoi@google.com <sugoi@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 15 Jan 2013 15:40:19 +0000 (15:40 +0000)
TEST=Added new GM called "displacement"
Review URL: https://codereview.appspot.com/7058075

git-svn-id: http://skia.googlecode.com/svn/trunk@7182 2bbb7eff-a529-9590-31e7-b0007b416f81

gm/displacement.cpp [new file with mode: 0644]
gyp/effects.gypi
gyp/gmslides.gypi
include/effects/SkDisplacementMapEffect.h [new file with mode: 0644]
src/effects/SkDisplacementMapEffect.cpp [new file with mode: 0644]
src/ports/SkGlobalInitialization_default.cpp

diff --git a/gm/displacement.cpp b/gm/displacement.cpp
new file mode 100644 (file)
index 0000000..342806d
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2013 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 "SkDisplacementMapEffect.h"
+#include "SkBitmapSource.h"
+
+namespace skiagm {
+
+class DisplacementMapGM : public GM {
+public:
+    DisplacementMapGM() : fInitialized(false) {
+        this->setBGColor(0xFF000000);
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("displacement");
+    }
+
+    void make_bitmap() {
+        fBitmap.setConfig(SkBitmap::kARGB_8888_Config, 80, 80);
+        fBitmap.allocPixels();
+        SkDevice device(fBitmap);
+        SkCanvas canvas(&device);
+        canvas.clear(0x00000000);
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setColor(0xFF884422);
+        paint.setTextSize(SkIntToScalar(96));
+        const char* str = "g";
+        canvas.drawText(str, strlen(str), SkIntToScalar(15), SkIntToScalar(55), paint);
+    }
+
+    void make_checkerboard() {
+        fCheckerboard.setConfig(SkBitmap::kARGB_8888_Config, 80, 80);
+        fCheckerboard.allocPixels();
+        SkDevice device(fCheckerboard);
+        SkCanvas canvas(&device);
+        canvas.clear(0x00000000);
+        SkPaint darkPaint;
+        darkPaint.setColor(0xFF804020);
+        SkPaint lightPaint;
+        lightPaint.setColor(0xFF244484);
+        for (int y = 0; y < 80; y += 16) {
+          for (int x = 0; x < 80; x += 16) {
+            canvas.save();
+            canvas.translate(SkIntToScalar(x), SkIntToScalar(y));
+            canvas.drawRect(SkRect::MakeXYWH(0, 0, 8, 8), darkPaint);
+            canvas.drawRect(SkRect::MakeXYWH(8, 0, 8, 8), lightPaint);
+            canvas.drawRect(SkRect::MakeXYWH(0, 8, 8, 8), lightPaint);
+            canvas.drawRect(SkRect::MakeXYWH(8, 8, 8, 8), darkPaint);
+            canvas.restore();
+          }
+        }
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(500, 200);
+    }
+    
+    void drawClippedBitmap(SkCanvas* canvas, int x, int y, const SkPaint& paint) {
+        canvas->save();
+        canvas->clipRect(SkRect::MakeXYWH(SkIntToScalar(x), SkIntToScalar(y),
+            SkIntToScalar(fBitmap.width()), SkIntToScalar(fBitmap.height())));
+        canvas->drawBitmap(fBitmap, SkIntToScalar(x), SkIntToScalar(y), &paint);
+        canvas->restore();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        if (!fInitialized) {
+            make_bitmap();
+            make_checkerboard();
+            fInitialized = true;
+        }
+        canvas->clear(0x00000000);
+        SkPaint paint;
+        SkAutoTUnref<SkImageFilter> displ(SkNEW_ARGS(SkBitmapSource, (fCheckerboard)));
+        paint.setImageFilter(SkNEW_ARGS(SkDisplacementMapEffect,
+            (SkDisplacementMapEffect::kR_ChannelSelectorType,
+             SkDisplacementMapEffect::kG_ChannelSelectorType, 0.0f, displ)))->unref();
+        drawClippedBitmap(canvas, 0, 0, paint);
+        paint.setImageFilter(SkNEW_ARGS(SkDisplacementMapEffect,
+            (SkDisplacementMapEffect::kB_ChannelSelectorType,
+             SkDisplacementMapEffect::kA_ChannelSelectorType, 0.2f, displ)))->unref();
+        drawClippedBitmap(canvas, 100, 0, paint);
+        paint.setImageFilter(SkNEW_ARGS(SkDisplacementMapEffect,
+            (SkDisplacementMapEffect::kR_ChannelSelectorType,
+             SkDisplacementMapEffect::kB_ChannelSelectorType, 0.4f, displ)))->unref();
+        drawClippedBitmap(canvas, 200, 0, paint);
+        paint.setImageFilter(SkNEW_ARGS(SkDisplacementMapEffect,
+            (SkDisplacementMapEffect::kG_ChannelSelectorType,
+             SkDisplacementMapEffect::kA_ChannelSelectorType, 0.6f, displ)))->unref();
+        drawClippedBitmap(canvas, 300, 0, paint);
+        paint.setImageFilter(SkNEW_ARGS(SkDisplacementMapEffect,
+            (SkDisplacementMapEffect::kR_ChannelSelectorType,
+             SkDisplacementMapEffect::kA_ChannelSelectorType, 0.8f, displ)))->unref();
+        drawClippedBitmap(canvas, 400, 0, paint);
+
+        paint.setImageFilter(SkNEW_ARGS(SkDisplacementMapEffect,
+            (SkDisplacementMapEffect::kR_ChannelSelectorType,
+             SkDisplacementMapEffect::kG_ChannelSelectorType, 0.5f, displ)))->unref();
+        drawClippedBitmap(canvas, 0, 100, paint);
+        paint.setImageFilter(SkNEW_ARGS(SkDisplacementMapEffect,
+            (SkDisplacementMapEffect::kB_ChannelSelectorType,
+             SkDisplacementMapEffect::kA_ChannelSelectorType, 0.5f, displ)))->unref();
+        drawClippedBitmap(canvas, 100, 100, paint);
+        paint.setImageFilter(SkNEW_ARGS(SkDisplacementMapEffect,
+            (SkDisplacementMapEffect::kR_ChannelSelectorType,
+             SkDisplacementMapEffect::kB_ChannelSelectorType, 0.5f, displ)))->unref();
+        drawClippedBitmap(canvas, 200, 100, paint);
+        paint.setImageFilter(SkNEW_ARGS(SkDisplacementMapEffect,
+            (SkDisplacementMapEffect::kG_ChannelSelectorType,
+             SkDisplacementMapEffect::kA_ChannelSelectorType, 0.5f, displ)))->unref();
+        drawClippedBitmap(canvas, 300, 100, paint);
+        paint.setImageFilter(SkNEW_ARGS(SkDisplacementMapEffect,
+            (SkDisplacementMapEffect::kR_ChannelSelectorType,
+             SkDisplacementMapEffect::kA_ChannelSelectorType, 0.5f, displ)))->unref();
+        drawClippedBitmap(canvas, 400, 100, paint);
+    }
+
+private:
+    typedef GM INHERITED;
+    SkBitmap fBitmap, fCheckerboard;
+    bool fInitialized;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new DisplacementMapGM; }
+static GMRegistry reg(MyFactory);
+
+}
index d981cd1e01cc5910a074d21aaf31f5d7219c4ed3..b8defb4a6ade64996616b63b0645f0b8f93eee40 100644 (file)
@@ -25,6 +25,7 @@
     '<(skia_src_path)/effects/SkCornerPathEffect.cpp',
     '<(skia_src_path)/effects/SkDashPathEffect.cpp',
     '<(skia_src_path)/effects/SkDiscretePathEffect.cpp',
+    '<(skia_src_path)/effects/SkDisplacementMapEffect.cpp',
     '<(skia_src_path)/effects/SkEmbossMask.cpp',
     '<(skia_src_path)/effects/SkEmbossMask.h',
     '<(skia_src_path)/effects/SkEmbossMask_Table.h',
@@ -81,6 +82,7 @@
     '<(skia_include_path)/effects/SkCornerPathEffect.h',
     '<(skia_include_path)/effects/SkDashPathEffect.h',
     '<(skia_include_path)/effects/SkDiscretePathEffect.h',
+    '<(skia_include_path)/effects/SkDisplacementMapEffect.h',
     '<(skia_include_path)/effects/SkDrawExtraPathEffect.h',
     '<(skia_include_path)/effects/SkEmbossMaskFilter.h',
     '<(skia_include_path)/effects/SkGradientShader.h',
index 1553e744fe0c70baa1701ca468abec4f13705c9c..5aa75f6aa3d039a9748d1771118f0d7cf55f96e4 100644 (file)
@@ -26,6 +26,7 @@
     '../gm/dashcubics.cpp',
     '../gm/dashing.cpp',
     '../gm/distantclip.cpp',
+    '../gm/displacement.cpp',
     '../gm/drawbitmaprect.cpp',
     '../gm/drawlooper.cpp',
     '../gm/extractbitmap.cpp',
diff --git a/include/effects/SkDisplacementMapEffect.h b/include/effects/SkDisplacementMapEffect.h
new file mode 100644 (file)
index 0000000..641b4cb
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkDisplacementMapEffect_DEFINED
+#define SkDisplacementMapEffect_DEFINED
+
+#include "SkImageFilter.h"
+#include "SkBitmap.h"
+
+class SK_API SkDisplacementMapEffect : public SkImageFilter {
+public:
+    enum ChannelSelectorType {
+        kUnknown_ChannelSelectorType,
+        kR_ChannelSelectorType,
+        kG_ChannelSelectorType,
+        kB_ChannelSelectorType,
+        kA_ChannelSelectorType,
+        kKeyBits = 3 // Max value is 4, so 3 bits are required at most
+    };
+
+    SkDisplacementMapEffect(ChannelSelectorType xChannelSelector, ChannelSelectorType yChannelSelector, SkScalar scale, SkImageFilter* displacement, SkImageFilter* color = NULL);
+
+    ~SkDisplacementMapEffect();
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDisplacementMapEffect)
+
+    virtual bool onFilterImage(Proxy* proxy,
+                               const SkBitmap& src,
+                               const SkMatrix& ctm,
+                               SkBitmap* dst,
+                               SkIPoint* offset) SK_OVERRIDE;
+#if SK_SUPPORT_GPU
+    virtual bool canFilterImageGPU() const SK_OVERRIDE { return true; }
+    virtual GrTexture* filterImageGPU(Proxy* proxy, GrTexture* src, const SkRect& rect) SK_OVERRIDE;
+#endif
+
+protected:
+    explicit SkDisplacementMapEffect(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+private:
+    ChannelSelectorType fXChannelSelector;
+    ChannelSelectorType fYChannelSelector;
+    SkScalar fScale;
+    typedef SkImageFilter INHERITED;
+    SkImageFilter* getDisplacementInput() { return getInput(0); }
+    SkImageFilter* getColorInput() { return getInput(1); }
+};
+
+#endif
diff --git a/src/effects/SkDisplacementMapEffect.cpp b/src/effects/SkDisplacementMapEffect.cpp
new file mode 100644 (file)
index 0000000..a10e623
--- /dev/null
@@ -0,0 +1,527 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkDisplacementMapEffect.h"
+#include "SkFlattenableBuffers.h"
+#include "SkUnPreMultiply.h"
+#if SK_SUPPORT_GPU
+#include "SkGr.h"
+#include "SkGrPixelRef.h"
+#include "gl/GrGLEffect.h"
+#include "gl/GrGLEffectMatrix.h"
+#include "GrTBackendEffectFactory.h"
+#endif
+
+namespace {
+
+template<SkDisplacementMapEffect::ChannelSelectorType type>
+uint32_t getValue(SkColor, const SkUnPreMultiply::Scale*) {
+    SkASSERT(!"Unknown channel selector");
+    return 0;
+}
+
+template<> uint32_t getValue<SkDisplacementMapEffect::kR_ChannelSelectorType>(
+    SkColor l, const SkUnPreMultiply::Scale* table) {
+    return SkUnPreMultiply::ApplyScale(table[SkGetPackedA32(l)], SkGetPackedR32(l));
+}
+
+template<> uint32_t getValue<SkDisplacementMapEffect::kG_ChannelSelectorType>(
+    SkColor l, const SkUnPreMultiply::Scale* table) {
+    return SkUnPreMultiply::ApplyScale(table[SkGetPackedA32(l)], SkGetPackedG32(l));
+}
+
+template<> uint32_t getValue<SkDisplacementMapEffect::kB_ChannelSelectorType>(
+    SkColor l, const SkUnPreMultiply::Scale* table) {
+    return SkUnPreMultiply::ApplyScale(table[SkGetPackedA32(l)], SkGetPackedB32(l));
+}
+
+template<> uint32_t getValue<SkDisplacementMapEffect::kA_ChannelSelectorType>(
+    SkColor l, const SkUnPreMultiply::Scale*) {
+    return SkGetPackedA32(l);
+}
+
+template<SkDisplacementMapEffect::ChannelSelectorType typeX,
+         SkDisplacementMapEffect::ChannelSelectorType typeY>
+void computeDisplacement(SkScalar scale, SkBitmap* dst, SkBitmap* displ, SkBitmap* src)
+{
+    static const SkScalar Inv8bit = SkScalarDiv(SK_Scalar1, SkFloatToScalar(255.0f));
+    static const SkScalar Half8bit = SkFloatToScalar(255.0f * 0.5f);
+    const int dstW = displ->width();
+    const int dstH = displ->height();
+    const int srcW = src->width();
+    const int srcH = src->height();
+    const SkScalar scaleX = SkScalarMul(SkScalarMul(scale, SkIntToScalar(dstW)), Inv8bit);
+    const SkScalar scaleY = SkScalarMul(SkScalarMul(scale, SkIntToScalar(dstH)), Inv8bit);
+    const SkUnPreMultiply::Scale* table = SkUnPreMultiply::GetScaleTable();
+    for (int y = 0; y < dstH; ++y) {
+        const SkPMColor* displPtr = displ->getAddr32(0, y);
+        SkPMColor* dstPtr = dst->getAddr32(0, y);
+        for (int x = 0; x < dstW; ++x, ++displPtr, ++dstPtr) {
+            const SkScalar displX =
+                SkScalarMul(scaleX, SkIntToScalar(getValue<typeX>(*displPtr, table))-Half8bit);
+            const SkScalar displY =
+                SkScalarMul(scaleY, SkIntToScalar(getValue<typeY>(*displPtr, table))-Half8bit);
+            const int coordX = x + SkScalarRoundToInt(displX);
+            const int coordY = y + SkScalarRoundToInt(displY);
+            *dstPtr = ((coordX < 0) || (coordX >= srcW) || (coordY < 0) || (coordY >= srcH)) ? 
+                      0 : *(src->getAddr32(coordX, coordY));
+        }
+    }
+}
+
+template<SkDisplacementMapEffect::ChannelSelectorType typeX>
+void computeDisplacement(SkDisplacementMapEffect::ChannelSelectorType yChannelSelector,
+                         SkScalar scale, SkBitmap* dst, SkBitmap* displ, SkBitmap* src)
+{
+    switch (yChannelSelector) {
+      case SkDisplacementMapEffect::kR_ChannelSelectorType:
+        computeDisplacement<typeX, SkDisplacementMapEffect::kR_ChannelSelectorType>(
+            scale, dst, displ, src);
+        break;
+      case SkDisplacementMapEffect::kG_ChannelSelectorType:
+        computeDisplacement<typeX, SkDisplacementMapEffect::kG_ChannelSelectorType>(
+            scale, dst, displ, src);
+        break;
+      case SkDisplacementMapEffect::kB_ChannelSelectorType:
+        computeDisplacement<typeX, SkDisplacementMapEffect::kB_ChannelSelectorType>(
+            scale, dst, displ, src);
+        break;
+      case SkDisplacementMapEffect::kA_ChannelSelectorType:
+        computeDisplacement<typeX, SkDisplacementMapEffect::kA_ChannelSelectorType>(
+            scale, dst, displ, src);
+        break;
+      case SkDisplacementMapEffect::kUnknown_ChannelSelectorType:
+      default:
+        SkASSERT(!"Unknown Y channel selector");
+    }
+}
+
+void computeDisplacement(SkDisplacementMapEffect::ChannelSelectorType xChannelSelector,
+                         SkDisplacementMapEffect::ChannelSelectorType yChannelSelector,
+                         SkScalar scale, SkBitmap* dst, SkBitmap* displ, SkBitmap* src)
+{
+    switch (xChannelSelector) {
+      case SkDisplacementMapEffect::kR_ChannelSelectorType:
+        computeDisplacement<SkDisplacementMapEffect::kR_ChannelSelectorType>(
+            yChannelSelector, scale, dst, displ, src);
+        break;
+      case SkDisplacementMapEffect::kG_ChannelSelectorType:
+        computeDisplacement<SkDisplacementMapEffect::kG_ChannelSelectorType>(
+            yChannelSelector, scale, dst, displ, src);
+        break;
+      case SkDisplacementMapEffect::kB_ChannelSelectorType:
+        computeDisplacement<SkDisplacementMapEffect::kB_ChannelSelectorType>(
+            yChannelSelector, scale, dst, displ, src);
+        break;
+      case SkDisplacementMapEffect::kA_ChannelSelectorType:
+        computeDisplacement<SkDisplacementMapEffect::kA_ChannelSelectorType>(
+            yChannelSelector, scale, dst, displ, src);
+        break;
+      case SkDisplacementMapEffect::kUnknown_ChannelSelectorType:
+      default:
+        SkASSERT(!"Unknown X channel selector");
+    }
+}
+
+} // end namespace
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkDisplacementMapEffect::SkDisplacementMapEffect(ChannelSelectorType xChannelSelector,
+                                                 ChannelSelectorType yChannelSelector,
+                                                 SkScalar scale,
+                                                 SkImageFilter* displacement,
+                                                 SkImageFilter* color)
+  : INHERITED(displacement, color)
+  , fXChannelSelector(xChannelSelector)
+  , fYChannelSelector(yChannelSelector)
+  , fScale(scale)
+{
+}
+
+SkDisplacementMapEffect::~SkDisplacementMapEffect() {
+}
+
+SkDisplacementMapEffect::SkDisplacementMapEffect(SkFlattenableReadBuffer& buffer)
+  : INHERITED(buffer)
+{
+    fXChannelSelector = (SkDisplacementMapEffect::ChannelSelectorType) buffer.readInt();
+    fYChannelSelector = (SkDisplacementMapEffect::ChannelSelectorType) buffer.readInt();
+    fScale            = buffer.readScalar();
+}
+
+void SkDisplacementMapEffect::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeInt((int) fXChannelSelector);
+    buffer.writeInt((int) fYChannelSelector);
+    buffer.writeScalar(fScale);
+}
+
+bool SkDisplacementMapEffect::onFilterImage(Proxy* proxy,
+                                            const SkBitmap& src,
+                                            const SkMatrix& ctm,
+                                            SkBitmap* dst,
+                                            SkIPoint* offset) {
+    SkBitmap displ, color = src;
+    SkImageFilter* colorInput = getColorInput();
+    SkImageFilter* displacementInput = getDisplacementInput();
+    SkASSERT(NULL != displacementInput);
+    if ((colorInput && !colorInput->filterImage(proxy, src, ctm, &color, offset)) ||
+        !displacementInput->filterImage(proxy, src, ctm, &displ, offset)) {
+        return false;
+    }
+    if ((displ.config() != SkBitmap::kARGB_8888_Config) ||
+        (color.config() != SkBitmap::kARGB_8888_Config)) {
+        return false;
+    }
+
+    SkAutoLockPixels alp_displacement(displ), alp_color(color);
+    if (!displ.getPixels() || !color.getPixels()) {
+        return false;
+    }
+    dst->setConfig(displ.config(), displ.width(), displ.height());
+    dst->allocPixels();
+    if (!dst->getPixels()) {
+        return false;
+    }
+
+    computeDisplacement(fXChannelSelector, fYChannelSelector, fScale, dst, &displ, &color);
+
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+class GrGLDisplacementMapEffect : public GrGLEffect {
+public:
+    GrGLDisplacementMapEffect(const GrBackendEffectFactory& factory,
+                              const GrEffect& effect);
+    virtual ~GrGLDisplacementMapEffect();
+
+    virtual void emitCode(GrGLShaderBuilder*,
+                          const GrEffectStage&,
+                          EffectKey,
+                          const char* vertexCoords,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TextureSamplerArray&) SK_OVERRIDE;
+
+    static inline EffectKey GenKey(const GrEffectStage&, const GrGLCaps&);
+
+    virtual void setData(const GrGLUniformManager&, const GrEffectStage&);
+
+private:
+    SkDisplacementMapEffect::ChannelSelectorType fXChannelSelector;
+    SkDisplacementMapEffect::ChannelSelectorType fYChannelSelector;
+    GrGLEffectMatrix fDisplacementEffectMatrix;
+    GrGLEffectMatrix fColorEffectMatrix;
+    GrGLUniformManager::UniformHandle fScaleUni;
+    GrGLUniformManager::UniformHandle fYSignColor;
+    GrGLUniformManager::UniformHandle fYSignDispl;
+
+    typedef GrGLEffect INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrDisplacementMapEffect : public GrEffect {
+public:
+    GrDisplacementMapEffect(SkDisplacementMapEffect::ChannelSelectorType xChannelSelector,
+                            SkDisplacementMapEffect::ChannelSelectorType yChannelSelector,
+                            SkScalar scale, GrTexture* displacement, GrTexture* color);
+    virtual ~GrDisplacementMapEffect();
+
+    virtual bool isEqual(const GrEffect&) const SK_OVERRIDE;
+    const GrBackendEffectFactory& getFactory() const;
+    SkDisplacementMapEffect::ChannelSelectorType xChannelSelector() const
+        { return fXChannelSelector; }
+    SkDisplacementMapEffect::ChannelSelectorType yChannelSelector() const
+        { return fYChannelSelector; }
+    SkScalar scale() const { return fScale; }
+
+    typedef GrGLDisplacementMapEffect GLEffect;
+    static const char* Name() { return "DisplacementMap"; }
+
+    void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
+private:
+    GR_DECLARE_EFFECT_TEST;
+
+    GrTextureAccess             fDisplacementAccess;
+    GrTextureAccess             fColorAccess;
+    SkDisplacementMapEffect::ChannelSelectorType fXChannelSelector;
+    SkDisplacementMapEffect::ChannelSelectorType fYChannelSelector;
+    SkScalar fScale;
+
+    typedef GrEffect INHERITED;
+};
+
+// FIXME:  This should be refactored with SkSingleInputImageFilter's version.
+static GrTexture* getInputResultAsTexture(SkImageFilter::Proxy* proxy,
+                                          SkImageFilter* input,
+                                          GrTexture* src,
+                                          const SkRect& rect) {
+    GrTexture* resultTex = NULL;
+    if (!input) {
+        resultTex = src;
+    } else if (input->canFilterImageGPU()) {
+        // filterImageGPU() already refs the result, so just return it here.
+        return input->filterImageGPU(proxy, src, rect);
+    } else {
+        SkBitmap srcBitmap, result;
+        srcBitmap.setConfig(SkBitmap::kARGB_8888_Config, src->width(), src->height());
+        srcBitmap.setPixelRef(new SkGrPixelRef(src))->unref();
+        SkIPoint offset;
+        if (input->filterImage(proxy, srcBitmap, SkMatrix(), &result, &offset)) {
+            if (result.getTexture()) {
+                resultTex = (GrTexture*) result.getTexture();
+            } else {
+                resultTex = GrLockCachedBitmapTexture(src->getContext(), result, NULL);
+                SkSafeRef(resultTex);
+                GrUnlockCachedBitmapTexture(resultTex);
+                return resultTex;
+            }
+        } else {
+            resultTex = src;
+        }
+    }
+    SkSafeRef(resultTex);
+    return resultTex;
+}
+
+GrTexture* SkDisplacementMapEffect::filterImageGPU(Proxy* proxy, GrTexture* src,
+                                                   const SkRect& rect) {
+    SkAutoTUnref<GrTexture> color(getInputResultAsTexture(proxy, getColorInput(), src, rect));
+    SkAutoTUnref<GrTexture> displacement(getInputResultAsTexture(proxy, getDisplacementInput(),
+                                                                 src, rect));
+    GrContext* context = src->getContext();
+
+    GrTextureDesc desc;
+    desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
+    desc.fWidth = SkScalarCeilToInt(rect.width());
+    desc.fHeight = SkScalarCeilToInt(rect.height());
+    desc.fConfig = kRGBA_8888_GrPixelConfig;
+
+    GrAutoScratchTexture ast(context, desc);
+    GrTexture* dst = ast.detach();
+
+    GrContext::AutoMatrix am;
+    am.setIdentity(context);
+
+    GrContext::AutoRenderTarget art(context, dst->asRenderTarget());
+    GrContext::AutoClip ac(context, rect);
+
+    GrPaint paint;
+    paint.colorStage(0)->setEffect(
+        SkNEW_ARGS(GrDisplacementMapEffect, (fXChannelSelector, fYChannelSelector, fScale, 
+                                             displacement.get(), color.get())))->unref();
+    context->drawRect(paint, rect);
+    return dst;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrDisplacementMapEffect::GrDisplacementMapEffect(
+                             SkDisplacementMapEffect::ChannelSelectorType xChannelSelector,
+                             SkDisplacementMapEffect::ChannelSelectorType yChannelSelector,
+                             SkScalar scale,
+                             GrTexture* displacement,
+                             GrTexture* color)
+    : fDisplacementAccess(displacement)
+    , fColorAccess(color)
+    , fXChannelSelector(xChannelSelector)
+    , fYChannelSelector(yChannelSelector)
+    , fScale(scale) {
+    this->addTextureAccess(&fDisplacementAccess);
+    this->addTextureAccess(&fColorAccess);
+}
+
+GrDisplacementMapEffect::~GrDisplacementMapEffect() {
+}
+
+bool GrDisplacementMapEffect::isEqual(const GrEffect& sBase) const {
+    const GrDisplacementMapEffect& s = static_cast<const GrDisplacementMapEffect&>(sBase);
+    return INHERITED::isEqual(sBase) && fXChannelSelector == s.fXChannelSelector &&
+           fYChannelSelector == s.fYChannelSelector && fScale == s.fScale;
+}
+
+const GrBackendEffectFactory& GrDisplacementMapEffect::getFactory() const {
+    return GrTBackendEffectFactory<GrDisplacementMapEffect>::getInstance();
+}
+
+void GrDisplacementMapEffect::getConstantColorComponents(GrColor* color, 
+                                                         uint32_t* validFlags) const {
+    // Any displacement offset bringing a pixel out of bounds will output a color of (0,0,0,0),
+    // so the only way we'd get a constant alpha is if the input color image has a constant alpha
+    // and no displacement offset push any texture coordinates out of bounds OR if the constant    
+    // alpha is 0. Since this isn't trivial to compute at this point, let's assume the output is
+    // not of constant color when a displacement effect is applied.
+    *validFlags = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_EFFECT_TEST(GrDisplacementMapEffect);
+
+GrEffect* GrDisplacementMapEffect::TestCreate(SkRandom* random,
+                                              GrContext* context,
+                                              GrTexture* textures[]) {
+    int texIdxDispl = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
+                                           GrEffectUnitTest::kAlphaTextureIdx;
+    int texIdxColor = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
+                                           GrEffectUnitTest::kAlphaTextureIdx;
+    static const int kMaxComponent = 4;
+    SkDisplacementMapEffect::ChannelSelectorType xChannelSelector =
+        static_cast<SkDisplacementMapEffect::ChannelSelectorType>(
+        random->nextRangeU(1, kMaxComponent));
+    SkDisplacementMapEffect::ChannelSelectorType yChannelSelector =
+        static_cast<SkDisplacementMapEffect::ChannelSelectorType>(
+        random->nextRangeU(1, kMaxComponent));
+    SkScalar scale = random->nextUScalar1();
+
+    return SkNEW_ARGS(GrDisplacementMapEffect, (xChannelSelector, yChannelSelector, scale,
+                                                textures[texIdxDispl], textures[texIdxColor]));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrGLDisplacementMapEffect::GrGLDisplacementMapEffect(const GrBackendEffectFactory& factory, const GrEffect& effect)
+    : INHERITED(factory)
+    , fXChannelSelector(static_cast<const GrDisplacementMapEffect&>(effect).xChannelSelector())
+    , fYChannelSelector(static_cast<const GrDisplacementMapEffect&>(effect).yChannelSelector()) {
+}
+
+GrGLDisplacementMapEffect::~GrGLDisplacementMapEffect() {
+}
+
+void GrGLDisplacementMapEffect::emitCode(GrGLShaderBuilder* builder,
+                               const GrEffectStage&,
+                               EffectKey key,
+                               const char* vertexCoords,
+                               const char* outputColor,
+                               const char* inputColor,
+                               const TextureSamplerArray& samplers) {
+    fScaleUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                    kVec2f_GrSLType, "Scale");
+    const char* scaleUni = builder->getUniformCStr(fScaleUni);
+
+    const char* dCoordsIn;
+    GrSLType dCoordsType = fDisplacementEffectMatrix.emitCode(
+                                builder, key, vertexCoords, &dCoordsIn, NULL, "DISPL");
+    const char* cCoordsIn;
+    GrSLType cCoordsType = fColorEffectMatrix.emitCode(
+                                builder, key, vertexCoords, &cCoordsIn, NULL, "COLOR");
+
+    SkString* code = &builder->fFSCode;
+    const char* dColor = "dColor";
+    const char* dCoords = "dCoords";
+    const char* cCoords = "cCoords";
+    const char* nearZero = "1e-6"; // Since 6.10352e−5 is the smallest half float, use
+                                   // a number smaller than that to approximate 0, but
+                                   // leave room for 32-bit float GPU rounding errors.
+
+    code->appendf("\t\tvec4 %s = ", dColor);
+    builder->appendTextureLookup(code, samplers[0], dCoordsIn, dCoordsType);
+    code->append(";\n");
+
+    // Unpremultiply the displacement
+    code->appendf("\t\t%s.rgb = (%s.a < %s) ? vec3(0.0) : clamp(%s.rgb / %s.a, 0.0, 1.0);",
+                  dColor, dColor, nearZero, dColor, dColor);
+
+    code->appendf("\t\tvec2 %s = %s + %s*(%s.",
+                  cCoords, cCoordsIn, scaleUni, dColor);
+
+    switch (fXChannelSelector) {
+      case SkDisplacementMapEffect::kR_ChannelSelectorType:
+        code->append("r");
+        break;
+      case SkDisplacementMapEffect::kG_ChannelSelectorType:
+        code->append("g");
+        break;
+      case SkDisplacementMapEffect::kB_ChannelSelectorType:
+        code->append("b");
+        break;
+      case SkDisplacementMapEffect::kA_ChannelSelectorType:
+        code->append("a");
+        break;
+      case SkDisplacementMapEffect::kUnknown_ChannelSelectorType:
+      default:
+        SkASSERT(!"Unknown X channel selector");
+    }
+
+    switch (fYChannelSelector) {
+      case SkDisplacementMapEffect::kR_ChannelSelectorType:
+        code->append("r");
+        break;
+      case SkDisplacementMapEffect::kG_ChannelSelectorType:
+        code->append("g");
+        break;
+      case SkDisplacementMapEffect::kB_ChannelSelectorType:
+        code->append("b");
+        break;
+      case SkDisplacementMapEffect::kA_ChannelSelectorType:
+        code->append("a");
+        break;
+      case SkDisplacementMapEffect::kUnknown_ChannelSelectorType:
+      default:
+        SkASSERT(!"Unknown Y channel selector");
+    }
+    code->append("-vec2(0.5));\t\t");
+
+    // FIXME : This can be achieved with a "clamp to border" texture repeat mode and
+    //         a 0 border color instead of computing if cCoords is out of bounds here.
+    code->appendf(
+        "%s = any(greaterThan(vec4(vec2(0.0), %s), vec4(%s, vec2(1.0)))) ? vec4(0.0) : ",
+        outputColor, cCoords, cCoords);
+    builder->appendTextureLookup(code, samplers[1], cCoords, cCoordsType);
+    code->append(";\n");
+}
+
+void GrGLDisplacementMapEffect::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
+    const GrDisplacementMapEffect& displacementMap = static_cast<const GrDisplacementMapEffect&>(*stage.getEffect());
+    GrTexture* displTex = displacementMap.texture(0);
+    GrTexture* colorTex = displacementMap.texture(1);
+    fDisplacementEffectMatrix.setData(uman,
+                                     GrEffect::MakeDivByTextureWHMatrix(displTex),
+                                     stage.getCoordChangeMatrix(),
+                                     displTex);
+    fColorEffectMatrix.setData(uman,
+                               GrEffect::MakeDivByTextureWHMatrix(colorTex),
+                               stage.getCoordChangeMatrix(),
+                               colorTex);
+    
+    uman.set2f(fScaleUni, SkScalarToFloat(displacementMap.scale()),
+                colorTex->origin() == GrSurface::kTopLeft_Origin ? 
+                SkScalarToFloat(displacementMap.scale()) :
+                SkScalarToFloat(-displacementMap.scale()));
+}
+
+GrGLEffect::EffectKey GrGLDisplacementMapEffect::GenKey(const GrEffectStage& stage,
+                                                        const GrGLCaps&) {
+    const GrDisplacementMapEffect& displacementMap =
+        static_cast<const GrDisplacementMapEffect&>(*stage.getEffect());
+
+    GrTexture* displTex = displacementMap.texture(0);
+    GrTexture* colorTex = displacementMap.texture(1);
+
+    EffectKey displKey = GrGLEffectMatrix::GenKey(GrEffect::MakeDivByTextureWHMatrix(displTex),
+                                                  stage.getCoordChangeMatrix(),
+                                                  displTex);
+
+    EffectKey colorKey = GrGLEffectMatrix::GenKey(GrEffect::MakeDivByTextureWHMatrix(colorTex),
+                                                  stage.getCoordChangeMatrix(),
+                                                  colorTex);
+    
+    colorKey <<= GrGLEffectMatrix::kKeyBits;
+    EffectKey xKey = displacementMap.xChannelSelector() << (2 * GrGLEffectMatrix::kKeyBits);
+    EffectKey yKey = displacementMap.yChannelSelector() << (2 * GrGLEffectMatrix::kKeyBits +
+                                                            SkDisplacementMapEffect::kKeyBits);
+
+    return xKey | yKey | displKey | colorKey;
+}
+#endif
index 4ed7f5e16d5b8a9c7530138b40a05092e62c81e9..ae0c5648282919f52625341e195faae6cc6558c1 100644 (file)
@@ -31,6 +31,7 @@
 #include "SkCornerPathEffect.h"
 #include "SkDashPathEffect.h"
 #include "SkDiscretePathEffect.h"
+#include "SkDisplacementMapEffect.h"
 #include "SkEmptyShader.h"
 #include "SkEmbossMaskFilter.h"
 #include "SkFlattenable.h"
@@ -66,6 +67,7 @@ void SkFlattenable::InitializeFlattenables() {
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDashPathEffect)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDilateImageFilter)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDiscretePathEffect)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDisplacementMapEffect)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkEmbossMaskFilter)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkEmptyShader)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkErodeImageFilter)