Improve color selector shaders

* Removed conversion from RGB -> HSV/HSL for each pixel (we just pass
  the HSV/HSL values directly to the shader). In this way we don't
  lost some hue information for some special cases like white/black
  colors (probably related to: https://community.aseprite.org/t/14904)
* Now Tint/Shade/Tone selector changes the aspect of the Hue slider
  depending on the current saturation/value (this was changed in the
  non-shader version too)
This commit is contained in:
David Capello 2022-07-18 12:01:12 -03:00
parent 4389788a1d
commit df33744c85
9 changed files with 77 additions and 64 deletions

View File

@ -468,8 +468,7 @@ void ColorSelector::onPaint(ui::PaintEvent& ev)
SkRuntimeShaderBuilder builder1(m_mainEffect); SkRuntimeShaderBuilder builder1(m_mainEffect);
builder1.uniform("iRes") = SkV3{float(rc2.w), float(rc2.h), 0.0f}; builder1.uniform("iRes") = SkV3{float(rc2.w), float(rc2.h), 0.0f};
builder1.uniform("iColor") = appColor_to_SkV4(m_color); setShaderParams(builder1, true);
setShaderMainAreaParams(builder1);
p.setShader(builder1.makeShader()); p.setShader(builder1.makeShader());
if (isSRGB) if (isSRGB)
@ -484,7 +483,7 @@ void ColorSelector::onPaint(ui::PaintEvent& ev)
SkRuntimeShaderBuilder builder2(m_bottomEffect); SkRuntimeShaderBuilder builder2(m_bottomEffect);
builder2.uniform("iRes") = SkV3{float(rc2.w), float(rc2.h), 0.0f}; builder2.uniform("iRes") = SkV3{float(rc2.w), float(rc2.h), 0.0f};
builder2.uniform("iColor") = appColor_to_SkV4(m_color); setShaderParams(builder2, false);
p.setShader(builder2.makeShader()); p.setShader(builder2.makeShader());
canvas->drawRect(SkRect::MakeXYWH(0, 0, rc2.w, rc2.h), p); canvas->drawRect(SkRect::MakeXYWH(0, 0, rc2.w, rc2.h), p);
@ -632,9 +631,7 @@ const char* ColorSelector::getAlphaBarShader()
{ {
return R"( return R"(
uniform half3 iRes; uniform half3 iRes;
uniform half4 iColor; uniform half4 iColor, iBg1, iBg2;
uniform half4 iBg1;
uniform half4 iBg2;
half4 main(vec2 fragcoord) { half4 main(vec2 fragcoord) {
vec2 d = (fragcoord.xy / iRes.xy); vec2 d = (fragcoord.xy / iRes.xy);

View File

@ -71,7 +71,7 @@ namespace app {
virtual const char* getMainAreaShader() { return nullptr; } virtual const char* getMainAreaShader() { return nullptr; }
virtual const char* getBottomBarShader() { return nullptr; } virtual const char* getBottomBarShader() { return nullptr; }
#if SK_ENABLE_SKSL #if SK_ENABLE_SKSL
virtual void setShaderMainAreaParams(SkRuntimeShaderBuilder& builder) { } virtual void setShaderParams(SkRuntimeShaderBuilder& builder, bool main) { }
#endif #endif
virtual app::Color getMainAreaColor(const int u, const int umax, virtual app::Color getMainAreaColor(const int u, const int umax,
const int v, const int vmax) = 0; const int v, const int vmax) = 0;

View File

@ -35,52 +35,50 @@ ColorSpectrum::ColorSpectrum()
{ {
} }
#if SK_ENABLE_SKSL
const char* ColorSpectrum::getMainAreaShader() const char* ColorSpectrum::getMainAreaShader()
{ {
#if SK_ENABLE_SKSL
if (m_mainShader.empty()) { if (m_mainShader.empty()) {
m_mainShader += "uniform half3 iRes;" m_mainShader += "uniform half3 iRes;"
"uniform half4 iColor;"; "uniform half4 iHsl;";
m_mainShader += kRGB_to_HSL_sksl;
m_mainShader += kHSL_to_RGB_sksl; m_mainShader += kHSL_to_RGB_sksl;
m_mainShader += R"( m_mainShader += R"(
half4 main(vec2 fragcoord) { half4 main(vec2 fragcoord) {
vec2 d = fragcoord.xy / iRes.xy; vec2 d = fragcoord.xy / iRes.xy;
half hue = d.x; half hue = d.x;
half sat = rgb_to_hsl(iColor.rgb).y; half sat = iHsl.y;
half lit = 1.0 - d.y; half lit = 1.0 - d.y;
return hsl_to_rgb(half3(hue, sat, lit)).rgb1; return hsl_to_rgb(half3(hue, sat, lit)).rgb1;
} }
)"; )";
} }
return m_mainShader.c_str(); return m_mainShader.c_str();
#else
return nullptr;
#endif
} }
const char* ColorSpectrum::getBottomBarShader() const char* ColorSpectrum::getBottomBarShader()
{ {
#if SK_ENABLE_SKSL
if (m_bottomShader.empty()) { if (m_bottomShader.empty()) {
m_bottomShader += "uniform half3 iRes;" m_bottomShader += "uniform half3 iRes;"
"uniform half4 iColor;"; "uniform half4 iHsl;";
m_bottomShader += kRGB_to_HSL_sksl;
m_bottomShader += kHSL_to_RGB_sksl; m_bottomShader += kHSL_to_RGB_sksl;
m_bottomShader += R"( m_bottomShader += R"(
half4 main(vec2 fragcoord) { half4 main(vec2 fragcoord) {
half s = (fragcoord.x / iRes.x); half s = (fragcoord.x / iRes.x);
half3 hsl = rgb_to_hsl(iColor.rgb); return hsl_to_rgb(half3(iHsl.x, s, iHsl.z)).rgb1;
return hsl_to_rgb(half3(hsl.x, s, hsl.z)).rgb1;
} }
)"; )";
} }
return m_bottomShader.c_str(); return m_bottomShader.c_str();
#else
return nullptr;
#endif
} }
void ColorSpectrum::setShaderParams(SkRuntimeShaderBuilder& builder, bool main)
{
builder.uniform("iHsl") = appColorHsl_to_SkV4(m_color);
}
#endif // SK_ENABLE_SKSL
app::Color ColorSpectrum::getMainAreaColor(const int u, const int umax, app::Color ColorSpectrum::getMainAreaColor(const int u, const int umax,
const int v, const int vmax) const int v, const int vmax)
{ {

View File

@ -18,8 +18,11 @@ namespace app {
ColorSpectrum(); ColorSpectrum();
protected: protected:
#if SK_ENABLE_SKSL
const char* getMainAreaShader() override; const char* getMainAreaShader() override;
const char* getBottomBarShader() override; const char* getBottomBarShader() override;
void setShaderParams(SkRuntimeShaderBuilder& builder, bool main) override;
#endif
app::Color getMainAreaColor(const int u, const int umax, app::Color getMainAreaColor(const int u, const int umax,
const int v, const int vmax) override; const int v, const int vmax) override;
app::Color getBottomBarColor(const int u, const int umax) override; app::Color getBottomBarColor(const int u, const int umax) override;

View File

@ -28,18 +28,18 @@ ColorTintShadeTone::ColorTintShadeTone()
{ {
} }
#if SK_ENABLE_SKSL
const char* ColorTintShadeTone::getMainAreaShader() const char* ColorTintShadeTone::getMainAreaShader()
{ {
#if SK_ENABLE_SKSL
if (m_mainShader.empty()) { if (m_mainShader.empty()) {
m_mainShader += "uniform half3 iRes;" m_mainShader += "uniform half3 iRes;"
"uniform half4 iColor;"; "uniform half4 iHsv;";
m_mainShader += kRGB_to_HSV_sksl;
m_mainShader += kHSV_to_RGB_sksl; m_mainShader += kHSV_to_RGB_sksl;
m_mainShader += R"( m_mainShader += R"(
half4 main(vec2 fragcoord) { half4 main(vec2 fragcoord) {
vec2 d = fragcoord.xy / iRes.xy; vec2 d = fragcoord.xy / iRes.xy;
half hue = rgb_to_hsv(iColor.rgb).x; half hue = iHsv.x;
half sat = d.x; half sat = d.x;
half val = 1.0 - d.y; half val = 1.0 - d.y;
return hsv_to_rgb(vec3(hue, sat, val)).rgb1; return hsv_to_rgb(vec3(hue, sat, val)).rgb1;
@ -47,35 +47,32 @@ half4 main(vec2 fragcoord) {
)"; )";
} }
return m_mainShader.c_str(); return m_mainShader.c_str();
#else
return nullptr;
#endif
} }
const char* ColorTintShadeTone::getBottomBarShader() const char* ColorTintShadeTone::getBottomBarShader()
{ {
#if SK_ENABLE_SKSL
if (m_bottomShader.empty()) { if (m_bottomShader.empty()) {
m_bottomShader += "uniform half3 iRes;" m_bottomShader += "uniform half3 iRes;"
"uniform half4 iColor;"; "uniform half4 iHsv;";
m_bottomShader += kRGB_to_HSV_sksl;
m_bottomShader += kHSV_to_RGB_sksl; m_bottomShader += kHSV_to_RGB_sksl;
// TODO should we display the hue bar with the current sat/value? // TODO should we display the hue bar with the current sat/value?
m_bottomShader += R"( m_bottomShader += R"(
half4 main(vec2 fragcoord) { half4 main(vec2 fragcoord) {
half h = (fragcoord.x / iRes.x); half h = (fragcoord.x / iRes.x);
// half3 hsv = rgb_to_hsv(iColor.rgb); return hsv_to_rgb(half3(h, iHsv.y, iHsv.z)).rgb1;
// return hsv_to_rgb(half3(h, hsv.y, hsv.z)).rgb1;
return hsv_to_rgb(half3(h, 1.0, 1.0)).rgb1;
} }
)"; )";
} }
return m_bottomShader.c_str(); return m_bottomShader.c_str();
#else
return nullptr;
#endif
} }
void ColorTintShadeTone::setShaderParams(SkRuntimeShaderBuilder& builder, bool main)
{
builder.uniform("iHsv") = appColorHsv_to_SkV4(m_color);
}
#endif // SK_ENABLE_SKSL
app::Color ColorTintShadeTone::getMainAreaColor(const int u, const int umax, app::Color ColorTintShadeTone::getMainAreaColor(const int u, const int umax,
const int v, const int vmax) const int v, const int vmax)
{ {
@ -114,9 +111,10 @@ void ColorTintShadeTone::onPaintBottomBar(ui::Graphics* g, const gfx::Rect& rc)
{ {
if (m_color.getType() != app::Color::MaskType) { if (m_color.getType() != app::Color::MaskType) {
double hue = m_color.getHsvHue(); double hue = m_color.getHsvHue();
double val = m_color.getHsvValue();
gfx::Point pos(rc.x + int(rc.w * hue / 360.0), gfx::Point pos(rc.x + int(rc.w * hue / 360.0),
rc.y + rc.h/2); rc.y + rc.h/2);
paintColorIndicator(g, pos, false); paintColorIndicator(g, pos, val < 0.5);
} }
} }
@ -153,11 +151,15 @@ void ColorTintShadeTone::onPaintSurfaceInBgThread(
if (m_paintFlags & BottomBarFlag) { if (m_paintFlags & BottomBarFlag) {
os::Paint paint; os::Paint paint;
const double sat = m_color.getHsvSaturation();
const double val = m_color.getHsvValue();
for (int x=0; x<bottom.w && !stop; ++x) { for (int x=0; x<bottom.w && !stop; ++x) {
paint.color( paint.color(
color_utils::color_for_ui( color_utils::color_for_ui(
app::Color::fromHsv( app::Color::fromHsv(
(360.0 * x / bottom.w), 1.0, 1.0))); (360.0 * x / bottom.w), sat, val)));
s->drawRect(gfx::Rect(bottom.x+x, bottom.y, 1, bottom.h), paint); s->drawRect(gfx::Rect(bottom.x+x, bottom.y, 1, bottom.h), paint);
} }
@ -175,6 +177,8 @@ int ColorTintShadeTone::onNeedsSurfaceRepaint(const app::Color& newColor)
return return
// Only if the hue changes we have to redraw the main surface. // Only if the hue changes we have to redraw the main surface.
(cs_double_diff(m_color.getHsvHue(), newColor.getHsvHue()) ? MainAreaFlag: 0) | (cs_double_diff(m_color.getHsvHue(), newColor.getHsvHue()) ? MainAreaFlag: 0) |
(cs_double_diff(m_color.getHsvSaturation(), newColor.getHsvSaturation()) ||
cs_double_diff(m_color.getHsvValue(), newColor.getHsvValue()) ? BottomBarFlag: 0) |
ColorSelector::onNeedsSurfaceRepaint(newColor); ColorSelector::onNeedsSurfaceRepaint(newColor);
} }

View File

@ -19,8 +19,11 @@ namespace app {
ColorTintShadeTone(); ColorTintShadeTone();
protected: protected:
#if SK_ENABLE_SKSL
const char* getMainAreaShader() override; const char* getMainAreaShader() override;
const char* getBottomBarShader() override; const char* getBottomBarShader() override;
void setShaderParams(SkRuntimeShaderBuilder& builder, bool main) override;
#endif
app::Color getMainAreaColor(const int u, const int umax, app::Color getMainAreaColor(const int u, const int umax,
const int v, const int vmax) override; const int v, const int vmax) override;
app::Color getBottomBarColor(const int u, const int umax) override; app::Color getBottomBarColor(const int u, const int umax) override;

View File

@ -66,13 +66,14 @@ ColorWheel::ColorWheel()
initTheme(); initTheme();
} }
#if SK_ENABLE_SKSL
const char* ColorWheel::getMainAreaShader() const char* ColorWheel::getMainAreaShader()
{ {
#if SK_ENABLE_SKSL
// TODO create one shader for each wheel mode (RGB, RYB, normal) // TODO create one shader for each wheel mode (RGB, RYB, normal)
if (m_mainShader.empty()) { if (m_mainShader.empty()) {
m_mainShader += "uniform half3 iRes;" m_mainShader += "uniform half3 iRes;"
"uniform half4 iColor;" "uniform half4 iHsv;"
"uniform half4 iBack;" "uniform half4 iBack;"
"uniform int iDiscrete;" "uniform int iDiscrete;"
"uniform int iMode;"; "uniform int iMode;";
@ -128,8 +129,7 @@ half4 main(vec2 fragcoord) {
sat /= 100.0; sat /= 100.0;
sat = clamp(sat, 0.0, 1.0); sat = clamp(sat, 0.0, 1.0);
} }
vec3 hsv = rgb_to_hsv(iColor.rgb); return hsv_to_rgb(vec3(hue, sat, iHsv.w > 0 ? iHsv.z: 1.0)).rgb1;
return hsv_to_rgb(vec3(hue, sat, iColor.w > 0 ? hsv.z: 1.0)).rgb1;
} }
else { else {
if (iMode == 2) // Normal map mode if (iMode == 2) // Normal map mode
@ -140,42 +140,36 @@ half4 main(vec2 fragcoord) {
)"; )";
} }
return m_mainShader.c_str(); return m_mainShader.c_str();
#else
return nullptr;
#endif
} }
const char* ColorWheel::getBottomBarShader() const char* ColorWheel::getBottomBarShader()
{ {
#if SK_ENABLE_SKSL
if (m_bottomShader.empty()) { if (m_bottomShader.empty()) {
m_bottomShader += "uniform half3 iRes;" m_bottomShader += "uniform half3 iRes;"
"uniform half4 iColor;"; "uniform half4 iHsv;";
m_bottomShader += kRGB_to_HSV_sksl;
m_bottomShader += kHSV_to_RGB_sksl; m_bottomShader += kHSV_to_RGB_sksl;
// TODO should we display the hue bar with the current sat/value? // TODO should we display the hue bar with the current sat/value?
m_bottomShader += R"( m_bottomShader += R"(
half4 main(vec2 fragcoord) { half4 main(vec2 fragcoord) {
half v = (fragcoord.x / iRes.x); half v = (fragcoord.x / iRes.x);
half3 hsv = rgb_to_hsv(iColor.rgb); return hsv_to_rgb(half3(iHsv.x, iHsv.y, v)).rgb1;
return hsv_to_rgb(half3(hsv.x, hsv.y, v)).rgb1;
} }
)"; )";
} }
return m_bottomShader.c_str(); return m_bottomShader.c_str();
#else
return nullptr;
#endif
} }
#if SK_ENABLE_SKSL void ColorWheel::setShaderParams(SkRuntimeShaderBuilder& builder, bool main)
void ColorWheel::setShaderMainAreaParams(SkRuntimeShaderBuilder& builder)
{ {
builder.uniform("iBack") = gfxColor_to_SkV4(m_bgColor); builder.uniform("iHsv") = appColorHsv_to_SkV4(m_color);
builder.uniform("iDiscrete") = (m_discrete ? 1: 0); if (main) {
builder.uniform("iMode") = int(m_colorModel); builder.uniform("iBack") = gfxColor_to_SkV4(m_bgColor);
builder.uniform("iDiscrete") = (m_discrete ? 1: 0);
builder.uniform("iMode") = int(m_colorModel);
}
} }
#endif
#endif // SK_ENABLE_SKSL
app::Color ColorWheel::getMainAreaColor(const int _u, const int umax, app::Color ColorWheel::getMainAreaColor(const int _u, const int umax,
const int _v, const int vmax) const int _v, const int vmax)

View File

@ -43,14 +43,14 @@ namespace app {
void setHarmony(Harmony harmony); void setHarmony(Harmony harmony);
protected: protected:
#if SK_ENABLE_SKSL
const char* getMainAreaShader() override; const char* getMainAreaShader() override;
const char* getBottomBarShader() override; const char* getBottomBarShader() override;
void setShaderParams(SkRuntimeShaderBuilder& builder, bool main) override;
#endif
app::Color getMainAreaColor(const int u, const int umax, app::Color getMainAreaColor(const int u, const int umax,
const int v, const int vmax) override; const int v, const int vmax) override;
app::Color getBottomBarColor(const int u, const int umax) override; app::Color getBottomBarColor(const int u, const int umax) override;
#if SK_ENABLE_SKSL
void setShaderMainAreaParams(SkRuntimeShaderBuilder& builder) override;
#endif
void onPaintMainArea(ui::Graphics* g, const gfx::Rect& rc) override; void onPaintMainArea(ui::Graphics* g, const gfx::Rect& rc) override;
void onPaintBottomBar(ui::Graphics* g, const gfx::Rect& rc) override; void onPaintBottomBar(ui::Graphics* g, const gfx::Rect& rc) override;
void onPaintSurfaceInBgThread(os::Surface* s, void onPaintSurfaceInBgThread(os::Surface* s,

View File

@ -60,6 +60,20 @@ inline SkV4 appColor_to_SkV4(const app::Color& color) {
float(color.getAlpha() / 255.0)}; float(color.getAlpha() / 255.0)};
} }
inline SkV4 appColorHsv_to_SkV4(const app::Color& color) {
return SkV4{float(color.getHsvHue() / 360.0),
float(color.getHsvSaturation()),
float(color.getHsvValue()),
float(color.getAlpha() / 255.0)};
}
inline SkV4 appColorHsl_to_SkV4(const app::Color& color) {
return SkV4{float(color.getHslHue() / 360.0),
float(color.getHslSaturation()),
float(color.getHslLightness()),
float(color.getAlpha() / 255.0)};
}
} // namespace app } // namespace app
#endif #endif