add a bulk shader path through SkRasterPipelineBlitter
authorMike Klein <mtklein@chromium.org>
Tue, 23 May 2017 19:06:17 +0000 (15:06 -0400)
committerSkia Commit-Bot <skia-commit-bot@chromium.org>
Wed, 24 May 2017 15:26:15 +0000 (15:26 +0000)
Change-Id: I76f1f0c29b3408bffb7732ee1afb8337c478605e
Reviewed-on: https://skia-review.googlesource.com/17768
Commit-Queue: Mike Klein <mtklein@chromium.org>
Reviewed-by: Florin Malita <fmalita@chromium.org>
Reviewed-by: Herb Derby <herb@google.com>
src/core/SkRasterPipelineBlitter.cpp
src/core/SkShader.cpp

index e5c5325..023ff1e 100644 (file)
@@ -10,6 +10,7 @@
 #include "SkBlendModePriv.h"
 #include "SkColor.h"
 #include "SkColorFilter.h"
+#include "SkColorSpaceXformer.h"
 #include "SkOpts.h"
 #include "SkPM4f.h"
 #include "SkPM4fPriv.h"
 
 class SkRasterPipelineBlitter final : public SkBlitter {
 public:
-    // Create using paint.getShader() or paint.getColor() if there is no shader.
-    // If there's a shader, we will modulate the shader color by the paint alpha.
-    static SkBlitter* Create(const SkPixmap&, const SkPaint&, SkArenaAlloc*, const SkMatrix& ctm);
-
-    // Create using pre-built shader pipeline.
-    // This pre-built pipeline must already have handled modulating with the paint alpha.
+    // This is our common entrypoint for creating the blitter once we've sorted out shaders.
     static SkBlitter* Create(const SkPixmap&, const SkPaint&, SkArenaAlloc*,
-                             const SkRasterPipeline& shaderPipeline,
+                             const SkRasterPipeline& shaderPipeline, SkShader::Context* shaderCtx,
                              bool is_opaque, bool is_constant, bool wants_dither);
 
-    SkRasterPipelineBlitter(SkPixmap dst, SkBlendMode blend, SkArenaAlloc* alloc)
+    SkRasterPipelineBlitter(SkPixmap dst,
+                            SkBlendMode blend,
+                            SkArenaAlloc* alloc,
+                            SkShader::Context* shaderCtx)
         : fDst(dst)
         , fBlend(blend)
         , fAlloc(alloc)
+        , fShaderCtx(shaderCtx)
         , fColorPipeline(alloc)
     {}
 
@@ -51,10 +51,14 @@ private:
     void maybe_clamp  (SkRasterPipeline*) const;
     void append_store (SkRasterPipeline*) const;
 
-    SkPixmap         fDst;
-    SkBlendMode      fBlend;
-    SkArenaAlloc*    fAlloc;
-    SkRasterPipeline fColorPipeline;
+    // If we have an SkShader::Context, use it to fill our shader buffer.
+    void maybe_shade(int x, int y, int w);
+
+    SkPixmap           fDst;
+    SkBlendMode        fBlend;
+    SkArenaAlloc*      fAlloc;
+    SkShader::Context* fShaderCtx;
+    SkRasterPipeline   fColorPipeline;
 
     // We may be able to specialize blitH() into a memset.
     bool     fCanMemsetInBlitH = false;
@@ -68,12 +72,15 @@ private:
 
     // These values are pointed to by the blit pipelines above,
     // which allows us to adjust them from call to call.
+    void*              fShaderOutput    = nullptr;
     void*              fDstPtr          = nullptr;
     const void*        fMaskPtr         = nullptr;
     float              fCurrentCoverage = 0.0f;
     int                fCurrentY        = 0;
     SkJumper_DitherCtx fDitherCtx = { &fCurrentY, 0.0f };
 
+    std::vector<SkPM4f> fShaderBuffer;
+
     typedef SkBlitter INHERITED;
 };
 
@@ -81,7 +88,54 @@ SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap& dst,
                                          const SkPaint& paint,
                                          const SkMatrix& ctm,
                                          SkArenaAlloc* alloc) {
-    return SkRasterPipelineBlitter::Create(dst, paint, alloc, ctm);
+    SkColorSpace* dstCS = dst.colorSpace();
+    auto paintColor = alloc->make<SkPM4f>(SkPM4f_from_SkColor(paint.getColor(), dstCS));
+    auto shader = paint.getShader();
+
+    SkRasterPipeline_<256> shaderPipeline;
+    if (!shader) {
+        // Having no shader makes things nice and easy... just use the paint color.
+        shaderPipeline.append(SkRasterPipeline::constant_color, paintColor);
+        bool is_opaque    = paintColor->a() == 1.0f,
+             is_constant  = true,
+             wants_dither = false;
+        return SkRasterPipelineBlitter::Create(dst, paint, alloc,
+                                               shaderPipeline, nullptr,
+                                               is_opaque, is_constant, wants_dither);
+    }
+
+    bool is_opaque    = shader->isOpaque() && paintColor->a() == 1.0f;
+    bool is_constant  = shader->isConstant();
+    bool wants_dither = shader->asAGradient(nullptr) >= SkShader::kLinear_GradientType;
+
+    // See if the shader can express itself by appending pipeline stages.
+    if (shader->appendStages(&shaderPipeline, dstCS, alloc, ctm, paint)) {
+        if (paintColor->a() != 1.0f) {
+            shaderPipeline.append(SkRasterPipeline::scale_1_float, &paintColor->fVec[SkPM4f::A]);
+        }
+        return SkRasterPipelineBlitter::Create(dst, paint, alloc,
+                                               shaderPipeline, nullptr,
+                                               is_opaque, is_constant, wants_dither);
+    }
+
+    // No, the shader wants us to create a context and call shadeSpan4f().
+    SkASSERT(!is_constant);  // All constant shaders should be able to appendStages().
+
+    if (dstCS) {
+        // We need to transform the shader into the dst color space, and extend its lifetime.
+        sk_sp<SkShader> in_dstCS = SkColorSpaceXformer::Make(sk_ref_sp(dstCS))->apply(shader);
+        shader = in_dstCS.get();
+        alloc->make<sk_sp<SkShader>>(std::move(in_dstCS));
+    }
+    SkShader::ContextRec rec(paint, ctm, nullptr, SkShader::ContextRec::kPM4f_DstType, dstCS);
+    SkShader::Context* shaderCtx = shader->makeContext(rec, alloc);
+    if (!shaderCtx) {
+        // When a shader fails to create a context, it has vetoed drawing entirely.
+        return alloc->make<SkNullBlitter>();
+    }
+    return SkRasterPipelineBlitter::Create(dst, paint, alloc,
+                                           shaderPipeline, shaderCtx,
+                                           is_opaque, is_constant, wants_dither);
 }
 
 SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap& dst,
@@ -92,59 +146,38 @@ SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap& dst,
                                          SkArenaAlloc* alloc) {
     bool is_constant = false;  // If this were the case, it'd be better to just set a paint color.
     return SkRasterPipelineBlitter::Create(dst, paint, alloc,
-                                           shaderPipeline, is_opaque, is_constant, wants_dither);
-}
-
-SkBlitter* SkRasterPipelineBlitter::Create(const SkPixmap& dst,
-                                           const SkPaint& paint,
-                                           SkArenaAlloc* alloc,
-                                           const SkMatrix& ctm) {
-    auto paintColor = alloc->make<SkPM4f>(SkPM4f_from_SkColor(paint.getColor(),
-                                                              dst.colorSpace()));
-    SkRasterPipeline_<256> shaderPipeline;
-    if (auto shader = paint.getShader()) {
-        if (!shader->appendStages(&shaderPipeline, dst.colorSpace(), alloc, ctm, paint)) {
-            // When a shader fails to append stages, it means it has vetoed drawing entirely.
-            return alloc->make<SkNullBlitter>();
-        }
-
-        bool is_opaque = shader->isOpaque();
-        if (paintColor->a() != 1.0f) {
-            shaderPipeline.append(SkRasterPipeline::scale_1_float, &paintColor->fVec[SkPM4f::A]);
-            is_opaque = false;
-        }
-
-        bool is_constant  = shader->isConstant();
-        bool wants_dither = shader->asAGradient(nullptr) >= SkShader::kLinear_GradientType;
-        return Create(dst, paint, alloc, shaderPipeline, is_opaque, is_constant, wants_dither);
-    }
-
-    shaderPipeline.append(SkRasterPipeline::constant_color, paintColor);
-    bool is_opaque    = paintColor->a() == 1.0f,
-         is_constant  = true,
-         wants_dither = false;
-    return Create(dst, paint, alloc, shaderPipeline, is_opaque, is_constant, wants_dither);
+                                           shaderPipeline, nullptr,
+                                           is_opaque, is_constant, wants_dither);
 }
 
 SkBlitter* SkRasterPipelineBlitter::Create(const SkPixmap& dst,
                                            const SkPaint& paint,
                                            SkArenaAlloc* alloc,
                                            const SkRasterPipeline& shaderPipeline,
+                                           SkShader::Context* shaderCtx,
                                            bool is_opaque,
                                            bool is_constant,
                                            bool wants_dither) {
-    auto blitter = alloc->make<SkRasterPipelineBlitter>(dst, paint.getBlendMode(), alloc);
+    auto blitter = alloc->make<SkRasterPipelineBlitter>(dst,
+                                                        paint.getBlendMode(),
+                                                        alloc,
+                                                        shaderCtx);
 
     // Our job in this factory is to fill out the blitter's color pipeline.
     // This is the common front of the full blit pipelines, each constructed lazily on first use.
     // The full blit pipelines handle reading and writing the dst, blending, coverage, dithering.
     auto colorPipeline = &blitter->fColorPipeline;
 
-    // Let's get the shader in first.  If the shader's not constant, it'll need seeding with x,y.
-    if (!is_constant) {
-        colorPipeline->append(SkRasterPipeline::seed_shader, &blitter->fCurrentY);
+    // Let's get the shader in first.
+    if (shaderCtx) {
+        colorPipeline->append(SkRasterPipeline::load_f32, &blitter->fShaderOutput);
+    } else {
+        // If the shader's not constant, it'll need seeding with x,y.
+        if (!is_constant) {
+            colorPipeline->append(SkRasterPipeline::seed_shader, &blitter->fCurrentY);
+        }
+        colorPipeline->extend(shaderPipeline);
     }
-    colorPipeline->extend(shaderPipeline);
 
     // If there's a color filter it comes next.
     if (auto colorFilter = paint.getColorFilter()) {
@@ -253,6 +286,17 @@ void SkRasterPipelineBlitter::maybe_clamp(SkRasterPipeline* p) const {
     }
 }
 
+void SkRasterPipelineBlitter::maybe_shade(int x, int y, int w) {
+    if (fShaderCtx) {
+        if (w > SkToInt(fShaderBuffer.size())) {
+            fShaderBuffer.resize(w);
+        }
+        fShaderCtx->shadeSpan4f(x,y, fShaderBuffer.data(), w);
+        // We'll be reading from fShaderOutput + x.
+        fShaderOutput = fShaderBuffer.data() - x;
+    }
+}
+
 void SkRasterPipelineBlitter::blitH(int x, int y, int w) {
     fDstPtr = fDst.writable_addr(0,y);
     fCurrentY = y;
@@ -278,6 +322,7 @@ void SkRasterPipelineBlitter::blitH(int x, int y, int w) {
         this->append_store(&p);
         fBlitH = p.compile();
     }
+    this->maybe_shade(x,y,w);
     fBlitH(x,w);
 }
 
@@ -306,6 +351,7 @@ void SkRasterPipelineBlitter::blitAntiH(int x, int y, const SkAlpha aa[], const
             case 0x00:                       break;
             case 0xff: this->blitH(x,y,run); break;
             default:
+                this->maybe_shade(x,y,run);
                 fCurrentCoverage = *aa * (1/255.0f);
                 fBlitAntiH(x,run);
         }
@@ -354,6 +400,7 @@ void SkRasterPipelineBlitter::blitMask(const SkMask& mask, const SkIRect& clip)
         fDstPtr = fDst.writable_addr(0,y);
         fCurrentY = y;
 
+        this->maybe_shade(x,y,clip.width());
         switch (mask.fFormat) {
             case SkMask::kA8_Format:
                 fMaskPtr = mask.getAddr8(x,y)-x;
index c8d5f08..ff5b400 100644 (file)
@@ -264,42 +264,7 @@ bool SkShader::appendStages(SkRasterPipeline* p,
                             const SkMatrix& ctm,
                             const SkPaint& paint,
                             const SkMatrix* localM) const {
-    SkRasterPipeline_<256> subclass;
-    if (this->onAppendStages(&subclass, dstCS, alloc, ctm, paint, localM)) {
-        p->extend(subclass);
-        return true;
-    }
-
-    // SkShader::Context::shadeSpan4f() handles the paint opacity internally,
-    // but SkRasterPipelineBlitter applies it as a separate stage.
-    // We skip the internal shadeSpan4f() step by forcing the paint opaque.
-    SkTCopyOnFirstWrite<SkPaint> opaquePaint(paint);
-    if (paint.getAlpha() != SK_AlphaOPAQUE) {
-        opaquePaint.writable()->setAlpha(SK_AlphaOPAQUE);
-    }
-
-    ContextRec rec(*opaquePaint, ctm, localM, ContextRec::kPM4f_DstType, dstCS);
-
-    struct CallbackCtx : SkJumper_CallbackCtx {
-        sk_sp<SkShader> shader;
-        Context*        ctx;
-    };
-    auto cb = alloc->make<CallbackCtx>();
-    cb->shader = dstCS ? SkColorSpaceXformer::Make(sk_ref_sp(dstCS))->apply(this)
-                       : sk_ref_sp(const_cast<SkShader*>(this));
-    cb->ctx = cb->shader->makeContext(rec, alloc);
-    cb->fn  = [](SkJumper_CallbackCtx* self, int active_pixels) {
-        auto c = (CallbackCtx*)self;
-        int x = (int)c->rgba[0],
-            y = (int)c->rgba[1];
-        c->ctx->shadeSpan4f(x,y, (SkPM4f*)c->rgba, active_pixels);
-    };
-
-    if (cb->ctx) {
-        p->append(SkRasterPipeline::callback, cb);
-        return true;
-    }
-    return false;
+    return this->onAppendStages(p, dstCS, alloc, ctm, paint, localM);
 }
 
 bool SkShader::onAppendStages(SkRasterPipeline* p,