1 | // Aseprite |
2 | // Copyright (C) 2020-2022 Igara Studio S.A. |
3 | // Copyright (C) 2016-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_tint_shade_tone.h" |
13 | |
14 | #include "app/color_utils.h" |
15 | #include "app/pref/preferences.h" |
16 | #include "app/ui/skin/skin_theme.h" |
17 | #include "app/util/shader_helpers.h" |
18 | #include "ui/graphics.h" |
19 | |
20 | #include <algorithm> |
21 | |
22 | namespace app { |
23 | |
24 | using namespace app::skin; |
25 | using namespace gfx; |
26 | using namespace ui; |
27 | |
28 | ColorTintShadeTone::ColorTintShadeTone() |
29 | { |
30 | m_conn = Preferences::instance() |
31 | .experimental.hueWithSatValueForColorSelector.AfterChange.connect( |
32 | [this](){ |
33 | m_paintFlags |= AllAreasFlag; |
34 | |
35 | #if SK_ENABLE_SKSL |
36 | m_bottomShader.clear(); |
37 | resetBottomEffect(); |
38 | #endif |
39 | |
40 | invalidate(); |
41 | }); |
42 | } |
43 | |
44 | #if SK_ENABLE_SKSL |
45 | |
46 | const char* ColorTintShadeTone::getMainAreaShader() |
47 | { |
48 | if (m_mainShader.empty()) { |
49 | m_mainShader += "uniform half3 iRes;" |
50 | "uniform half4 iHsv;" ; |
51 | m_mainShader += kHSV_to_RGB_sksl; |
52 | m_mainShader += R"( |
53 | half4 main(vec2 fragcoord) { |
54 | vec2 d = fragcoord.xy / iRes.xy; |
55 | half hue = iHsv.x; |
56 | half sat = d.x; |
57 | half val = 1.0 - d.y; |
58 | return hsv_to_rgb(vec3(hue, sat, val)).rgb1; |
59 | } |
60 | )" ; |
61 | } |
62 | return m_mainShader.c_str(); |
63 | } |
64 | |
65 | const char* ColorTintShadeTone::getBottomBarShader() |
66 | { |
67 | if (m_bottomShader.empty()) { |
68 | m_bottomShader += "uniform half3 iRes;" |
69 | "uniform half4 iHsv;" ; |
70 | m_bottomShader += kHSV_to_RGB_sksl; |
71 | |
72 | if (m_hueWithSatValue) |
73 | m_bottomShader += R"( |
74 | half4 main(vec2 fragcoord) { |
75 | half h = (fragcoord.x / iRes.x); |
76 | return hsv_to_rgb(half3(h, iHsv.y, iHsv.z)).rgb1; |
77 | } |
78 | )" ; |
79 | else |
80 | m_bottomShader += R"( |
81 | half4 main(vec2 fragcoord) { |
82 | half h = (fragcoord.x / iRes.x); |
83 | return hsv_to_rgb(half3(h, 1.0, 1.0)).rgb1; |
84 | } |
85 | )" ; |
86 | } |
87 | return m_bottomShader.c_str(); |
88 | } |
89 | |
90 | void ColorTintShadeTone::setShaderParams(SkRuntimeShaderBuilder& builder, bool main) |
91 | { |
92 | builder.uniform("iHsv" ) = appColorHsv_to_SkV4(m_color); |
93 | } |
94 | |
95 | #endif // SK_ENABLE_SKSL |
96 | |
97 | app::Color ColorTintShadeTone::getMainAreaColor(const int u, const int umax, |
98 | const int v, const int vmax) |
99 | { |
100 | double sat = (1.0 * u / umax); |
101 | double val = (1.0 - double(v) / double(vmax)); |
102 | return app::Color::fromHsv( |
103 | m_color.getHsvHue(), |
104 | std::clamp(sat, 0.0, 1.0), |
105 | std::clamp(val, 0.0, 1.0), |
106 | getCurrentAlphaForNewColor()); |
107 | } |
108 | |
109 | app::Color ColorTintShadeTone::getBottomBarColor(const int u, const int umax) |
110 | { |
111 | double hue = (360.0 * u / umax); |
112 | return app::Color::fromHsv( |
113 | std::clamp(hue, 0.0, 360.0), |
114 | m_color.getHsvSaturation(), |
115 | m_color.getHsvValue(), |
116 | getCurrentAlphaForNewColor()); |
117 | } |
118 | |
119 | void ColorTintShadeTone::onPaintMainArea(ui::Graphics* g, const gfx::Rect& rc) |
120 | { |
121 | if (m_color.getType() != app::Color::MaskType) { |
122 | double sat = m_color.getHsvSaturation(); |
123 | double val = m_color.getHsvValue(); |
124 | gfx::Point pos(rc.x + int(sat * rc.w), |
125 | rc.y + int((1.0-val) * rc.h)); |
126 | |
127 | paintColorIndicator(g, pos, val < 0.5); |
128 | } |
129 | } |
130 | |
131 | void ColorTintShadeTone::onPaint(ui::PaintEvent& ev) |
132 | { |
133 | m_hueWithSatValue = Preferences::instance().experimental.hueWithSatValueForColorSelector(); |
134 | ColorSelector::onPaint(ev); |
135 | } |
136 | |
137 | void ColorTintShadeTone::onPaintBottomBar(ui::Graphics* g, const gfx::Rect& rc) |
138 | { |
139 | if (m_color.getType() != app::Color::MaskType) { |
140 | double hue = m_color.getHsvHue(); |
141 | double val; |
142 | if (m_hueWithSatValue) |
143 | val = m_color.getHsvValue(); |
144 | else |
145 | val = 1.0; |
146 | |
147 | gfx::Point pos(rc.x + int(rc.w * hue / 360.0), |
148 | rc.y + rc.h/2); |
149 | paintColorIndicator(g, pos, val < 0.5); |
150 | } |
151 | } |
152 | |
153 | void ColorTintShadeTone::onPaintSurfaceInBgThread( |
154 | os::Surface* s, |
155 | const gfx::Rect& main, |
156 | const gfx::Rect& bottom, |
157 | const gfx::Rect& alpha, |
158 | bool& stop) |
159 | { |
160 | double hue = m_color.getHsvHue(); |
161 | int umax = std::max(1, main.w-1); |
162 | int vmax = std::max(1, main.h-1); |
163 | |
164 | if (m_paintFlags & MainAreaFlag) { |
165 | for (int y=0; y<main.h && !stop; ++y) { |
166 | for (int x=0; x<main.w && !stop; ++x) { |
167 | double sat = double(x) / double(umax); |
168 | double val = 1.0 - double(y) / double(vmax); |
169 | |
170 | gfx::Color color = color_utils::color_for_ui( |
171 | app::Color::fromHsv( |
172 | hue, |
173 | std::clamp(sat, 0.0, 1.0), |
174 | std::clamp(val, 0.0, 1.0))); |
175 | |
176 | s->putPixel(color, main.x+x, main.y+y); |
177 | } |
178 | } |
179 | if (stop) |
180 | return; |
181 | m_paintFlags ^= MainAreaFlag; |
182 | } |
183 | |
184 | if (m_paintFlags & BottomBarFlag) { |
185 | os::Paint paint; |
186 | double sat, val; |
187 | |
188 | if (m_hueWithSatValue) { |
189 | sat = m_color.getHsvSaturation(); |
190 | val = m_color.getHsvValue(); |
191 | } |
192 | else { |
193 | sat = 1.0; |
194 | val = 1.0; |
195 | } |
196 | |
197 | for (int x=0; x<bottom.w && !stop; ++x) { |
198 | paint.color( |
199 | color_utils::color_for_ui( |
200 | app::Color::fromHsv( |
201 | (360.0 * x / bottom.w), sat, val))); |
202 | |
203 | s->drawRect(gfx::Rect(bottom.x+x, bottom.y, 1, bottom.h), paint); |
204 | } |
205 | if (stop) |
206 | return; |
207 | m_paintFlags ^= BottomBarFlag; |
208 | } |
209 | |
210 | // Paint alpha bar |
211 | ColorSelector::onPaintSurfaceInBgThread(s, main, bottom, alpha, stop); |
212 | } |
213 | |
214 | int ColorTintShadeTone::onNeedsSurfaceRepaint(const app::Color& newColor) |
215 | { |
216 | int flags = |
217 | // Only if the hue changes we have to redraw the main surface. |
218 | (cs_double_diff(m_color.getHsvHue(), newColor.getHsvHue()) ? MainAreaFlag: 0) | |
219 | ColorSelector::onNeedsSurfaceRepaint(newColor); |
220 | |
221 | if (m_hueWithSatValue) { |
222 | flags |= |
223 | (cs_double_diff(m_color.getHsvSaturation(), newColor.getHsvSaturation()) || |
224 | cs_double_diff(m_color.getHsvValue(), newColor.getHsvValue()) ? BottomBarFlag: 0); |
225 | } |
226 | return flags; |
227 | } |
228 | |
229 | } // namespace app |
230 | |