michael@0: /* michael@0: * Copyright 2013 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #include "SkDisplacementMapEffect.h" michael@0: #include "SkReadBuffer.h" michael@0: #include "SkWriteBuffer.h" michael@0: #include "SkUnPreMultiply.h" michael@0: #include "SkColorPriv.h" michael@0: #if SK_SUPPORT_GPU michael@0: #include "GrContext.h" michael@0: #include "GrCoordTransform.h" michael@0: #include "gl/GrGLEffect.h" michael@0: #include "GrTBackendEffectFactory.h" michael@0: #endif michael@0: michael@0: namespace { michael@0: michael@0: #define kChannelSelectorKeyBits 3; // Max value is 4, so 3 bits are required at most michael@0: michael@0: template michael@0: uint32_t getValue(SkColor, const SkUnPreMultiply::Scale*) { michael@0: SkDEBUGFAIL("Unknown channel selector"); michael@0: return 0; michael@0: } michael@0: michael@0: template<> uint32_t getValue( michael@0: SkColor l, const SkUnPreMultiply::Scale* table) { michael@0: return SkUnPreMultiply::ApplyScale(table[SkGetPackedA32(l)], SkGetPackedR32(l)); michael@0: } michael@0: michael@0: template<> uint32_t getValue( michael@0: SkColor l, const SkUnPreMultiply::Scale* table) { michael@0: return SkUnPreMultiply::ApplyScale(table[SkGetPackedA32(l)], SkGetPackedG32(l)); michael@0: } michael@0: michael@0: template<> uint32_t getValue( michael@0: SkColor l, const SkUnPreMultiply::Scale* table) { michael@0: return SkUnPreMultiply::ApplyScale(table[SkGetPackedA32(l)], SkGetPackedB32(l)); michael@0: } michael@0: michael@0: template<> uint32_t getValue( michael@0: SkColor l, const SkUnPreMultiply::Scale*) { michael@0: return SkGetPackedA32(l); michael@0: } michael@0: michael@0: template michael@0: void computeDisplacement(const SkVector& scale, SkBitmap* dst, michael@0: SkBitmap* displ, const SkIPoint& offset, michael@0: SkBitmap* src, michael@0: const SkIRect& bounds) michael@0: { michael@0: static const SkScalar Inv8bit = SkScalarDiv(SK_Scalar1, 255.0f); michael@0: const int srcW = src->width(); michael@0: const int srcH = src->height(); michael@0: const SkVector scaleForColor = SkVector::Make(SkScalarMul(scale.fX, Inv8bit), michael@0: SkScalarMul(scale.fY, Inv8bit)); michael@0: const SkVector scaleAdj = SkVector::Make(SK_ScalarHalf - SkScalarMul(scale.fX, SK_ScalarHalf), michael@0: SK_ScalarHalf - SkScalarMul(scale.fY, SK_ScalarHalf)); michael@0: const SkUnPreMultiply::Scale* table = SkUnPreMultiply::GetScaleTable(); michael@0: SkPMColor* dstPtr = dst->getAddr32(0, 0); michael@0: for (int y = bounds.top(); y < bounds.bottom(); ++y) { michael@0: const SkPMColor* displPtr = displ->getAddr32(bounds.left() + offset.fX, michael@0: y + offset.fY); michael@0: for (int x = bounds.left(); x < bounds.right(); ++x, ++displPtr) { michael@0: const SkScalar displX = SkScalarMul(scaleForColor.fX, michael@0: SkIntToScalar(getValue(*displPtr, table))) + scaleAdj.fX; michael@0: const SkScalar displY = SkScalarMul(scaleForColor.fY, michael@0: SkIntToScalar(getValue(*displPtr, table))) + scaleAdj.fY; michael@0: // Truncate the displacement values michael@0: const int srcX = x + SkScalarTruncToInt(displX); michael@0: const int srcY = y + SkScalarTruncToInt(displY); michael@0: *dstPtr++ = ((srcX < 0) || (srcX >= srcW) || (srcY < 0) || (srcY >= srcH)) ? michael@0: 0 : *(src->getAddr32(srcX, srcY)); michael@0: } michael@0: } michael@0: } michael@0: michael@0: template michael@0: void computeDisplacement(SkDisplacementMapEffect::ChannelSelectorType yChannelSelector, michael@0: const SkVector& scale, SkBitmap* dst, michael@0: SkBitmap* displ, const SkIPoint& offset, michael@0: SkBitmap* src, michael@0: const SkIRect& bounds) michael@0: { michael@0: switch (yChannelSelector) { michael@0: case SkDisplacementMapEffect::kR_ChannelSelectorType: michael@0: computeDisplacement( michael@0: scale, dst, displ, offset, src, bounds); michael@0: break; michael@0: case SkDisplacementMapEffect::kG_ChannelSelectorType: michael@0: computeDisplacement( michael@0: scale, dst, displ, offset, src, bounds); michael@0: break; michael@0: case SkDisplacementMapEffect::kB_ChannelSelectorType: michael@0: computeDisplacement( michael@0: scale, dst, displ, offset, src, bounds); michael@0: break; michael@0: case SkDisplacementMapEffect::kA_ChannelSelectorType: michael@0: computeDisplacement( michael@0: scale, dst, displ, offset, src, bounds); michael@0: break; michael@0: case SkDisplacementMapEffect::kUnknown_ChannelSelectorType: michael@0: default: michael@0: SkDEBUGFAIL("Unknown Y channel selector"); michael@0: } michael@0: } michael@0: michael@0: void computeDisplacement(SkDisplacementMapEffect::ChannelSelectorType xChannelSelector, michael@0: SkDisplacementMapEffect::ChannelSelectorType yChannelSelector, michael@0: const SkVector& scale, SkBitmap* dst, michael@0: SkBitmap* displ, const SkIPoint& offset, michael@0: SkBitmap* src, michael@0: const SkIRect& bounds) michael@0: { michael@0: switch (xChannelSelector) { michael@0: case SkDisplacementMapEffect::kR_ChannelSelectorType: michael@0: computeDisplacement( michael@0: yChannelSelector, scale, dst, displ, offset, src, bounds); michael@0: break; michael@0: case SkDisplacementMapEffect::kG_ChannelSelectorType: michael@0: computeDisplacement( michael@0: yChannelSelector, scale, dst, displ, offset, src, bounds); michael@0: break; michael@0: case SkDisplacementMapEffect::kB_ChannelSelectorType: michael@0: computeDisplacement( michael@0: yChannelSelector, scale, dst, displ, offset, src, bounds); michael@0: break; michael@0: case SkDisplacementMapEffect::kA_ChannelSelectorType: michael@0: computeDisplacement( michael@0: yChannelSelector, scale, dst, displ, offset, src, bounds); michael@0: break; michael@0: case SkDisplacementMapEffect::kUnknown_ChannelSelectorType: michael@0: default: michael@0: SkDEBUGFAIL("Unknown X channel selector"); michael@0: } michael@0: } michael@0: michael@0: bool channel_selector_type_is_valid(SkDisplacementMapEffect::ChannelSelectorType cst) { michael@0: switch (cst) { michael@0: case SkDisplacementMapEffect::kUnknown_ChannelSelectorType: michael@0: case SkDisplacementMapEffect::kR_ChannelSelectorType: michael@0: case SkDisplacementMapEffect::kG_ChannelSelectorType: michael@0: case SkDisplacementMapEffect::kB_ChannelSelectorType: michael@0: case SkDisplacementMapEffect::kA_ChannelSelectorType: michael@0: return true; michael@0: default: michael@0: break; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: } // end namespace michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: SkDisplacementMapEffect::SkDisplacementMapEffect(ChannelSelectorType xChannelSelector, michael@0: ChannelSelectorType yChannelSelector, michael@0: SkScalar scale, michael@0: SkImageFilter* displacement, michael@0: SkImageFilter* color, michael@0: const CropRect* cropRect) michael@0: : INHERITED(displacement, color, cropRect) michael@0: , fXChannelSelector(xChannelSelector) michael@0: , fYChannelSelector(yChannelSelector) michael@0: , fScale(scale) michael@0: { michael@0: } michael@0: michael@0: SkDisplacementMapEffect::~SkDisplacementMapEffect() { michael@0: } michael@0: michael@0: SkDisplacementMapEffect::SkDisplacementMapEffect(SkReadBuffer& buffer) michael@0: : INHERITED(2, buffer) michael@0: { michael@0: fXChannelSelector = (SkDisplacementMapEffect::ChannelSelectorType) buffer.readInt(); michael@0: fYChannelSelector = (SkDisplacementMapEffect::ChannelSelectorType) buffer.readInt(); michael@0: fScale = buffer.readScalar(); michael@0: buffer.validate(channel_selector_type_is_valid(fXChannelSelector) && michael@0: channel_selector_type_is_valid(fYChannelSelector) && michael@0: SkScalarIsFinite(fScale)); michael@0: } michael@0: michael@0: void SkDisplacementMapEffect::flatten(SkWriteBuffer& buffer) const { michael@0: this->INHERITED::flatten(buffer); michael@0: buffer.writeInt((int) fXChannelSelector); michael@0: buffer.writeInt((int) fYChannelSelector); michael@0: buffer.writeScalar(fScale); michael@0: } michael@0: michael@0: bool SkDisplacementMapEffect::onFilterImage(Proxy* proxy, michael@0: const SkBitmap& src, michael@0: const Context& ctx, michael@0: SkBitmap* dst, michael@0: SkIPoint* offset) const { michael@0: SkBitmap displ = src, color = src; michael@0: const SkImageFilter* colorInput = getColorInput(); michael@0: const SkImageFilter* displInput = getDisplacementInput(); michael@0: SkIPoint colorOffset = SkIPoint::Make(0, 0), displOffset = SkIPoint::Make(0, 0); michael@0: if ((colorInput && !colorInput->filterImage(proxy, src, ctx, &color, &colorOffset)) || michael@0: (displInput && !displInput->filterImage(proxy, src, ctx, &displ, &displOffset))) { michael@0: return false; michael@0: } michael@0: if ((displ.colorType() != kPMColor_SkColorType) || michael@0: (color.colorType() != kPMColor_SkColorType)) { michael@0: return false; michael@0: } michael@0: SkIRect bounds; michael@0: // Since computeDisplacement does bounds checking on color pixel access, we don't need to pad michael@0: // the color bitmap to bounds here. michael@0: if (!this->applyCropRect(ctx, color, colorOffset, &bounds)) { michael@0: return false; michael@0: } michael@0: SkIRect displBounds; michael@0: if (!this->applyCropRect(ctx, proxy, displ, &displOffset, &displBounds, &displ)) { michael@0: return false; michael@0: } michael@0: if (!bounds.intersect(displBounds)) { michael@0: return false; michael@0: } michael@0: SkAutoLockPixels alp_displacement(displ), alp_color(color); michael@0: if (!displ.getPixels() || !color.getPixels()) { michael@0: return false; michael@0: } michael@0: michael@0: dst->setConfig(color.config(), bounds.width(), bounds.height()); michael@0: if (!dst->allocPixels()) { michael@0: return false; michael@0: } michael@0: michael@0: SkVector scale = SkVector::Make(fScale, fScale); michael@0: ctx.ctm().mapVectors(&scale, 1); michael@0: SkIRect colorBounds = bounds; michael@0: colorBounds.offset(-colorOffset); michael@0: michael@0: computeDisplacement(fXChannelSelector, fYChannelSelector, scale, dst, michael@0: &displ, colorOffset - displOffset, &color, colorBounds); michael@0: michael@0: offset->fX = bounds.left(); michael@0: offset->fY = bounds.top(); michael@0: return true; michael@0: } michael@0: michael@0: void SkDisplacementMapEffect::computeFastBounds(const SkRect& src, SkRect* dst) const { michael@0: if (getColorInput()) { michael@0: getColorInput()->computeFastBounds(src, dst); michael@0: } else { michael@0: *dst = src; michael@0: } michael@0: dst->outset(fScale * SK_ScalarHalf, fScale * SK_ScalarHalf); michael@0: } michael@0: michael@0: bool SkDisplacementMapEffect::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, michael@0: SkIRect* dst) const { michael@0: SkIRect bounds = src; michael@0: if (getColorInput() && !getColorInput()->filterBounds(src, ctm, &bounds)) { michael@0: return false; michael@0: } michael@0: bounds.outset(SkScalarCeilToInt(fScale * SK_ScalarHalf), michael@0: SkScalarCeilToInt(fScale * SK_ScalarHalf)); michael@0: *dst = bounds; michael@0: return true; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: #if SK_SUPPORT_GPU michael@0: class GrGLDisplacementMapEffect : public GrGLEffect { michael@0: public: michael@0: GrGLDisplacementMapEffect(const GrBackendEffectFactory& factory, michael@0: const GrDrawEffect& drawEffect); michael@0: virtual ~GrGLDisplacementMapEffect(); michael@0: michael@0: virtual void emitCode(GrGLShaderBuilder*, michael@0: const GrDrawEffect&, michael@0: EffectKey, michael@0: const char* outputColor, michael@0: const char* inputColor, michael@0: const TransformedCoordsArray&, michael@0: const TextureSamplerArray&) SK_OVERRIDE; michael@0: michael@0: static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&); michael@0: michael@0: virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE; michael@0: michael@0: private: michael@0: SkDisplacementMapEffect::ChannelSelectorType fXChannelSelector; michael@0: SkDisplacementMapEffect::ChannelSelectorType fYChannelSelector; michael@0: GrGLUniformManager::UniformHandle fScaleUni; michael@0: michael@0: typedef GrGLEffect INHERITED; michael@0: }; michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: class GrDisplacementMapEffect : public GrEffect { michael@0: public: michael@0: static GrEffectRef* Create(SkDisplacementMapEffect::ChannelSelectorType xChannelSelector, michael@0: SkDisplacementMapEffect::ChannelSelectorType yChannelSelector, michael@0: SkVector scale, michael@0: GrTexture* displacement, const SkMatrix& offsetMatrix, michael@0: GrTexture* color) { michael@0: AutoEffectUnref effect(SkNEW_ARGS(GrDisplacementMapEffect, (xChannelSelector, michael@0: yChannelSelector, michael@0: scale, michael@0: displacement, michael@0: offsetMatrix, michael@0: color))); michael@0: return CreateEffectRef(effect); michael@0: } michael@0: michael@0: virtual ~GrDisplacementMapEffect(); michael@0: michael@0: virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; michael@0: SkDisplacementMapEffect::ChannelSelectorType xChannelSelector() const michael@0: { return fXChannelSelector; } michael@0: SkDisplacementMapEffect::ChannelSelectorType yChannelSelector() const michael@0: { return fYChannelSelector; } michael@0: const SkVector& scale() const { return fScale; } michael@0: michael@0: typedef GrGLDisplacementMapEffect GLEffect; michael@0: static const char* Name() { return "DisplacementMap"; } michael@0: michael@0: virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE; michael@0: michael@0: private: michael@0: virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE; michael@0: michael@0: GrDisplacementMapEffect(SkDisplacementMapEffect::ChannelSelectorType xChannelSelector, michael@0: SkDisplacementMapEffect::ChannelSelectorType yChannelSelector, michael@0: const SkVector& scale, michael@0: GrTexture* displacement, const SkMatrix& offsetMatrix, michael@0: GrTexture* color); michael@0: michael@0: GR_DECLARE_EFFECT_TEST; michael@0: michael@0: GrCoordTransform fDisplacementTransform; michael@0: GrTextureAccess fDisplacementAccess; michael@0: GrCoordTransform fColorTransform; michael@0: GrTextureAccess fColorAccess; michael@0: SkDisplacementMapEffect::ChannelSelectorType fXChannelSelector; michael@0: SkDisplacementMapEffect::ChannelSelectorType fYChannelSelector; michael@0: SkVector fScale; michael@0: michael@0: typedef GrEffect INHERITED; michael@0: }; michael@0: michael@0: bool SkDisplacementMapEffect::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx, michael@0: SkBitmap* result, SkIPoint* offset) const { michael@0: SkBitmap colorBM = src; michael@0: SkIPoint colorOffset = SkIPoint::Make(0, 0); michael@0: if (getColorInput() && !getColorInput()->getInputResultGPU(proxy, src, ctx, &colorBM, michael@0: &colorOffset)) { michael@0: return false; michael@0: } michael@0: SkBitmap displacementBM = src; michael@0: SkIPoint displacementOffset = SkIPoint::Make(0, 0); michael@0: if (getDisplacementInput() && michael@0: !getDisplacementInput()->getInputResultGPU(proxy, src, ctx, &displacementBM, michael@0: &displacementOffset)) { michael@0: return false; michael@0: } michael@0: SkIRect bounds; michael@0: // Since GrDisplacementMapEffect does bounds checking on color pixel access, we don't need to michael@0: // pad the color bitmap to bounds here. michael@0: if (!this->applyCropRect(ctx, colorBM, colorOffset, &bounds)) { michael@0: return false; michael@0: } michael@0: SkIRect displBounds; michael@0: if (!this->applyCropRect(ctx, proxy, displacementBM, michael@0: &displacementOffset, &displBounds, &displacementBM)) { michael@0: return false; michael@0: } michael@0: if (!bounds.intersect(displBounds)) { michael@0: return false; michael@0: } michael@0: GrTexture* color = colorBM.getTexture(); michael@0: GrTexture* displacement = displacementBM.getTexture(); michael@0: GrContext* context = color->getContext(); michael@0: michael@0: GrTextureDesc desc; michael@0: desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit; michael@0: desc.fWidth = colorBM.width(); michael@0: desc.fHeight = colorBM.height(); michael@0: desc.fConfig = kSkia8888_GrPixelConfig; michael@0: michael@0: GrAutoScratchTexture ast(context, desc); michael@0: SkAutoTUnref dst(ast.detach()); michael@0: michael@0: GrContext::AutoRenderTarget art(context, dst->asRenderTarget()); michael@0: michael@0: SkVector scale = SkVector::Make(fScale, fScale); michael@0: ctx.ctm().mapVectors(&scale, 1); michael@0: michael@0: GrPaint paint; michael@0: SkMatrix offsetMatrix = GrEffect::MakeDivByTextureWHMatrix(displacement); michael@0: offsetMatrix.preTranslate(SkIntToScalar(colorOffset.fX - displacementOffset.fX), michael@0: SkIntToScalar(colorOffset.fY - displacementOffset.fY)); michael@0: michael@0: paint.addColorEffect( michael@0: GrDisplacementMapEffect::Create(fXChannelSelector, michael@0: fYChannelSelector, michael@0: scale, michael@0: displacement, michael@0: offsetMatrix, michael@0: color))->unref(); michael@0: SkIRect colorBounds = bounds; michael@0: colorBounds.offset(-colorOffset); michael@0: SkMatrix matrix; michael@0: matrix.setTranslate(-SkIntToScalar(colorBounds.x()), michael@0: -SkIntToScalar(colorBounds.y())); michael@0: context->concatMatrix(matrix); michael@0: context->drawRect(paint, SkRect::Make(colorBounds)); michael@0: offset->fX = bounds.left(); michael@0: offset->fY = bounds.top(); michael@0: WrapTexture(dst, bounds.width(), bounds.height(), result); michael@0: return true; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: GrDisplacementMapEffect::GrDisplacementMapEffect( michael@0: SkDisplacementMapEffect::ChannelSelectorType xChannelSelector, michael@0: SkDisplacementMapEffect::ChannelSelectorType yChannelSelector, michael@0: const SkVector& scale, michael@0: GrTexture* displacement, michael@0: const SkMatrix& offsetMatrix, michael@0: GrTexture* color) michael@0: : fDisplacementTransform(kLocal_GrCoordSet, offsetMatrix, displacement) michael@0: , fDisplacementAccess(displacement) michael@0: , fColorTransform(kLocal_GrCoordSet, color) michael@0: , fColorAccess(color) michael@0: , fXChannelSelector(xChannelSelector) michael@0: , fYChannelSelector(yChannelSelector) michael@0: , fScale(scale) { michael@0: this->addCoordTransform(&fDisplacementTransform); michael@0: this->addTextureAccess(&fDisplacementAccess); michael@0: this->addCoordTransform(&fColorTransform); michael@0: this->addTextureAccess(&fColorAccess); michael@0: this->setWillNotUseInputColor(); michael@0: } michael@0: michael@0: GrDisplacementMapEffect::~GrDisplacementMapEffect() { michael@0: } michael@0: michael@0: bool GrDisplacementMapEffect::onIsEqual(const GrEffect& sBase) const { michael@0: const GrDisplacementMapEffect& s = CastEffect(sBase); michael@0: return fDisplacementAccess.getTexture() == s.fDisplacementAccess.getTexture() && michael@0: fColorAccess.getTexture() == s.fColorAccess.getTexture() && michael@0: fXChannelSelector == s.fXChannelSelector && michael@0: fYChannelSelector == s.fYChannelSelector && michael@0: fScale == s.fScale; michael@0: } michael@0: michael@0: const GrBackendEffectFactory& GrDisplacementMapEffect::getFactory() const { michael@0: return GrTBackendEffectFactory::getInstance(); michael@0: } michael@0: michael@0: void GrDisplacementMapEffect::getConstantColorComponents(GrColor*, michael@0: uint32_t* validFlags) const { michael@0: // Any displacement offset bringing a pixel out of bounds will output a color of (0,0,0,0), michael@0: // so the only way we'd get a constant alpha is if the input color image has a constant alpha michael@0: // and no displacement offset push any texture coordinates out of bounds OR if the constant michael@0: // alpha is 0. Since this isn't trivial to compute at this point, let's assume the output is michael@0: // not of constant color when a displacement effect is applied. michael@0: *validFlags = 0; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: GR_DEFINE_EFFECT_TEST(GrDisplacementMapEffect); michael@0: michael@0: GrEffectRef* GrDisplacementMapEffect::TestCreate(SkRandom* random, michael@0: GrContext*, michael@0: const GrDrawTargetCaps&, michael@0: GrTexture* textures[]) { michael@0: int texIdxDispl = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx : michael@0: GrEffectUnitTest::kAlphaTextureIdx; michael@0: int texIdxColor = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx : michael@0: GrEffectUnitTest::kAlphaTextureIdx; michael@0: static const int kMaxComponent = 4; michael@0: SkDisplacementMapEffect::ChannelSelectorType xChannelSelector = michael@0: static_cast( michael@0: random->nextRangeU(1, kMaxComponent)); michael@0: SkDisplacementMapEffect::ChannelSelectorType yChannelSelector = michael@0: static_cast( michael@0: random->nextRangeU(1, kMaxComponent)); michael@0: SkVector scale = SkVector::Make(random->nextRangeScalar(0, 100.0f), michael@0: random->nextRangeScalar(0, 100.0f)); michael@0: michael@0: return GrDisplacementMapEffect::Create(xChannelSelector, yChannelSelector, scale, michael@0: textures[texIdxDispl], SkMatrix::I(), michael@0: textures[texIdxColor]); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: GrGLDisplacementMapEffect::GrGLDisplacementMapEffect(const GrBackendEffectFactory& factory, michael@0: const GrDrawEffect& drawEffect) michael@0: : INHERITED(factory) michael@0: , fXChannelSelector(drawEffect.castEffect().xChannelSelector()) michael@0: , fYChannelSelector(drawEffect.castEffect().yChannelSelector()) { michael@0: } michael@0: michael@0: GrGLDisplacementMapEffect::~GrGLDisplacementMapEffect() { michael@0: } michael@0: michael@0: void GrGLDisplacementMapEffect::emitCode(GrGLShaderBuilder* builder, michael@0: const GrDrawEffect&, michael@0: EffectKey key, michael@0: const char* outputColor, michael@0: const char* inputColor, michael@0: const TransformedCoordsArray& coords, michael@0: const TextureSamplerArray& samplers) { michael@0: sk_ignore_unused_variable(inputColor); michael@0: michael@0: fScaleUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, michael@0: kVec2f_GrSLType, "Scale"); michael@0: const char* scaleUni = builder->getUniformCStr(fScaleUni); michael@0: const char* dColor = "dColor"; michael@0: const char* cCoords = "cCoords"; michael@0: const char* outOfBounds = "outOfBounds"; michael@0: const char* nearZero = "1e-6"; // Since 6.10352e−5 is the smallest half float, use michael@0: // a number smaller than that to approximate 0, but michael@0: // leave room for 32-bit float GPU rounding errors. michael@0: michael@0: builder->fsCodeAppendf("\t\tvec4 %s = ", dColor); michael@0: builder->fsAppendTextureLookup(samplers[0], coords[0].c_str(), coords[0].type()); michael@0: builder->fsCodeAppend(";\n"); michael@0: michael@0: // Unpremultiply the displacement michael@0: builder->fsCodeAppendf("\t\t%s.rgb = (%s.a < %s) ? vec3(0.0) : clamp(%s.rgb / %s.a, 0.0, 1.0);", michael@0: dColor, dColor, nearZero, dColor, dColor); michael@0: michael@0: builder->fsCodeAppendf("\t\tvec2 %s = %s + %s*(%s.", michael@0: cCoords, coords[1].c_str(), scaleUni, dColor); michael@0: michael@0: switch (fXChannelSelector) { michael@0: case SkDisplacementMapEffect::kR_ChannelSelectorType: michael@0: builder->fsCodeAppend("r"); michael@0: break; michael@0: case SkDisplacementMapEffect::kG_ChannelSelectorType: michael@0: builder->fsCodeAppend("g"); michael@0: break; michael@0: case SkDisplacementMapEffect::kB_ChannelSelectorType: michael@0: builder->fsCodeAppend("b"); michael@0: break; michael@0: case SkDisplacementMapEffect::kA_ChannelSelectorType: michael@0: builder->fsCodeAppend("a"); michael@0: break; michael@0: case SkDisplacementMapEffect::kUnknown_ChannelSelectorType: michael@0: default: michael@0: SkDEBUGFAIL("Unknown X channel selector"); michael@0: } michael@0: michael@0: switch (fYChannelSelector) { michael@0: case SkDisplacementMapEffect::kR_ChannelSelectorType: michael@0: builder->fsCodeAppend("r"); michael@0: break; michael@0: case SkDisplacementMapEffect::kG_ChannelSelectorType: michael@0: builder->fsCodeAppend("g"); michael@0: break; michael@0: case SkDisplacementMapEffect::kB_ChannelSelectorType: michael@0: builder->fsCodeAppend("b"); michael@0: break; michael@0: case SkDisplacementMapEffect::kA_ChannelSelectorType: michael@0: builder->fsCodeAppend("a"); michael@0: break; michael@0: case SkDisplacementMapEffect::kUnknown_ChannelSelectorType: michael@0: default: michael@0: SkDEBUGFAIL("Unknown Y channel selector"); michael@0: } michael@0: builder->fsCodeAppend("-vec2(0.5));\t\t"); michael@0: michael@0: // FIXME : This can be achieved with a "clamp to border" texture repeat mode and michael@0: // a 0 border color instead of computing if cCoords is out of bounds here. michael@0: builder->fsCodeAppendf( michael@0: "bool %s = (%s.x < 0.0) || (%s.y < 0.0) || (%s.x > 1.0) || (%s.y > 1.0);\t\t", michael@0: outOfBounds, cCoords, cCoords, cCoords, cCoords); michael@0: builder->fsCodeAppendf("%s = %s ? vec4(0.0) : ", outputColor, outOfBounds); michael@0: builder->fsAppendTextureLookup(samplers[1], cCoords, coords[1].type()); michael@0: builder->fsCodeAppend(";\n"); michael@0: } michael@0: michael@0: void GrGLDisplacementMapEffect::setData(const GrGLUniformManager& uman, michael@0: const GrDrawEffect& drawEffect) { michael@0: const GrDisplacementMapEffect& displacementMap = michael@0: drawEffect.castEffect(); michael@0: GrTexture* colorTex = displacementMap.texture(1); michael@0: SkScalar scaleX = SkScalarDiv(displacementMap.scale().fX, SkIntToScalar(colorTex->width())); michael@0: SkScalar scaleY = SkScalarDiv(displacementMap.scale().fY, SkIntToScalar(colorTex->height())); michael@0: uman.set2f(fScaleUni, SkScalarToFloat(scaleX), michael@0: colorTex->origin() == kTopLeft_GrSurfaceOrigin ? michael@0: SkScalarToFloat(scaleY) : SkScalarToFloat(-scaleY)); michael@0: } michael@0: michael@0: GrGLEffect::EffectKey GrGLDisplacementMapEffect::GenKey(const GrDrawEffect& drawEffect, michael@0: const GrGLCaps&) { michael@0: const GrDisplacementMapEffect& displacementMap = michael@0: drawEffect.castEffect(); michael@0: michael@0: EffectKey xKey = displacementMap.xChannelSelector(); michael@0: EffectKey yKey = displacementMap.yChannelSelector() << kChannelSelectorKeyBits; michael@0: michael@0: return xKey | yKey; michael@0: } michael@0: #endif