/* * 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 "SkBitmapProcShader.h" #include "SkError.h" #include "SkErrorInternals.h" #include "SkLightingShader.h" #include "SkReadBuffer.h" #include "SkWriteBuffer.h" // Genretating vtable SkLightingShader::NormalSource::~NormalSource() {} /////////////////////////////////////////////////////////////////////////////// class NormalMapSourceImpl : public SkLightingShader::NormalSource { public: NormalMapSourceImpl(const SkBitmap &normal, const SkVector &invNormRotation, const SkMatrix *normLocalM) : fNormalMap(normal) , fInvNormRotation(invNormRotation) { if (normLocalM) { fNormLocalMatrix = *normLocalM; } else { fNormLocalMatrix.reset(); } // Pre-cache so future calls to fNormLocalMatrix.getType() are threadsafe. (void)fNormLocalMatrix.getType(); } #if SK_SUPPORT_GPU sk_sp asFragmentProcessor(GrContext*, const SkMatrix& viewM, const SkMatrix* localMatrix, SkFilterQuality, SkSourceGammaTreatment) const override; #endif SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(NormalMapSourceImpl) protected: void flatten(SkWriteBuffer& buf) const override; private: SkBitmap fNormalMap; SkMatrix fNormLocalMatrix; SkVector fInvNormRotation; friend class SkLightingShader::NormalSource; typedef SkLightingShader::NormalSource INHERITED; }; //////////////////////////////////////////////////////////////////////////// #if SK_SUPPORT_GPU #include "GrCoordTransform.h" #include "GrInvariantOutput.h" #include "GrTextureParams.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" #include "SkGr.h" class NormalMapFP : public GrFragmentProcessor { public: NormalMapFP(GrTexture* normal, const SkMatrix& normMatrix, const GrTextureParams& normParams, const SkVector& invNormRotation) : fNormDeviceTransform(kLocal_GrCoordSet, normMatrix, normal, normParams.filterMode()) , fNormalTextureAccess(normal, normParams) , fInvNormRotation(invNormRotation) { this->addCoordTransform(&fNormDeviceTransform); this->addTextureAccess(&fNormalTextureAccess); this->initClassID(); } class GLSLNormalMapFP : public GrGLSLFragmentProcessor { public: GLSLNormalMapFP() { fInvNormRotation.set(0.0f, 0.0f); } void emitCode(EmitArgs& args) override { GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // add uniform const char* xformUniName = nullptr; fXformUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kVec2f_GrSLType, kDefault_GrSLPrecision, "Xform", &xformUniName); fragBuilder->codeAppend("vec4 normalColor = "); fragBuilder->appendTextureLookup(args.fTexSamplers[0], args.fCoords[0].c_str(), args.fCoords[0].getType()); fragBuilder->codeAppend(";"); fragBuilder->codeAppend("vec3 normal = normalColor.rgb - vec3(0.5);"); // TODO: inverse map the light direction vectors in the vertex shader rather than // transforming all the normals here! fragBuilder->codeAppendf( "mat3 m = mat3(%s.x, -%s.y, 0.0, %s.y, %s.x, 0.0, 0.0, 0.0, 1.0);", xformUniName, xformUniName, xformUniName, xformUniName); fragBuilder->codeAppend("normal = normalize(m*normal);"); fragBuilder->codeAppendf("%s = vec4(normal, 0);", args.fOutputColor); } static void GenKey(const GrProcessor& proc, const GrGLSLCaps&, GrProcessorKeyBuilder* b) { b->add32(0x0); } protected: void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override { const NormalMapFP& normalMapFP = proc.cast(); const SkVector& invNormRotation = normalMapFP.invNormRotation(); if (invNormRotation != fInvNormRotation) { pdman.set2fv(fXformUni, 1, &invNormRotation.fX); fInvNormRotation = invNormRotation; } } private: SkVector fInvNormRotation; GrGLSLProgramDataManager::UniformHandle fXformUni; }; void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override { GLSLNormalMapFP::GenKey(*this, caps, b); } const char* name() const override { return "NormalMapFP"; } void onComputeInvariantOutput(GrInvariantOutput* inout) const override { inout->setToUnknown(GrInvariantOutput::ReadInput::kWillNot_ReadInput); } const SkVector& invNormRotation() const { return fInvNormRotation; } private: GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLNormalMapFP; } bool onIsEqual(const GrFragmentProcessor& proc) const override { const NormalMapFP& normalMapFP = proc.cast(); return fNormDeviceTransform == normalMapFP.fNormDeviceTransform && fNormalTextureAccess == normalMapFP.fNormalTextureAccess && fInvNormRotation == normalMapFP.fInvNormRotation; } GrCoordTransform fNormDeviceTransform; GrTextureAccess fNormalTextureAccess; SkVector fInvNormRotation; }; // TODO same code at SkLightingShader.cpp. Refactor to common source! static bool make_mat(const SkBitmap& bm, const SkMatrix& localMatrix1, const SkMatrix* localMatrix2, SkMatrix* result) { result->setIDiv(bm.width(), bm.height()); SkMatrix lmInverse; if (!localMatrix1.invert(&lmInverse)) { return false; } if (localMatrix2) { SkMatrix inv; if (!localMatrix2->invert(&inv)) { return false; } lmInverse.postConcat(inv); } result->preConcat(lmInverse); return true; } sk_sp NormalMapSourceImpl::asFragmentProcessor( GrContext *context, const SkMatrix &viewM, const SkMatrix *localMatrix, SkFilterQuality filterQuality, SkSourceGammaTreatment gammaTreatment) const { // TODO Here, the old code was checking that diffuse map and normal map are same size, that // will be addressed when diffuse maps are factored out of SkLightingShader in a future CL SkMatrix normM; if (!make_mat(fNormalMap, fNormLocalMatrix, localMatrix, &normM)) { return nullptr; } bool doBicubic; GrTextureParams::FilterMode normFilterMode = GrSkFilterQualityToGrFilterMode( SkTMin(filterQuality, kMedium_SkFilterQuality), viewM, fNormLocalMatrix, &doBicubic); SkASSERT(!doBicubic); // TODO: support other tile modes GrTextureParams normParams(SkShader::kClamp_TileMode, normFilterMode); SkAutoTUnref normalTexture(GrRefCachedBitmapTexture(context, fNormalMap, normParams, gammaTreatment)); if (!normalTexture) { SkErrorInternals::SetError(kInternalError_SkError, "Couldn't convert bitmap to texture."); return nullptr; } return sk_make_sp(normalTexture, normM, normParams, fInvNormRotation); } #endif // SK_SUPPORT_GPU //////////////////////////////////////////////////////////////////////////// sk_sp NormalMapSourceImpl::CreateProc(SkReadBuffer& buf) { SkMatrix normLocalM; bool hasNormLocalM = buf.readBool(); if (hasNormLocalM) { buf.readMatrix(&normLocalM); } else { normLocalM.reset(); } SkBitmap normal; if (!buf.readBitmap(&normal)) { return nullptr; } normal.setImmutable(); SkVector invNormRotation = {1,0}; if (!buf.isVersionLT(SkReadBuffer::kLightingShaderWritesInvNormRotation)) { invNormRotation = buf.readPoint(); } return sk_make_sp(normal, invNormRotation, &normLocalM); } void NormalMapSourceImpl::flatten(SkWriteBuffer& buf) const { this->INHERITED::flatten(buf); bool hasNormLocalM = !fNormLocalMatrix.isIdentity(); buf.writeBool(hasNormLocalM); if (hasNormLocalM) { buf.writeMatrix(fNormLocalMatrix); } buf.writeBitmap(fNormalMap); buf.writePoint(fInvNormRotation); } //////////////////////////////////////////////////////////////////////////// sk_sp SkLightingShader::NormalSource::MakeMap( const SkBitmap &normal, const SkVector &invNormRotation, const SkMatrix *normLocalM) { // TODO not checking normal and diffuse maps to be same size, will be addressed when diffuse // maps are factored out of SkLightingShader in a future CL if (normal.isNull() || SkBitmapProcShader::BitmapIsTooBig(normal)) { return nullptr; } SkASSERT(SkScalarNearlyEqual(invNormRotation.lengthSqd(), SK_Scalar1)); return sk_make_sp(normal, invNormRotation, normLocalM); } //////////////////////////////////////////////////////////////////////////// SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkLightingShader::NormalSource) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(NormalMapSourceImpl) SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END ////////////////////////////////////////////////////////////////////////////