Arithmetic blender

... to aid in removing special ArithmeticImageFilter.

Can we use something like this to reimpl the ArithmeticImageFilter
--> BlenderImageFilter?

I see some special checking in filterBounds for ArithIF -- is that
required? Is that generalizable for any BlenderImageFilter?
If we can generalize blend[er]imagefilter, let's just move this
logic into Chrome -- no need for Arithmetic to appear in Skia
in any form!

Re: https://skia-review.googlesource.com/c/skia/+/424436

Change-Id: Ifa0ff2fa25de6385f6f60505e73ecc28463a80b3
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/424417
Commit-Queue: Mike Reed <reed@google.com>
Reviewed-by: Florin Malita <fmalita@chromium.org>
This commit is contained in:
Mike Reed 2021-07-08 10:43:31 -04:00 committed by Skia Commit-Bot
parent 8c26479048
commit 26ea975e6c
4 changed files with 162 additions and 12 deletions

View File

@ -30,15 +30,12 @@
#include <utility>
#define WW 100
#define HH 32
static sk_sp<SkImage> make_src() {
sk_sp<SkSurface> surface(SkSurface::MakeRasterN32Premul(WW, HH));
static sk_sp<SkImage> make_src(int w, int h) {
sk_sp<SkSurface> surface(SkSurface::MakeRasterN32Premul(w, h));
SkCanvas* canvas = surface->getCanvas();
SkPaint paint;
SkPoint pts[] = { {0, 0}, {SkIntToScalar(WW), SkIntToScalar(HH)} };
SkPoint pts[] = { {0, 0}, {SkIntToScalar(w), SkIntToScalar(h)} };
SkColor colors[] = {
SK_ColorTRANSPARENT, SK_ColorGREEN, SK_ColorCYAN,
SK_ColorRED, SK_ColorMAGENTA, SK_ColorWHITE,
@ -49,12 +46,12 @@ static sk_sp<SkImage> make_src() {
return surface->makeImageSnapshot();
}
static sk_sp<SkImage> make_dst() {
sk_sp<SkSurface> surface(SkSurface::MakeRasterN32Premul(WW, HH));
static sk_sp<SkImage> make_dst(int w, int h) {
sk_sp<SkSurface> surface(SkSurface::MakeRasterN32Premul(w, h));
SkCanvas* canvas = surface->getCanvas();
SkPaint paint;
SkPoint pts[] = { {0, SkIntToScalar(HH)}, {SkIntToScalar(WW), 0} };
SkPoint pts[] = { {0, SkIntToScalar(h)}, {SkIntToScalar(w), 0} };
SkColor colors[] = {
SK_ColorBLUE, SK_ColorYELLOW, SK_ColorBLACK, SK_ColorGREEN,
SK_ColorGRAY,
@ -85,8 +82,11 @@ class ArithmodeGM : public skiagm::GM {
SkISize onISize() override { return {640, 572}; }
void onDraw(SkCanvas* canvas) override {
sk_sp<SkImage> src = make_src();
sk_sp<SkImage> dst = make_dst();
constexpr int WW = 100,
HH = 32;
sk_sp<SkImage> src = make_src(WW, HH);
sk_sp<SkImage> dst = make_dst(WW, HH);
sk_sp<SkImageFilter> srcFilter = SkImageFilters::Image(src);
sk_sp<SkImageFilter> dstFilter = SkImageFilters::Image(dst);
@ -166,7 +166,66 @@ class ArithmodeGM : public skiagm::GM {
private:
using INHERITED = GM;
};
DEF_GM( return new ArithmodeGM; )
///////////////////////////////////////////////////////////////////////////////
DEF_GM( return new ArithmodeGM; )
#include "include/effects/SkBlenders.h"
class Arithmode2GM : public skiagm::GM {
float fK1, fK2, fK3, fK4;
sk_sp<SkImage> fSrc, fDst, fChecker;
SkString onShortName() override { return SkString("arithmode_blender"); }
SkISize onISize() override { return {430, 430}; }
enum {
W = 200,
H = 200,
};
void onOnceBeforeDraw() override {
// need something interesting, in case we're drawn w/o calling animate()
fK1 = -0.25f;
fK2 = 0.25f;
fK3 = 0.25f;
fK4 = 0;
fSrc = make_src(W, H);
fDst = make_dst(W, H);
fChecker = ToolUtils::create_checkerboard_image(W, H, 0xFF999999, 0xFFCCCCCC, 8);
}
bool onAnimate(double nanos) override {
double theta = nanos * 1e-6 * 0.001;
fK1 = sin(theta + 0) * 0.25;
fK2 = cos(theta + 1) * 0.25;
fK3 = sin(theta + 2) * 0.25;
fK4 = 0.5;
return true;
}
void onDraw(SkCanvas* canvas) override {
const SkRect rect = SkRect::MakeWH(W, H);
canvas->drawImage(fSrc, 10, 10);
canvas->drawImage(fDst, 10, 10 + fSrc->height() + 10);
canvas->translate(10 + fSrc->width() + 10, 10);
canvas->drawImage(fChecker, 0, 0);
SkPaint paint;
paint.setBlender(SkBlenders::Arithmetic(fK1, fK2, fK3, fK4, true));
canvas->saveLayer(&rect, nullptr);
canvas->drawImage(fDst, 0, 0);
canvas->drawImage(fSrc, 0, 0, SkSamplingOptions(), &paint);
canvas->restore();
}
private:
using INHERITED = GM;
};
DEF_GM( return new Arithmode2GM; )

View File

@ -10,6 +10,7 @@ _include = get_path_info("../include", "abspath")
skia_effects_public = [
"$_include/effects/Sk1DPathEffect.h",
"$_include/effects/Sk2DPathEffect.h",
"$_include/effects/SkBlenders.h",
"$_include/effects/SkBlurMaskFilter.h",
"$_include/effects/SkColorMatrix.h",
"$_include/effects/SkColorMatrixFilter.h",
@ -31,6 +32,7 @@ skia_effects_sources = [
"$_src/c/sk_effects.cpp",
"$_src/effects/Sk1DPathEffect.cpp",
"$_src/effects/Sk2DPathEffect.cpp",
"$_src/effects/SkBlenders.cpp",
"$_src/effects/SkColorMatrix.cpp",
"$_src/effects/SkColorMatrixFilter.cpp",
"$_src/effects/SkCornerPathEffect.cpp",

View File

@ -0,0 +1,27 @@
/*
* Copyright 2021 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkBlenders_DEFINED
#define SkBlenders_DEFINED
#include "include/core/SkBlender.h"
class SK_API SkBlenders {
public:
/**
* Create a blender that implements the following:
* k1 * src * dst + k2 * src + k3 * dst + k4
* @param k1, k2, k3, k4 The four coefficients.
* @param enforcePMColor If true, the RGB channels will be clamped to the calculated alpha.
*/
static sk_sp<SkBlender> Arithmetic(float k1, float k2, float k3, float k4, bool enforcePremul);
private:
SkBlenders() = delete;
};
#endif

View File

@ -0,0 +1,62 @@
/*
* Copyright 2021 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/effects/SkBlenders.h"
#include "include/effects/SkRuntimeEffect.h"
sk_sp<SkBlender> SkBlenders::Arithmetic(float k1, float k2, float k3, float k4,
bool enforcePremul) {
if (!SkScalarIsFinite(k1) ||
!SkScalarIsFinite(k2) ||
!SkScalarIsFinite(k3) ||
!SkScalarIsFinite(k4)) {
return nullptr;
}
// Are we nearly a SkBlendMode?
const struct {
float k1, k2, k3, k4;
SkBlendMode mode;
} table[] = {
{ 0, 1, 0, 0, SkBlendMode::kSrc },
{ 0, 0, 1, 0, SkBlendMode::kDst },
{ 0, 0, 0, 0, SkBlendMode::kClear },
};
for (const auto& t : table) {
if (SkScalarNearlyEqual(k1, t.k1) &&
SkScalarNearlyEqual(k2, t.k2) &&
SkScalarNearlyEqual(k3, t.k3) &&
SkScalarNearlyEqual(k4, t.k4)) {
return SkBlender::Mode(t.mode);
}
}
// If we get here, we need the actual blender effect.
static SkRuntimeEffect* gArithmeticEffect = []{
const char prog[] = R"(
uniform half4 k;
uniform half pmClamp;
half4 main(half4 src, half4 dst) {
half4 c = k.x * src * dst + k.y * src + k.z * dst + k.w;
c.rgb = min(c.rgb, max(c.a, pmClamp));
// rely on skia to saturate our alpha
return c;
}
)";
auto result = SkRuntimeEffect::MakeForBlender(SkString(prog));
SkASSERTF(result.effect, "SkBlenders::Arithmetic: %s", result.errorText.c_str());
return result.effect.release();
}();
const float array[] = {
k1, k2, k3, k4,
enforcePremul ? 0.0f : 1.0f,
};
return gArithmeticEffect->makeBlender(SkData::MakeWithCopy(array, sizeof(array)));
}