diff options
author | Florin Malita <fmalita@chromium.org> | 2017-05-25 15:29:13 -0400 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2017-05-25 20:05:08 +0000 |
commit | fabe0b26d05624ce7374f6ca89bd66df6142534e (patch) | |
tree | f32b5873b31185d7e86e6c48fbdbd654efb8af7a /src/shaders/SkComposeShader.cpp | |
parent | 1c214313248a4b5a69af14539608c54fb67c2bf8 (diff) |
Relocate shaders to own dir
Consolidate all shader impls under src/shaders/.
Change-Id: I450e37541214704c1ad9e379d9d753b7cc62fac3
Reviewed-on: https://skia-review.googlesource.com/17927
Commit-Queue: Florin Malita <fmalita@chromium.org>
Reviewed-by: Herb Derby <herb@google.com>
Diffstat (limited to 'src/shaders/SkComposeShader.cpp')
-rw-r--r-- | src/shaders/SkComposeShader.cpp | 311 |
1 files changed, 311 insertions, 0 deletions
diff --git a/src/shaders/SkComposeShader.cpp b/src/shaders/SkComposeShader.cpp new file mode 100644 index 0000000000..7735494291 --- /dev/null +++ b/src/shaders/SkComposeShader.cpp @@ -0,0 +1,311 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkArenaAlloc.h" +#include "SkBlendModePriv.h" +#include "SkComposeShader.h" +#include "SkColorFilter.h" +#include "SkColorPriv.h" +#include "SkColorShader.h" +#include "SkRasterPipeline.h" +#include "SkReadBuffer.h" +#include "SkWriteBuffer.h" +#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) { + return nullptr; + } + if (SkBlendMode::kSrc == mode) { + return src; + } + if (SkBlendMode::kDst == mode) { + return dst; + } + return sk_sp<SkShader>(new SkComposeShader(std::move(dst), std::move(src), mode)); +} + +/////////////////////////////////////////////////////////////////////////////// + +class SkAutoAlphaRestore { +public: + SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha) { + fAlpha = paint->getAlpha(); + fPaint = paint; + paint->setAlpha(newAlpha); + } + + ~SkAutoAlphaRestore() { + fPaint->setAlpha(fAlpha); + } +private: + SkPaint* fPaint; + uint8_t fAlpha; +}; +#define SkAutoAlphaRestore(...) SK_REQUIRE_LOCAL_VAR(SkAutoAlphaRestore) + +sk_sp<SkFlattenable> SkComposeShader::CreateProc(SkReadBuffer& buffer) { + sk_sp<SkShader> shaderA(buffer.readShader()); + sk_sp<SkShader> shaderB(buffer.readShader()); + SkBlendMode mode; + if (buffer.isVersionLT(SkReadBuffer::kXfermodeToBlendMode2_Version)) { + sk_sp<SkXfermode> xfer = buffer.readXfermode(); + mode = xfer ? xfer->blend() : SkBlendMode::kSrcOver; + } else { + mode = (SkBlendMode)buffer.read32(); + } + if (!shaderA || !shaderB) { + return nullptr; + } + return sk_make_sp<SkComposeShader>(std::move(shaderA), std::move(shaderB), mode); +} + +void SkComposeShader::flatten(SkWriteBuffer& buffer) const { + buffer.writeFlattenable(fShaderA.get()); + buffer.writeFlattenable(fShaderB.get()); + buffer.write32((int)fMode); +} + +SkShaderBase::Context* SkComposeShader::onMakeContext( + const ContextRec& rec, SkArenaAlloc* alloc) const +{ + // we preconcat our localMatrix (if any) with the device matrix + // before calling our sub-shaders + SkMatrix tmpM; + tmpM.setConcat(*rec.fMatrix, this->getLocalMatrix()); + + // Our sub-shaders need to see opaque, so by combining them we don't double-alphatize the + // result. ComposeShader itself will respect the alpha, and post-apply it after calling the + // sub-shaders. + SkPaint opaquePaint(*rec.fPaint); + opaquePaint.setAlpha(0xFF); + + ContextRec newRec(rec); + newRec.fMatrix = &tmpM; + newRec.fPaint = &opaquePaint; + + SkShaderBase::Context* contextA = as_SB(fShaderA)->makeContext(newRec, alloc); + SkShaderBase::Context* contextB = as_SB(fShaderB)->makeContext(newRec, alloc); + if (!contextA || !contextB) { + return nullptr; + } + + return alloc->make<ComposeShaderContext>(*this, rec, contextA, contextB); +} + +sk_sp<SkShader> SkComposeShader::onMakeColorSpace(SkColorSpaceXformer* xformer) const { + return SkShader::MakeComposeShader(xformer->apply(fShaderA.get()), + xformer->apply(fShaderB.get()), fMode); +} + +SkComposeShader::ComposeShaderContext::ComposeShaderContext( + const SkComposeShader& shader, const ContextRec& rec, + SkShaderBase::Context* contextA, SkShaderBase::Context* contextB) + : INHERITED(shader, rec) + , fShaderContextA(contextA) + , fShaderContextB(contextB) {} + +bool SkComposeShader::asACompose(ComposeRec* rec) const { + if (rec) { + rec->fShaderA = fShaderA.get(); + rec->fShaderB = fShaderB.get(); + rec->fBlendMode = fMode; + } + return true; +} + +bool SkComposeShader::isRasterPipelineOnly() const { + return as_SB(fShaderA)->isRasterPipelineOnly() || as_SB(fShaderB)->isRasterPipelineOnly(); +} + +bool SkComposeShader::onAppendStages(SkRasterPipeline* pipeline, SkColorSpace* dstCS, + SkArenaAlloc* alloc, const SkMatrix& ctm, + const SkPaint& paint, const SkMatrix* localM) const { + struct Storage { + float fXY[4 * SkJumper_kMaxStride]; + float fRGBA[4 * SkJumper_kMaxStride]; + float fAlpha; + }; + auto storage = alloc->make<Storage>(); + + // We need to save off device x,y (inputs to shader), since after calling fShaderA they + // will be smashed, and I'll need them again for fShaderB. store_rgba saves off 4 registers + // even though we only need to save r,g. + pipeline->append(SkRasterPipeline::store_rgba, storage->fXY); + if (!as_SB(fShaderB)->appendStages(pipeline, dstCS, alloc, ctm, paint, localM)) { // SRC + 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); + // Now we restore the device x,y for the next shader + pipeline->append(SkRasterPipeline::load_rgba, storage->fXY); + if (!as_SB(fShaderA)->appendStages(pipeline, dstCS, alloc, ctm, paint, localM)) { // DST + 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 + // 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); + } + return true; +} + +// larger is better (fewer times we have to loop), but we shouldn't +// take up too much stack-space (each element is 4 bytes) +#define TMP_COLOR_COUNT 64 + +void SkComposeShader::ComposeShaderContext::shadeSpan(int x, int y, SkPMColor result[], int count) { + auto* shaderContextA = fShaderContextA; + auto* shaderContextB = fShaderContextB; + SkBlendMode mode = static_cast<const SkComposeShader&>(fShader).fMode; + unsigned scale = SkAlpha255To256(this->getPaintAlpha()); + + SkPMColor tmp[TMP_COLOR_COUNT]; + + SkXfermode* xfer = SkXfermode::Peek(mode); + if (nullptr == xfer) { // implied SRC_OVER + // TODO: when we have a good test-case, should use SkBlitRow::Proc32 + // for these loops + do { + int n = count; + if (n > TMP_COLOR_COUNT) { + n = TMP_COLOR_COUNT; + } + + shaderContextA->shadeSpan(x, y, result, n); + shaderContextB->shadeSpan(x, y, tmp, n); + + if (256 == scale) { + for (int i = 0; i < n; i++) { + result[i] = SkPMSrcOver(tmp[i], result[i]); + } + } else { + for (int i = 0; i < n; i++) { + result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]), + scale); + } + } + + result += n; + x += n; + count -= n; + } while (count > 0); + } else { // use mode for the composition + do { + int n = count; + if (n > TMP_COLOR_COUNT) { + n = TMP_COLOR_COUNT; + } + + shaderContextA->shadeSpan(x, y, result, n); + shaderContextB->shadeSpan(x, y, tmp, n); + xfer->xfer32(result, tmp, n, nullptr); + + if (256 != scale) { + for (int i = 0; i < n; i++) { + result[i] = SkAlphaMulQ(result[i], scale); + } + } + + result += n; + x += n; + count -= n; + } while (count > 0); + } +} + +void SkComposeShader::ComposeShaderContext::shadeSpan4f(int x, int y, SkPM4f result[], int count) { + auto* shaderContextA = fShaderContextA; + auto* shaderContextB = fShaderContextB; + SkBlendMode mode = static_cast<const SkComposeShader&>(fShader).fMode; + unsigned alpha = this->getPaintAlpha(); + Sk4f scale(alpha * (1.0f / 255)); + + SkPM4f tmp[TMP_COLOR_COUNT]; + + SkXfermodeProc4f xfer = SkXfermode::GetProc4f(mode); + do { + int n = SkTMin(count, TMP_COLOR_COUNT); + + shaderContextA->shadeSpan4f(x, y, result, n); + shaderContextB->shadeSpan4f(x, y, tmp, n); + if (255 == alpha) { + for (int i = 0; i < n; ++i) { + result[i] = xfer(tmp[i], result[i]); + } + } else { + for (int i = 0; i < n; ++i) { + (xfer(tmp[i], result[i]).to4f() * scale).store(result + i); + } + } + result += n; + x += n; + count -= n; + } while (count > 0); +} + +#if SK_SUPPORT_GPU + +#include "effects/GrConstColorProcessor.h" +#include "effects/GrXfermodeFragmentProcessor.h" + +///////////////////////////////////////////////////////////////////// + +sk_sp<GrFragmentProcessor> SkComposeShader::asFragmentProcessor(const AsFPArgs& args) const { + switch (fMode) { + case 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); + } +} +#endif + +#ifndef SK_IGNORE_TO_STRING +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", SkXfermode::ModeName(fMode)); + } + + this->INHERITED::toString(str); + + str->append(")"); +} +#endif |