diff options
Diffstat (limited to 'src/core/SkNormalMapSource.cpp')
-rw-r--r-- | src/core/SkNormalMapSource.cpp | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/src/core/SkNormalMapSource.cpp b/src/core/SkNormalMapSource.cpp new file mode 100644 index 0000000000..1b88366690 --- /dev/null +++ b/src/core/SkNormalMapSource.cpp @@ -0,0 +1,253 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkNormalMapSource.h" + +#include "SkArenaAlloc.h" +#include "SkLightingShader.h" +#include "SkMatrix.h" +#include "SkNormalSource.h" +#include "SkPM4f.h" +#include "SkReadBuffer.h" +#include "SkWriteBuffer.h" + +#if SK_SUPPORT_GPU +#include "GrCoordTransform.h" +#include "glsl/GrGLSLFragmentProcessor.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" +#include "SkGr.h" + +class NormalMapFP : public GrFragmentProcessor { +public: + static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> mapFP, + const SkMatrix& invCTM) { + return std::unique_ptr<GrFragmentProcessor>(new NormalMapFP(std::move(mapFP), invCTM)); + } + + const char* name() const override { return "NormalMapFP"; } + + const SkMatrix& invCTM() const { return fInvCTM; } + + std::unique_ptr<GrFragmentProcessor> clone() const override { + return Make(this->childProcessor(0).clone(), fInvCTM); + } + +private: + class GLSLNormalMapFP : public GrGLSLFragmentProcessor { + public: + GLSLNormalMapFP() : fColumnMajorInvCTM22{0.0f} {} + + void emitCode(EmitArgs& args) override { + GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; + + // add uniform + const char* xformUniName = nullptr; + fXformUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat2x2_GrSLType, + kDefault_GrSLPrecision, "Xform", &xformUniName); + + SkString dstNormalColorName("dstNormalColor"); + this->emitChild(0, &dstNormalColorName, args); + fragBuilder->codeAppendf("float3 normal = normalize(%s.rgb - float3(0.5));", + dstNormalColorName.c_str()); + + // If there's no x & y components, return (0, 0, +/- 1) instead to avoid division by 0 + fragBuilder->codeAppend( "if (abs(normal.z) > 0.999) {"); + fragBuilder->codeAppendf(" %s = normalize(float4(0.0, 0.0, normal.z, 0.0));", + args.fOutputColor); + // Else, Normalizing the transformed X and Y, while keeping constant both Z and the + // vector's angle in the XY plane. This maintains the "slope" for the surface while + // appropriately rotating the normal regardless of any anisotropic scaling that occurs. + // Here, we call 'scaling factor' the number that must divide the transformed X and Y so + // that the normal's length remains equal to 1. + fragBuilder->codeAppend( "} else {"); + fragBuilder->codeAppendf(" float2 transformed = %s * normal.xy;", + xformUniName); + fragBuilder->codeAppend( " float scalingFactorSquared = " + "( (transformed.x * transformed.x) " + "+ (transformed.y * transformed.y) )" + "/(1.0 - (normal.z * normal.z));"); + fragBuilder->codeAppendf(" %s = float4(transformed*inversesqrt(scalingFactorSquared)," + "normal.z, 0.0);", + args.fOutputColor); + fragBuilder->codeAppend( "}"); + } + + static void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder* b) { + b->add32(0x0); + } + + private: + void onSetData(const GrGLSLProgramDataManager& pdman, + const GrFragmentProcessor& proc) override { + const NormalMapFP& normalMapFP = proc.cast<NormalMapFP>(); + + const SkMatrix& invCTM = normalMapFP.invCTM(); + fColumnMajorInvCTM22[0] = invCTM.get(SkMatrix::kMScaleX); + fColumnMajorInvCTM22[1] = invCTM.get(SkMatrix::kMSkewY); + fColumnMajorInvCTM22[2] = invCTM.get(SkMatrix::kMSkewX); + fColumnMajorInvCTM22[3] = invCTM.get(SkMatrix::kMScaleY); + pdman.setMatrix2f(fXformUni, fColumnMajorInvCTM22); + } + + private: + // Upper-right 2x2 corner of the inverse of the CTM in column-major form + float fColumnMajorInvCTM22[4]; + GrGLSLProgramDataManager::UniformHandle fXformUni; + }; + + void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override { + GLSLNormalMapFP::GenKey(*this, caps, b); + } + NormalMapFP(std::unique_ptr<GrFragmentProcessor> mapFP, const SkMatrix& invCTM) + : INHERITED(kMappedNormalsFP_ClassID, kNone_OptimizationFlags) + , fInvCTM(invCTM) { + this->registerChildProcessor(std::move(mapFP)); + } + + GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLNormalMapFP; } + + bool onIsEqual(const GrFragmentProcessor& proc) const override { + const NormalMapFP& normalMapFP = proc.cast<NormalMapFP>(); + return fInvCTM == normalMapFP.fInvCTM; + } + + SkMatrix fInvCTM; + + typedef GrFragmentProcessor INHERITED; +}; + +std::unique_ptr<GrFragmentProcessor> SkNormalMapSourceImpl::asFragmentProcessor( + const GrFPArgs& args) const { + std::unique_ptr<GrFragmentProcessor> mapFP = as_SB(fMapShader)->asFragmentProcessor(args); + if (!mapFP) { + return nullptr; + } + + return NormalMapFP::Make(std::move(mapFP), fInvCTM); +} + +#endif // SK_SUPPORT_GPU + +//////////////////////////////////////////////////////////////////////////// + +SkNormalMapSourceImpl::Provider::Provider(const SkNormalMapSourceImpl& source, + SkShaderBase::Context* mapContext) + : fSource(source) + , fMapContext(mapContext) {} + +SkNormalSource::Provider* SkNormalMapSourceImpl::asProvider(const SkShaderBase::ContextRec &rec, + SkArenaAlloc* alloc) const { + SkMatrix normTotalInv; + if (!this->computeNormTotalInverse(rec, &normTotalInv)) { + return nullptr; + } + + // Overriding paint's alpha because we need the normal map's RGB channels to be unpremul'd + SkPaint overridePaint {*(rec.fPaint)}; + overridePaint.setAlpha(0xFF); + SkShaderBase::ContextRec overrideRec(overridePaint, *(rec.fMatrix), rec.fLocalMatrix, + rec.fPreferredDstType, rec.fDstColorSpace); + + auto* context = as_SB(fMapShader)->makeContext(overrideRec, alloc); + if (!context) { + return nullptr; + } + + return alloc->make<Provider>(*this, context); +} + +bool SkNormalMapSourceImpl::computeNormTotalInverse(const SkShaderBase::ContextRec& rec, + SkMatrix* normTotalInverse) const { + SkMatrix total = SkMatrix::Concat(*rec.fMatrix, fMapShader->getLocalMatrix()); + if (rec.fLocalMatrix) { + total.preConcat(*rec.fLocalMatrix); + } + + return total.invert(normTotalInverse); +} + +#define BUFFER_MAX 16 +void SkNormalMapSourceImpl::Provider::fillScanLine(int x, int y, SkPoint3 output[], + int count) const { + SkPMColor tmpNormalColors[BUFFER_MAX]; + + do { + int n = SkTMin(count, BUFFER_MAX); + + fMapContext->shadeSpan(x, y, tmpNormalColors, n); + + for (int i = 0; i < n; i++) { + SkPoint3 tempNorm; + + tempNorm.set(SkIntToScalar(SkGetPackedR32(tmpNormalColors[i])) - 127.0f, + SkIntToScalar(SkGetPackedG32(tmpNormalColors[i])) - 127.0f, + SkIntToScalar(SkGetPackedB32(tmpNormalColors[i])) - 127.0f); + + tempNorm.normalize(); + + + if (!SkScalarNearlyEqual(SkScalarAbs(tempNorm.fZ), 1.0f)) { + SkVector transformed = fSource.fInvCTM.mapVector(tempNorm.fX, tempNorm.fY); + + // Normalizing the transformed X and Y, while keeping constant both Z and the + // vector's angle in the XY plane. This maintains the "slope" for the surface while + // appropriately rotating the normal for any anisotropic scaling that occurs. + // Here, we call scaling factor the number that must divide the transformed X and Y + // so that the normal's length remains equal to 1. + SkScalar scalingFactorSquared = + (SkScalarSquare(transformed.fX) + SkScalarSquare(transformed.fY)) + / (1.0f - SkScalarSquare(tempNorm.fZ)); + SkScalar invScalingFactor = SkScalarInvert(SkScalarSqrt(scalingFactorSquared)); + + output[i].fX = transformed.fX * invScalingFactor; + output[i].fY = transformed.fY * invScalingFactor; + output[i].fZ = tempNorm.fZ; + } else { + output[i] = {0.0f, 0.0f, tempNorm.fZ}; + output[i].normalize(); + } + + SkASSERT(SkScalarNearlyEqual(output[i].length(), 1.0f)); + } + + output += n; + x += n; + count -= n; + } while (count > 0); +} + +//////////////////////////////////////////////////////////////////////////////// + +sk_sp<SkFlattenable> SkNormalMapSourceImpl::CreateProc(SkReadBuffer& buf) { + + sk_sp<SkShader> mapShader = buf.readFlattenable<SkShaderBase>(); + + SkMatrix invCTM; + buf.readMatrix(&invCTM); + + return sk_make_sp<SkNormalMapSourceImpl>(std::move(mapShader), invCTM); +} + +void SkNormalMapSourceImpl::flatten(SkWriteBuffer& buf) const { + this->INHERITED::flatten(buf); + + buf.writeFlattenable(fMapShader.get()); + buf.writeMatrix(fInvCTM); +} + +//////////////////////////////////////////////////////////////////////////// + +sk_sp<SkNormalSource> SkNormalSource::MakeFromNormalMap(sk_sp<SkShader> map, const SkMatrix& ctm) { + SkMatrix invCTM; + + if (!ctm.invert(&invCTM) || !map) { + return nullptr; + } + + return sk_make_sp<SkNormalMapSourceImpl>(std::move(map), invCTM); +} |