Start hooking shaders into SkRasterPipelineBlitter.

Here first just the simplest, constant-color shaders.

BUG=skia:

GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=4743

Change-Id: I92c6523660e21a1e2aa353524570230282ba5dfe
Reviewed-on: https://skia-review.googlesource.com/4743
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Mike Klein <mtklein@chromium.org>
diff --git a/include/core/SkShader.h b/include/core/SkShader.h
index 5e21c28..48e8419 100644
--- a/include/core/SkShader.h
+++ b/include/core/SkShader.h
@@ -18,9 +18,11 @@
 
 class SkColorFilter;
 class SkColorSpace;
+class SkFallbackAlloc;
 class SkImage;
 class SkPath;
 class SkPicture;
+class SkRasterPipeline;
 class GrContext;
 class GrFragmentProcessor;
 
@@ -98,6 +100,12 @@
     virtual bool isOpaque() const { return false; }
 
     /**
+     *  Returns true if the shader is guaranteed to produce only a single color.
+     *  Subclasses can override this to allow loop-hoisting optimization.
+     */
+    virtual bool isConstant() const { return false; }
+
+    /**
      *  ContextRec acts as a parameter bundle for creating Contexts.
      */
     struct ContextRec {
@@ -399,7 +407,7 @@
 
     //////////////////////////////////////////////////////////////////////////
     //  Factory methods for stock shaders
-    
+
     /**
      *  Call this to create a new "empty" shader, that will not draw anything.
      */
@@ -467,6 +475,8 @@
     SK_DEFINE_FLATTENABLE_TYPE(SkShader)
     SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
 
+    bool appendStages(SkRasterPipeline*, SkColorSpace*, SkFallbackAlloc*) const;
+
 protected:
     void flatten(SkWriteBuffer&) const override;
 
@@ -498,6 +508,10 @@
         return nullptr;
     }
 
+    virtual bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkFallbackAlloc*) const {
+        return false;
+    }
+
 private:
     // This is essentially const, but not officially so it can be modified in
     // constructors.
diff --git a/src/core/SkColorShader.cpp b/src/core/SkColorShader.cpp
index aa7adaf..a761d60 100644
--- a/src/core/SkColorShader.cpp
+++ b/src/core/SkColorShader.cpp
@@ -7,6 +7,9 @@
 
 #include "SkColorShader.h"
 #include "SkColorSpace.h"
+#include "SkFixedAlloc.h"
+#include "SkPM4fPriv.h"
+#include "SkRasterPipeline.h"
 #include "SkReadBuffer.h"
 #include "SkUtils.h"
 
@@ -305,3 +308,30 @@
 bool SkColor4Shader::Color4Context::onChooseBlitProcs(const SkImageInfo& info, BlitState* state) {
     return choose_blitprocs(&fPM4f, info, state);
 }
+
+// To shade a constant color:
+//    1) move the paint color to dst registers
+//    2) load the constant color into the src registers
+//    3) srcin, s' = s*da, modulating the src color by the paint alpha.
+
+bool SkColorShader::onAppendStages(SkRasterPipeline* p,
+                                   SkColorSpace* dst,
+                                   SkFallbackAlloc* scratch) const {
+    auto color = scratch->make<SkPM4f>(SkPM4f_from_SkColor(fColor, dst));
+    p->append(SkRasterPipeline::move_src_dst);
+    p->append(SkRasterPipeline::constant_color, color);
+    // TODO: sRGB -> dst gamut correction if needed
+    p->append(SkRasterPipeline::srcin);
+    return true;
+}
+
+bool SkColor4Shader::onAppendStages(SkRasterPipeline* p,
+                                    SkColorSpace* dst,
+                                    SkFallbackAlloc* scratch) const {
+    auto color = scratch->make<SkPM4f>(fColor4.premul());
+    p->append(SkRasterPipeline::move_src_dst);
+    p->append(SkRasterPipeline::constant_color, color);
+    // TODO: fColorSpace -> dst gamut correction if needed
+    p->append(SkRasterPipeline::srcin);
+    return true;
+}
diff --git a/src/core/SkColorShader.h b/src/core/SkColorShader.h
index 0bd2702..9aee365 100644
--- a/src/core/SkColorShader.h
+++ b/src/core/SkColorShader.h
@@ -25,6 +25,7 @@
     explicit SkColorShader(SkColor c);
 
     bool isOpaque() const override;
+    bool isConstant() const override { return true; }
 
     class ColorShaderContext : public SkShader::Context {
     public:
@@ -64,6 +65,7 @@
         *lum = fColor;
         return true;
     }
+    bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkFallbackAlloc*) const override;
 
 private:
     SkColor fColor;
@@ -78,6 +80,7 @@
     bool isOpaque() const override {
         return SkColorGetA(fCachedByteColor) == 255;
     }
+    bool isConstant() const override { return true; }
 
     class Color4Context : public SkShader::Context {
     public:
@@ -117,12 +120,13 @@
         *lum = fCachedByteColor;
         return true;
     }
+    bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkFallbackAlloc*) const override;
 
 private:
     sk_sp<SkColorSpace> fColorSpace;
     const SkColor4f     fColor4;
     const SkColor       fCachedByteColor;
-    
+
     typedef SkShader INHERITED;
 };
 
diff --git a/src/core/SkRasterPipelineBlitter.cpp b/src/core/SkRasterPipelineBlitter.cpp
index d382eba..2cd84f0 100644
--- a/src/core/SkRasterPipelineBlitter.cpp
+++ b/src/core/SkRasterPipelineBlitter.cpp
@@ -106,19 +106,20 @@
     SkColorFilter* colorFilter = paint.getColorFilter();
 
     // TODO: all temporary
-    if (!supported(dst.info()) || shader || !SkBlendMode_AppendStages(*blend)) {
+    if (!supported(dst.info()) || !SkBlendMode_AppendStages(*blend)) {
         return earlyOut();
     }
 
-    bool is_opaque, is_constant;
+    bool is_opaque   = paintColor->a() == 1.0f,
+         is_constant = true;
+    pipeline->append(SkRasterPipeline::constant_color, paintColor);
+
     if (shader) {
-        is_opaque   = shader->isOpaque();
-        is_constant = false;  // TODO: shader->isConstant()
-        // TODO: append shader stages, of course!
-    } else {
-        is_opaque   = paintColor->a() == 1.0f;
-        is_constant = true;
-        pipeline->append(SkRasterPipeline::constant_color, paintColor);
+        is_opaque   = is_opaque && shader->isOpaque();
+        is_constant = shader->isConstant();
+        if (!shader->appendStages(pipeline, dst.colorSpace(), &blitter->fScratchFallback)) {
+            return earlyOut();
+        }
     }
 
     if (colorFilter) {
diff --git a/src/core/SkShader.cpp b/src/core/SkShader.cpp
index c452d07..c232cf7 100644
--- a/src/core/SkShader.cpp
+++ b/src/core/SkShader.cpp
@@ -257,6 +257,12 @@
 }
 #endif
 
+bool SkShader::appendStages(SkRasterPipeline* pipeline,
+                            SkColorSpace* dst,
+                            SkFallbackAlloc* scratch) const {
+    return this->onAppendStages(pipeline, dst, scratch);
+}
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 sk_sp<SkFlattenable> SkEmptyShader::CreateProc(SkReadBuffer&) {