Modules/Math/public/Color.hpp
Шурупов Илья Викторович 5ddb26ac9e Add the smudge brush with blur, resaturation and the linear hack
A smudge dab reads the patch under the brush, mixes the paint carried
from earlier dabs into it and then refreshes the carry from the
(optionally box-blurred) pickup. The smudge parameter is how much of the
carry survives each dab, blur softens what is picked up, and
resaturation pulls the carry towards the brush colour, so a blur brush
is a smudge brush with no carry and a positive blur.

The linear hack (Leonardo flag 0x40000) mixes in linear light: the
binary un-premultiplies, applies a gamma 2.2 table and re-premultiplies,
which Math now provides as gammaToLinear/linearToGamma.

The smudge tool in the strip switches to the smudge brush and back like
the eraser, S triggers it, empty layers refuse it with Leonardo's
message, and strokes are labelled Smudge in history.
2026-10-15 18:44:51 +03:00

114 lines
1.8 KiB
C++

#pragma once
#include "Vec.hpp"
namespace tp {
class RGB;
class HSV;
class RGB {
public:
RGB();
RGB(flt4 pr, flt4 pg, flt4 pb);
RGB(flt4 val);
public:
void set(flt4 pr, flt4 pg, flt4 pb);
operator HSV() const;
public:
halnf r, g, b;
};
class HSV {
public:
HSV();
HSV(flt4 ph, flt4 ps, flt4 pv);
public:
void set(flt4 ph, flt4 ps, flt4 pv);
operator RGB() const;
public:
halnf h, s, v;
};
class RGBA {
public:
RGBA() :
r(0),
g(0),
b(0),
a(0) {}
RGBA(flt4 val) :
rgbs(val),
a(val) {}
RGBA(const RGB& RGBs, flt4 val) :
rgbs(RGBs),
a(val) {}
RGBA(flt4 pr, flt4 pg, flt4 pb, flt4 pa) :
rgbs(pr, pg, pb),
a(pa) {}
public:
RGBA& operator=(const HSV& in) {
rgbs = in;
a = 1;
return *this;
}
RGBA operator-(const RGBA& in) const {
const auto nr = tp::clamp(r - in.r, 0.f, 1.f);
const auto ng = tp::clamp(g - in.g, 0.f, 1.f);
const auto nb = tp::clamp(b - in.b, 0.f, 1.f);
const auto na = tp::clamp(a - in.a, 0.f, 1.f);
return { nr, ng, nb, na };
}
RGBA operator+(const RGBA& in) const {
const auto nr = tp::clamp(r + in.r, 0.f, 1.f);
const auto ng = tp::clamp(g + in.g, 0.f, 1.f);
const auto nb = tp::clamp(b + in.b, 0.f, 1.f);
const auto na = tp::clamp(a + in.a, 0.f, 1.f);
return { nr, ng, nb, na };
}
bool operator==(const RGBA& in) const { return r == in.r && g == in.g && b == in.b && a == in.a; }
static RGBA random() {
return RGBA{
(halnf) randomFloat(),
(halnf) randomFloat(),
(halnf) randomFloat(),
1.f,
};
}
public:
union {
RGB rgbs;
struct {
flt4 r;
flt4 g;
flt4 b;
};
};
flt4 a;
};
class HSVA {
HSV rgbs;
flt4 a = 1.f;
};
constexpr halnf kDisplayGamma = 2.2f;
[[nodiscard]] halnf gammaToLinear(halnf encoded);
[[nodiscard]] halnf linearToGamma(halnf linear);
}