1 | // Aseprite |
2 | // Copyright (C) 2020-2022 Igara Studio S.A. |
3 | // Copyright (C) 2001-2018 David Capello |
4 | // |
5 | // This program is distributed under the terms of |
6 | // the End-User License Agreement for Aseprite. |
7 | |
8 | #ifdef HAVE_CONFIG_H |
9 | #include "config.h" |
10 | #endif |
11 | |
12 | #include "app/ui/color_spectrum.h" |
13 | |
14 | #include "app/color_utils.h" |
15 | #include "app/ui/skin/skin_theme.h" |
16 | #include "app/ui/status_bar.h" |
17 | #include "app/util/shader_helpers.h" |
18 | #include "os/surface.h" |
19 | #include "ui/graphics.h" |
20 | #include "ui/message.h" |
21 | #include "ui/paint_event.h" |
22 | #include "ui/resize_event.h" |
23 | #include "ui/size_hint_event.h" |
24 | #include "ui/system.h" |
25 | |
26 | #include <algorithm> |
27 | |
28 | namespace app { |
29 | |
30 | using namespace app::skin; |
31 | using namespace gfx; |
32 | using namespace ui; |
33 | |
34 | ColorSpectrum::ColorSpectrum() |
35 | { |
36 | } |
37 | |
38 | #if SK_ENABLE_SKSL |
39 | |
40 | const char* ColorSpectrum::getMainAreaShader() |
41 | { |
42 | if (m_mainShader.empty()) { |
43 | m_mainShader += "uniform half3 iRes;" |
44 | "uniform half4 iHsl;" ; |
45 | m_mainShader += kHSL_to_RGB_sksl; |
46 | m_mainShader += R"( |
47 | half4 main(vec2 fragcoord) { |
48 | vec2 d = fragcoord.xy / iRes.xy; |
49 | half hue = d.x; |
50 | half sat = iHsl.y; |
51 | half lit = 1.0 - d.y; |
52 | return hsl_to_rgb(half3(hue, sat, lit)).rgb1; |
53 | } |
54 | )" ; |
55 | } |
56 | return m_mainShader.c_str(); |
57 | } |
58 | |
59 | const char* ColorSpectrum::getBottomBarShader() |
60 | { |
61 | if (m_bottomShader.empty()) { |
62 | m_bottomShader += "uniform half3 iRes;" |
63 | "uniform half4 iHsl;" ; |
64 | m_bottomShader += kHSL_to_RGB_sksl; |
65 | m_bottomShader += R"( |
66 | half4 main(vec2 fragcoord) { |
67 | half s = (fragcoord.x / iRes.x); |
68 | return hsl_to_rgb(half3(iHsl.x, s, iHsl.z)).rgb1; |
69 | } |
70 | )" ; |
71 | } |
72 | return m_bottomShader.c_str(); |
73 | } |
74 | |
75 | void ColorSpectrum::setShaderParams(SkRuntimeShaderBuilder& builder, bool main) |
76 | { |
77 | builder.uniform("iHsl" ) = appColorHsl_to_SkV4(m_color); |
78 | } |
79 | |
80 | #endif // SK_ENABLE_SKSL |
81 | |
82 | app::Color ColorSpectrum::getMainAreaColor(const int u, const int umax, |
83 | const int v, const int vmax) |
84 | { |
85 | double hue = 360.0 * u / umax; |
86 | double lit = 1.0 - (double(v)/double(vmax)); |
87 | return app::Color::fromHsl( |
88 | std::clamp(hue, 0.0, 360.0), |
89 | m_color.getHslSaturation(), |
90 | std::clamp(lit, 0.0, 1.0), |
91 | getCurrentAlphaForNewColor()); |
92 | } |
93 | |
94 | app::Color ColorSpectrum::getBottomBarColor(const int u, const int umax) |
95 | { |
96 | double sat = double(u) / double(umax); |
97 | return app::Color::fromHsl( |
98 | m_color.getHslHue(), |
99 | std::clamp(sat, 0.0, 1.0), |
100 | m_color.getHslLightness(), |
101 | getCurrentAlphaForNewColor()); |
102 | } |
103 | |
104 | void ColorSpectrum::onPaintMainArea(ui::Graphics* g, const gfx::Rect& rc) |
105 | { |
106 | if (m_color.getType() != app::Color::MaskType) { |
107 | double hue = m_color.getHslHue(); |
108 | double lit = m_color.getHslLightness(); |
109 | gfx::Point pos(rc.x + int(hue * rc.w / 360.0), |
110 | rc.y + rc.h - int(lit * rc.h)); |
111 | |
112 | paintColorIndicator(g, pos, lit < 0.5); |
113 | } |
114 | } |
115 | |
116 | void ColorSpectrum::onPaintBottomBar(ui::Graphics* g, const gfx::Rect& rc) |
117 | { |
118 | double lit = m_color.getHslLightness(); |
119 | |
120 | if (m_color.getType() != app::Color::MaskType) { |
121 | double sat = m_color.getHslSaturation(); |
122 | gfx::Point pos(rc.x + int(double(rc.w) * sat), |
123 | rc.y + rc.h/2); |
124 | paintColorIndicator(g, pos, lit < 0.5); |
125 | } |
126 | } |
127 | |
128 | void ColorSpectrum::onPaintSurfaceInBgThread( |
129 | os::Surface* s, |
130 | const gfx::Rect& main, |
131 | const gfx::Rect& bottom, |
132 | const gfx::Rect& alpha, |
133 | bool& stop) |
134 | { |
135 | if (m_paintFlags & MainAreaFlag) { |
136 | double sat = m_color.getHslSaturation(); |
137 | int umax = std::max(1, main.w-1); |
138 | int vmax = std::max(1, main.h-1); |
139 | |
140 | for (int y=0; y<main.h && !stop; ++y) { |
141 | for (int x=0; x<main.w && !stop; ++x) { |
142 | double hue = 360.0 * double(x) / double(umax); |
143 | double lit = 1.0 - double(y) / double(vmax); |
144 | |
145 | gfx::Color color = color_utils::color_for_ui( |
146 | app::Color::fromHsl( |
147 | std::clamp(hue, 0.0, 360.0), |
148 | sat, |
149 | std::clamp(lit, 0.0, 1.0))); |
150 | |
151 | s->putPixel(color, main.x+x, main.y+y); |
152 | } |
153 | } |
154 | if (stop) |
155 | return; |
156 | m_paintFlags ^= MainAreaFlag; |
157 | } |
158 | |
159 | if (m_paintFlags & BottomBarFlag) { |
160 | double lit = m_color.getHslLightness(); |
161 | double hue = m_color.getHslHue(); |
162 | os::Paint paint; |
163 | for (int x=0; x<bottom.w && !stop; ++x) { |
164 | paint.color( |
165 | color_utils::color_for_ui( |
166 | app::Color::fromHsl(hue, double(x) / double(bottom.w), lit))); |
167 | s->drawRect(gfx::Rect(bottom.x+x, bottom.y, 1, bottom.h), paint); |
168 | } |
169 | if (stop) |
170 | return; |
171 | m_paintFlags ^= BottomBarFlag; |
172 | } |
173 | |
174 | // Paint alpha bar |
175 | ColorSelector::onPaintSurfaceInBgThread(s, main, bottom, alpha, stop); |
176 | } |
177 | |
178 | int ColorSpectrum::onNeedsSurfaceRepaint(const app::Color& newColor) |
179 | { |
180 | return |
181 | // Only if the saturation changes we have to redraw the main surface. |
182 | (cs_double_diff(m_color.getHslSaturation(), newColor.getHslSaturation()) ? MainAreaFlag: 0) | |
183 | (cs_double_diff(m_color.getHslHue(), newColor.getHslHue()) || |
184 | cs_double_diff(m_color.getHslLightness(), newColor.getHslLightness()) ? BottomBarFlag: 0) | |
185 | ColorSelector::onNeedsSurfaceRepaint(newColor); |
186 | } |
187 | |
188 | } // namespace app |
189 | |