mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-28 16:20:50 +00:00
Use shaders for ColorWheel selector
This patch includes a fix for the RYB color wheel (the blue was not in the correct hue angle).
This commit is contained in:
parent
b561a6fbfb
commit
23b269a6bc
@ -471,6 +471,7 @@ void ColorSelector::onPaint(ui::PaintEvent& ev)
|
||||
SkRuntimeShaderBuilder builder1(m_mainEffect);
|
||||
builder1.uniform("iRes") = SkV3{float(rc2.w), float(rc2.h), 0.0f};
|
||||
builder1.uniform("iColor") = appColor_to_SkV4(m_color);
|
||||
setShaderMainAreaParams(builder1);
|
||||
p.setShader(builder1.makeShader());
|
||||
|
||||
if (isSRGB)
|
||||
@ -677,6 +678,6 @@ sk_sp<SkRuntimeEffect> ColorSelector::buildEffect(const char* code)
|
||||
return result.effect;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif // SK_ENABLE_SKSL
|
||||
|
||||
} // namespace app
|
||||
|
@ -70,6 +70,9 @@ namespace app {
|
||||
|
||||
virtual const char* getMainAreaShader() { return nullptr; }
|
||||
virtual const char* getBottomBarShader() { return nullptr; }
|
||||
#if SK_ENABLE_SKSL
|
||||
virtual void setShaderMainAreaParams(SkRuntimeShaderBuilder& builder) { }
|
||||
#endif
|
||||
virtual app::Color getMainAreaColor(const int u, const int umax,
|
||||
const int v, const int vmax) = 0;
|
||||
virtual app::Color getBottomBarColor(const int u, const int umax) = 0;
|
||||
|
@ -15,9 +15,9 @@
|
||||
#include "app/pref/preferences.h"
|
||||
#include "app/ui/skin/skin_theme.h"
|
||||
#include "app/ui/status_bar.h"
|
||||
#include "app/util/shader_helpers.h"
|
||||
#include "base/clamp.h"
|
||||
#include "base/pi.h"
|
||||
#include "filters/color_curve.h"
|
||||
#include "os/surface.h"
|
||||
#include "ui/graphics.h"
|
||||
#include "ui/menu.h"
|
||||
@ -67,6 +67,117 @@ ColorWheel::ColorWheel()
|
||||
initTheme();
|
||||
}
|
||||
|
||||
const char* ColorWheel::getMainAreaShader()
|
||||
{
|
||||
#if SK_ENABLE_SKSL
|
||||
// TODO create one shader for each wheel mode (RGB, RYB, normal)
|
||||
if (m_mainShader.empty()) {
|
||||
m_mainShader += "uniform half3 iRes;"
|
||||
"uniform half4 iColor;"
|
||||
"uniform half4 iBack;"
|
||||
"uniform int iDiscrete;"
|
||||
"uniform int iMode;";
|
||||
m_mainShader += kRGB_to_HSV_sksl;
|
||||
m_mainShader += kHSV_to_RGB_sksl;
|
||||
m_mainShader += R"(
|
||||
const half PI = 3.1415;
|
||||
|
||||
half rybhue_to_rgbhue(half h) {
|
||||
if (h >= 0 && h < 120) return h / 2; // from red to yellow
|
||||
else if (h < 180) return (h-60.0); // from yellow to green
|
||||
else if (h < 240) return 120 + 2*(h-180); // from green to blue
|
||||
else return h; // from blue to red (same hue)
|
||||
}
|
||||
|
||||
half4 main(vec2 fragcoord) {
|
||||
vec2 res = vec2(min(iRes.x, iRes.y), min(iRes.x, iRes.y));
|
||||
vec2 d = (fragcoord.xy-iRes.xy/2) / res.xy;
|
||||
half r = length(d);
|
||||
|
||||
if (r <= 0.5) {
|
||||
half a = atan(-d.y, d.x);
|
||||
half hue = (floor(180.0 * a / PI)
|
||||
+ 180 // To avoid [-180,0) range
|
||||
+ 180 + 30 // To locate green at 12 o'clock
|
||||
);
|
||||
|
||||
hue = mod(hue, 360); // To leave hue in [0,360) range
|
||||
if (iDiscrete != 0) {
|
||||
hue += 15.0;
|
||||
hue = floor(hue / 30.0);
|
||||
hue *= 30.0;
|
||||
}
|
||||
if (iMode == 1) { // RYB color wheel
|
||||
hue = rybhue_to_rgbhue(hue);
|
||||
}
|
||||
hue /= 360.0;
|
||||
|
||||
if (iMode == 2) { // Normal map mode
|
||||
float di = 0.5 * r / 0.5;
|
||||
half3 rgb = half3(0.5+di*cos(a), 0.5+di*sin(a), 1.0-di);
|
||||
return half4(
|
||||
clamp(rgb.x, 0, 1),
|
||||
clamp(rgb.y, 0, 1),
|
||||
clamp(rgb.z, 0.5, 1), 1);
|
||||
}
|
||||
|
||||
half sat = r / 0.5;
|
||||
if (iDiscrete != 0) {
|
||||
sat *= 120.0;
|
||||
sat = floor(sat / 20.0);
|
||||
sat *= 20.0;
|
||||
sat /= 100.0;
|
||||
sat = clamp(sat, 0.0, 1.0);
|
||||
}
|
||||
vec3 hsv = rgb_to_hsv(iColor.rgb);
|
||||
return hsv_to_rgb(vec3(hue, sat, iColor.w > 0 ? hsv.z: 1.0)).rgb1;
|
||||
}
|
||||
else {
|
||||
if (iMode == 2) // Normal map mode
|
||||
return half4(0.5, 0.5, 1, 1);
|
||||
return iBack;
|
||||
}
|
||||
}
|
||||
)";
|
||||
}
|
||||
return m_mainShader.c_str();
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
const char* ColorWheel::getBottomBarShader()
|
||||
{
|
||||
#if SK_ENABLE_SKSL
|
||||
if (m_bottomShader.empty()) {
|
||||
m_bottomShader += "uniform half3 iRes;"
|
||||
"uniform half4 iColor;";
|
||||
m_bottomShader += kRGB_to_HSV_sksl;
|
||||
m_bottomShader += kHSV_to_RGB_sksl;
|
||||
// TODO should we display the hue bar with the current sat/value?
|
||||
m_bottomShader += R"(
|
||||
half4 main(vec2 fragcoord) {
|
||||
half v = (fragcoord.x / iRes.x);
|
||||
half3 hsv = rgb_to_hsv(iColor.rgb);
|
||||
return hsv_to_rgb(half3(hsv.x, hsv.y, v)).rgb1;
|
||||
}
|
||||
)";
|
||||
}
|
||||
return m_bottomShader.c_str();
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if SK_ENABLE_SKSL
|
||||
void ColorWheel::setShaderMainAreaParams(SkRuntimeShaderBuilder& builder)
|
||||
{
|
||||
builder.uniform("iBack") = gfxColor_to_SkV4(m_bgColor);
|
||||
builder.uniform("iDiscrete") = (m_discrete ? 1: 0);
|
||||
builder.uniform("iMode") = int(m_colorModel);
|
||||
}
|
||||
#endif
|
||||
|
||||
app::Color ColorWheel::getMainAreaColor(const int _u, const int umax,
|
||||
const int _v, const int vmax)
|
||||
{
|
||||
@ -89,7 +200,7 @@ app::Color ColorWheel::getMainAreaColor(const int _u, const int umax,
|
||||
boxsize, boxsize).contains(pos)) {
|
||||
m_harmonyPicked = true;
|
||||
|
||||
color = app::Color::fromHsv(convertHueAngle(int(color.getHsvHue()), 1),
|
||||
color = app::Color::fromHsv(convertHueAngle(color.getHsvHue(), 1),
|
||||
color.getHsvSaturation(),
|
||||
color.getHsvValue(),
|
||||
m_color.getAlpha());
|
||||
@ -212,7 +323,7 @@ void ColorWheel::onPaintMainArea(ui::Graphics* g, const gfx::Rect& rc)
|
||||
double angle = color.getHsvHue()-30.0;
|
||||
double dist = color.getHsvSaturation();
|
||||
|
||||
color = app::Color::fromHsv(convertHueAngle(int(color.getHsvHue()), 1),
|
||||
color = app::Color::fromHsv(convertHueAngle(color.getHsvHue(), 1),
|
||||
color.getHsvSaturation(),
|
||||
color.getHsvValue());
|
||||
|
||||
@ -347,7 +458,7 @@ app::Color ColorWheel::getColorInHarmony(int j) const
|
||||
{
|
||||
int i = base::clamp((int)m_harmony, 0, (int)Harmony::LAST);
|
||||
j = base::clamp(j, 0, harmonies[i].n-1);
|
||||
double hue = convertHueAngle(int(m_color.getHsvHue()), -1) + harmonies[i].hues[j];
|
||||
double hue = convertHueAngle(m_color.getHsvHue(), -1) + harmonies[i].hues[j];
|
||||
double sat = m_color.getHsvSaturation() * harmonies[i].sats[j] / 100.0;
|
||||
return app::Color::fromHsv(std::fmod(hue, 360),
|
||||
base::clamp(sat, 0.0, 1.0),
|
||||
@ -421,51 +532,33 @@ void ColorWheel::onOptions()
|
||||
menu.showPopup(gfx::Point(rc.x+rc.w, rc.y));
|
||||
}
|
||||
|
||||
int ColorWheel::convertHueAngle(int hue, int dir) const
|
||||
float ColorWheel::convertHueAngle(float h, int dir) const
|
||||
{
|
||||
switch (m_colorModel) {
|
||||
|
||||
case ColorModel::RGB:
|
||||
return hue;
|
||||
|
||||
case ColorModel::RYB: {
|
||||
static std::vector<int> map1;
|
||||
static std::vector<int> map2;
|
||||
|
||||
if (map2.empty()) {
|
||||
filters::ColorCurve curve1(filters::ColorCurve::Linear);
|
||||
curve1.addPoint(gfx::Point(0, 0));
|
||||
curve1.addPoint(gfx::Point(60, 35));
|
||||
curve1.addPoint(gfx::Point(122, 60));
|
||||
curve1.addPoint(gfx::Point(165, 120));
|
||||
curve1.addPoint(gfx::Point(218, 180));
|
||||
curve1.addPoint(gfx::Point(275, 240));
|
||||
curve1.addPoint(gfx::Point(330, 300));
|
||||
curve1.addPoint(gfx::Point(360, 360));
|
||||
|
||||
filters::ColorCurve curve2(filters::ColorCurve::Linear);
|
||||
for (const auto& pt : curve1)
|
||||
curve2.addPoint(gfx::Point(pt.y, pt.x));
|
||||
|
||||
map1.resize(360);
|
||||
map2.resize(360);
|
||||
curve1.getValues(0, 359, map1);
|
||||
curve2.getValues(0, 359, map2);
|
||||
}
|
||||
|
||||
if (hue < 0)
|
||||
hue += 360;
|
||||
hue %= 360;
|
||||
|
||||
ASSERT(hue >= 0 && hue < 360);
|
||||
if (dir > 0)
|
||||
return map1[hue];
|
||||
else if (dir < 0)
|
||||
return map2[hue];
|
||||
if (m_colorModel == ColorModel::RYB) {
|
||||
if (dir == 1) {
|
||||
// rybhue_to_rgbhue() maps:
|
||||
// [0,120) -> [0,60)
|
||||
// [120,180) -> [60,120)
|
||||
// [180,240) -> [120,240)
|
||||
// [240,360] -> [240,360]
|
||||
if (h >= 0 && h < 120) return h / 2; // from red to yellow
|
||||
else if (h < 180) return (h-60); // from yellow to green
|
||||
else if (h < 240) return 120 + 2*(h-180); // from green to blue
|
||||
else return h; // from blue to red (same hue)
|
||||
}
|
||||
else {
|
||||
// rgbhue_to_rybhue()
|
||||
// [0,60) -> [0,120)
|
||||
// [60,120) -> [120,180)
|
||||
// [120,240) -> [180,240)
|
||||
// [240,360] -> [240,360]
|
||||
if (h >= 0 && h < 60) return 2 * h; // from red to yellow
|
||||
else if (h < 120) return 60 + h; // from yellow to green
|
||||
else if (h < 240) return 180 + (h-120)/2; // from green to blue
|
||||
else return h; // from blue to red (same hue)
|
||||
}
|
||||
|
||||
}
|
||||
return hue;
|
||||
return h;
|
||||
}
|
||||
|
||||
} // namespace app
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2021 Igara Studio S.A.
|
||||
// Copyright (C) 2021-2022 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -43,9 +43,14 @@ namespace app {
|
||||
void setHarmony(Harmony harmony);
|
||||
|
||||
protected:
|
||||
const char* getMainAreaShader() override;
|
||||
const char* getBottomBarShader() override;
|
||||
app::Color getMainAreaColor(const int u, const int umax,
|
||||
const int v, const int vmax) 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 onPaintBottomBar(ui::Graphics* g, const gfx::Rect& rc) override;
|
||||
void onPaintSurfaceInBgThread(os::Surface* s,
|
||||
@ -65,8 +70,10 @@ namespace app {
|
||||
// Converts an hue angle from HSV <-> current color model hue.
|
||||
// With dir == +1, the angle is from the color model and it's converted to HSV hue.
|
||||
// With dir == -1, the angle came from HSV and is converted to the current color model.
|
||||
int convertHueAngle(int angle, int dir) const;
|
||||
float convertHueAngle(float angle, int dir) const;
|
||||
|
||||
std::string m_mainShader;
|
||||
std::string m_bottomShader;
|
||||
gfx::Rect m_wheelBounds;
|
||||
gfx::Color m_bgColor;
|
||||
double m_wheelRadius;
|
||||
|
Loading…
x
Reference in New Issue
Block a user