/* * 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 "include/core/SkColorFilter.h" #include "include/core/SkString.h" #include "include/private/SkColorData.h" #include "src/core/SkArenaAlloc.h" #include "src/core/SkBlendModePriv.h" #include "src/core/SkRasterPipeline.h" #include "src/core/SkReadBuffer.h" #include "src/core/SkWriteBuffer.h" #include "src/shaders/SkColorShader.h" #include "src/shaders/SkComposeShader.h" namespace { sk_sp wrap_lm(sk_sp shader, const SkMatrix* lm) { return (shader && lm) ? shader->makeWithLocalMatrix(*lm) : shader; } struct LocalMatrixStageRec final : public SkStageRec { LocalMatrixStageRec(const SkStageRec& rec, const SkMatrix& lm) : INHERITED(rec) { if (!lm.isIdentity()) { if (fLocalM) { fStorage.setConcat(lm, *fLocalM); fLocalM = fStorage.isIdentity() ? nullptr : &fStorage; } else { fLocalM = &lm; } } } private: SkMatrix fStorage; using INHERITED = SkStageRec; }; } // namespace sk_sp SkShaders::Blend(SkBlendMode mode, sk_sp dst, sk_sp src, const SkMatrix* lm) { switch (mode) { case SkBlendMode::kClear: return Color(0); case SkBlendMode::kDst: return wrap_lm(std::move(dst), lm); case SkBlendMode::kSrc: return wrap_lm(std::move(src), lm); default: break; } return sk_sp(new SkShader_Blend(mode, std::move(dst), std::move(src), lm)); } sk_sp SkShaders::Lerp(float weight, sk_sp dst, sk_sp src, const SkMatrix* lm) { if (SkScalarIsNaN(weight)) { return nullptr; } if (dst == src || weight <= 0) { return wrap_lm(std::move(dst), lm); } if (weight >= 1) { return wrap_lm(std::move(src), lm); } return sk_sp(new SkShader_Lerp(weight, std::move(dst), std::move(src), lm)); } sk_sp SkShaders::Lerp(sk_sp red, sk_sp dst, sk_sp src, const SkMatrix* lm) { if (!red) { return nullptr; } if (dst == src) { return wrap_lm(std::move(dst), lm); } return sk_sp(new SkShader_LerpRed(std::move(red), std::move(dst), std::move(src), lm)); } /////////////////////////////////////////////////////////////////////////////// static bool append_shader_or_paint(const SkStageRec& rec, SkShader* shader) { if (shader) { if (!as_SB(shader)->appendStages(rec)) { return false; } } else { rec.fPipeline->append_constant_color(rec.fAlloc, rec.fPaint.getColor4f().premul().vec()); } return true; } // Returns the output of e0, and leaves the output of e1 in r,g,b,a static float* append_two_shaders(const SkStageRec& rec, SkShader* s0, SkShader* s1) { struct Storage { float fRes0[4 * SkRasterPipeline_kMaxStride]; }; auto storage = rec.fAlloc->make(); if (!append_shader_or_paint(rec, s0)) { return nullptr; } rec.fPipeline->append(SkRasterPipeline::store_src, storage->fRes0); if (!append_shader_or_paint(rec, s1)) { return nullptr; } return storage->fRes0; } /////////////////////////////////////////////////////////////////////////////// sk_sp SkShader_Blend::CreateProc(SkReadBuffer& buffer) { sk_sp dst(buffer.readShader()); sk_sp src(buffer.readShader()); unsigned mode = buffer.read32(); // check for valid mode before we cast to the enum type if (!buffer.validate(mode <= (unsigned)SkBlendMode::kLastMode)) { return nullptr; } return SkShaders::Blend(static_cast(mode), std::move(dst), std::move(src)); } void SkShader_Blend::flatten(SkWriteBuffer& buffer) const { buffer.writeFlattenable(fDst.get()); buffer.writeFlattenable(fSrc.get()); buffer.write32((int)fMode); } bool SkShader_Blend::onAppendStages(const SkStageRec& orig_rec) const { const LocalMatrixStageRec rec(orig_rec, this->getLocalMatrix()); float* res0 = append_two_shaders(rec, fDst.get(), fSrc.get()); if (!res0) { return false; } rec.fPipeline->append(SkRasterPipeline::load_dst, res0); SkBlendMode_AppendStages(fMode, rec.fPipeline); return true; } sk_sp SkShader_Lerp::CreateProc(SkReadBuffer& buffer) { sk_sp dst(buffer.readShader()); sk_sp src(buffer.readShader()); float t = buffer.readScalar(); return buffer.isValid() ? SkShaders::Lerp(t, std::move(dst), std::move(src)) : nullptr; } void SkShader_Lerp::flatten(SkWriteBuffer& buffer) const { buffer.writeFlattenable(fDst.get()); buffer.writeFlattenable(fSrc.get()); buffer.writeScalar(fWeight); } bool SkShader_Lerp::onAppendStages(const SkStageRec& orig_rec) const { const LocalMatrixStageRec rec(orig_rec, this->getLocalMatrix()); float* res0 = append_two_shaders(rec, fDst.get(), fSrc.get()); if (!res0) { return false; } rec.fPipeline->append(SkRasterPipeline::load_dst, res0); rec.fPipeline->append(SkRasterPipeline::lerp_1_float, &fWeight); return true; } sk_sp SkShader_LerpRed::CreateProc(SkReadBuffer& buffer) { sk_sp dst(buffer.readShader()); sk_sp src(buffer.readShader()); sk_sp red(buffer.readShader()); return buffer.isValid() ? SkShaders::Lerp(std::move(red), std::move(dst), std::move(src)) : nullptr; } void SkShader_LerpRed::flatten(SkWriteBuffer& buffer) const { buffer.writeFlattenable(fDst.get()); buffer.writeFlattenable(fSrc.get()); buffer.writeFlattenable(fRed.get()); } bool SkShader_LerpRed::onAppendStages(const SkStageRec& orig_rec) const { const LocalMatrixStageRec rec(orig_rec, this->getLocalMatrix()); struct Storage { float fRed[4 * SkRasterPipeline_kMaxStride]; }; auto storage = rec.fAlloc->make(); if (!as_SB(fRed)->appendStages(rec)) { return false; } // actually, we just need the first (red) channel, but for now we store rgba rec.fPipeline->append(SkRasterPipeline::store_src, storage->fRed); float* res0 = append_two_shaders(rec, fDst.get(), fSrc.get()); if (!res0) { return false; } rec.fPipeline->append(SkRasterPipeline::load_dst, res0); rec.fPipeline->append(SkRasterPipeline::lerp_native, &storage->fRed[0]); return true; } #if SK_SUPPORT_GPU #include "include/private/GrRecordingContext.h" #include "src/gpu/effects/GrXfermodeFragmentProcessor.h" #include "src/gpu/effects/generated/GrComposeLerpEffect.h" #include "src/gpu/effects/generated/GrComposeLerpRedEffect.h" #include "src/gpu/effects/generated/GrConstColorProcessor.h" static std::unique_ptr as_fp(const GrFPArgs& args, SkShader* shader) { return shader ? as_SB(shader)->asFragmentProcessor(args) : nullptr; } std::unique_ptr SkShader_Blend::asFragmentProcessor( const GrFPArgs& orig_args) const { const GrFPArgs::WithPreLocalMatrix args(orig_args, this->getLocalMatrix()); auto fpA = as_fp(args, fDst.get()); auto fpB = as_fp(args, fSrc.get()); if (!fpA || !fpB) { return nullptr; } return GrXfermodeFragmentProcessor::MakeFromTwoProcessors(std::move(fpB), std::move(fpA), fMode); } std::unique_ptr SkShader_Lerp::asFragmentProcessor( const GrFPArgs& orig_args) const { const GrFPArgs::WithPreLocalMatrix args(orig_args, this->getLocalMatrix()); auto fpA = as_fp(args, fDst.get()); auto fpB = as_fp(args, fSrc.get()); return GrComposeLerpEffect::Make(std::move(fpA), std::move(fpB), fWeight); } std::unique_ptr SkShader_LerpRed::asFragmentProcessor( const GrFPArgs& orig_args) const { const GrFPArgs::WithPreLocalMatrix args(orig_args, this->getLocalMatrix()); auto fpA = as_fp(args, fDst.get()); auto fpB = as_fp(args, fSrc.get()); auto red = as_SB(fRed)->asFragmentProcessor(args); if (!red) { return nullptr; } return GrComposeLerpRedEffect::Make(std::move(fpA), std::move(fpB), std::move(red)); } #endif