add a bulk shader path through SkRasterPipelineBlitter

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>
This commit is contained in:
Mike Klein 2017-05-23 15:06:17 -04:00 committed by Skia Commit-Bot
parent bc146b1bd7
commit 3b840e9304
2 changed files with 98 additions and 86 deletions

View 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"
@ -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;

View 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,