blob: 5fb76f1f702a3dee6fa520509aa3a2b3342b8958 [file] [log] [blame]
/*
* Copyright 2019 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkBlendModePriv.h"
#include "SkEffectPriv.h"
#include "SkMixerBase.h"
#include "SkMixerShader.h"
#include "SkReadBuffer.h"
#include "SkRasterPipeline.h"
#include "SkWriteBuffer.h"
#if SK_SUPPORT_GPU
#include "GrRecordingContext.h"
#include "effects/generated/GrConstColorProcessor.h"
#include "effects/GrSkSLFP.h"
#include "effects/GrXfermodeFragmentProcessor.h"
static std::unique_ptr<GrFragmentProcessor> sksl_mixer_fp(
const GrFPArgs& args,
int index,
const char* sksl,
sk_sp<SkData> inputs,
std::unique_ptr<GrFragmentProcessor> fp1,
std::unique_ptr<GrFragmentProcessor> fp2,
std::unique_ptr<GrFragmentProcessor> fp3 = nullptr) {
std::unique_ptr<GrSkSLFP> result = GrSkSLFP::Make(args.fContext, index, "Runtime Mixer", sksl,
inputs ? inputs->data() : nullptr,
inputs ? inputs->size() : 0,
SkSL::Program::kMixer_Kind);
result->addChild(std::move(fp1));
result->addChild(std::move(fp2));
if (fp3) {
result->addChild(std::move(fp3));
}
return std::move(result);
}
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////
class SkMixer_Const final : public SkMixerBase {
SkMixer_Const(const SkPMColor4f& pm) : fPM(pm) {}
const SkPMColor4f fPM;
friend class SkMixer;
public:
SK_FLATTENABLE_HOOKS(SkMixer_Const)
void flatten(SkWriteBuffer& buffer) const override {
buffer.writePad32(&fPM, sizeof(SkPMColor4f));
}
bool appendStages(const SkStageRec& rec) const override {
rec.fPipeline->append_constant_color(rec.fAlloc, (const float*)&fPM);
return true;
}
#if SK_SUPPORT_GPU
std::unique_ptr<GrFragmentProcessor>
asFragmentProcessor(const GrFPArgs& args, const sk_sp<SkShader> shader1,
const sk_sp<SkShader> shader2) const override {
return GrConstColorProcessor::Make(fPM, GrConstColorProcessor::InputMode::kIgnore);
}
#endif
};
sk_sp<SkFlattenable> SkMixer_Const::CreateProc(SkReadBuffer& buffer) {
SkPMColor4f pm;
buffer.readPad32(&pm, sizeof(SkPMColor4f));
return sk_sp<SkFlattenable>(new SkMixer_Const(pm));
}
///////////////////////////////////////////////////////////////////////////////////////////////////
class SkMixer_Reverse final : public SkMixerBase {
SkMixer_Reverse(sk_sp<SkMixer> proxy) : fProxy(std::move(proxy)) {
SkASSERT(fProxy);
}
sk_sp<SkMixer> fProxy;
friend class SkMixer;
public:
SK_FLATTENABLE_HOOKS(SkMixer_Reverse)
void flatten(SkWriteBuffer& buffer) const override {
buffer.writeFlattenable(fProxy.get());
}
bool appendStages(const SkStageRec& rec) const override {
struct Storage {
float fRGBA[4 * SkRasterPipeline_kMaxStride];
};
auto storage = rec.fAlloc->make<Storage>();
SkRasterPipeline* pipeline = rec.fPipeline;
// swap src,dst
pipeline->append(SkRasterPipeline::store_dst, storage->fRGBA);
pipeline->append(SkRasterPipeline::move_src_dst);
pipeline->append(SkRasterPipeline::load_src, storage->fRGBA);
return as_MB(fProxy)->appendStages(rec);
}
#if SK_SUPPORT_GPU
std::unique_ptr<GrFragmentProcessor>
asFragmentProcessor(const GrFPArgs& args, const sk_sp<SkShader> shader1,
const sk_sp<SkShader> shader2) const override {
return as_MB(fProxy)->asFragmentProcessor(args, shader2, shader1);
}
#endif
};
sk_sp<SkFlattenable> SkMixer_Reverse::CreateProc(SkReadBuffer& buffer) {
auto orig = buffer.readMixer();
return orig ? orig->makeReverse() : nullptr;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
class SkMixer_Blend final : public SkMixerBase {
SkMixer_Blend(SkBlendMode mode) : fMode(mode) {}
const SkBlendMode fMode;
friend class SkMixer;
public:
SK_FLATTENABLE_HOOKS(SkMixer_Blend)
void flatten(SkWriteBuffer& buffer) const override {
buffer.write32(static_cast<int>(fMode));
}
bool appendStages(const SkStageRec& rec) const override {
SkBlendMode_AppendStages(fMode, rec.fPipeline);
return true;
}
#if SK_SUPPORT_GPU
std::unique_ptr<GrFragmentProcessor>
asFragmentProcessor(const GrFPArgs& args, const sk_sp<SkShader> shader1,
const sk_sp<SkShader> shader2) const override {
std::unique_ptr<GrFragmentProcessor> fp1 = as_SB(shader1)->asFragmentProcessor(args);
if (!fp1) {
return nullptr;
}
std::unique_ptr<GrFragmentProcessor> fp2 = as_SB(shader2)->asFragmentProcessor(args);
if (!fp2) {
return nullptr;
}
return GrXfermodeFragmentProcessor::MakeFromTwoProcessors(std::move(fp2), std::move(fp1),
fMode);
}
#endif
};
sk_sp<SkFlattenable> SkMixer_Blend::CreateProc(SkReadBuffer& buffer) {
unsigned mode = buffer.read32();
if (!buffer.validate(mode <= (unsigned)SkBlendMode::kLastMode)) {
return nullptr;
}
return MakeBlend(static_cast<SkBlendMode>(mode));
}
///////////////////////////////////////////////////////////////////////////////////////////////////
class SkMixer_Lerp final : public SkMixerBase {
SkMixer_Lerp(float weight) : fWeight(weight) {
SkASSERT(fWeight >= 0 && fWeight <= 1);
}
const float fWeight;
friend class SkMixer;
public:
SK_FLATTENABLE_HOOKS(SkMixer_Lerp)
void flatten(SkWriteBuffer& buffer) const override {
buffer.writeScalar(fWeight);
}
bool appendStages(const SkStageRec& rec) const override {
rec.fPipeline->append(SkRasterPipeline::lerp_1_float, &fWeight);
return true;
}
#if SK_SUPPORT_GPU
std::unique_ptr<GrFragmentProcessor>
asFragmentProcessor(const GrFPArgs& args, const sk_sp<SkShader> shader1,
const sk_sp<SkShader> shader2) const override {
std::unique_ptr<GrFragmentProcessor> fp1 = as_SB(shader1)->asFragmentProcessor(args);
if (!fp1) {
return nullptr;
}
std::unique_ptr<GrFragmentProcessor> fp2 = as_SB(shader2)->asFragmentProcessor(args);
if (!fp2) {
return nullptr;
}
static int index = GrSkSLFP::NewIndex();
return sksl_mixer_fp(args,
index,
"in uniform float weight;"
"void main(half4 input1, half4 input2) {"
" sk_OutColor = mix(input1, input2, half(weight));"
"}",
SkData::MakeWithCopy(&fWeight, sizeof(fWeight)),
std::move(fp1),
std::move(fp2));
}
#endif
};
sk_sp<SkFlattenable> SkMixer_Lerp::CreateProc(SkReadBuffer& buffer) {
return MakeLerp(buffer.readScalar());
}
///////////////////////////////////////////////////////////////////////////////////////////////////
class SkMixer_ShaderLerp final : public SkMixerBase {
SkMixer_ShaderLerp(sk_sp<SkShader> shader) : fShader(std::move(shader)) {
SkASSERT(fShader);
}
sk_sp<SkShader> fShader;
friend class SkMixer;
public:
SK_FLATTENABLE_HOOKS(SkMixer_ShaderLerp)
void flatten(SkWriteBuffer& buffer) const override {
buffer.writeFlattenable(fShader.get());
}
bool appendStages(const SkStageRec& rec) const override {
struct Storage {
float fSrc[4 * SkRasterPipeline_kMaxStride];
float fDst[4 * SkRasterPipeline_kMaxStride];
float fShaderOutput[4 * SkRasterPipeline_kMaxStride];
};
auto storage = rec.fAlloc->make<Storage>();
// we've been given our inputs as (drdgdbda, rgba)
rec.fPipeline->append(SkRasterPipeline::store_dst, storage->fDst);
rec.fPipeline->append(SkRasterPipeline::store_src, storage->fSrc);
if (!as_SB(fShader)->appendStages(rec)) {
return false;
}
// the shader's output is in rgba. We need to store "r" as our "t" values
rec.fPipeline->append(SkRasterPipeline::store_src, storage->fShaderOutput);
// now we need to reload the original dst and src so we can run our stage (lerp)
rec.fPipeline->append(SkRasterPipeline::load_dst, storage->fDst);
rec.fPipeline->append(SkRasterPipeline::load_src, storage->fSrc);
// we use the first channel (e.g. R) as our T values
rec.fPipeline->append(SkRasterPipeline::lerp_native, &storage->fShaderOutput[0]);
return true;
}
#if SK_SUPPORT_GPU
std::unique_ptr<GrFragmentProcessor>
asFragmentProcessor(const GrFPArgs& args, const sk_sp<SkShader> shader1,
const sk_sp<SkShader> shader2) const override {
std::unique_ptr<GrFragmentProcessor> fp1 = as_SB(shader1)->asFragmentProcessor(args);
if (!fp1) {
return nullptr;
}
std::unique_ptr<GrFragmentProcessor> fp2 = as_SB(shader2)->asFragmentProcessor(args);
if (!fp2) {
return nullptr;
}
std::unique_ptr<GrFragmentProcessor> fp3 = as_SB(fShader)->asFragmentProcessor(args);
if (!fp3) {
return nullptr;
}
static int index = GrSkSLFP::NewIndex();
return sksl_mixer_fp(args,
index,
"in fragmentProcessor lerpControl;"
"void main(half4 input1, half4 input2) {"
" sk_OutColor = mix(input1, input2, process(lerpControl).r);"
"}",
nullptr,
std::move(fp1),
std::move(fp2),
std::move(fp3));
}
#endif
};
sk_sp<SkFlattenable> SkMixer_ShaderLerp::CreateProc(SkReadBuffer& buffer) {
return MakeShaderLerp(buffer.readShader());
}
///////////////////////////////////////////////////////////////////////////////////////////////////
class SkMixer_Merge final : public SkMixerBase {
SkMixer_Merge(sk_sp<SkMixer> m0, sk_sp<SkMixer> m1, sk_sp<SkMixer> combine)
: fM0(std::move(m0))
, fM1(std::move(m1))
, fCombine(std::move(combine))
{
SkASSERT(fCombine);
SkASSERT(fM0 || fM1); // need at least one. If not, the caller just wants combine
}
sk_sp<SkMixer> fM0, fM1, fCombine;
friend class SkMixer;
public:
SK_FLATTENABLE_HOOKS(SkMixer_Merge)
void flatten(SkWriteBuffer& buffer) const override {
buffer.writeFlattenable(fM0.get()); // could be null
buffer.writeFlattenable(fM1.get()); // could be null
buffer.writeFlattenable(fCombine.get());
}
bool appendStages(const SkStageRec& rec) const override {
struct Storage {
float fDst[4 * SkRasterPipeline_kMaxStride];
float fSrc[4 * SkRasterPipeline_kMaxStride];
float fM0Output[4 * SkRasterPipeline_kMaxStride];
};
auto storage = rec.fAlloc->make<Storage>();
SkRasterPipeline* pipeline = rec.fPipeline;
// Need to save off dr,dg,db,da and r,g,b,a so we can use them twice (for fM0 and fM1)
pipeline->append(SkRasterPipeline::store_dst, storage->fDst);
pipeline->append(SkRasterPipeline::store_src, storage->fSrc);
if (!as_MB(fM0)->appendStages(rec)) {
return false;
}
// This outputs r,g,b,a, which we'll need later when we apply the mixer, but we save it off
// now since fM1 will overwrite them.
pipeline->append(SkRasterPipeline::store_src, storage->fM0Output);
// Now restore the original colors to call the first mixer
pipeline->append(SkRasterPipeline::load_dst, storage->fDst);
pipeline->append(SkRasterPipeline::load_src, storage->fSrc);
if (!as_MB(fM1)->appendStages(rec)) {
return false;
}
// M1's output is in r,g,b,a, which is the 2nd argument to fCombine, so we just need
// to load M0's output back into dr,dg,db,da
pipeline->append(SkRasterPipeline::load_dst, storage->fM0Output);
// 1st color in dr,dg,db,da <-- M0's output
// 2nd color in r, g, b, a <-- M1's output
return as_MB(fCombine)->appendStages(rec);
}
#if SK_SUPPORT_GPU
std::unique_ptr<GrFragmentProcessor>
asFragmentProcessor(const GrFPArgs& args, const sk_sp<SkShader> shader1,
const sk_sp<SkShader> shader2) const override {
return SkShader_Mixer(sk_sp<SkShader>(new SkShader_Mixer(shader1, shader2, fM0)),
sk_sp<SkShader>(new SkShader_Mixer(shader1, shader2, fM1)),
fCombine).asFragmentProcessor(args);
}
#endif
};
sk_sp<SkFlattenable> SkMixer_Merge::CreateProc(SkReadBuffer& buffer) {
sk_sp<SkMixer> m0 = buffer.readMixer();
sk_sp<SkMixer> m1 = buffer.readMixer();
sk_sp<SkMixer> combine = buffer.readMixer();
return combine ? combine->makeMerge(std::move(m0), std::move(m1)) : nullptr;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
sk_sp<SkMixer> SkMixer::MakeFirst() {
return MakeBlend(SkBlendMode::kDst);
}
sk_sp<SkMixer> SkMixer::MakeSecond() {
return MakeBlend(SkBlendMode::kSrc);
}
sk_sp<SkMixer> SkMixer::MakeConst(const SkColor4f& c) {
return sk_sp<SkMixer>(new SkMixer_Const(c.premul()));
}
sk_sp<SkMixer> SkMixer::MakeConst(SkColor c) {
return MakeConst(SkColor4f::FromColor(c));
}
sk_sp<SkMixer> SkMixer::MakeBlend(SkBlendMode mode) {
return sk_sp<SkMixer>(new SkMixer_Blend(mode));
}
sk_sp<SkMixer> SkMixer::MakeLerp(float t) {
if (SkScalarIsNaN(t)) {
t = 0; // is some other value better? return null?
}
if (t <= 0) {
return MakeFirst();
}
if (t >= 1) {
return MakeSecond();
}
return sk_sp<SkMixer>(new SkMixer_Lerp(t));
}
sk_sp<SkMixer> SkMixer::MakeShaderLerp(sk_sp<SkShader> shader) {
if (!shader) {
return MakeFirst();
}
return sk_sp<SkMixer>(new SkMixer_ShaderLerp(std::move(shader)));
}
///////////////////////////////////////////////////////////////////////////////////////////////////
sk_sp<SkMixer> SkMixer::makeReverse() const {
return sk_sp<SkMixer>(new SkMixer_Reverse(sk_ref_sp(this)));
}
sk_sp<SkMixer> SkMixer::makeMerge(sk_sp<SkMixer> m0, sk_sp<SkMixer> m1) const {
auto self = sk_ref_sp(this);
if (!m0 && !m1) {
return self;
}
return sk_sp<SkMixer>(new SkMixer_Merge(std::move(m0), std::move(m1), self));
}
///////////////////////////////////////////////////////////////////////////////////////////////////
SkPMColor4f SkMixerBase::test_mix(const SkPMColor4f& a, const SkPMColor4f& b) const {
SkPMColor4f dst = a,
src = b;
SkSTArenaAlloc<128> alloc;
SkRasterPipeline pipeline(&alloc);
SkPaint dummyPaint;
SkStageRec rec = {
&pipeline, &alloc, kRGBA_F32_SkColorType, nullptr, dummyPaint, nullptr, SkMatrix::I()
};
SkRasterPipeline_MemoryCtx dstPtr = { &dst, 0 };
SkRasterPipeline_MemoryCtx srcPtr = { &src, 0 };
pipeline.append(SkRasterPipeline::load_f32_dst, &dstPtr); // our 1st arg
pipeline.append(SkRasterPipeline::load_f32, &srcPtr); // our 2nd arg
as_MB(this)->appendStages(rec);
pipeline.append(SkRasterPipeline::store_f32, &dstPtr);
pipeline.run(0,0, 1,1);
return dst;
}
void SkMixerBase::RegisterFlattenables() {
SK_REGISTER_FLATTENABLE(SkMixer_Const);
SK_REGISTER_FLATTENABLE(SkMixer_Reverse);
SK_REGISTER_FLATTENABLE(SkMixer_Blend);
SK_REGISTER_FLATTENABLE(SkMixer_Lerp);
SK_REGISTER_FLATTENABLE(SkMixer_ShaderLerp);
SK_REGISTER_FLATTENABLE(SkMixer_Merge);
}