fission bitmapprocstate

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1753903002

CQ_EXTRA_TRYBOTS=client.skia:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-SKNX_NO_SIMD-Trybot

Review URL: https://codereview.chromium.org/1753903002
diff --git a/src/core/SkBitmapProcShader.cpp b/src/core/SkBitmapProcShader.cpp
index 804c688..895922c 100644
--- a/src/core/SkBitmapProcShader.cpp
+++ b/src/core/SkBitmapProcShader.cpp
@@ -20,12 +20,138 @@
 #include "effects/GrSimpleTextureEffect.h"
 #endif
 
-size_t SkBitmapProcShader::ContextSize() {
+static bool only_scale_and_translate(const SkMatrix& matrix) {
+    unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
+    return (matrix.getType() & ~mask) == 0;
+}
+
+class BitmapProcInfoContext : public SkShader::Context {
+public:
+    // The context takes ownership of the info. It will call its destructor
+    // but will NOT free the memory.
+    BitmapProcInfoContext(const SkShader& shader, const SkShader::ContextRec& rec,
+                            SkBitmapProcInfo* info)
+        : INHERITED(shader, rec)
+        , fInfo(info)
+    {
+        fFlags = 0;
+        if (fInfo->fPixmap.isOpaque() && (255 == this->getPaintAlpha())) {
+            fFlags |= SkShader::kOpaqueAlpha_Flag;
+        }
+
+        if (1 == fInfo->fPixmap.height() && only_scale_and_translate(this->getTotalInverse())) {
+            fFlags |= SkShader::kConstInY32_Flag;
+        }
+    }
+
+    ~BitmapProcInfoContext() override {
+        // The bitmap proc state has been created outside of the context on memory that will be freed
+        // elsewhere. Only call the destructor but leave the freeing of the memory to the caller.
+        fInfo->~SkBitmapProcInfo();
+    }
+    
+    uint32_t getFlags() const override { return fFlags; }
+
+private:
+    SkBitmapProcInfo*   fInfo;
+    uint32_t            fFlags;
+
+    typedef SkShader::Context INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class BitmapProcShaderContext : public BitmapProcInfoContext {
+public:
+    // The context takes ownership of the state. It will call its destructor
+    // but will NOT free the memory.
+    BitmapProcShaderContext(const SkShader& shader, const SkShader::ContextRec& rec,
+                            SkBitmapProcState* state)
+        : INHERITED(shader, rec, state)
+        , fState(state)
+    {}
+
+    void shadeSpan(int x, int y, SkPMColor dstC[], int count) override {
+        const SkBitmapProcState& state = *fState;
+        if (state.getShaderProc32()) {
+            state.getShaderProc32()(&state, x, y, dstC, count);
+            return;
+        }
+
+        const int BUF_MAX = 128;
+        uint32_t buffer[BUF_MAX];
+        SkBitmapProcState::MatrixProc   mproc = state.getMatrixProc();
+        SkBitmapProcState::SampleProc32 sproc = state.getSampleProc32();
+        const int max = state.maxCountForBufferSize(sizeof(buffer[0]) * BUF_MAX);
+
+        SkASSERT(state.fPixmap.addr());
+
+        for (;;) {
+            int n = SkTMin(count, max);
+            SkASSERT(n > 0 && n < BUF_MAX*2);
+            mproc(state, buffer, n, x, y);
+            sproc(state, buffer, n, dstC);
+
+            if ((count -= n) == 0) {
+                break;
+            }
+            SkASSERT(count > 0);
+            x += n;
+            dstC += n;
+        }
+    }
+
+    ShadeProc asAShadeProc(void** ctx) override {
+        if (fState->getShaderProc32()) {
+            *ctx = fState;
+            return (ShadeProc)fState->getShaderProc32();
+        }
+        return nullptr;
+    }
+
+private:
+    SkBitmapProcState*  fState;
+
+    typedef BitmapProcInfoContext INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+size_t SkBitmapProcShader::ContextSize(const ContextRec& rec) {
     // The SkBitmapProcState is stored outside of the context object, with the context holding
     // a pointer to it.
     return sizeof(BitmapProcShaderContext) + sizeof(SkBitmapProcState);
 }
 
+SkShader::Context* SkBitmapProcShader::MakeContext(const SkShader& shader,
+                                                   TileMode tmx, TileMode tmy,
+                                                   const SkBitmapProvider& provider,
+                                                   const ContextRec& rec, void* storage) {
+    SkMatrix totalInverse;
+    // Do this first, so we know the matrix can be inverted.
+    if (!shader.computeTotalInverse(rec, &totalInverse)) {
+        return nullptr;
+    }
+
+    void* stateStorage = (char*)storage + sizeof(BitmapProcShaderContext);
+    SkBitmapProcState* state = new (stateStorage) SkBitmapProcState(provider, tmx, tmy);
+
+    SkASSERT(state);
+    if (!state->setup(totalInverse, *rec.fPaint)) {
+        state->~SkBitmapProcState();
+        return nullptr;
+    }
+
+    return new (storage) BitmapProcShaderContext(shader, rec, state);
+}
+
+SkShader::Context* SkBitmapProcShader::onCreateContext(const ContextRec& rec, void* storage) const {
+    return MakeContext(*this, (TileMode)fTileModeX, (TileMode)fTileModeY,
+                       SkBitmapProvider(fRawBitmap), rec, storage);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
 SkBitmapProcShader::SkBitmapProcShader(const SkBitmap& src, TileMode tmx, TileMode tmy,
                                        const SkMatrix* localMatrix)
         : INHERITED(localMatrix) {
@@ -72,123 +198,7 @@
     return fRawBitmap.isOpaque();
 }
 
-SkShader::Context* SkBitmapProcShader::MakeContext(const SkShader& shader,
-                                                   TileMode tmx, TileMode tmy,
-                                                   const SkBitmapProvider& provider,
-                                                   const ContextRec& rec, void* storage) {
-    SkMatrix totalInverse;
-    // Do this first, so we know the matrix can be inverted.
-    if (!shader.computeTotalInverse(rec, &totalInverse)) {
-        return nullptr;
-    }
-
-    void* stateStorage = (char*)storage + sizeof(BitmapProcShaderContext);
-    SkBitmapProcState* state = new (stateStorage) SkBitmapProcState(provider, tmx, tmy);
-
-    SkASSERT(state);
-    if (!state->chooseProcs(totalInverse, *rec.fPaint)) {
-        state->~SkBitmapProcState();
-        return nullptr;
-    }
-
-    return new (storage) BitmapProcShaderContext(shader, rec, state);
-}
-
-SkShader::Context* SkBitmapProcShader::onCreateContext(const ContextRec& rec, void* storage) const {
-    return MakeContext(*this, (TileMode)fTileModeX, (TileMode)fTileModeY,
-                       SkBitmapProvider(fRawBitmap), rec, storage);
-}
-
-static bool only_scale_and_translate(const SkMatrix& matrix) {
-    unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
-    return (matrix.getType() & ~mask) == 0;
-}
-
-SkBitmapProcShader::BitmapProcShaderContext::BitmapProcShaderContext(const SkShader& shader,
-                                                                     const ContextRec& rec,
-                                                                     SkBitmapProcState* state)
-    : INHERITED(shader, rec)
-    , fState(state)
-{
-    fFlags = 0;
-    if (fState->fPixmap.isOpaque() && (255 == this->getPaintAlpha())) {
-        fFlags |= kOpaqueAlpha_Flag;
-    }
-
-    if (1 == fState->fPixmap.height() && only_scale_and_translate(this->getTotalInverse())) {
-        fFlags |= kConstInY32_Flag;
-    }
-}
-
-SkBitmapProcShader::BitmapProcShaderContext::~BitmapProcShaderContext() {
-    // The bitmap proc state has been created outside of the context on memory that will be freed
-    // elsewhere. Only call the destructor but leave the freeing of the memory to the caller.
-    fState->~SkBitmapProcState();
-}
-
-#define BUF_MAX     128
-
-#define TEST_BUFFER_OVERRITEx
-
-#ifdef TEST_BUFFER_OVERRITE
-    #define TEST_BUFFER_EXTRA   32
-    #define TEST_PATTERN    0x88888888
-#else
-    #define TEST_BUFFER_EXTRA   0
-#endif
-
-void SkBitmapProcShader::BitmapProcShaderContext::shadeSpan(int x, int y, SkPMColor dstC[],
-                                                            int count) {
-    const SkBitmapProcState& state = *fState;
-    if (state.getShaderProc32()) {
-        state.getShaderProc32()(&state, x, y, dstC, count);
-        return;
-    }
-
-    uint32_t buffer[BUF_MAX + TEST_BUFFER_EXTRA];
-    SkBitmapProcState::MatrixProc   mproc = state.getMatrixProc();
-    SkBitmapProcState::SampleProc32 sproc = state.getSampleProc32();
-    int max = state.maxCountForBufferSize(sizeof(buffer[0]) * BUF_MAX);
-
-    SkASSERT(state.fPixmap.addr());
-
-    for (;;) {
-        int n = count;
-        if (n > max) {
-            n = max;
-        }
-        SkASSERT(n > 0 && n < BUF_MAX*2);
-#ifdef TEST_BUFFER_OVERRITE
-        for (int i = 0; i < TEST_BUFFER_EXTRA; i++) {
-            buffer[BUF_MAX + i] = TEST_PATTERN;
-        }
-#endif
-        mproc(state, buffer, n, x, y);
-#ifdef TEST_BUFFER_OVERRITE
-        for (int j = 0; j < TEST_BUFFER_EXTRA; j++) {
-            SkASSERT(buffer[BUF_MAX + j] == TEST_PATTERN);
-        }
-#endif
-        sproc(state, buffer, n, dstC);
-
-        if ((count -= n) == 0) {
-            break;
-        }
-        SkASSERT(count > 0);
-        x += n;
-        dstC += n;
-    }
-}
-
-SkShader::Context::ShadeProc SkBitmapProcShader::BitmapProcShaderContext::asAShadeProc(void** ctx) {
-    if (fState->getShaderProc32()) {
-        *ctx = fState;
-        return (ShadeProc)fState->getShaderProc32();
-    }
-    return nullptr;
-}
-
-///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
 
 #include "SkUnPreMultiply.h"
 #include "SkColorShader.h"
diff --git a/src/core/SkBitmapProcShader.h b/src/core/SkBitmapProcShader.h
index b897c6e..9f4c162 100644
--- a/src/core/SkBitmapProcShader.h
+++ b/src/core/SkBitmapProcShader.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-
 #ifndef SkBitmapProcShader_DEFINED
 #define SkBitmapProcShader_DEFINED
 
@@ -23,7 +21,7 @@
 
     bool isOpaque() const override;
 
-    size_t contextSize(const ContextRec&) const override { return ContextSize(); }
+    size_t contextSize(const ContextRec& rec) const override { return ContextSize(rec); }
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBitmapProcShader)
@@ -34,25 +32,6 @@
 #endif
 
 protected:
-    class BitmapProcShaderContext : public SkShader::Context {
-    public:
-        // The context takes ownership of the state. It will call its destructor
-        // but will NOT free the memory.
-        BitmapProcShaderContext(const SkShader&, const ContextRec&, SkBitmapProcState*);
-        ~BitmapProcShaderContext() override;
-
-        void shadeSpan(int x, int y, SkPMColor dstC[], int count) override;
-        ShadeProc asAShadeProc(void** ctx) override;
-
-        uint32_t getFlags() const override { return fFlags; }
-
-    private:
-        SkBitmapProcState*  fState;
-        uint32_t            fFlags;
-
-        typedef SkShader::Context INHERITED;
-    };
-    
     void flatten(SkWriteBuffer&) const override;
     Context* onCreateContext(const ContextRec&, void* storage) const override;
     bool onIsABitmap(SkBitmap*, SkMatrix*, TileMode*) const override;
@@ -63,7 +42,7 @@
 private:
     friend class SkImageShader;
 
-    static size_t ContextSize();
+    static size_t ContextSize(const ContextRec&);
     static Context* MakeContext(const SkShader&, TileMode tmx, TileMode tmy,
                                 const SkBitmapProvider&, const ContextRec&, void* storage);
 
diff --git a/src/core/SkBitmapProcState.cpp b/src/core/SkBitmapProcState.cpp
index 7c37a15..c169288 100644
--- a/src/core/SkBitmapProcState.cpp
+++ b/src/core/SkBitmapProcState.cpp
@@ -36,25 +36,23 @@
 #include "SkBitmapProcState_filter.h"
 #include "SkBitmapProcState_procs.h"
 
-SkBitmapProcState::SkBitmapProcState(const SkBitmapProvider& provider,
-                                     SkShader::TileMode tmx, SkShader::TileMode tmy)
+SkBitmapProcInfo::SkBitmapProcInfo(const SkBitmapProvider& provider,
+                                   SkShader::TileMode tmx, SkShader::TileMode tmy)
     : fProvider(provider)
+    , fTileModeX(tmx)
+    , fTileModeY(tmy)
     , fBMState(nullptr)
-{
-    fTileModeX = tmx;
-    fTileModeY = tmy;
-}
+{}
 
-SkBitmapProcState::SkBitmapProcState(const SkBitmap& bm,
-                                     SkShader::TileMode tmx, SkShader::TileMode tmy)
+SkBitmapProcInfo::SkBitmapProcInfo(const SkBitmap& bm,
+                                   SkShader::TileMode tmx, SkShader::TileMode tmy)
     : fProvider(SkBitmapProvider(bm))
+    , fTileModeX(tmx)
+    , fTileModeY(tmy)
     , fBMState(nullptr)
-{
-    fTileModeX = tmx;
-    fTileModeY = tmy;
-}
+{}
 
-SkBitmapProcState::~SkBitmapProcState() {
+SkBitmapProcInfo::~SkBitmapProcInfo() {
     SkInPlaceDeleteCheck(fBMState, fBMStateStorage.get());
 }
 
@@ -118,25 +116,16 @@
     return (dimension & ~0x3FFF) == 0;
 }
 
-/*
- *  Analyze filter-quality and matrix, and decide how to implement that.
- *
- *  In general, we cascade down the request level [ High ... None ]
- *  - for a given level, if we can fulfill it, fine, else
- *    - else we downgrade to the next lower level and try again.
- *  We can always fulfill requests for Low and None
- *  - sometimes we will "ignore" Low and give None, but this is likely a legacy perf hack
- *    and may be removed.
- */
-bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
-    fPixmap.reset();
-    fInvMatrix = inv;
-    fFilterLevel = paint.getFilterQuality();
-
+bool SkBitmapProcInfo::init(const SkMatrix& inv, const SkPaint& paint) {
     const int origW = fProvider.info().width();
     const int origH = fProvider.info().height();
+
+    fPixmap.reset();
+    fInvMatrix = inv;
+    fFilterQuality = paint.getFilterQuality();
+
     bool allow_ignore_fractional_translate = true;  // historical default
-    if (kMedium_SkFilterQuality == fFilterLevel) {
+    if (kMedium_SkFilterQuality == fFilterQuality) {
         allow_ignore_fractional_translate = false;
     }
 
@@ -149,9 +138,10 @@
     }
     fPixmap = fBMState->pixmap();
     fInvMatrix = fBMState->invMatrix();
-    fFilterLevel = fBMState->quality();
+    fPaintColor = paint.getColor();
+    fFilterQuality = fBMState->quality();
     SkASSERT(fPixmap.addr());
-    
+
     bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
     bool clampClamp = SkShader::kClamp_TileMode == fTileModeX &&
                       SkShader::kClamp_TileMode == fTileModeY;
@@ -179,30 +169,14 @@
         SkMatrix forward;
         if (fInvMatrix.invert(&forward)) {
             if ((clampClamp && allow_ignore_fractional_translate)
-                           ? just_trans_clamp(forward, fPixmap)
-                           : just_trans_general(forward)) {
+                ? just_trans_clamp(forward, fPixmap)
+                : just_trans_general(forward)) {
                 fInvMatrix.setTranslate(-forward.getTranslateX(), -forward.getTranslateY());
             }
         }
     }
 
-    fInvProc        = fInvMatrix.getMapXYProc();
-    fInvType        = fInvMatrix.getType();
-    fInvSx          = SkScalarToFixed(fInvMatrix.getScaleX());
-    fInvSxFractionalInt = SkScalarToFractionalInt(fInvMatrix.getScaleX());
-    fInvKy          = SkScalarToFixed(fInvMatrix.getSkewY());
-    fInvKyFractionalInt = SkScalarToFractionalInt(fInvMatrix.getSkewY());
-
-    fAlphaScale = SkAlpha255To256(paint.getAlpha());
-
-    fShaderProc32 = nullptr;
-    fShaderProc16 = nullptr;
-    fSampleProc32 = nullptr;
-
-    // recompute the triviality of the matrix here because we may have
-    // changed it!
-
-    trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
+    fInvType = fInvMatrix.getType();
 
     // If our target pixmap is the same as the original, then we revert back to legacy behavior
     // and allow the code to ignore fractional translate.
@@ -214,22 +188,51 @@
         allow_ignore_fractional_translate = true;
     }
 
-    if (kLow_SkFilterQuality == fFilterLevel && allow_ignore_fractional_translate) {
+    if (kLow_SkFilterQuality == fFilterQuality && allow_ignore_fractional_translate) {
         // Only try bilerp if the matrix is "interesting" and
         // the image has a suitable size.
 
         if (fInvType <= SkMatrix::kTranslate_Mask ||
             !valid_for_filtering(fPixmap.width() | fPixmap.height()))
         {
-            fFilterLevel = kNone_SkFilterQuality;
+            fFilterQuality = kNone_SkFilterQuality;
         }
     }
-
-    return this->chooseScanlineProcs(trivialMatrix, clampClamp, paint);
+    
+    return true;
 }
 
-bool SkBitmapProcState::chooseScanlineProcs(bool trivialMatrix, bool clampClamp,
-                                            const SkPaint& paint) {
+/*
+ *  Analyze filter-quality and matrix, and decide how to implement that.
+ *
+ *  In general, we cascade down the request level [ High ... None ]
+ *  - for a given level, if we can fulfill it, fine, else
+ *    - else we downgrade to the next lower level and try again.
+ *  We can always fulfill requests for Low and None
+ *  - sometimes we will "ignore" Low and give None, but this is likely a legacy perf hack
+ *    and may be removed.
+ */
+bool SkBitmapProcState::chooseProcs() {
+    fInvProc            = fInvMatrix.getMapXYProc();
+    fInvSx              = SkScalarToFixed(fInvMatrix.getScaleX());
+    fInvSxFractionalInt = SkScalarToFractionalInt(fInvMatrix.getScaleX());
+    fInvKy              = SkScalarToFixed(fInvMatrix.getSkewY());
+    fInvKyFractionalInt = SkScalarToFractionalInt(fInvMatrix.getSkewY());
+
+    fAlphaScale = SkAlpha255To256(SkColorGetA(fPaintColor));
+
+    fShaderProc32 = nullptr;
+    fShaderProc16 = nullptr;
+    fSampleProc32 = nullptr;
+
+    const bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
+    const bool clampClamp = SkShader::kClamp_TileMode == fTileModeX &&
+                            SkShader::kClamp_TileMode == fTileModeY;
+
+    return this->chooseScanlineProcs(trivialMatrix, clampClamp);
+}
+
+bool SkBitmapProcState::chooseScanlineProcs(bool trivialMatrix, bool clampClamp) {
     fMatrixProc = this->chooseMatrixProc(trivialMatrix);
     // TODO(dominikg): SkASSERT(fMatrixProc) instead? chooseMatrixProc never returns nullptr.
     if (nullptr == fMatrixProc) {
@@ -244,7 +247,7 @@
     // still set to HQ by the time we get here, then we must have installed
     // the shader procs above and can skip all this.
 
-    if (fFilterLevel < kHigh_SkFilterQuality) {
+    if (fFilterQuality < kHigh_SkFilterQuality) {
 
         int index = 0;
         if (fAlphaScale < 256) {  // note: this distinction is not used for D16
@@ -253,7 +256,7 @@
         if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
             index |= 2;
         }
-        if (fFilterLevel > kNone_SkFilterQuality) {
+        if (fFilterQuality > kNone_SkFilterQuality) {
             index |= 4;
         }
         // bits 3,4,5 encoding the source bitmap format
@@ -281,11 +284,11 @@
                 break;
             case kAlpha_8_SkColorType:
                 index |= 32;
-                fPaintPMColor = SkPreMultiplyColor(paint.getColor());
+                fPaintPMColor = SkPreMultiplyColor(fPaintColor);
                 break;
             case kGray_8_SkColorType:
                 index |= 40;
-                fPaintPMColor = SkPreMultiplyColor(paint.getColor());
+                fPaintPMColor = SkPreMultiplyColor(fPaintColor);
                 break;
             default:
                 // TODO(dominikg): Should we ever get here? SkASSERT(false) instead?
@@ -381,7 +384,7 @@
     SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
     SkASSERT(s.fInvKy == 0);
     SkASSERT(count > 0 && colors != nullptr);
-    SkASSERT(kNone_SkFilterQuality == s.fFilterLevel);
+    SkASSERT(kNone_SkFilterQuality == s.fFilterQuality);
 
     const int maxX = s.fPixmap.width() - 1;
     const int maxY = s.fPixmap.height() - 1;
@@ -444,7 +447,7 @@
     SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
     SkASSERT(s.fInvKy == 0);
     SkASSERT(count > 0 && colors != nullptr);
-    SkASSERT(kNone_SkFilterQuality == s.fFilterLevel);
+    SkASSERT(kNone_SkFilterQuality == s.fFilterQuality);
 
     const int stopX = s.fPixmap.width();
     const int stopY = s.fPixmap.height();
@@ -479,7 +482,7 @@
     int iY1   SK_INIT_TO_AVOID_WARNING;
     int iSubY SK_INIT_TO_AVOID_WARNING;
 
-    if (kNone_SkFilterQuality != s.fFilterLevel) {
+    if (kNone_SkFilterQuality != s.fFilterQuality) {
         SkBitmapProcState::MatrixProc mproc = s.getMatrixProc();
         uint32_t xy[2];
 
@@ -556,7 +559,7 @@
     const SkPMColor* row0 = s.fPixmap.addr32(0, iY0);
     SkPMColor color;
 
-    if (kNone_SkFilterQuality != s.fFilterLevel) {
+    if (kNone_SkFilterQuality != s.fFilterQuality) {
         const SkPMColor* row1 = s.fPixmap.addr32(0, iY1);
 
         if (s.fAlphaScale < 256) {
@@ -613,7 +616,7 @@
     static const unsigned kMask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
 
     if (1 == fPixmap.width() && 0 == (fInvType & ~kMask)) {
-        if (kNone_SkFilterQuality == fFilterLevel &&
+        if (kNone_SkFilterQuality == fFilterQuality &&
             fInvType <= SkMatrix::kTranslate_Mask &&
             !this->setupForTranslate()) {
             return DoNothing_shaderproc;
@@ -627,7 +630,7 @@
     if (fInvType > SkMatrix::kTranslate_Mask) {
         return nullptr;
     }
-    if (kNone_SkFilterQuality != fFilterLevel) {
+    if (kNone_SkFilterQuality != fFilterQuality) {
         return nullptr;
     }
 
@@ -723,9 +726,11 @@
     //  scale -vs- affine
     //  filter -vs- nofilter
     if (state.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
-        proc = state.fFilterLevel != kNone_SkFilterQuality ? check_scale_filter : check_scale_nofilter;
+        proc = state.fFilterQuality != kNone_SkFilterQuality ?
+                    check_scale_filter : check_scale_nofilter;
     } else {
-        proc = state.fFilterLevel != kNone_SkFilterQuality ? check_affine_filter : check_affine_nofilter;
+        proc = state.fFilterQuality != kNone_SkFilterQuality ?
+                    check_affine_filter : check_affine_nofilter;
     }
     proc(bitmapXY, count, state.fPixmap.width(), state.fPixmap.height());
 }
@@ -760,7 +765,7 @@
         size >>= 2;
     }
 
-    if (fFilterLevel != kNone_SkFilterQuality) {
+    if (fFilterQuality != kNone_SkFilterQuality) {
         size >>= 1;
     }
 
diff --git a/src/core/SkBitmapProcState.h b/src/core/SkBitmapProcState.h
index a006e09..2d3d0e4 100644
--- a/src/core/SkBitmapProcState.h
+++ b/src/core/SkBitmapProcState.h
@@ -27,10 +27,40 @@
 
 class SkPaint;
 
-struct SkBitmapProcState {
-    SkBitmapProcState(const SkBitmapProvider&, SkShader::TileMode tmx, SkShader::TileMode tmy);
-    SkBitmapProcState(const SkBitmap&, SkShader::TileMode tmx, SkShader::TileMode tmy);
-    ~SkBitmapProcState();
+struct SkBitmapProcInfo {
+    SkBitmapProcInfo(const SkBitmapProvider&, SkShader::TileMode tmx, SkShader::TileMode tmy);
+    SkBitmapProcInfo(const SkBitmap&, SkShader::TileMode tmx, SkShader::TileMode tmy);
+    ~SkBitmapProcInfo();
+
+    const SkBitmapProvider fProvider;
+
+    SkPixmap            fPixmap;
+    SkMatrix            fInvMatrix;         // copy of what is in fBMState, can we remove the dup?
+    SkColor             fPaintColor;
+    SkShader::TileMode  fTileModeX;
+    SkShader::TileMode  fTileModeY;
+    SkFilterQuality     fFilterQuality;
+    SkMatrix::TypeMask  fInvType;
+
+    bool init(const SkMatrix& inverse, const SkPaint&);
+
+private:
+    enum {
+        kBMStateSize = 136  // found by inspection. if too small, we will call new/delete
+    };
+    SkAlignedSStorage<kBMStateSize> fBMStateStorage;
+    SkBitmapController::State* fBMState;
+};
+
+struct SkBitmapProcState : public SkBitmapProcInfo {
+    SkBitmapProcState(const SkBitmapProvider& prov, SkShader::TileMode tmx, SkShader::TileMode tmy)
+        : SkBitmapProcInfo(prov, tmx, tmy) {}
+    SkBitmapProcState(const SkBitmap& bitmap, SkShader::TileMode tmx, SkShader::TileMode tmy)
+        : SkBitmapProcInfo(bitmap, tmx, tmy) {}
+
+    bool setup(const SkMatrix& inv, const SkPaint& paint) {
+        return this->init(inv, paint) && this->chooseProcs();
+    }
 
     typedef void (*ShaderProc32)(const void* ctx, int x, int y, SkPMColor[], int count);
 
@@ -50,11 +80,7 @@
     typedef U16CPU (*FixedTileLowBitsProc)(SkFixed, int);   // returns 0..0xF
     typedef U16CPU (*IntTileProc)(int value, int count);   // returns 0..count-1
 
-    SkPixmap            fPixmap;
-    SkMatrix            fInvMatrix;         // copy of what is in fBMState, can we remove the dup?
-
     SkMatrix::MapXYProc fInvProc;           // chooseProcs
-
     SkFractionalInt     fInvSxFractionalInt;
     SkFractionalInt     fInvKyFractionalInt;
 
@@ -66,14 +92,10 @@
     SkFixed             fFilterOneX;
     SkFixed             fFilterOneY;
 
-    SkPMColor           fPaintPMColor;      // chooseProcs - A8 config
     SkFixed             fInvSx;             // chooseProcs
     SkFixed             fInvKy;             // chooseProcs
+    SkPMColor           fPaintPMColor;      // chooseProcs - A8 config
     uint16_t            fAlphaScale;        // chooseProcs
-    uint8_t             fInvType;           // chooseProcs
-    uint8_t             fTileModeX;         // CONSTRUCTOR
-    uint8_t             fTileModeY;         // CONSTRUCTOR
-    uint8_t             fFilterLevel;       // chooseProcs
 
     /** Platforms implement this, and can optionally overwrite only the
         following fields:
@@ -114,26 +136,15 @@
     SampleProc32 getSampleProc32() const { return fSampleProc32; }
 
 private:
-    friend class SkBitmapProcShader;
-    friend class SkLightingShaderImpl;
-
     ShaderProc32        fShaderProc32;      // chooseProcs
     ShaderProc16        fShaderProc16;      // chooseProcs
     // These are used if the shaderproc is nullptr
     MatrixProc          fMatrixProc;        // chooseProcs
     SampleProc32        fSampleProc32;      // chooseProcs
 
-    const SkBitmapProvider fProvider;
-
-    enum {
-        kBMStateSize = 136  // found by inspection. if too small, we will call new/delete
-    };
-    SkAlignedSStorage<kBMStateSize> fBMStateStorage;
-    SkBitmapController::State* fBMState;
-
     MatrixProc chooseMatrixProc(bool trivial_matrix);
-    bool chooseProcs(const SkMatrix& inv, const SkPaint&);
-    bool chooseScanlineProcs(bool trivialMatrix, bool clampClamp, const SkPaint& paint);
+    bool chooseProcs(); // caller must have called init() first (on our base-class)
+    bool chooseScanlineProcs(bool trivialMatrix, bool clampClamp);
     ShaderProc32 chooseShaderProc32();
 
     // Return false if we failed to setup for fast translate (e.g. overflow)
@@ -199,7 +210,7 @@
                    SkIntToScalar(y) + SK_ScalarHalf, &pt);
 
         SkFixed biasX, biasY;
-        if (s.fFilterLevel == kNone_SkFilterQuality) {
+        if (s.fFilterQuality == kNone_SkFilterQuality) {
             // SkFixed epsilon bias to ensure inverse-mapped bitmap coordinates are rounded
             // consistently WRT geometry.  Note that we only need the bias for positive scales:
             // for negative scales, the rounding is intrinsically correct.
diff --git a/src/core/SkBitmapProcState_matrixProcs.cpp b/src/core/SkBitmapProcState_matrixProcs.cpp
index be0e371..de97a90 100644
--- a/src/core/SkBitmapProcState_matrixProcs.cpp
+++ b/src/core/SkBitmapProcState_matrixProcs.cpp
@@ -485,7 +485,7 @@
 SkBitmapProcState::MatrixProc SkBitmapProcState::chooseMatrixProc(bool trivial_matrix) {
 //    test_int_tileprocs();
     // check for our special case when there is no scale/affine/perspective
-    if (trivial_matrix && kNone_SkFilterQuality == fFilterLevel) {
+    if (trivial_matrix && kNone_SkFilterQuality == fFilterQuality) {
         fIntTileProcY = choose_int_tile_proc(fTileModeY);
         switch (fTileModeX) {
             case SkShader::kClamp_TileMode:
@@ -498,7 +498,7 @@
     }
 
     int index = 0;
-    if (fFilterLevel != kNone_SkFilterQuality) {
+    if (fFilterQuality != kNone_SkFilterQuality) {
         index = 1;
     }
     if (fInvType & SkMatrix::kPerspective_Mask) {
diff --git a/src/core/SkBitmapProcState_sample.h b/src/core/SkBitmapProcState_sample.h
index 4a02288..8526c47 100644
--- a/src/core/SkBitmapProcState_sample.h
+++ b/src/core/SkBitmapProcState_sample.h
@@ -25,7 +25,7 @@
                               const uint32_t* SK_RESTRICT xy,
                               int count, SkPMColor* SK_RESTRICT colors) {
     SkASSERT(count > 0 && colors != nullptr);
-    SkASSERT(kNone_SkFilterQuality == s.fFilterLevel);
+    SkASSERT(kNone_SkFilterQuality == s.fFilterQuality);
     SkDEBUGCODE(CHECKSTATE(s);)
 
 #ifdef PREAMBLE
@@ -68,7 +68,7 @@
                             int count, SkPMColor* SK_RESTRICT colors) {
     SkASSERT(count > 0 && colors != nullptr);
     SkASSERT(s.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask));
-    SkASSERT(kNone_SkFilterQuality == s.fFilterLevel);
+    SkASSERT(kNone_SkFilterQuality == s.fFilterQuality);
     SkDEBUGCODE(CHECKSTATE(s);)
 
 #ifdef PREAMBLE
@@ -122,7 +122,7 @@
                           const uint32_t* SK_RESTRICT xy,
                            int count, SkPMColor* SK_RESTRICT colors) {
     SkASSERT(count > 0 && colors != nullptr);
-    SkASSERT(s.fFilterLevel != kNone_SkFilterQuality);
+    SkASSERT(s.fFilterQuality != kNone_SkFilterQuality);
     SkDEBUGCODE(CHECKSTATE(s);)
 
 #ifdef PREAMBLE
@@ -168,7 +168,7 @@
                             const uint32_t* SK_RESTRICT xy,
                             int count, SkPMColor* SK_RESTRICT colors) {
     SkASSERT(count > 0 && colors != nullptr);
-    SkASSERT(s.fFilterLevel != kNone_SkFilterQuality);
+    SkASSERT(s.fFilterQuality != kNone_SkFilterQuality);
     SkDEBUGCODE(CHECKSTATE(s);)
 
 #ifdef PREAMBLE
diff --git a/src/core/SkBitmapProcState_shaderproc.h b/src/core/SkBitmapProcState_shaderproc.h
index 5324456..523b562 100644
--- a/src/core/SkBitmapProcState_shaderproc.h
+++ b/src/core/SkBitmapProcState_shaderproc.h
@@ -19,7 +19,7 @@
                              SkMatrix::kScale_Mask)) == 0);
     SkASSERT(s.fInvKy == 0);
     SkASSERT(count > 0 && colors != nullptr);
-    SkASSERT(s.fFilterLevel != kNone_SkFilterQuality);
+    SkASSERT(s.fFilterQuality != kNone_SkFilterQuality);
     SkDEBUGCODE(CHECKSTATE(s);)
 
     const unsigned maxX = s.fPixmap.width() - 1;
diff --git a/src/core/SkLightingShader.cpp b/src/core/SkLightingShader.cpp
index 9d6c586..c8a5a2b 100644
--- a/src/core/SkLightingShader.cpp
+++ b/src/core/SkLightingShader.cpp
@@ -671,7 +671,7 @@
     SkBitmapProcState* diffuseState = new (diffuseStateStorage) SkBitmapProcState(fDiffuseMap,
                                               SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
     SkASSERT(diffuseState);
-    if (!diffuseState->chooseProcs(diffTotalInv, *rec.fPaint)) {
+    if (!diffuseState->setup(diffTotalInv, *rec.fPaint)) {
         diffuseState->~SkBitmapProcState();
         return nullptr;
     }
@@ -680,7 +680,7 @@
     SkBitmapProcState* normalState = new (normalStateStorage) SkBitmapProcState(fNormalMap,
                                             SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
     SkASSERT(normalState);
-    if (!normalState->chooseProcs(normTotalInv, *rec.fPaint)) {
+    if (!normalState->setup(normTotalInv, *rec.fPaint)) {
         diffuseState->~SkBitmapProcState();
         normalState->~SkBitmapProcState();
         return nullptr;
diff --git a/src/image/SkImageShader.cpp b/src/image/SkImageShader.cpp
index 1c4d422..a07603c 100644
--- a/src/image/SkImageShader.cpp
+++ b/src/image/SkImageShader.cpp
@@ -42,8 +42,8 @@
     return fImage->isOpaque();
 }
 
-size_t SkImageShader::contextSize(const ContextRec&) const {
-    return SkBitmapProcShader::ContextSize();
+size_t SkImageShader::contextSize(const ContextRec& rec) const {
+    return SkBitmapProcShader::ContextSize(rec);
 }
 
 SkShader::Context* SkImageShader::onCreateContext(const ContextRec& rec, void* storage) const {
diff --git a/src/opts/SkBitmapProcState_opts_SSE2.cpp b/src/opts/SkBitmapProcState_opts_SSE2.cpp
index 8247d87..7a3bef0 100644
--- a/src/opts/SkBitmapProcState_opts_SSE2.cpp
+++ b/src/opts/SkBitmapProcState_opts_SSE2.cpp
@@ -15,7 +15,7 @@
                                    const uint32_t* xy,
                                    int count, uint32_t* colors) {
     SkASSERT(count > 0 && colors != nullptr);
-    SkASSERT(s.fFilterLevel != kNone_SkFilterQuality);
+    SkASSERT(s.fFilterQuality != kNone_SkFilterQuality);
     SkASSERT(kN32_SkColorType == s.fPixmap.colorType());
     SkASSERT(s.fAlphaScale == 256);
 
@@ -121,7 +121,7 @@
                                   const uint32_t* xy,
                                   int count, uint32_t* colors) {
     SkASSERT(count > 0 && colors != nullptr);
-    SkASSERT(s.fFilterLevel != kNone_SkFilterQuality);
+    SkASSERT(s.fFilterQuality != kNone_SkFilterQuality);
     SkASSERT(kN32_SkColorType == s.fPixmap.colorType());
     SkASSERT(s.fAlphaScale < 256);
 
diff --git a/src/opts/SkBitmapProcState_opts_SSSE3.cpp b/src/opts/SkBitmapProcState_opts_SSSE3.cpp
index 2de8e40..17d7da9 100644
--- a/src/opts/SkBitmapProcState_opts_SSSE3.cpp
+++ b/src/opts/SkBitmapProcState_opts_SSSE3.cpp
@@ -394,7 +394,7 @@
                                      const uint32_t* xy,
                                      int count, uint32_t* colors) {
     SkASSERT(count > 0 && colors != nullptr);
-    SkASSERT(s.fFilterLevel != kNone_SkFilterQuality);
+    SkASSERT(s.fFilterQuality != kNone_SkFilterQuality);
     SkASSERT(kN32_SkColorType == s.fPixmap.colorType());
     if (has_alpha) {
         SkASSERT(s.fAlphaScale < 256);
@@ -586,7 +586,7 @@
                                        const uint32_t* xy,
                                        int count, uint32_t* colors) {
     SkASSERT(count > 0 && colors != nullptr);
-    SkASSERT(s.fFilterLevel != kNone_SkFilterQuality);
+    SkASSERT(s.fFilterQuality != kNone_SkFilterQuality);
     SkASSERT(kN32_SkColorType == s.fPixmap.colorType());
     if (has_alpha) {
         SkASSERT(s.fAlphaScale < 256);
diff --git a/src/opts/SkBitmapProcState_opts_mips_dsp.cpp b/src/opts/SkBitmapProcState_opts_mips_dsp.cpp
index a80e955..10f80c2 100644
--- a/src/opts/SkBitmapProcState_opts_mips_dsp.cpp
+++ b/src/opts/SkBitmapProcState_opts_mips_dsp.cpp
@@ -17,7 +17,7 @@
                                                 int count, SkPMColor* SK_RESTRICT colors) {
     SkASSERT(count > 0 && colors != nullptr);
     SkASSERT(s.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask));
-    SkASSERT(kNone_SkFilterQuality == s.fFilterLevel);
+    SkASSERT(kNone_SkFilterQuality == s.fFilterQuality);
     const SkPMColor* SK_RESTRICT table = s.fPixmap.ctable()->readColors();
     const uint8_t* SK_RESTRICT srcAddr = (const uint8_t*)s.fPixmap.addr();
     srcAddr = (const uint8_t*)((const char*)srcAddr + xy[0] * s.fPixmap.rowBytes());
@@ -246,7 +246,7 @@
 
     switch (fPixmap.colorType()) {
         case kIndex_8_SkColorType:
-            if (justDx && kNone_SkFilterQuality == fFilterLevel) {
+            if (justDx && kNone_SkFilterQuality == fFilterQuality) {
                 if (isOpaque) {
                     fSampleProc32 = SI8_opaque_D32_nofilter_DX_mips_dsp;
                     fShaderProc32 = nullptr;