Extend composeshader to support a lerp parameter
Bug: skia: Change-Id: I3bbb2cb8d0a84fca0309654498548ebc94d8938f Reviewed-on: https://skia-review.googlesource.com/18460 Commit-Queue: Mike Reed <reed@google.com> Reviewed-by: Florin Malita <fmalita@chromium.org>
This commit is contained in:
parent
6a0feba05b
commit
01b2b83aba
@ -223,3 +223,33 @@ DEF_SIMPLE_GM(colorfiltershader, canvas, 610, 610) {
|
||||
canvas->translate(0, 150);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_SIMPLE_GM(mixershader, canvas, 800, 700) {
|
||||
auto shaderA = GetResourceAsImage("mandrill_128.png")->makeShader(SkShader::kClamp_TileMode,
|
||||
SkShader::kClamp_TileMode);
|
||||
const SkColor colors[] = { SK_ColorGREEN, 0 };
|
||||
auto shaderB = SkGradientShader::MakeRadial({60, 60}, 55, colors, nullptr, 2,
|
||||
SkShader::kClamp_TileMode,
|
||||
SkGradientShader::kInterpolateColorsInPremul_Flag,
|
||||
nullptr);
|
||||
const SkBlendMode modes[] = {
|
||||
SkBlendMode::kSrc, SkBlendMode::kModulate, SkBlendMode::kColorBurn, SkBlendMode::kPlus,
|
||||
SkBlendMode::kDstATop,
|
||||
};
|
||||
SkPaint paint;
|
||||
SkRect r = SkRect::MakeWH(120, 120);
|
||||
|
||||
canvas->translate(10, 10);
|
||||
for (auto mode : modes) {
|
||||
canvas->save();
|
||||
const int count = 6;
|
||||
for (int x = 0; x < count; ++x) {
|
||||
const float t = x * 1.0f / (count - 1);
|
||||
paint.setShader(SkShader::MakeCompose(shaderA, shaderB, mode, t));
|
||||
canvas->drawRect(r, paint);
|
||||
canvas->translate(r.width() + 10, 0);
|
||||
}
|
||||
canvas->restore();
|
||||
canvas->translate(0, r.height() + 20);
|
||||
}
|
||||
}
|
||||
|
@ -201,10 +201,11 @@ private:
|
||||
// V51: more SkXfermode -> SkBlendMode
|
||||
// V52: Remove SkTextBlob::fRunCount
|
||||
// V53: SaveLayerRec clip mask
|
||||
// V54: ComposeShader can use a Mode or a Lerp
|
||||
|
||||
// Only SKPs within the min/current picture version range (inclusive) can be read.
|
||||
static const uint32_t MIN_PICTURE_VERSION = 51; // Produced by Chrome ~M56.
|
||||
static const uint32_t CURRENT_PICTURE_VERSION = 53;
|
||||
static const uint32_t CURRENT_PICTURE_VERSION = 54;
|
||||
|
||||
static bool IsValidPictInfo(const SkPictInfo& info);
|
||||
static sk_sp<SkPicture> Forwardport(const SkPictInfo&,
|
||||
|
@ -210,7 +210,38 @@ public:
|
||||
*/
|
||||
static sk_sp<SkShader> MakeColorShader(const SkColor4f&, sk_sp<SkColorSpace>);
|
||||
|
||||
static sk_sp<SkShader> MakeComposeShader(sk_sp<SkShader> dst, sk_sp<SkShader> src, SkBlendMode);
|
||||
/**
|
||||
* Compose two shaders together, using two operators: mode and lerp. The resulting colors
|
||||
* are computed by first combining the src and dst shaders using mode, and then linearly
|
||||
* interpolating between the dst and result colors using lerp.
|
||||
*
|
||||
* result = dst * (1 - lerp) + (src (mode) dst) * lerp
|
||||
*
|
||||
* If either shader is nullptr, then this returns nullptr.
|
||||
* If lerp is NaN then this returns nullptr, otherwise lerp is clamped to [0..1].
|
||||
*/
|
||||
static sk_sp<SkShader> MakeCompose(sk_sp<SkShader> dst, sk_sp<SkShader> src,
|
||||
SkBlendMode mode, float lerp = 1);
|
||||
|
||||
/*
|
||||
* DEPRECATED: call MakeCompose.
|
||||
*/
|
||||
static sk_sp<SkShader> MakeComposeShader(sk_sp<SkShader> dst, sk_sp<SkShader> src,
|
||||
SkBlendMode mode) {
|
||||
return MakeCompose(std::move(dst), std::move(src), mode, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose two shaders together using a weighted average.
|
||||
*
|
||||
* result = dst * (1 - lerp) + src * lerp
|
||||
*
|
||||
* If either shader is nullptr, then this returns nullptr.
|
||||
* If lerp is NaN then this returns nullptr, otherwise lerp is clamped to [0..1].
|
||||
*/
|
||||
static sk_sp<SkShader> MakeMixer(sk_sp<SkShader> dst, sk_sp<SkShader> src, float lerp) {
|
||||
return MakeCompose(std::move(dst), std::move(src), SkBlendMode::kSrc, lerp);
|
||||
}
|
||||
|
||||
/** Call this to create a new shader that will draw with the specified bitmap.
|
||||
*
|
||||
|
@ -247,7 +247,7 @@ void SkDraw::drawVertices(SkVertices::VertexMode vmode, int count,
|
||||
matrix43 = triShader->getMatrix43();
|
||||
if (shader) {
|
||||
shader = outerAlloc.make<SkComposeShader>(sk_ref_sp(triShader), sk_ref_sp(shader),
|
||||
bmode);
|
||||
bmode, 1);
|
||||
} else {
|
||||
shader = triShader;
|
||||
}
|
||||
|
@ -72,6 +72,7 @@ public:
|
||||
kXfermodeToBlendMode2_Version = 51,
|
||||
*/
|
||||
kTextBlobImplicitRunCount_Version = 52,
|
||||
kComposeShaderCanLerp_Version = 54,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -17,57 +17,71 @@
|
||||
#include "SkString.h"
|
||||
#include "../jumper/SkJumper.h"
|
||||
|
||||
sk_sp<SkShader> SkShader::MakeComposeShader(sk_sp<SkShader> dst, sk_sp<SkShader> src,
|
||||
SkBlendMode mode) {
|
||||
if (!src || !dst) {
|
||||
sk_sp<SkShader> SkShader::MakeCompose(sk_sp<SkShader> dst, sk_sp<SkShader> src, SkBlendMode mode,
|
||||
float lerpT) {
|
||||
if (!src || !dst || SkScalarIsNaN(lerpT)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (SkBlendMode::kSrc == mode) {
|
||||
return src;
|
||||
}
|
||||
if (SkBlendMode::kDst == mode) {
|
||||
lerpT = SkScalarPin(lerpT, 0, 1);
|
||||
|
||||
if (lerpT == 0) {
|
||||
return dst;
|
||||
} else if (lerpT == 1) {
|
||||
if (mode == SkBlendMode::kSrc) {
|
||||
return src;
|
||||
}
|
||||
if (mode == SkBlendMode::kDst) {
|
||||
return dst;
|
||||
}
|
||||
}
|
||||
return sk_sp<SkShader>(new SkComposeShader(std::move(dst), std::move(src), mode));
|
||||
return sk_sp<SkShader>(new SkComposeShader(std::move(dst), std::move(src), mode, lerpT));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
sk_sp<SkFlattenable> SkComposeShader::CreateProc(SkReadBuffer& buffer) {
|
||||
sk_sp<SkShader> shaderA(buffer.readShader());
|
||||
sk_sp<SkShader> shaderB(buffer.readShader());
|
||||
SkBlendMode mode = (SkBlendMode)buffer.read32();
|
||||
sk_sp<SkShader> dst(buffer.readShader());
|
||||
sk_sp<SkShader> src(buffer.readShader());
|
||||
unsigned mode = buffer.read32();
|
||||
|
||||
if (!shaderA || !shaderB) {
|
||||
float lerp = 1;
|
||||
if (!buffer.isVersionLT(SkReadBuffer::kComposeShaderCanLerp_Version)) {
|
||||
lerp = buffer.readScalar();
|
||||
}
|
||||
|
||||
// check for valid mode before we cast to the enum type
|
||||
if (mode > (unsigned)SkBlendMode::kLastMode) {
|
||||
return nullptr;
|
||||
}
|
||||
return sk_make_sp<SkComposeShader>(std::move(shaderA), std::move(shaderB), mode);
|
||||
|
||||
return MakeCompose(std::move(dst), std::move(src), static_cast<SkBlendMode>(mode), lerp);
|
||||
}
|
||||
|
||||
void SkComposeShader::flatten(SkWriteBuffer& buffer) const {
|
||||
buffer.writeFlattenable(fShaderA.get());
|
||||
buffer.writeFlattenable(fShaderB.get());
|
||||
buffer.writeFlattenable(fDst.get());
|
||||
buffer.writeFlattenable(fSrc.get());
|
||||
buffer.write32((int)fMode);
|
||||
buffer.writeScalar(fLerpT);
|
||||
}
|
||||
|
||||
sk_sp<SkShader> SkComposeShader::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
|
||||
return SkShader::MakeComposeShader(xformer->apply(fShaderA.get()),
|
||||
xformer->apply(fShaderB.get()), fMode);
|
||||
return MakeCompose(xformer->apply(fDst.get()), xformer->apply(fSrc.get()),
|
||||
fMode, fLerpT);
|
||||
}
|
||||
|
||||
bool SkComposeShader::asACompose(ComposeRec* rec) const {
|
||||
if (!this->isJustMode()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rec) {
|
||||
rec->fShaderA = fShaderA.get();
|
||||
rec->fShaderB = fShaderB.get();
|
||||
rec->fShaderA = fDst.get();
|
||||
rec->fShaderB = fSrc.get();
|
||||
rec->fBlendMode = fMode;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkComposeShader::isRasterPipelineOnly() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkComposeShader::onAppendStages(SkRasterPipeline* pipeline, SkColorSpace* dstCS,
|
||||
SkArenaAlloc* alloc, const SkMatrix& ctm,
|
||||
const SkPaint& paint, const SkMatrix* localM) const {
|
||||
@ -77,27 +91,32 @@ bool SkComposeShader::onAppendStages(SkRasterPipeline* pipeline, SkColorSpace* d
|
||||
};
|
||||
auto storage = alloc->make<Storage>();
|
||||
|
||||
if (!as_SB(fShaderB)->appendStages(pipeline, dstCS, alloc, ctm, paint, localM)) { // SRC
|
||||
if (!as_SB(fSrc)->appendStages(pipeline, dstCS, alloc, ctm, paint, localM)) {
|
||||
return false;
|
||||
}
|
||||
// This outputs r,g,b,a, which we'll need later when we apply the mode, but we save it off now
|
||||
// since fShaderB will overwrite them.
|
||||
pipeline->append(SkRasterPipeline::store_rgba, storage->fRGBA);
|
||||
|
||||
if (!as_SB(fShaderA)->appendStages(pipeline, dstCS, alloc, ctm, paint, localM)) { // DST
|
||||
if (!as_SB(fDst)->appendStages(pipeline, dstCS, alloc, ctm, paint, localM)) {
|
||||
return false;
|
||||
}
|
||||
// We now have our logical 'dst' in r,g,b,a, but we need it in dr,dg,db,da for the mode
|
||||
// We now have our logical 'dst' in r,g,b,a, but we need it in dr,dg,db,da for the mode/lerp
|
||||
// so we have to shuttle them. If we had a stage the would load_into_dst, then we could
|
||||
// reverse the two shader invocations, and avoid this move...
|
||||
pipeline->append(SkRasterPipeline::move_src_dst);
|
||||
pipeline->append(SkRasterPipeline::load_rgba, storage->fRGBA);
|
||||
|
||||
// Idea: should time this, and see if it helps to have custom versions of the overflow modes
|
||||
// that do their own clamping, avoiding the overhead of an extra stage.
|
||||
SkBlendMode_AppendStages(fMode, pipeline);
|
||||
if (SkBlendMode_CanOverflow(fMode)) {
|
||||
pipeline->append(SkRasterPipeline::clamp_a);
|
||||
if (!this->isJustLerp()) {
|
||||
// Idea: should time this, and see if it helps to have custom versions of the overflow modes
|
||||
// that do their own clamping, avoiding the overhead of an extra stage.
|
||||
SkBlendMode_AppendStages(fMode, pipeline);
|
||||
if (SkBlendMode_CanOverflow(fMode)) {
|
||||
pipeline->append(SkRasterPipeline::clamp_a);
|
||||
}
|
||||
}
|
||||
if (!this->isJustMode()) {
|
||||
pipeline->append(SkRasterPipeline::lerp_1_float, &fLerpT);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -110,29 +129,25 @@ bool SkComposeShader::onAppendStages(SkRasterPipeline* pipeline, SkColorSpace* d
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
sk_sp<GrFragmentProcessor> SkComposeShader::asFragmentProcessor(const AsFPArgs& args) const {
|
||||
switch (fMode) {
|
||||
case SkBlendMode::kClear:
|
||||
if (this->isJustMode()) {
|
||||
SkASSERT(fMode != SkBlendMode::kSrc && fMode != SkBlendMode::kDst); // caught in factory
|
||||
if (fMode == SkBlendMode::kClear) {
|
||||
return GrConstColorProcessor::Make(GrColor4f::TransparentBlack(),
|
||||
GrConstColorProcessor::kIgnore_InputMode);
|
||||
break;
|
||||
case SkBlendMode::kSrc:
|
||||
return as_SB(fShaderB)->asFragmentProcessor(args);
|
||||
break;
|
||||
case SkBlendMode::kDst:
|
||||
return as_SB(fShaderA)->asFragmentProcessor(args);
|
||||
break;
|
||||
default:
|
||||
sk_sp<GrFragmentProcessor> fpA(as_SB(fShaderA)->asFragmentProcessor(args));
|
||||
if (!fpA) {
|
||||
return nullptr;
|
||||
}
|
||||
sk_sp<GrFragmentProcessor> fpB(as_SB(fShaderB)->asFragmentProcessor(args));
|
||||
if (!fpB) {
|
||||
return nullptr;
|
||||
}
|
||||
return GrXfermodeFragmentProcessor::MakeFromTwoProcessors(std::move(fpB),
|
||||
std::move(fpA), fMode);
|
||||
}
|
||||
}
|
||||
|
||||
sk_sp<GrFragmentProcessor> fpA(as_SB(fDst)->asFragmentProcessor(args));
|
||||
if (!fpA) {
|
||||
return nullptr;
|
||||
}
|
||||
sk_sp<GrFragmentProcessor> fpB(as_SB(fSrc)->asFragmentProcessor(args));
|
||||
if (!fpB) {
|
||||
return nullptr;
|
||||
}
|
||||
// TODO: account for fLerpT when it is < 1
|
||||
return GrXfermodeFragmentProcessor::MakeFromTwoProcessors(std::move(fpB),
|
||||
std::move(fpA), fMode);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -140,13 +155,12 @@ sk_sp<GrFragmentProcessor> SkComposeShader::asFragmentProcessor(const AsFPArgs&
|
||||
void SkComposeShader::toString(SkString* str) const {
|
||||
str->append("SkComposeShader: (");
|
||||
|
||||
str->append("ShaderA: ");
|
||||
as_SB(fShaderA)->toString(str);
|
||||
str->append(" ShaderB: ");
|
||||
as_SB(fShaderB)->toString(str);
|
||||
if (SkBlendMode::kSrcOver != fMode) {
|
||||
str->appendf(" Xfermode: %s", SkBlendMode_Name(fMode));
|
||||
}
|
||||
str->append("dst: ");
|
||||
as_SB(fDst)->toString(str);
|
||||
str->append(" src: ");
|
||||
as_SB(fSrc)->toString(str);
|
||||
str->appendf(" mode: %s", SkBlendMode_Name(fMode));
|
||||
str->appendf(" lerpT: %g", fLerpT);
|
||||
|
||||
this->INHERITED::toString(str);
|
||||
|
||||
|
@ -11,36 +11,22 @@
|
||||
#include "SkShaderBase.h"
|
||||
#include "SkBlendMode.h"
|
||||
|
||||
class SkColorSpacXformer;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/** \class SkComposeShader
|
||||
This subclass of shader returns the composition of two other shaders, combined by
|
||||
a xfermode.
|
||||
*/
|
||||
class SkComposeShader : public SkShaderBase {
|
||||
public:
|
||||
/** Create a new compose shader, given shaders A, B, and a combining xfermode mode.
|
||||
When the xfermode is called, it will be given the result from shader A as its
|
||||
"dst", and the result from shader B as its "src".
|
||||
mode->xfer32(sA_result, sB_result, ...)
|
||||
@param shaderA The colors from this shader are seen as the "dst" by the xfermode
|
||||
@param shaderB The colors from this shader are seen as the "src" by the xfermode
|
||||
@param mode The xfermode that combines the colors from the two shaders. If mode
|
||||
is null, then SRC_OVER is assumed.
|
||||
*/
|
||||
SkComposeShader(sk_sp<SkShader> sA, sk_sp<SkShader> sB, SkBlendMode mode)
|
||||
: fShaderA(std::move(sA))
|
||||
, fShaderB(std::move(sB))
|
||||
SkComposeShader(sk_sp<SkShader> dst, sk_sp<SkShader> src, SkBlendMode mode, float lerpT)
|
||||
: fDst(std::move(dst))
|
||||
, fSrc(std::move(src))
|
||||
, fLerpT(lerpT)
|
||||
, fMode(mode)
|
||||
{}
|
||||
{
|
||||
SkASSERT(lerpT >= 0 && lerpT <= 1);
|
||||
}
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
|
||||
#endif
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
#ifdef SK_DEBUGx
|
||||
SkShader* getShaderA() { return fShaderA.get(); }
|
||||
SkShader* getShaderB() { return fShaderB.get(); }
|
||||
#endif
|
||||
@ -57,12 +43,16 @@ protected:
|
||||
bool onAppendStages(SkRasterPipeline*, SkColorSpace* dstCS, SkArenaAlloc*,
|
||||
const SkMatrix&, const SkPaint&, const SkMatrix* localM) const override;
|
||||
|
||||
bool isRasterPipelineOnly() const final;
|
||||
bool isRasterPipelineOnly() const final { return true; }
|
||||
|
||||
private:
|
||||
sk_sp<SkShader> fShaderA;
|
||||
sk_sp<SkShader> fShaderB;
|
||||
SkBlendMode fMode;
|
||||
sk_sp<SkShader> fDst;
|
||||
sk_sp<SkShader> fSrc;
|
||||
const float fLerpT;
|
||||
const SkBlendMode fMode;
|
||||
|
||||
bool isJustMode() const { return fLerpT == 1; }
|
||||
bool isJustLerp() const { return fMode == SkBlendMode::kSrc; }
|
||||
|
||||
typedef SkShaderBase INHERITED;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user