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
28namespace app {
29
30using namespace app::skin;
31using namespace gfx;
32using namespace ui;
33
34ColorSpectrum::ColorSpectrum()
35{
36}
37
38#if SK_ENABLE_SKSL
39
40const 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"(
47half4 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
59const 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"(
66half4 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
75void ColorSpectrum::setShaderParams(SkRuntimeShaderBuilder& builder, bool main)
76{
77 builder.uniform("iHsl") = appColorHsl_to_SkV4(m_color);
78}
79
80#endif // SK_ENABLE_SKSL
81
82app::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
94app::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
104void 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
116void 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
128void 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
178int 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