diff options
-rw-r--r-- | gm/colorfilterimagefilter.cpp | 30 | ||||
-rw-r--r-- | include/core/SkPicture.h | 3 | ||||
-rw-r--r-- | include/core/SkShader.h | 33 | ||||
-rw-r--r-- | src/core/SkDraw_vertices.cpp | 2 | ||||
-rw-r--r-- | src/core/SkReadBuffer.h | 1 | ||||
-rw-r--r-- | src/shaders/SkComposeShader.cpp | 130 | ||||
-rw-r--r-- | src/shaders/SkComposeShader.h | 42 |
7 files changed, 154 insertions, 87 deletions
diff --git a/gm/colorfilterimagefilter.cpp b/gm/colorfilterimagefilter.cpp index 5ef6379b9c..73b9f20d2b 100644 --- a/gm/colorfilterimagefilter.cpp +++ b/gm/colorfilterimagefilter.cpp @@ -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); + } +} diff --git a/include/core/SkPicture.h b/include/core/SkPicture.h index 9f14f80a17..f90e17af3e 100644 --- a/include/core/SkPicture.h +++ b/include/core/SkPicture.h @@ -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&, diff --git a/include/core/SkShader.h b/include/core/SkShader.h index 7f78d4ba93..daed2ba718 100644 --- a/include/core/SkShader.h +++ b/include/core/SkShader.h @@ -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. * diff --git a/src/core/SkDraw_vertices.cpp b/src/core/SkDraw_vertices.cpp index d0ef2d7116..4f7fc13b63 100644 --- a/src/core/SkDraw_vertices.cpp +++ b/src/core/SkDraw_vertices.cpp @@ -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; } diff --git a/src/core/SkReadBuffer.h b/src/core/SkReadBuffer.h index 4e6105013a..48cb22511f 100644 --- a/src/core/SkReadBuffer.h +++ b/src/core/SkReadBuffer.h @@ -72,6 +72,7 @@ public: kXfermodeToBlendMode2_Version = 51, */ kTextBlobImplicitRunCount_Version = 52, + kComposeShaderCanLerp_Version = 54, }; /** diff --git a/src/shaders/SkComposeShader.cpp b/src/shaders/SkComposeShader.cpp index 75f9cd2654..3155784338 100644 --- a/src/shaders/SkComposeShader.cpp +++ b/src/shaders/SkComposeShader.cpp @@ -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); diff --git a/src/shaders/SkComposeShader.h b/src/shaders/SkComposeShader.h index 39c43d6b77..03861009a9 100644 --- a/src/shaders/SkComposeShader.h +++ b/src/shaders/SkComposeShader.h @@ -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; }; |