| /* | |
| * 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 "SkColorData.h" | |
| #include "SkColorShader.h" | |
| #include "SkRasterPipeline.h" | |
| #include "SkReadBuffer.h" | |
| #include "SkWriteBuffer.h" | |
| #include "SkString.h" | |
| #include "../jumper/SkJumper.h" | |
| sk_sp<SkShader> SkShader::MakeCompose(sk_sp<SkShader> dst, sk_sp<SkShader> src, SkBlendMode mode, | |
| float lerpT) { | |
| if (!src || !dst || SkScalarIsNaN(lerpT)) { | |
| return nullptr; | |
| } | |
| 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, lerpT)); | |
| } | |
| /////////////////////////////////////////////////////////////////////////////// | |
| sk_sp<SkFlattenable> SkComposeShader::CreateProc(SkReadBuffer& buffer) { | |
| sk_sp<SkShader> dst(buffer.readShader()); | |
| sk_sp<SkShader> src(buffer.readShader()); | |
| unsigned mode = buffer.read32(); | |
| float lerp = buffer.readScalar(); | |
| // check for valid mode before we cast to the enum type | |
| if (!buffer.validate(mode <= (unsigned)SkBlendMode::kLastMode)) { | |
| return nullptr; | |
| } | |
| return MakeCompose(std::move(dst), std::move(src), static_cast<SkBlendMode>(mode), lerp); | |
| } | |
| void SkComposeShader::flatten(SkWriteBuffer& buffer) const { | |
| buffer.writeFlattenable(fDst.get()); | |
| buffer.writeFlattenable(fSrc.get()); | |
| buffer.write32((int)fMode); | |
| buffer.writeScalar(fLerpT); | |
| } | |
| sk_sp<SkShader> SkComposeShader::onMakeColorSpace(SkColorSpaceXformer* xformer) const { | |
| return MakeCompose(xformer->apply(fDst.get()), xformer->apply(fSrc.get()), | |
| fMode, fLerpT); | |
| } | |
| #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK | |
| bool SkComposeShader::asACompose(ComposeRec* rec) const { | |
| if (!this->isJustMode()) { | |
| return false; | |
| } | |
| if (rec) { | |
| rec->fShaderA = fDst.get(); | |
| rec->fShaderB = fSrc.get(); | |
| rec->fBlendMode = fMode; | |
| } | |
| return true; | |
| } | |
| #endif | |
| bool SkComposeShader::onAppendStages(const StageRec& rec) const { | |
| struct Storage { | |
| float fRGBA[4 * SkJumper_kMaxStride]; | |
| float fAlpha; | |
| }; | |
| auto storage = rec.fAlloc->make<Storage>(); | |
| if (!as_SB(fSrc)->appendStages(rec)) { | |
| 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. | |
| rec.fPipeline->append(SkRasterPipeline::store_rgba, storage->fRGBA); | |
| if (!as_SB(fDst)->appendStages(rec)) { | |
| 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/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... | |
| rec.fPipeline->append(SkRasterPipeline::move_src_dst); | |
| rec.fPipeline->append(SkRasterPipeline::load_rgba, storage->fRGBA); | |
| if (!this->isJustLerp()) { | |
| SkBlendMode_AppendStages(fMode, rec.fPipeline); | |
| } | |
| if (!this->isJustMode()) { | |
| rec.fPipeline->append(SkRasterPipeline::lerp_1_float, &fLerpT); | |
| } | |
| return true; | |
| } | |
| #if SK_SUPPORT_GPU | |
| #include "effects/GrConstColorProcessor.h" | |
| #include "effects/GrXfermodeFragmentProcessor.h" | |
| ///////////////////////////////////////////////////////////////////// | |
| std::unique_ptr<GrFragmentProcessor> SkComposeShader::asFragmentProcessor( | |
| const GrFPArgs& args) const { | |
| if (this->isJustMode()) { | |
| SkASSERT(fMode != SkBlendMode::kSrc && fMode != SkBlendMode::kDst); // caught in factory | |
| if (fMode == SkBlendMode::kClear) { | |
| return GrConstColorProcessor::Make(GrColor4f::TransparentBlack(), | |
| GrConstColorProcessor::InputMode::kIgnore); | |
| } | |
| } | |
| std::unique_ptr<GrFragmentProcessor> fpA(as_SB(fDst)->asFragmentProcessor(args)); | |
| if (!fpA) { | |
| return nullptr; | |
| } | |
| std::unique_ptr<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 |