--- /dev/null
+/*
+ * 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);
+
+}
--- /dev/null
+/*
+ * 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