aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/SkLightingShader.cpp119
-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
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