add mixer colorfilter
BUG=skia:
Change-Id: Icbee96056b17c5396a9d3783055ddd0b7d4fd3ff
Reviewed-on: https://skia-review.googlesource.com/c/9514
Commit-Queue: Florin Malita <fmalita@chromium.org>
Commit-Queue: Mike Reed <reed@google.com>
Reviewed-by: Florin Malita <fmalita@chromium.org>
diff --git a/gn/samples.gni b/gn/samples.gni
index d1b7904..1795584 100644
--- a/gn/samples.gni
+++ b/gn/samples.gni
@@ -62,6 +62,7 @@
"$_samplecode/SampleManyRects.cpp",
"$_samplecode/SampleMegaStroke.cpp",
"$_samplecode/SampleNima.cpp",
+ "$_samplecode/SampleMixer.cpp",
"$_samplecode/SamplePatch.cpp",
"$_samplecode/SamplePath.cpp",
"$_samplecode/SamplePathText.cpp",
diff --git a/include/core/SkColorFilter.h b/include/core/SkColorFilter.h
index 4fbacf3..ac167b3 100644
--- a/include/core/SkColorFilter.h
+++ b/include/core/SkColorFilter.h
@@ -120,6 +120,17 @@
*/
static sk_sp<SkColorFilter> MakeSRGBToLinearGamma();
+ /**
+ * Returns a new filter that returns the weighted average between the outputs of
+ * two other filters. If either is null, then it is treated as an identity filter.
+ *
+ * result = cf0(color) * (1 - weight) + cf1(color) * weight
+ *
+ * If both filters are null, or if weight is NaN, then null is returned.
+ */
+ static sk_sp<SkColorFilter> MakeMixer(sk_sp<SkColorFilter> cf0, sk_sp<SkColorFilter> cf1,
+ float weight);
+
#if SK_SUPPORT_GPU
/**
* A subclass may implement this factory function to work with the GPU backend. It returns
diff --git a/samplecode/SampleMixer.cpp b/samplecode/SampleMixer.cpp
new file mode 100644
index 0000000..65dcddc
--- /dev/null
+++ b/samplecode/SampleMixer.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Sample.h"
+#include "SkCanvas.h"
+#include "SkColorFilter.h"
+#include "SkGradientShader.h"
+#include "SkImage.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "Resources.h"
+
+const SkScalar gMat[] = {
+ .3f, .6f, .1f, 0, 0,
+ .3f, .6f, .1f, 0, 0,
+ .3f, .6f, .1f, 0, 0,
+ 0, 0, 0, 1, 0,
+};
+
+class MixerView : public Sample {
+ sk_sp<SkImage> fImg;
+ sk_sp<SkColorFilter> fCF0;
+ sk_sp<SkColorFilter> fCF1;
+
+ float fWeight = 0;
+ float fDW = 0.02f;
+
+public:
+ MixerView() {}
+
+protected:
+ bool onQuery(Event* evt) override {
+ if (Sample::TitleQ(*evt)) {
+ Sample::TitleR(evt, "Mixer");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void dodraw(SkCanvas* canvas, sk_sp<SkColorFilter> cf0, sk_sp<SkColorFilter> cf1, float gap) {
+ SkPaint paint;
+ paint.setColorFilter(cf0);
+ canvas->drawImage(fImg, 0, 0, &paint);
+
+ paint.setColorFilter(SkColorFilter::MakeMixer(cf0, cf1, fWeight));
+ canvas->drawImage(fImg, fImg->width() + gap * fWeight, 0, &paint);
+
+ paint.setColorFilter(cf1);
+ canvas->drawImage(fImg, 2*fImg->width() + gap, 0, &paint);
+ }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ if (!fImg) {
+ fImg = GetResourceAsImage("images/mandrill_256.png");
+ fCF0 = SkColorFilter::MakeMatrixFilterRowMajor255(gMat);
+ fCF1 = SkColorFilter::MakeModeFilter(0xFF44CC88, SkBlendMode::kScreen);
+ }
+
+ float gap = fImg->width() * 3;
+
+ canvas->translate(10, 10);
+ dodraw(canvas, nullptr, fCF1, gap);
+ canvas->translate(0, fImg->height() + 10);
+ dodraw(canvas, fCF0, nullptr, gap);
+ canvas->translate(0, fImg->height() + 10);
+ dodraw(canvas, fCF0, fCF1, gap);
+
+ fWeight += fDW;
+ if (fWeight > 1 || fWeight < 0) {
+ fDW = -fDW;
+ }
+ }
+
+ virtual Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned) override {
+ return fRect.contains(SkScalarRoundToInt(x),
+ SkScalarRoundToInt(y)) ? new Click(this) : nullptr;
+ }
+
+ bool onClick(Click* click) override {
+ fRect.offset(click->fICurr.fX - click->fIPrev.fX,
+ click->fICurr.fY - click->fIPrev.fY);
+ return true;
+ }
+
+private:
+ SkIRect fRect;
+
+ typedef Sample INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+DEF_SAMPLE( return new MixerView; )
diff --git a/src/core/SkColorFilter.cpp b/src/core/SkColorFilter.cpp
index 2089c08..7b2cc22 100644
--- a/src/core/SkColorFilter.cpp
+++ b/src/core/SkColorFilter.cpp
@@ -279,10 +279,116 @@
///////////////////////////////////////////////////////////////////////////////////////////////////
+class SkMixerColorFilter : public SkColorFilter {
+public:
+ SkMixerColorFilter(sk_sp<SkColorFilter> cf0, sk_sp<SkColorFilter> cf1, float weight)
+ : fCF0(std::move(cf0)), fCF1(std::move(cf1)), fWeight(weight)
+ {
+ SkASSERT(fCF0 || fCF1);
+ SkASSERT(fWeight >= 0 && fWeight <= 1);
+ }
+
+ uint32_t getFlags() const override {
+ uint32_t f0 = fCF0 ? fCF0->getFlags() : ~0U;
+ uint32_t f1 = fCF1 ? fCF1->getFlags() : ~0U;
+ return f0 & f1;
+ }
+
+ void onAppendStages(SkRasterPipeline* p, SkColorSpace* dst, SkArenaAlloc* alloc,
+ bool shaderIsOpaque) const override {
+ // want cf0 * (1 - w) + cf1 * w == lerp(w)
+ // which means
+ // dr,dg,db,da <-- cf0
+ // r,g,b,a <-- cf1
+ struct State {
+ float orig_rgba[4 * SkRasterPipeline_kMaxStride];
+ float filtered_rgba[4 * SkRasterPipeline_kMaxStride];
+ };
+ auto state = alloc->make<State>();
+
+ p->append(SkRasterPipeline::store_rgba, state->orig_rgba);
+ if (fCF0 && !fCF1) {
+ fCF0->appendStages(p, dst, alloc, shaderIsOpaque);
+ p->append(SkRasterPipeline::move_src_dst);
+ p->append(SkRasterPipeline::load_rgba, state->orig_rgba);
+ } else {
+ fCF1->appendStages(p, dst, alloc, shaderIsOpaque);
+ p->append(SkRasterPipeline::store_rgba, state->filtered_rgba);
+ p->append(SkRasterPipeline::load_rgba, state->orig_rgba);
+ if (fCF0) {
+ fCF0->appendStages(p, dst, alloc, shaderIsOpaque);
+ }
+ p->append(SkRasterPipeline::move_src_dst);
+ p->append(SkRasterPipeline::load_rgba, state->filtered_rgba);
+ }
+ float* storage = alloc->make<float>(fWeight);
+ p->append(SkRasterPipeline::lerp_1_float, storage);
+ }
+
+#if SK_SUPPORT_GPU
+ std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(
+ GrContext* context, const GrColorSpaceInfo&) const override {
+ return nullptr;
+ }
+#endif
+
+protected:
+ void flatten(SkWriteBuffer& buffer) const override {
+ buffer.writeFlattenable(fCF0.get());
+ buffer.writeFlattenable(fCF1.get());
+ buffer.writeScalar(fWeight);
+ }
+
+private:
+ SK_FLATTENABLE_HOOKS(SkMixerColorFilter)
+
+ sk_sp<SkColorFilter> fCF0;
+ sk_sp<SkColorFilter> fCF1;
+ const float fWeight;
+
+ friend class SkColorFilter;
+
+ typedef SkColorFilter INHERITED;
+};
+
+sk_sp<SkFlattenable> SkMixerColorFilter::CreateProc(SkReadBuffer& buffer) {
+ sk_sp<SkColorFilter> cf0(buffer.readColorFilter());
+ sk_sp<SkColorFilter> cf1(buffer.readColorFilter());
+ const float weight = buffer.readScalar();
+ return MakeMixer(std::move(cf0), std::move(cf1), weight);
+}
+
+sk_sp<SkColorFilter> SkColorFilter::MakeMixer(sk_sp<SkColorFilter> cf0,
+ sk_sp<SkColorFilter> cf1,
+ float weight) {
+ if (!cf0 && !cf1) {
+ return nullptr;
+ }
+ if (SkScalarIsNaN(weight)) {
+ return nullptr;
+ }
+
+ if (cf0 == cf1) {
+ return cf0; // or cf1
+ }
+
+ if (weight <= 0) {
+ return cf0;
+ }
+ if (weight >= 1) {
+ return cf1;
+ }
+
+ return sk_sp<SkColorFilter>(new SkMixerColorFilter(std::move(cf0), std::move(cf1), weight));
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
#include "SkModeColorFilter.h"
void SkColorFilter::RegisterFlattenables() {
SK_REGISTER_FLATTENABLE(SkComposeColorFilter);
SK_REGISTER_FLATTENABLE(SkModeColorFilter);
SK_REGISTER_FLATTENABLE(SkSRGBGammaColorFilter);
+ SK_REGISTER_FLATTENABLE(SkMixerColorFilter);
}