Inkscape
Vector Graphics Editor
color-slider.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later/*
5 * Authors:
6 * see git history
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * bulia byak <buliabyak@users.sf.net>
9 *
10 * Copyright (C) 2018 Authors
11 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
12 */
13
14#include <utility>
15#include <sigc++/functors/mem_fun.h>
16#include <gdkmm/general.h>
17#include <gtkmm/adjustment.h>
18#include <gtkmm/drawingarea.h>
19#include <gtkmm/gestureclick.h>
20
22#include "preferences.h"
23#include "ui/controller.h"
25
26static const gint ARROW_SIZE = 8;
27
28static const guchar *sp_color_slider_render_gradient(gint x0, gint y0, gint width, gint height, gint c[], gint dc[],
29 guint b0, guint b1, guint mask);
30static const guchar *sp_color_slider_render_map(gint x0, gint y0, gint width, gint height, guchar *map, gint start,
31 gint step, guint b0, guint b1, guint mask);
32
33namespace Inkscape::UI::Widget {
34
35ColorSlider::ColorSlider(Glib::RefPtr<Gtk::Adjustment> adjustment)
36 : _dragging(false)
37 , _value(0.0)
38 , _oldvalue(0.0)
39 , _map(nullptr)
40{
41 set_name("ColorSlider");
42
43 set_draw_func(sigc::mem_fun(*this, &ColorSlider::draw_func));
44
45 _c0[0] = 0x00;
46 _c0[1] = 0x00;
47 _c0[2] = 0x00;
48 _c0[3] = 0xff;
49
50 _cm[0] = 0xff;
51 _cm[1] = 0x00;
52 _cm[2] = 0x00;
53 _cm[3] = 0xff;
54
55 _c0[0] = 0xff;
56 _c0[1] = 0xff;
57 _c0[2] = 0xff;
58 _c0[3] = 0xff;
59
60 _b0 = 0x5f;
61 _b1 = 0xa0;
62 _bmask = 0x08;
63
64 setAdjustment(std::move(adjustment));
65
67 sigc::mem_fun(*this, &ColorSlider::on_click_pressed ),
68 sigc::mem_fun(*this, &ColorSlider::on_click_released),
70 Controller::add_motion<nullptr, &ColorSlider::on_motion, nullptr>
71 (*this, *this);
72}
73
75{
76 if (_adjustment) {
79 _adjustment.reset();
80 }
81}
82
83static bool get_constrained(Gdk::ModifierType const state)
84{
85 return Controller::has_flag(state, Gdk::ModifierType::CONTROL_MASK);
86}
87
88static double get_value_at(Gtk::Widget const &self, double const x, double const y)
89{
90
91 constexpr auto cx = 0; // formerly held CSS padding, now Box handles that
92 auto const cw = self.get_width() - 2 * cx;
93 return CLAMP((x - cx) / cw, 0.0, 1.0);
94}
95
96Gtk::EventSequenceState ColorSlider::on_click_pressed(Gtk::GestureClick const &click,
97 int /*n_press*/, double const x, double const y)
98{
99 signal_grabbed.emit();
100 _dragging = true;
102 auto const value = get_value_at(*this, x, y);
103 auto const state = click.get_current_event_state();
104 auto const constrained = get_constrained(state);
105 ColorScales<>::setScaled(_adjustment, value, constrained);
106 signal_dragged.emit();
108}
109
110Gtk::EventSequenceState ColorSlider::on_click_released(Gtk::GestureClick const & /*click*/,
111 int /*n_press*/, double /*x*/, double /*y*/)
112{
113 _dragging = false;
114 signal_released.emit();
115 if (_value != _oldvalue) {
117 }
119}
120
121void ColorSlider::on_motion(GtkEventControllerMotion const * const motion,
122 double const x, double const y)
123{
124 if (_dragging) {
125 auto const value = get_value_at(*this, x, y);
126 auto const state = gtk_event_controller_get_current_event_state(GTK_EVENT_CONTROLLER(motion));
127 auto const constrained = get_constrained((Gdk::ModifierType)state);
128 ColorScales<>::setScaled(_adjustment, value, constrained);
129 signal_dragged.emit();
130 }
131}
132
133void ColorSlider::setAdjustment(Glib::RefPtr<Gtk::Adjustment> adjustment)
134{
135 if (!adjustment) {
136 _adjustment = Gtk::Adjustment::create(0.0, 0.0, 1.0, 0.01, 0.0, 0.0);
137 }
138 else {
139 adjustment->set_page_increment(0.0);
140 adjustment->set_page_size(0.0);
141 }
142
143 if (_adjustment != adjustment) {
144 if (_adjustment) {
147 }
148
149 _adjustment = std::move(adjustment);
151 _adjustment->signal_changed().connect(sigc::mem_fun(*this, &ColorSlider::_onAdjustmentChanged));
153 _adjustment->signal_value_changed().connect(sigc::mem_fun(*this, &ColorSlider::_onAdjustmentValueChanged));
154
156
158 }
159}
160
161void ColorSlider::_onAdjustmentChanged() { queue_draw(); }
162
164{
166 constexpr int cx = 0, cy = 0; // formerly held CSS padding, now Box handles that
167 auto const cw = get_width();
168 if ((gint)(ColorScales<>::getScaled(_adjustment) * cw) != (gint)(_value * cw)) {
169 gint ax, ay;
170 gfloat value;
171 value = _value;
173 ax = (int)(cx + value * cw - ARROW_SIZE / 2 - 2);
174 ay = cy;
175 queue_draw();
176 }
177 else {
179 }
180 }
181}
182
184{
185 // Remove any map, if set
186 _map = nullptr;
187
188 _c0[0] = start >> 24;
189 _c0[1] = (start >> 16) & 0xff;
190 _c0[2] = (start >> 8) & 0xff;
191 _c0[3] = start & 0xff;
192
193 _cm[0] = mid >> 24;
194 _cm[1] = (mid >> 16) & 0xff;
195 _cm[2] = (mid >> 8) & 0xff;
196 _cm[3] = mid & 0xff;
197
198 _c1[0] = end >> 24;
199 _c1[1] = (end >> 16) & 0xff;
200 _c1[2] = (end >> 8) & 0xff;
201 _c1[3] = end & 0xff;
202
203 queue_draw();
204}
205
206void ColorSlider::setMap(const guchar *map)
207{
208 _map = const_cast<guchar *>(map);
209
210 queue_draw();
211}
212
213void ColorSlider::setBackground(guint dark, guint light, guint size)
214{
215 _b0 = dark;
216 _b1 = light;
217 _bmask = size;
218
219 queue_draw();
220}
221
222void ColorSlider::draw_func(Cairo::RefPtr<Cairo::Context> const &cr,
223 int const width, int const height)
224{
225 // padding/carea are no longer used/useful, just kept to minimise code diff
226 static Gtk::Border const padding{};
227 auto const scale = get_scale_factor();
228 Gdk::Rectangle const carea{0, 0, width * scale, height * scale};
229
230 // changing scale to draw pixmap at display resolution
231 cr->save();
232 cr->scale(1.0 / scale, 1.0 / scale);
233
234 if (_map) {
235 /* Render map pixelstore */
236 gint d = (1024 << 16) / carea.get_width();
237 gint s = 0;
238
239 const guchar *b =
240 sp_color_slider_render_map(0, 0, carea.get_width(), carea.get_height(), _map, s, d, _b0, _b1, _bmask * scale);
241
242 if (b != nullptr && carea.get_width() > 0) {
243 Glib::RefPtr<Gdk::Pixbuf> pb = Gdk::Pixbuf::create_from_data(
244 b, Gdk::Colorspace::RGB, false, 8, carea.get_width(), carea.get_height(), carea.get_width() * 3);
245
246 Gdk::Cairo::set_source_pixbuf(cr, pb, carea.get_x(), carea.get_y());
247 cr->paint();
248 }
249 }
250 else {
251 gint c[4], dc[4];
252
253 /* Render gradient */
254
255 // part 1: from c0 to cm
256 if (carea.get_width() > 0) {
257 for (gint i = 0; i < 4; i++) {
258 c[i] = _c0[i] << 16;
259 dc[i] = ((_cm[i] << 16) - c[i]) / (carea.get_width() / 2);
260 }
261 guint wi = carea.get_width() / 2;
262 const guchar *b = sp_color_slider_render_gradient(0, 0, wi, carea.get_height(), c, dc, _b0, _b1, _bmask * scale);
263
264 /* Draw pixelstore 1 */
265 if (b != nullptr && wi > 0) {
266 Glib::RefPtr<Gdk::Pixbuf> pb =
267 Gdk::Pixbuf::create_from_data(b, Gdk::Colorspace::RGB, false, 8, wi, carea.get_height(), wi * 3);
268
269 Gdk::Cairo::set_source_pixbuf(cr, pb, carea.get_x(), carea.get_y());
270 cr->paint();
271 }
272 }
273
274 // part 2: from cm to c1
275 if (carea.get_width() > 0) {
276 for (gint i = 0; i < 4; i++) {
277 c[i] = _cm[i] << 16;
278 dc[i] = ((_c1[i] << 16) - c[i]) / (carea.get_width() / 2);
279 }
280 guint wi = carea.get_width() / 2;
281 const guchar *b = sp_color_slider_render_gradient(carea.get_width() / 2, 0, wi, carea.get_height(), c, dc,
282 _b0, _b1, _bmask * scale);
283
284 /* Draw pixelstore 2 */
285 if (b != nullptr && wi > 0) {
286 Glib::RefPtr<Gdk::Pixbuf> pb =
287 Gdk::Pixbuf::create_from_data(b, Gdk::Colorspace::RGB, false, 8, wi, carea.get_height(), wi * 3);
288
289 Gdk::Cairo::set_source_pixbuf(cr, pb, carea.get_width() / 2 + carea.get_x(), carea.get_y());
290 cr->paint();
291 }
292 }
293 }
294
295 cr->restore();
296
297 /* Draw arrow */
298 gint x = (int)(_value * (carea.get_width() / scale) - ARROW_SIZE / 2 + carea.get_x() / scale);
299 gint y1 = carea.get_y() / scale;
300 gint y2 = carea.get_y() / scale + carea.get_height() / scale - 1;
301 cr->set_line_width(2.0);
302
303 // Define top arrow
304 cr->move_to(x - 0.5, y1 + 0.5);
305 cr->line_to(x + ARROW_SIZE - 0.5, y1 + 0.5);
306 cr->line_to(x + (ARROW_SIZE - 1) / 2.0, y1 + ARROW_SIZE / 2.0 + 0.5);
307 cr->close_path();
308
309 // Define bottom arrow
310 cr->move_to(x - 0.5, y2 + 0.5);
311 cr->line_to(x + ARROW_SIZE - 0.5, y2 + 0.5);
312 cr->line_to(x + (ARROW_SIZE - 1) / 2.0, y2 - ARROW_SIZE / 2.0 + 0.5);
313 cr->close_path();
314
315 // Render both arrows
316 cr->set_source_rgb(0.0, 0.0, 0.0);
317 cr->stroke_preserve();
318 cr->set_source_rgb(1.0, 1.0, 1.0);
319 cr->fill();
320}
321
322} // namespace Inkscape::UI::Widget
323
324/* Colors are << 16 */
325
326inline bool checkerboard(gint x, gint y, guint size) {
327 return ((x / size) & 1) != ((y / size) & 1);
328}
329
330static const guchar *sp_color_slider_render_gradient(gint x0, gint y0, gint width, gint height, gint c[], gint dc[],
331 guint b0, guint b1, guint mask)
332{
333 static guchar *buf = nullptr;
334 static gint bs = 0;
335 guchar *dp;
336 gint x, y;
337 guint r, g, b, a;
338
339 if (buf && (bs < width * height)) {
340 g_free(buf);
341 buf = nullptr;
342 }
343 if (!buf) {
344 buf = g_new(guchar, width * height * 3);
345 bs = width * height;
346 }
347
348 dp = buf;
349 r = c[0];
350 g = c[1];
351 b = c[2];
352 a = c[3];
353 for (x = x0; x < x0 + width; x++) {
354 gint cr, cg, cb, ca;
355 guchar *d;
356 cr = r >> 16;
357 cg = g >> 16;
358 cb = b >> 16;
359 ca = a >> 16;
360 d = dp;
361 for (y = y0; y < y0 + height; y++) {
362 guint bg, fc;
363 /* Background value */
364 bg = checkerboard(x, y, mask) ? b0 : b1;
365 fc = (cr - bg) * ca;
366 d[0] = bg + ((fc + (fc >> 8) + 0x80) >> 8);
367 fc = (cg - bg) * ca;
368 d[1] = bg + ((fc + (fc >> 8) + 0x80) >> 8);
369 fc = (cb - bg) * ca;
370 d[2] = bg + ((fc + (fc >> 8) + 0x80) >> 8);
371 d += 3 * width;
372 }
373 r += dc[0];
374 g += dc[1];
375 b += dc[2];
376 a += dc[3];
377 dp += 3;
378 }
379
380 return buf;
381}
382
383/* Positions are << 16 */
384
385static const guchar *sp_color_slider_render_map(gint x0, gint y0, gint width, gint height, guchar *map, gint start,
386 gint step, guint b0, guint b1, guint mask)
387{
388 static guchar *buf = nullptr;
389 static gint bs = 0;
390 guchar *dp;
391 gint x, y;
392
393 if (buf && (bs < width * height)) {
394 g_free(buf);
395 buf = nullptr;
396 }
397 if (!buf) {
398 buf = g_new(guchar, width * height * 3);
399 bs = width * height;
400 }
401
402 dp = buf;
403 for (x = x0; x < x0 + width; x++) {
404 gint cr, cg, cb, ca;
405 guchar *d = dp;
406 guchar *sp = map + 4 * (start >> 16);
407 cr = *sp++;
408 cg = *sp++;
409 cb = *sp++;
410 ca = *sp++;
411 for (y = y0; y < y0 + height; y++) {
412 guint bg, fc;
413 /* Background value */
414 bg = checkerboard(x, y, mask) ? b0 : b1;
415 fc = (cr - bg) * ca;
416 d[0] = bg + ((fc + (fc >> 8) + 0x80) >> 8);
417 fc = (cg - bg) * ca;
418 d[1] = bg + ((fc + (fc >> 8) + 0x80) >> 8);
419 fc = (cb - bg) * ca;
420 d[2] = bg + ((fc + (fc >> 8) + 0x80) >> 8);
421 d += 3 * width;
422 }
423 dp += 3;
424 start += step;
425 }
426
427 return buf;
428}
429
430/*
431 Local Variables:
432 mode:c++
433 c-file-style:"stroustrup"
434 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
435 indent-tabs-mode:nil
436 fill-column:99
437 End:
438*/
439// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
double scale
Definition: aa.cpp:228
static double getScaled(Glib::RefPtr< Gtk::Adjustment > const &a)
static void setScaled(Glib::RefPtr< Gtk::Adjustment > &a, double v, bool constrained=false)
sigc::signal< void()> signal_released
Definition: color-slider.h:45
void setBackground(guint dark, guint light, guint size)
void on_motion(GtkEventControllerMotion const *motion, double x, double y)
Gtk::EventSequenceState on_click_pressed(Gtk::GestureClick const &click, int n_press, double x, double y)
sigc::connection _adjustment_changed_connection
Definition: color-slider.h:63
ColorSlider(Glib::RefPtr< Gtk::Adjustment > adjustment)
void setColors(guint32 start, guint32 mid, guint32 end)
void draw_func(Cairo::RefPtr< Cairo::Context > const &cr, int width, int height)
Gtk::EventSequenceState on_click_released(Gtk::GestureClick const &click, int n_press, double x, double y)
sigc::connection _adjustment_value_changed_connection
Definition: color-slider.h:64
sigc::signal< void()> signal_value_changed
Definition: color-slider.h:46
Glib::RefPtr< Gtk::Adjustment > _adjustment
Definition: color-slider.h:62
sigc::signal< void()> signal_dragged
Definition: color-slider.h:44
void setMap(const guchar *map)
void setAdjustment(Glib::RefPtr< Gtk::Adjustment > adjustment)
sigc::signal< void()> signal_grabbed
Definition: color-slider.h:43
Color selector using sliders for each components, for multiple color modes.
static const guchar * sp_color_slider_render_map(gint x0, gint y0, gint width, gint height, guchar *map, gint start, gint step, guint b0, guint b1, guint mask)
static const gint ARROW_SIZE
static const guchar * sp_color_slider_render_gradient(gint x0, gint y0, gint width, gint height, gint c[], gint dc[], guint b0, guint b1, guint mask)
bool checkerboard(gint x, gint y, guint size)
A slider with colored background - implementation.
unsigned int guint32
Definition: color.h:22
Utilities to more easily use Gtk::EventController & subclasses like Gesture.
double c[8][4]
Definition: cylinder3d.cpp:40
Geom::Point start
Geom::Point end
bool has_flag(Gdk::ModifierType const state, Gdk::ModifierType const flags)
Helper to query if ModifierType state contains one or more of given flag(s).
Definition: controller.h:47
Gtk::GestureClick & add_click(Gtk::Widget &widget, ClickSlot on_pressed, ClickSlot on_released, Button const button, Gtk::PropagationPhase const phase, When const when)
Create a click gesture for the given widget; by default claim sequence.
Definition: controller.cpp:105
static double get_width(SprayTool *tc)
Definition: spray-tool.cpp:307
Custom widgets.
Definition: desktop.h:127
static double get_value_at(Gtk::Widget const &self, double const x, double const y)
static constexpr int height
static bool get_constrained(Gdk::ModifierType const state)
@ NONE
Definition: axis-manip.h:35
int size
int buf
Singleton class to access the preferences file in a convenient way.
unsigned long bs
Definition: quantize.cpp:38
double height
double width
std::unique_ptr< Toolbar >(* create)(SPDesktop *desktop)
Definition: toolbars.cpp:63