Index: src/core/SkLightingShader_NormalSource.cpp |
diff --git a/src/core/SkLightingShader_NormalSource.cpp b/src/core/SkLightingShader_NormalSource.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b96b1bf083c314b4f6e33f94bb21b5c0c5c0fb59 |
--- /dev/null |
+++ b/src/core/SkLightingShader_NormalSource.cpp |
@@ -0,0 +1,290 @@ |
+/* |
+ * 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<GrFragmentProcessor> 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<NormalMapFP>(); |
+ } |
+ |
+ 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<NormalMapFP>(); |
+ |
+ 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<NormalMapFP>(); |
+ 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<GrFragmentProcessor> 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<GrTexture> normalTexture(GrRefCachedBitmapTexture(context, |
+ fNormalMap, |
+ normParams, |
+ gammaTreatment)); |
+ if (!normalTexture) { |
+ SkErrorInternals::SetError(kInternalError_SkError, "Couldn't convert bitmap to texture."); |
+ return nullptr; |
+ } |
+ |
+ return sk_make_sp<NormalMapFP>(normalTexture, normM, normParams, fInvNormRotation); |
+} |
+ |
+#endif // SK_SUPPORT_GPU |
+ |
+//////////////////////////////////////////////////////////////////////////// |
+ |
+sk_sp<SkFlattenable> 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<NormalMapSourceImpl>(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> 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<NormalMapSourceImpl>(normal, invNormRotation, normLocalM); |
+} |
+ |
+//////////////////////////////////////////////////////////////////////////// |
+ |
+SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkLightingShader::NormalSource) |
+ SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(NormalMapSourceImpl) |
+SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END |
+ |
+//////////////////////////////////////////////////////////////////////////// |