diff options
-rw-r--r-- | src/core/SkRasterPipelineBlitter.cpp | 147 | ||||
-rw-r--r-- | src/core/SkShader.cpp | 37 |
2 files changed, 98 insertions, 86 deletions
diff --git a/src/core/SkRasterPipelineBlitter.cpp b/src/core/SkRasterPipelineBlitter.cpp index e5c532597a..023ff1e234 100644 --- a/src/core/SkRasterPipelineBlitter.cpp +++ b/src/core/SkRasterPipelineBlitter.cpp @@ -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" @@ -20,20 +21,19 @@ 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; diff --git a/src/core/SkShader.cpp b/src/core/SkShader.cpp index c8d5f08776..ff5b400a25 100644 --- a/src/core/SkShader.cpp +++ b/src/core/SkShader.cpp @@ -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, |