use mixer to combine two filters

Change-Id: Idb221248606dc683d17f0934b4e3152ff0d6c2d5
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/204360
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Mike Reed <reed@google.com>
Auto-Submit: Mike Reed <reed@google.com>
This commit is contained in:
Mike Reed 2019-03-28 09:22:28 -06:00 committed by Skia Commit-Bot
parent 61a5f0aef8
commit 05be23dbd3
6 changed files with 221 additions and 36 deletions

View File

@ -14,6 +14,42 @@
#include "SkLumaColorFilter.h"
#include "SkTableColorFilter.h"
// A tint filter maps colors to a given range (gradient), based on the input luminance:
//
// c' = lerp(lo, hi, luma(c))
//
// TODO: move to public headers/API?
//
static sk_sp<SkColorFilter> MakeTintColorFilter(SkColor lo, SkColor hi) {
const auto r_lo = SkColorGetR(lo),
g_lo = SkColorGetG(lo),
b_lo = SkColorGetB(lo),
a_lo = SkColorGetA(lo),
r_hi = SkColorGetR(hi),
g_hi = SkColorGetG(hi),
b_hi = SkColorGetB(hi),
a_hi = SkColorGetA(hi);
// We map component-wise:
//
// r' = lo.r + (hi.r - lo.r) * luma
// g' = lo.g + (hi.g - lo.g) * luma
// b' = lo.b + (hi.b - lo.b) * luma
// a' = lo.a + (hi.a - lo.a) * luma
//
// The input luminance is stored in the alpha channel
// (and RGB are cleared -- see SkLumaColorFilter). Thus:
const SkScalar tint_matrix[] = {
0, 0, 0, (r_hi - r_lo) / 255.0f, SkIntToScalar(r_lo),
0, 0, 0, (g_hi - g_lo) / 255.0f, SkIntToScalar(g_lo),
0, 0, 0, (b_hi - b_lo) / 255.0f, SkIntToScalar(b_lo),
0, 0, 0, (a_hi - a_lo) / 255.0f, SkIntToScalar(a_lo),
};
return SkColorFilter::MakeMatrixFilterRowMajor255(tint_matrix)
->makeComposed(SkLumaColorFilter::Make());
}
namespace {
class MixerCFGM final : public skiagm::GM {
@ -69,42 +105,6 @@ private:
canvas->translate(0, fTileSize.height() * 1.1f);
}
// A tint filter maps colors to a given range (gradient), based on the input luminance:
//
// c' = lerp(lo, hi, luma(c))
//
// TODO: move to public headers/API?
//
static sk_sp<SkColorFilter> MakeTintColorFilter(SkColor lo, SkColor hi) {
const auto r_lo = SkColorGetR(lo),
g_lo = SkColorGetG(lo),
b_lo = SkColorGetB(lo),
a_lo = SkColorGetA(lo),
r_hi = SkColorGetR(hi),
g_hi = SkColorGetG(hi),
b_hi = SkColorGetB(hi),
a_hi = SkColorGetA(hi);
// We map component-wise:
//
// r' = lo.r + (hi.r - lo.r) * luma
// g' = lo.g + (hi.g - lo.g) * luma
// b' = lo.b + (hi.b - lo.b) * luma
// a' = lo.a + (hi.a - lo.a) * luma
//
// The input luminance is stored in the alpha channel
// (and RGB are cleared -- see SkLumaColorFilter). Thus:
const SkScalar tint_matrix[] = {
0, 0, 0, (r_hi - r_lo) / 255.0f, SkIntToScalar(r_lo),
0, 0, 0, (g_hi - g_lo) / 255.0f, SkIntToScalar(g_lo),
0, 0, 0, (b_hi - b_lo) / 255.0f, SkIntToScalar(b_lo),
0, 0, 0, (a_hi - a_lo) / 255.0f, SkIntToScalar(a_lo),
};
return SkColorFilter::MakeMatrixFilterRowMajor255(tint_matrix)
->makeComposed(SkLumaColorFilter::Make());
}
using INHERITED = skiagm::GM;
};
@ -195,3 +195,35 @@ private:
using INHERITED = skiagm::GM;
};
DEF_GM( return new ShaderMixerGM; )
static void draw_rect(SkCanvas* c, const SkRect& r, const SkPaint& p, SkScalar x, SkScalar y) {
c->save();
c->translate(x, y);
c->drawRect(r, p);
c->restore();
}
DEF_SIMPLE_GM(mixercolorfilter, canvas, 768, 512) {
auto cf0 = MakeTintColorFilter(0xff300000, 0xffa00000); // red tint
auto cf1 = MakeTintColorFilter(0xff003000, 0xff00a000); // green tint
SkRect r = { 0, 0, 256, 256 };
SkPaint p;
p.setShader(make_resource_shader("images/mandrill_256.png", 256));
draw_rect(canvas, r, p, 0, 0);
p.setColorFilter(cf0);
draw_rect(canvas, r, p, 256, 0);
p.setColorFilter(cf1);
draw_rect(canvas, r, p, 512, 0);
auto mx = SkMixer::MakeLerp(0.5f);
p.setColorFilter(SkColorFilter::MakeMixer(cf0, cf1, mx));
draw_rect(canvas, r, p, 0, 256);
p.setColorFilter(SkColorFilter::MakeMixer(cf0, nullptr, mx));
draw_rect(canvas, r, p, 256, 256);
p.setColorFilter(SkColorFilter::MakeMixer(nullptr, cf1, mx));
draw_rect(canvas, r, p, 512, 256);
}

View File

@ -151,6 +151,7 @@ skia_core_sources = [
"$_src/core/SkClipStackDevice.h",
"$_src/core/SkColor.cpp",
"$_src/core/SkColorFilter.cpp",
"$_src/core/SkColorFilter_Mixer.cpp",
"$_src/core/SkColorMatrixFilterRowMajor255.cpp",
"$_src/core/SkColorMatrixFilterRowMajor255.h",
"$_src/core/SkColorSpace.cpp",

View File

@ -18,6 +18,7 @@ class GrFragmentProcessor;
class GrRecordingContext;
class SkBitmap;
class SkColorSpace;
class SkMixer;
struct SkStageRec;
class SkString;
@ -129,6 +130,15 @@ public:
static sk_sp<SkColorFilter> MakeLerp(sk_sp<SkColorFilter> cf0, sk_sp<SkColorFilter> cf1,
float weight);
/**
* Returns a new filter that mixes the output of two other filters. If either filter is null,
* then it is treated like an identity filter.
*
* result = mx(cf0(color), cf1(color))
*/
static sk_sp<SkColorFilter> MakeMixer(sk_sp<SkColorFilter> cf0, sk_sp<SkColorFilter> cf1,
sk_sp<SkMixer> mx);
#if SK_SUPPORT_GPU
/**
* A subclass may implement this factory function to work with the GPU backend. It returns

View File

@ -7,6 +7,7 @@
#include "SkArenaAlloc.h"
#include "SkColorFilter.h"
#include "SkColorFilter_Mixer.h"
#include "SkColorFilterPriv.h"
#include "SkColorSpacePriv.h"
#include "SkColorSpaceXformSteps.h"
@ -498,6 +499,7 @@ void SkColorFilter::RegisterFlattenables() {
SK_REGISTER_FLATTENABLE(SkModeColorFilter);
SK_REGISTER_FLATTENABLE(SkSRGBGammaColorFilter);
SK_REGISTER_FLATTENABLE(SkMixerColorFilter);
SK_REGISTER_FLATTENABLE(SkColorFilter_Mixer);
#if SK_SUPPORT_GPU
SK_REGISTER_FLATTENABLE(SkRuntimeColorFilter);
#endif

View File

@ -0,0 +1,90 @@
/*
* 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 "SkArenaAlloc.h"
#include "SkColorFilter_Mixer.h"
#include "SkRasterPipeline.h"
#include "SkReadBuffer.h"
#include "SkWriteBuffer.h"
#include "SkString.h"
sk_sp<SkColorFilter> SkColorFilter::MakeMixer(sk_sp<SkColorFilter> f0, sk_sp<SkColorFilter> f1, sk_sp<SkMixer> mixer) {
if (!mixer) {
return nullptr;
}
return sk_sp<SkColorFilter>(new SkColorFilter_Mixer(std::move(f0), std::move(f1),
std::move(mixer)));
}
///////////////////////////////////////////////////////////////////////////////
sk_sp<SkFlattenable> SkColorFilter_Mixer::CreateProc(SkReadBuffer& buffer) {
sk_sp<SkColorFilter> s0(buffer.readColorFilter());
sk_sp<SkColorFilter> s1(buffer.readColorFilter());
sk_sp<SkMixer> mx(buffer.readMixer());
return MakeMixer(std::move(s0), std::move(s1), std::move(mx));
}
void SkColorFilter_Mixer::flatten(SkWriteBuffer& buffer) const {
buffer.writeFlattenable(fFilter0.get());
buffer.writeFlattenable(fFilter1.get());
buffer.writeFlattenable(fMixer.get());
}
void SkColorFilter_Mixer::onAppendStages(const SkStageRec& rec,
bool shaderIsOpaque) const {
struct Storage {
float fOrig[4 * SkRasterPipeline_kMaxStride];
float fRes0[4 * SkRasterPipeline_kMaxStride];
};
Storage* storage = nullptr;
if (fFilter0 || fFilter1) {
storage = rec.fAlloc->make<Storage>();
rec.fPipeline->append(SkRasterPipeline::store_src, storage->fOrig);
}
if (fFilter0 && fFilter1) {
fFilter0->appendStages(rec, shaderIsOpaque);
rec.fPipeline->append(SkRasterPipeline::store_src, storage->fRes0);
rec.fPipeline->append(SkRasterPipeline::load_src, storage->fOrig);
fFilter1->appendStages(rec, shaderIsOpaque);
rec.fPipeline->append(SkRasterPipeline::load_dst, storage->fRes0);
} else if (fFilter0 && !fFilter1) {
fFilter0->appendStages(rec, shaderIsOpaque);
rec.fPipeline->append(SkRasterPipeline::move_src_dst);
rec.fPipeline->append(SkRasterPipeline::load_src, storage->fOrig);
} else if (!fFilter0 && fFilter1) {
fFilter1->appendStages(rec, shaderIsOpaque);
rec.fPipeline->append(SkRasterPipeline::load_dst, storage->fOrig);
} else {
SkASSERT(!fFilter0);
SkASSERT(!fFilter1);
rec.fPipeline->append(SkRasterPipeline::move_src_dst);
}
// 1st color in dr,dg,db,da
// 2nd color in r, g, b, a
(void) as_MB(fMixer)->appendStages(rec);
}
#if SK_SUPPORT_GPU
#include "effects/GrConstColorProcessor.h"
#include "effects/GrXfermodeFragmentProcessor.h"
/////////////////////////////////////////////////////////////////////
std::unique_ptr<GrFragmentProcessor>
SkColorFilter_Mixer::asFragmentProcessor(GrRecordingContext*,
const GrColorSpaceInfo& dstColorSpaceInfo) const {
// TODO: need to make a mixer-processor...
return nullptr;
}
#endif

View File

@ -0,0 +1,50 @@
/*
* Copyright 2019 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkMixerColorFilter_DEFINED
#define SkMixerColorFilter_DEFINED
#include "SkColorFilter.h"
#include "SkMixer.h"
#if SK_SUPPORT_GPU
#include "GrFPArgs.h"
#endif
class SkColorFilter_Mixer final : public SkColorFilter {
public:
SkColorFilter_Mixer(sk_sp<SkColorFilter> f0, sk_sp<SkColorFilter> f1, sk_sp<SkMixer> mixer)
: fFilter0(std::move(f0))
, fFilter1(std::move(f1))
, fMixer(std::move(mixer))
{
SkASSERT(fMixer);
}
#if SK_SUPPORT_GPU
std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(
GrRecordingContext*, const GrColorSpaceInfo& dstColorSpaceInfo) const override;
#endif
protected:
SkColorFilter_Mixer(SkReadBuffer&);
void flatten(SkWriteBuffer&) const override;
void onAppendStages(const SkStageRec&, bool shaderIsOpaque) const override;
private:
SK_FLATTENABLE_HOOKS(SkColorFilter_Mixer)
sk_sp<SkColorFilter> fFilter0;
sk_sp<SkColorFilter> fFilter1;
sk_sp<SkMixer> fMixer;
friend class SkColorFilter;
typedef SkColorFilter INHERITED;
};
#endif