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:
parent
61a5f0aef8
commit
05be23dbd3
@ -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);
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
90
src/core/SkColorFilter_Mixer.cpp
Normal file
90
src/core/SkColorFilter_Mixer.cpp
Normal 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
|
50
src/core/SkColorFilter_Mixer.h
Normal file
50
src/core/SkColorFilter_Mixer.h
Normal 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
|
Loading…
Reference in New Issue
Block a user