aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar dvonbeck <dvonbeck@google.com>2016-06-27 08:43:58 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2016-06-27 08:43:58 -0700
commit790a70118327a129cb6b48fabe80f4e184c1e67c (patch)
tree72b671c60e8d7ceccba6c17cf6544e564a6a4179
parentb85e63d6ec7c5d7837e2a3a58b6f2be19398dc23 (diff)
SkLightingShader normal vector CPU computation refactor.
The purpose of this change is to refactor the handling of normal maps out of SkLightingShader, laying the groundwork to eventually allow for multiple normal sources. This CL's base was the CL for GPU handling: https://codereview.chromium.org/2043393002/ What this CL includes: - A refactor of the SkLightingShader context's code that deals with reading normals off of a normal map. This is now abstracted out into a NormalSource::Provider class that the context uses. BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2050773002 Review-Url: https://codereview.chromium.org/2050773002
-rw-r--r--gyp/core.gypi3
-rw-r--r--include/core/SkFlattenable.h2
-rw-r--r--src/core/SkLightingShader.cpp111
-rw-r--r--src/core/SkLightingShader.h57
-rw-r--r--src/core/SkLightingShader_NormalSource.cpp290
-rw-r--r--src/core/SkNormalSource.cpp294
-rw-r--r--src/core/SkNormalSource.h76
-rw-r--r--src/ports/SkGlobalInitialization_default.cpp3
8 files changed, 419 insertions, 417 deletions
diff --git a/gyp/core.gypi b/gyp/core.gypi
index 516faf3095..7c89960e66 100644
--- a/gyp/core.gypi
+++ b/gyp/core.gypi
@@ -154,7 +154,6 @@
'<(skia_src_path)/core/SkLayerInfo.h',
'<(skia_src_path)/core/SkLightingShader.h',
'<(skia_src_path)/core/SkLightingShader.cpp',
- '<(skia_src_path)/core/SkLightingShader_NormalSource.cpp',
'<(skia_src_path)/core/SkLinearBitmapPipeline.cpp',
'<(skia_src_path)/core/SkLinearBitmapPipeline.h',
'<(skia_src_path)/core/SkLinearBitmapPipeline_core.h',
@@ -190,6 +189,8 @@
'<(skia_src_path)/core/SkNextID.h',
'<(skia_src_path)/core/SkNinePatchIter.cpp',
'<(skia_src_path)/core/SkNinePatchIter.h',
+ '<(skia_src_path)/core/SkNormalSource.cpp',
+ '<(skia_src_path)/core/SkNormalSource.h',
'<(skia_src_path)/core/SkNx.h',
'<(skia_src_path)/core/SkOpts.cpp',
'<(skia_src_path)/core/SkOpts.h',
diff --git a/include/core/SkFlattenable.h b/include/core/SkFlattenable.h
index 5eabcb3d6d..88aeb7ee38 100644
--- a/include/core/SkFlattenable.h
+++ b/include/core/SkFlattenable.h
@@ -81,7 +81,7 @@ public:
kSkShader_Type,
kSkUnused_Type, // used to be SkUnitMapper
kSkXfermode_Type,
- kNormalSource_Type,
+ kSkNormalSource_Type,
};
typedef sk_sp<SkFlattenable> (*Factory)(SkReadBuffer&);
diff --git a/src/core/SkLightingShader.cpp b/src/core/SkLightingShader.cpp
index ca1c3417b4..52b208f8df 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,7 @@ 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*);
~LightingShaderContext() override;
void shadeSpan(int x, int y, SkPMColor[], int count) override;
@@ -96,9 +97,9 @@ public:
uint32_t getFlags() const override { return fFlags; }
private:
- SkBitmapProcState* fDiffuseState;
- SkBitmapProcState* fNormalState;
- uint32_t fFlags;
+ SkBitmapProcState* fDiffuseState;
+ SkNormalSource::Provider* fNormalProvider;
+ uint32_t fFlags;
typedef SkShader::Context INHERITED;
};
@@ -110,7 +111,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 +121,7 @@ private:
SkMatrix fNormLocalMatrix;
SkVector fInvNormRotation;
- sk_sp<SkLightingShader::NormalSource> fNormalSource;
+ sk_sp<SkNormalSource> fNormalSource;
friend class SkLightingShader;
@@ -367,13 +367,11 @@ 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)
: INHERITED(shader, rec)
, fDiffuseState(diffuseState)
- , fNormalState(normalState) {
+ , fNormalProvider(normalProvider) {
const SkPixmap& pixmap = fDiffuseState->fPixmap;
bool isOpaque = pixmap.isOpaque();
@@ -390,7 +388,7 @@ 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();
}
static inline SkPMColor convert(SkColor3f color, U8CPU a) {
@@ -417,29 +415,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 +443,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 +457,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 +544,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 +579,10 @@ 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) +
+ sizeof(SkBitmapProcState) +
+ fNormalSource->providerSize(rec);
}
SkShader::Context* SkLightingShaderImpl::onCreateContext(const ContextRec& rec,
@@ -623,11 +592,6 @@ 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;
- }
-
void* diffuseStateStorage = (char*)storage + sizeof(LightingShaderContext);
SkBitmapProcState* diffuseState = new (diffuseStateStorage) SkBitmapProcState(fDiffuseMap,
SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
@@ -637,21 +601,18 @@ SkShader::Context* SkLightingShaderImpl::onCreateContext(const ContextRec& rec,
diffuseState->~SkBitmapProcState();
return nullptr;
}
+ void* normalProviderStorage = (char*)storage +
+ sizeof(LightingShaderContext) +
+ 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();
return nullptr;
}
- return new (storage) LightingShaderContext(*this, rec, diffuseState, normalState);
+ return new (storage) LightingShaderContext(*this, rec, diffuseState, normalProvider);
}
///////////////////////////////////////////////////////////////////////////////
@@ -668,8 +629,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