diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/core/SkLightingShader.cpp | 119 | ||||
-rw-r--r-- | src/core/SkLightingShader.h | 57 | ||||
-rw-r--r-- | src/core/SkLightingShader_NormalSource.cpp | 290 | ||||
-rw-r--r-- | src/core/SkNormalSource.cpp | 294 | ||||
-rw-r--r-- | src/core/SkNormalSource.h | 76 | ||||
-rw-r--r-- | src/ports/SkGlobalInitialization_default.cpp | 3 |
6 files changed, 424 insertions, 415 deletions
diff --git a/src/core/SkLightingShader.cpp b/src/core/SkLightingShader.cpp index ca1c3417b4..f32aa9f411 100644 --- a/src/core/SkLightingShader.cpp +++ b/src/core/SkLightingShader.cpp @@ -12,6 +12,7 @@ #include "SkErrorInternals.h" #include "SkLightingShader.h" #include "SkMathPriv.h" +#include "SkNormalSource.h" #include "SkPoint3.h" #include "SkReadBuffer.h" #include "SkWriteBuffer.h" @@ -55,7 +56,7 @@ public: const sk_sp<SkLights> lights, const SkVector& invNormRotation, const SkMatrix* diffLocalM, const SkMatrix* normLocalM, - sk_sp<SkLightingShader::NormalSource> normalSource) + sk_sp<SkNormalSource> normalSource) : INHERITED(diffLocalM) , fDiffuseMap(diffuse) , fNormalMap(normal) @@ -88,7 +89,8 @@ public: // The context takes ownership of the states. It will call their destructors // but will NOT free the memory. LightingShaderContext(const SkLightingShaderImpl&, const ContextRec&, - SkBitmapProcState* diffuseState, SkBitmapProcState* normalState); + SkBitmapProcState* diffuseState, SkNormalSource::Provider*, + void* heapAllocated); ~LightingShaderContext() override; void shadeSpan(int x, int y, SkPMColor[], int count) override; @@ -96,9 +98,11 @@ public: uint32_t getFlags() const override { return fFlags; } private: - SkBitmapProcState* fDiffuseState; - SkBitmapProcState* fNormalState; - uint32_t fFlags; + SkBitmapProcState* fDiffuseState; + SkNormalSource::Provider* fNormalProvider; + uint32_t fFlags; + + void* fHeapAllocated; typedef SkShader::Context INHERITED; }; @@ -110,7 +114,6 @@ protected: void flatten(SkWriteBuffer&) const override; size_t onContextSize(const ContextRec&) const override; Context* onCreateContext(const ContextRec&, void*) const override; - bool computeNormTotalInverse(const ContextRec& rec, SkMatrix* normTotalInverse) const; private: SkBitmap fDiffuseMap; @@ -121,7 +124,7 @@ private: SkMatrix fNormLocalMatrix; SkVector fInvNormRotation; - sk_sp<SkLightingShader::NormalSource> fNormalSource; + sk_sp<SkNormalSource> fNormalSource; friend class SkLightingShader; @@ -367,13 +370,12 @@ bool SkLightingShaderImpl::isOpaque() const { } SkLightingShaderImpl::LightingShaderContext::LightingShaderContext( - const SkLightingShaderImpl& shader, - const ContextRec& rec, - SkBitmapProcState* diffuseState, - SkBitmapProcState* normalState) + const SkLightingShaderImpl& shader, const ContextRec& rec, SkBitmapProcState* diffuseState, + SkNormalSource::Provider* normalProvider, void* heapAllocated) : INHERITED(shader, rec) , fDiffuseState(diffuseState) - , fNormalState(normalState) { + , fNormalProvider(normalProvider) + , fHeapAllocated(heapAllocated) { const SkPixmap& pixmap = fDiffuseState->fPixmap; bool isOpaque = pixmap.isOpaque(); @@ -390,7 +392,9 @@ SkLightingShaderImpl::LightingShaderContext::~LightingShaderContext() { // The bitmap proc states have been created outside of the context on memory that will be freed // elsewhere. Call the destructors but leave the freeing of the memory to the caller. fDiffuseState->~SkBitmapProcState(); - fNormalState->~SkBitmapProcState(); + fNormalProvider->~Provider(); + + sk_free(fHeapAllocated); } static inline SkPMColor convert(SkColor3f color, U8CPU a) { @@ -417,29 +421,24 @@ static inline SkPMColor convert(SkColor3f color, U8CPU a) { // larger is better (fewer times we have to loop), but we shouldn't // take up too much stack-space (each one here costs 16 bytes) -#define TMP_COUNT 16 - +#define TMP_COUNT 16 +#define BUFFER_MAX ((int)(TMP_COUNT * sizeof(uint32_t))) void SkLightingShaderImpl::LightingShaderContext::shadeSpan(int x, int y, SkPMColor result[], int count) { const SkLightingShaderImpl& lightShader = static_cast<const SkLightingShaderImpl&>(fShader); - uint32_t tmpColor[TMP_COUNT], tmpNormal[TMP_COUNT]; - SkPMColor tmpColor2[2*TMP_COUNT], tmpNormal2[2*TMP_COUNT]; + uint32_t tmpColor[TMP_COUNT]; + SkPMColor tmpColor2[2*TMP_COUNT]; SkBitmapProcState::MatrixProc diffMProc = fDiffuseState->getMatrixProc(); SkBitmapProcState::SampleProc32 diffSProc = fDiffuseState->getSampleProc32(); - SkBitmapProcState::MatrixProc normalMProc = fNormalState->getMatrixProc(); - SkBitmapProcState::SampleProc32 normalSProc = fNormalState->getSampleProc32(); - - int diffMax = fDiffuseState->maxCountForBufferSize(sizeof(tmpColor[0]) * TMP_COUNT); - int normMax = fNormalState->maxCountForBufferSize(sizeof(tmpNormal[0]) * TMP_COUNT); - int max = SkTMin(diffMax, normMax); + int max = fDiffuseState->maxCountForBufferSize(BUFFER_MAX); SkASSERT(fDiffuseState->fPixmap.addr()); - SkASSERT(fNormalState->fPixmap.addr()); - SkPoint3 norm, xformedNorm; + SkASSERT(max <= BUFFER_MAX); + SkPoint3 normals[BUFFER_MAX]; do { int n = count; @@ -450,21 +449,9 @@ void SkLightingShaderImpl::LightingShaderContext::shadeSpan(int x, int y, diffMProc(*fDiffuseState, tmpColor, n, x, y); diffSProc(*fDiffuseState, tmpColor, n, tmpColor2); - normalMProc(*fNormalState, tmpNormal, n, x, y); - normalSProc(*fNormalState, tmpNormal, n, tmpNormal2); + fNormalProvider->fillScanLine(x, y, normals, n); for (int i = 0; i < n; ++i) { - SkASSERT(0xFF == SkColorGetA(tmpNormal2[i])); // opaque -> unpremul - norm.set(SkIntToScalar(SkGetPackedR32(tmpNormal2[i]))-127.0f, - SkIntToScalar(SkGetPackedG32(tmpNormal2[i]))-127.0f, - SkIntToScalar(SkGetPackedB32(tmpNormal2[i]))-127.0f); - norm.normalize(); - - xformedNorm.fX = lightShader.fInvNormRotation.fX * norm.fX + - lightShader.fInvNormRotation.fY * norm.fY; - xformedNorm.fY = -lightShader.fInvNormRotation.fY * norm.fX + - lightShader.fInvNormRotation.fX * norm.fY; - xformedNorm.fZ = norm.fZ; SkColor diffColor = SkUnPreMultiply::PMColorToColor(tmpColor2[i]); @@ -476,7 +463,7 @@ void SkLightingShaderImpl::LightingShaderContext::shadeSpan(int x, int y, if (SkLights::Light::kAmbient_LightType == light.type()) { accum += light.color().makeScale(255.0f); } else { - SkScalar NdotL = xformedNorm.dot(light.dir()); + SkScalar NdotL = normals[i].dot(light.dir()); if (NdotL < 0.0f) { NdotL = 0.0f; } @@ -563,8 +550,7 @@ sk_sp<SkFlattenable> SkLightingShaderImpl::CreateProc(SkReadBuffer& buf) { invNormRotation = buf.readPoint(); } - sk_sp<SkLightingShader::NormalSource> normalSource( - buf.readFlattenable<SkLightingShader::NormalSource>()); + sk_sp<SkNormalSource> normalSource(buf.readFlattenable<SkNormalSource>()); return sk_make_sp<SkLightingShaderImpl>(diffuse, normal, std::move(lights), invNormRotation, &diffLocalM, &normLocalM, std::move(normalSource)); @@ -599,21 +585,8 @@ void SkLightingShaderImpl::flatten(SkWriteBuffer& buf) const { buf.writeFlattenable(fNormalSource.get()); } -bool SkLightingShaderImpl::computeNormTotalInverse(const ContextRec& rec, - SkMatrix* normTotalInverse) const { - SkMatrix total; - total.setConcat(*rec.fMatrix, fNormLocalMatrix); - - const SkMatrix* m = &total; - if (rec.fLocalMatrix) { - total.setConcat(*m, *rec.fLocalMatrix); - m = &total; - } - return m->invert(normTotalInverse); -} - -size_t SkLightingShaderImpl::onContextSize(const ContextRec&) const { - return 2 * sizeof(SkBitmapProcState) + sizeof(LightingShaderContext); +size_t SkLightingShaderImpl::onContextSize(const ContextRec& rec) const { + return sizeof(LightingShaderContext); } SkShader::Context* SkLightingShaderImpl::onCreateContext(const ContextRec& rec, @@ -623,35 +596,31 @@ SkShader::Context* SkLightingShaderImpl::onCreateContext(const ContextRec& rec, // computeTotalInverse was called in SkShader::createContext so we know it will succeed SkAssertResult(this->computeTotalInverse(rec, &diffTotalInv)); - SkMatrix normTotalInv; - if (!this->computeNormTotalInverse(rec, &normTotalInv)) { - return nullptr; - } + size_t heapRequired = sizeof(SkBitmapProcState) + fNormalSource->providerSize(rec); + void* heapAllocated = sk_malloc_throw(heapRequired); - void* diffuseStateStorage = (char*)storage + sizeof(LightingShaderContext); + void* diffuseStateStorage = heapAllocated; SkBitmapProcState* diffuseState = new (diffuseStateStorage) SkBitmapProcState(fDiffuseMap, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, SkMipMap::DeduceTreatment(rec)); SkASSERT(diffuseState); if (!diffuseState->setup(diffTotalInv, *rec.fPaint)) { diffuseState->~SkBitmapProcState(); + sk_free(heapAllocated); return nullptr; } + void* normalProviderStorage = (char*)heapAllocated + sizeof(SkBitmapProcState); - void* normalStateStorage = (char*)storage + - sizeof(LightingShaderContext) + - sizeof(SkBitmapProcState); - SkBitmapProcState* normalState = new (normalStateStorage) SkBitmapProcState(fNormalMap, - SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, - SkMipMap::DeduceTreatment(rec)); - SkASSERT(normalState); - if (!normalState->setup(normTotalInv, *rec.fPaint)) { + SkNormalSource::Provider* normalProvider = fNormalSource->asProvider(rec, + normalProviderStorage); + if (!normalProvider) { diffuseState->~SkBitmapProcState(); - normalState->~SkBitmapProcState(); + sk_free(heapAllocated); return nullptr; } - return new (storage) LightingShaderContext(*this, rec, diffuseState, normalState); + return new (storage) LightingShaderContext(*this, rec, diffuseState, normalProvider, + heapAllocated); } /////////////////////////////////////////////////////////////////////////////// @@ -668,8 +637,12 @@ sk_sp<SkShader> SkLightingShader::Make(const SkBitmap& diffuse, const SkBitmap& } SkASSERT(SkScalarNearlyEqual(invNormRotation.lengthSqd(), SK_Scalar1)); - sk_sp<SkLightingShader::NormalSource> normalSource = - SkLightingShader::NormalSource::MakeMap(normal, invNormRotation, normLocalM); + // TODO: support other tile modes + sk_sp<SkShader> mapShader = SkMakeBitmapShader(normal, SkShader::kClamp_TileMode, + SkShader::kClamp_TileMode, normLocalM, nullptr); + + sk_sp<SkNormalSource> normalSource = SkNormalSource::MakeFromNormalMap(mapShader, + invNormRotation); return sk_make_sp<SkLightingShaderImpl>(diffuse, normal, std::move(lights), invNormRotation, diffLocalM, normLocalM, std::move(normalSource)); diff --git a/src/core/SkLightingShader.h b/src/core/SkLightingShader.h index c2b65472b9..e21b94266e 100644 --- a/src/core/SkLightingShader.h +++ b/src/core/SkLightingShader.h @@ -16,55 +16,6 @@ class SkMatrix; class SK_API SkLightingShader { public: - /** Abstract class that generates or reads in normals for use by SkLightingShader. Currently - implements the GPU side only. Not to be used as part of the API yet. Used internally by - SkLightingShader. - */ - class SK_API NormalSource : public SkFlattenable { - public: - virtual ~NormalSource(); - -#if SK_SUPPORT_GPU - /** Returns a fragment processor that takes no input and outputs a normal (already rotated) - as its output color. To be used as a child fragment processor. - */ - virtual sk_sp<GrFragmentProcessor> asFragmentProcessor( - GrContext* context, - const SkMatrix& viewM, - const SkMatrix* localMatrix, - SkFilterQuality filterQuality, - SkSourceGammaTreatment gammaTreatment) const = 0; -#endif - - /** Returns a normal source that provides normals sourced from the the normal map argument. - Not to be used as part of the API yet. Used internally by SkLightingShader. - - @param normal the normal map - @param invNormRotation rotation applied to the normal map's normals - @param normLocalM the local matrix for the normal map - - nullptr will be returned if - 'normal' is empty - 'normal' too big (> 65535 on either side) - - The normal map is currently assumed to be an 8888 image where the normal at a texel - is retrieved by: - N.x = R-127; - N.y = G-127; - N.z = B-127; - N.normalize(); - The +Z axis is thus encoded in RGB as (127, 127, 255) while the -Z axis is - (127, 127, 0). - */ - static sk_sp<NormalSource> MakeMap(const SkBitmap& normal, const SkVector& invNormRotation, - const SkMatrix* normLocalM); - - SK_DEFINE_FLATTENABLE_TYPE(NormalSource) - SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP() - }; - - - /** Returns a shader that lights the diffuse and normal maps with a set of lights. It returns a shader with a reference count of 1. @@ -74,8 +25,12 @@ public: @param normal the normal map @param lights the lights applied to the normal map @param invNormRotation rotation applied to the normal map's normals - @param diffLocalMatrix the local matrix for the diffuse texture - @param normLocalMatrix the local matrix for the normal map + @param diffLocalMatrix the local matrix for the diffuse map (transform from + texture coordinates to shape source coordinates). nullptr is + interpreted as an identity matrix. + @param normLocalMatrix the local matrix for the normal map (transform from + texture coordinates to shape source coordinates). nullptr is + interpreted as an identity matrix. nullptr will be returned if: either 'diffuse' or 'normal' are empty diff --git a/src/core/SkLightingShader_NormalSource.cpp b/src/core/SkLightingShader_NormalSource.cpp deleted file mode 100644 index b96b1bf083..0000000000 --- a/src/core/SkLightingShader_NormalSource.cpp +++ /dev/null @@ -1,290 +0,0 @@ -/* - * 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 - -//////////////////////////////////////////////////////////////////////////// diff --git a/src/core/SkNormalSource.cpp b/src/core/SkNormalSource.cpp new file mode 100644 index 0000000000..2f52530382 --- /dev/null +++ b/src/core/SkNormalSource.cpp @@ -0,0 +1,294 @@ +/* + * 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 "SkError.h" +#include "SkErrorInternals.h" +#include "SkLightingShader.h" +#include "SkNormalSource.h" +#include "SkReadBuffer.h" +#include "SkWriteBuffer.h" + +// Genretating vtable +SkNormalSource::~SkNormalSource() {} + +/////////////////////////////////////////////////////////////////////////////// + +class NormalMapSourceImpl : public SkNormalSource { +public: + NormalMapSourceImpl(sk_sp<SkShader> mapShader, const SkVector &normRotation) + : fMapShader(std::move(mapShader)) + , fNormRotation(normRotation) {} + +#if SK_SUPPORT_GPU + sk_sp<GrFragmentProcessor> asFragmentProcessor(GrContext*, + const SkMatrix& viewM, + const SkMatrix* localMatrix, + SkFilterQuality, + SkSourceGammaTreatment) const override; +#endif + + SkNormalSource::Provider* asProvider(const SkShader::ContextRec& rec, + void* storage) const override; + + size_t providerSize(const SkShader::ContextRec& rec) const override; + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(NormalMapSourceImpl) + +protected: + void flatten(SkWriteBuffer& buf) const override; + + bool computeNormTotalInverse(const SkShader::ContextRec& rec, SkMatrix* normTotalInverse) const; + +private: + class Provider : public SkNormalSource::Provider { + public: + Provider(const NormalMapSourceImpl& source, SkShader::Context* fMapContext); + + virtual ~Provider() override; + + void fillScanLine(int x, int y, SkPoint3 output[], int count) const override; + private: + const NormalMapSourceImpl& fSource; + SkShader::Context* fMapContext; + + typedef SkNormalSource::Provider INHERITED; + }; + + sk_sp<SkShader> fMapShader; + SkVector fNormRotation; + + friend class SkNormalSource; + + typedef SkNormalSource 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(sk_sp<GrFragmentProcessor> mapFP, const SkVector& normRotation) + : fNormRotation(normRotation) { + this->registerChildProcessor(mapFP); + + this->initClassID<NormalMapFP>(); + } + + class GLSLNormalMapFP : public GrGLSLFragmentProcessor { + public: + GLSLNormalMapFP() { + fNormRotation.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); + + SkString dstNormalColorName("dstNormalColor"); + this->emitChild(0, nullptr, &dstNormalColorName, args); + fragBuilder->codeAppendf("vec3 normal = %s.rgb - vec3(0.5);", + dstNormalColorName.c_str()); + + // 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& normRotation = normalMapFP.normRotation(); + if (normRotation != fNormRotation) { + pdman.set2fv(fXformUni, 1, &normRotation.fX); + fNormRotation = normRotation; + } + } + + private: + SkVector fNormRotation; + 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& normRotation() const { return fNormRotation; } + +private: + GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLNormalMapFP; } + + bool onIsEqual(const GrFragmentProcessor& proc) const override { + const NormalMapFP& normalMapFP = proc.cast<NormalMapFP>(); + return fNormRotation == normalMapFP.fNormRotation; + } + + SkVector fNormRotation; +}; + +sk_sp<GrFragmentProcessor> NormalMapSourceImpl::asFragmentProcessor( + GrContext *context, + const SkMatrix &viewM, + const SkMatrix *localMatrix, + SkFilterQuality filterQuality, + SkSourceGammaTreatment gammaTreatment) const { + + sk_sp<GrFragmentProcessor> mapFP = fMapShader->asFragmentProcessor(context, viewM, + localMatrix, filterQuality, gammaTreatment); + + return sk_make_sp<NormalMapFP>(std::move(mapFP), fNormRotation); +} + +#endif // SK_SUPPORT_GPU + +//////////////////////////////////////////////////////////////////////////// + +NormalMapSourceImpl::Provider::Provider(const NormalMapSourceImpl& source, + SkShader::Context* mapContext) + : fSource(source) + , fMapContext(mapContext) { +} + +NormalMapSourceImpl::Provider::~Provider() { + fMapContext->~Context(); +} + +SkNormalSource::Provider* NormalMapSourceImpl::asProvider( + const SkShader::ContextRec &rec, void *storage) const { + SkMatrix normTotalInv; + if (!this->computeNormTotalInverse(rec, &normTotalInv)) { + return nullptr; + } + + void* mapContextStorage = (char*)storage + sizeof(Provider); + SkShader::Context* context = fMapShader->createContext(rec, mapContextStorage); + if (!context) { + return nullptr; + } + + return new (storage) Provider(*this, context); +} + +size_t NormalMapSourceImpl::providerSize(const SkShader::ContextRec& rec) const { + return sizeof(Provider) + fMapShader->contextSize(rec); +} + +bool NormalMapSourceImpl::computeNormTotalInverse(const SkShader::ContextRec& rec, + SkMatrix* normTotalInverse) const { + SkMatrix total; + total.setConcat(*rec.fMatrix, fMapShader->getLocalMatrix()); + + const SkMatrix* m = &total; + if (rec.fLocalMatrix) { + total.setConcat(*m, *rec.fLocalMatrix); + m = &total; + } + return m->invert(normTotalInverse); +} + +#define BUFFER_MAX 16 +void NormalMapSourceImpl::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(); + + output[i].fX = fSource.fNormRotation.fX * tempNorm.fX + + fSource.fNormRotation.fY * tempNorm.fY; + output[i].fY = -fSource.fNormRotation.fY * tempNorm.fX + + fSource.fNormRotation.fX * tempNorm.fY; + output[i].fZ = tempNorm.fZ; + } + + output += n; + x += n; + count -= n; + } while (count > 0); +} + +//////////////////////////////////////////////////////////////////////////////// + +sk_sp<SkFlattenable> NormalMapSourceImpl::CreateProc(SkReadBuffer& buf) { + + sk_sp<SkShader> mapShader = buf.readFlattenable<SkShader>(); + + SkVector normRotation = {1,0}; + if (!buf.isVersionLT(SkReadBuffer::kLightingShaderWritesInvNormRotation)) { + normRotation = buf.readPoint(); + } + + return sk_make_sp<NormalMapSourceImpl>(std::move(mapShader), normRotation); +} + +void NormalMapSourceImpl::flatten(SkWriteBuffer& buf) const { + this->INHERITED::flatten(buf); + + buf.writeFlattenable(fMapShader.get()); + buf.writePoint(fNormRotation); +} + +//////////////////////////////////////////////////////////////////////////// + +sk_sp<SkNormalSource> SkNormalSource::MakeFromNormalMap(sk_sp<SkShader> map, + const SkVector &normRotation) { + SkASSERT(SkScalarNearlyEqual(normRotation.lengthSqd(), SK_Scalar1)); + if (!map) { + return nullptr; + } + + return sk_make_sp<NormalMapSourceImpl>(std::move(map), normRotation); +} + +//////////////////////////////////////////////////////////////////////////// + +SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkNormalSource) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(NormalMapSourceImpl) +SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END + +//////////////////////////////////////////////////////////////////////////// diff --git a/src/core/SkNormalSource.h b/src/core/SkNormalSource.h new file mode 100644 index 0000000000..0d0c672fa3 --- /dev/null +++ b/src/core/SkNormalSource.h @@ -0,0 +1,76 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkNormalSource_DEFINED +#define SkNormalSource_DEFINED + +#include "SkFlattenable.h" + +/** Abstract class that generates or reads in normals for use by SkLightingShader. Not to be + used as part of the API yet. Used internally by SkLightingShader. +*/ +class SK_API SkNormalSource : public SkFlattenable { +public: + virtual ~SkNormalSource() override; + +#if SK_SUPPORT_GPU + /** Returns a fragment processor that takes no input and outputs a normal (already rotated) + as its output color. To be used as a child fragment processor. + */ + virtual sk_sp<GrFragmentProcessor> asFragmentProcessor( + GrContext* context, + const SkMatrix& viewM, + const SkMatrix* localMatrix, + SkFilterQuality filterQuality, + SkSourceGammaTreatment gammaTreatment) const = 0; +#endif + + class Provider { + public: + virtual ~Provider() {}; + + /** Called for each span of the object being drawn on the CPU. Your subclass should set + the appropriate normals that correspond to the specified device coordinates. + */ + virtual void fillScanLine(int x, int y, SkPoint3 output[], int count) const = 0; + }; + + /** Returns an instance of 'Provider' that provides normals for the CPU pipeline. The + necessary data will be initialized in place at 'storage'. + */ + virtual Provider* asProvider(const SkShader::ContextRec&, void* storage) const = 0; + + /** Amount of memory needed to store a provider object and its dependencies. + */ + virtual size_t providerSize(const SkShader::ContextRec&) const = 0; + + /** Returns a normal source that provides normals sourced from the the normal map argument. + Not to be used as part of the API yet. Used internally by SkLightingShader. + + @param map a shader that outputs the normal map + @param normRotation rotation applied to the normal map's normals, in the + [cos a, sin a] form. + + nullptr will be returned if 'map' is null + + The normal map is currently assumed to be an 8888 image where the normal at a texel + is retrieved by: + N.x = R-127; + N.y = G-127; + N.z = B-127; + N.normalize(); + The +Z axis is thus encoded in RGB as (127, 127, 255) while the -Z axis is + (127, 127, 0). + */ + static sk_sp<SkNormalSource> MakeFromNormalMap(sk_sp<SkShader> map, + const SkVector& normRotation); + + SK_DEFINE_FLATTENABLE_TYPE(SkNormalSource) + SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP() +}; + +#endif diff --git a/src/ports/SkGlobalInitialization_default.cpp b/src/ports/SkGlobalInitialization_default.cpp index 933973eb93..cc697ce0ba 100644 --- a/src/ports/SkGlobalInitialization_default.cpp +++ b/src/ports/SkGlobalInitialization_default.cpp @@ -36,6 +36,7 @@ #include "SkMatrixConvolutionImageFilter.h" #include "SkMergeImageFilter.h" #include "SkMorphologyImageFilter.h" +#include "SkNormalSource.h" #include "SkOffsetImageFilter.h" #include "SkPaintImageFilter.h" #include "SkPerlinNoiseShader.h" @@ -87,7 +88,7 @@ void SkFlattenable::PrivateInitializer::InitEffects() { SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPerlinNoiseShader) SkGradientShader::InitializeFlattenables(); SkLightingShader::InitializeFlattenables(); - SkLightingShader::NormalSource::InitializeFlattenables(); + SkNormalSource::InitializeFlattenables(); // PathEffect |