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
diff --git a/gyp/core.gypi b/gyp/core.gypi
index 516faf3..7c89960 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 5eabcb3..88aeb7e 100644
--- a/include/core/SkFlattenable.h
+++ b/include/core/SkFlattenable.h
@@ -81,7 +81,7 @@
         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 ca1c341..52b208f 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 @@
                          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 @@
         // 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 @@
         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 @@
     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 @@
     SkMatrix        fNormLocalMatrix;
     SkVector        fInvNormRotation;
 
-    sk_sp<SkLightingShader::NormalSource> fNormalSource;
+    sk_sp<SkNormalSource> fNormalSource;
 
     friend class SkLightingShader;
 
@@ -367,13 +367,11 @@
 }
 
 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 @@
     // 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 @@
 
 // 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 @@
         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 @@
                 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 @@
         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 @@
     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 @@
     // 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 @@
         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 @@
     }
     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 c2b6547..e21b942 100644
--- a/src/core/SkLightingShader.h
+++ b/src/core/SkLightingShader.h
@@ -16,55 +16,6 @@
 
 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 @@
         @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 b96b1bf..0000000
--- 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 0000000..2f52530
--- /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 0000000..0d0c672
--- /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 933973e..cc697ce 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 @@
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPerlinNoiseShader)
     SkGradientShader::InitializeFlattenables();
     SkLightingShader::InitializeFlattenables();
-    SkLightingShader::NormalSource::InitializeFlattenables();
+    SkNormalSource::InitializeFlattenables();
 
 
     // PathEffect