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
22namespace app {
23
24using namespace app::skin;
25using namespace gfx;
26using namespace ui;
27
28ColorTintShadeTone::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
46const 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"(
53half4 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
65const 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"(
74half4 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"(
81half4 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
90void ColorTintShadeTone::setShaderParams(SkRuntimeShaderBuilder& builder, bool main)
91{
92 builder.uniform("iHsv") = appColorHsv_to_SkV4(m_color);
93}
94
95#endif // SK_ENABLE_SKSL
96
97app::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
109app::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
119void 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
131void ColorTintShadeTone::onPaint(ui::PaintEvent& ev)
132{
133 m_hueWithSatValue = Preferences::instance().experimental.hueWithSatValueForColorSelector();
134 ColorSelector::onPaint(ev);
135}
136
137void 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
153void 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
214int 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