Blender V5.0
interface_region_color_picker.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2008 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10
11#include <cstdarg>
12#include <cstdlib>
13#include <cstring>
14
15#include "MEM_guardedalloc.h"
16
17#include "DNA_userdef_types.h"
18
19#include "BLI_listbase.h"
20#include "BLI_string_utf8.h"
21#include "BLI_utildefines.h"
22
23#include "BKE_context.hh"
24
25#include "UI_interface_c.hh"
26#include "WM_api.hh"
27#include "WM_types.hh"
28
29#include "RNA_access.hh"
30
31#include "BLT_translation.hh"
32
34
35#include "interface_intern.hh"
36
41
46
49
50/* -------------------------------------------------------------------- */
53
54static void ui_color_picker_rgb_round(float rgb[3])
55{
56 /* Handle small rounding errors in color space conversions. Doing these for
57 * all color space conversions would be expensive, but for the color picker
58 * we can do the extra work. */
59 for (int i = 0; i < 3; i++) {
60 if (fabsf(rgb[i]) < 5e-5f) {
61 rgb[i] = 0.0f;
62 }
63 else if (fabsf(1.0f - rgb[i]) < 5e-5f) {
64 rgb[i] = 1.0f;
65 }
66 }
67}
68
69void ui_color_picker_rgb_to_hsv_compat(const float rgb[3], float r_cp[3])
70{
71 /* Convert RGB to HSV, remaining as compatible as possible with the existing
72 * r_hsv value (for example when value goes to zero, preserve the hue). */
73 switch (U.color_picker_type) {
75 rgb_to_hsl_compat_v(rgb, r_cp);
76 break;
77 default:
78 rgb_to_hsv_compat_v(rgb, r_cp);
79 break;
80 }
81}
82
83void ui_color_picker_rgb_to_hsv(const float rgb[3], float r_cp[3])
84{
85 switch (U.color_picker_type) {
87 rgb_to_hsl_v(rgb, r_cp);
88 break;
89 default:
90 rgb_to_hsv_v(rgb, r_cp);
91 break;
92 }
93}
94
95void ui_color_picker_hsv_to_rgb(const float r_cp[3], float rgb[3])
96{
97 switch (U.color_picker_type) {
99 hsl_to_rgb_v(r_cp, rgb);
100 break;
101 default:
102 hsv_to_rgb_v(r_cp, rgb);
103 break;
104 }
105}
106
108{
109 if (but->rnaprop) {
111 return true;
112 }
113 }
114
115 return but->block->is_color_gamma_picker;
116}
117
119{
120 if (but->rnaprop) {
121 const PropertySubType prop_subtype = RNA_property_subtype(but->rnaprop);
122 if (ELEM(prop_subtype, PROP_COLOR, PROP_COLOR_GAMMA)) {
123 const int color_components_count = RNA_property_array_length(&but->rnapoin, but->rnaprop);
124 if (color_components_count == 4) {
125 return true;
126 }
127 }
128 }
129
130 return false;
131}
132
133static void ui_scene_linear_to_perceptual_space(const bool is_gamma, float rgb[3])
134{
135 /* Map to color picking space for HSV values and HSV cube/circle,
136 * assuming it is more perceptually linear than the scene linear
137 * space for intuitive color picking. */
138 if (!is_gamma) {
141 }
142}
143
144static void ui_perceptual_to_scene_linear_space(const bool is_gamma, float rgb[3])
145{
146 if (!is_gamma) {
149 }
150}
151
156
161
163
164/* -------------------------------------------------------------------- */
167
169 const bool is_gamma,
170 const bool is_editing_sliders,
171 const float rgb_scene_linear[3])
172{
173 /* Note that we skip updating values if we are editing the same number sliders.
174 * This avoids numerical drift from precision errors converting between color
175 * space and between RGB and HSV. */
176
177 /* Convert from RGB linear to RGB perceptual for number editing. */
178 if (cpicker->is_init == false ||
179 !(is_editing_sliders && g_color_picker_type == PICKER_TYPE_RGB &&
181 {
182 copy_v3_v3(cpicker->rgb_perceptual_slider, rgb_scene_linear);
184 }
185
186 /* Convert from RGB perceptual to HSV perceptual. */
187 if (cpicker->is_init == false) {
189 }
190 else if (!(is_editing_sliders && g_color_picker_type == PICKER_TYPE_HSV &&
192 {
194 cpicker->hsv_perceptual_slider);
195 }
196
197 /* Convert from RGB linear to HSV linear. */
198 if (cpicker->is_init == false) {
199 ui_color_picker_rgb_to_hsv(rgb_scene_linear, cpicker->hsv_linear_slider);
200 }
201 else if (!(is_editing_sliders && g_color_picker_type == PICKER_TYPE_HSV &&
203 {
204 ui_color_picker_rgb_to_hsv_compat(rgb_scene_linear, cpicker->hsv_linear_slider);
205 }
206
210
211 /* Convert from RGB to HSV in perceptually linear space for picker widgets. */
212 float rgb_perceptual_slider[3];
213 copy_v3_v3(rgb_perceptual_slider, rgb_scene_linear);
214 ui_scene_linear_to_perceptual_space(is_gamma, rgb_perceptual_slider);
215
216 if (cpicker->is_init == false) {
217 ui_color_picker_rgb_to_hsv(rgb_perceptual_slider, cpicker->hsv_perceptual);
219 }
220 else {
221 ui_color_picker_rgb_to_hsv_compat(rgb_perceptual_slider, cpicker->hsv_perceptual);
222 }
223
224 cpicker->is_init = true;
225}
226
228{
229 float rgb_perceptual_slider[3];
230 ColorPicker *cpicker = static_cast<ColorPicker *>(but->custom_data);
231 float *hsv_perceptual = cpicker->hsv_perceptual;
232
233 ui_color_picker_hsv_to_rgb(hsv_perceptual, rgb_perceptual_slider);
234
235 ui_but_v3_set(but, rgb_perceptual_slider);
236}
237
238/* Updates all buttons who share the same color picker as the one passed. */
240 ColorPicker *cpicker,
241 const bool is_editing_sliders,
242 const float rgba_scene_linear[4])
243{
245 cpicker, block->is_color_gamma_picker, is_editing_sliders, rgba_scene_linear);
246
247 for (const std::unique_ptr<uiBut> &bt : block->buttons) {
248 if (bt->custom_data != cpicker) {
249 continue;
250 }
251
252 if (bt->rnaprop) {
253 ui_but_v4_set(bt.get(), rgba_scene_linear);
254 /* original button that created the color picker already does undo
255 * push, so disable it on RNA buttons in the color picker block */
257 }
258 else if (bt->type == ButType::Text) {
259 /* Hex text input field. */
260 float rgba_hex[4];
261 uchar rgba_hex_uchar[4];
262 char col[16];
263
264 /* Hex code is assumed to be in sRGB space (coming from other applications, web, etc...). */
265 copy_v4_v4(rgba_hex, rgba_scene_linear);
266 if (!block->is_color_gamma_picker) {
269 }
270
271 rgba_float_to_uchar(rgba_hex_uchar, rgba_hex);
272
273 int col_len;
274 if (cpicker->has_alpha) {
275 col_len = SNPRINTF_UTF8_RLEN(
276 col, "#%02X%02X%02X%02X", UNPACK4_EX((uint), rgba_hex_uchar, ));
277 }
278 else {
279 col_len = SNPRINTF_UTF8_RLEN(col, "#%02X%02X%02X", UNPACK3_EX((uint), rgba_hex_uchar, ));
280 }
281 memcpy(bt->poin, col, col_len + 1); /* +1 offset for the # symbol. */
282 }
283
284 ui_but_update(bt.get());
285 }
286}
287
288static void ui_colorpicker_rgba_update_cb(bContext * /*C*/, void *picker_bt1, void *prop_bt1)
289{
290 uiBut *picker_but = static_cast<uiBut *>(picker_bt1);
291 uiBlock *block = picker_but->block;
292 uiPopupBlockHandle *popup = block->handle;
293 ColorPicker *cpicker = static_cast<ColorPicker *>(picker_but->custom_data);
294
295 uiBut *prop_but = static_cast<uiBut *>(prop_bt1);
296 PointerRNA ptr = prop_but->rnapoin;
297 PropertyRNA *prop = prop_but->rnaprop;
298
299 if (prop) {
300 float rgba_scene_linear[4];
301
302 zero_v4(rgba_scene_linear);
304 &ptr, prop, rgba_scene_linear, ARRAY_SIZE(rgba_scene_linear));
305 ui_update_color_picker_buts_rgba(block, cpicker, false, rgba_scene_linear);
306 }
307
308 if (popup) {
310 }
311}
312
313static void ui_colorpicker_hsv_perceptual_slider_update_cb(bContext * /*C*/, void *bt1, void *bt2)
314{
315 uiBut *but = static_cast<uiBut *>(bt1);
316 uiPopupBlockHandle *popup = but->block->handle;
317 ColorPicker *cpicker = static_cast<ColorPicker *>(but->custom_data);
318
319 /* Get RNA ptr/prop from the original color datablock button (bt2) since the HSV buttons (bt1)
320 * do not directly point to it. */
321 uiBut *prop_but = static_cast<uiBut *>(bt2);
322 PointerRNA ptr = prop_but->rnapoin;
323 PropertyRNA *prop = prop_but->rnaprop;
324 float rgba_scene_linear[4];
325
326 if (prop) {
327 zero_v4(rgba_scene_linear);
328 /* Get the current RGBA color for its (optional) Alpha component,
329 * then update RGB components from the current HSV values. */
331 &ptr, prop, rgba_scene_linear, ARRAY_SIZE(rgba_scene_linear));
333 copy_v3_v3(rgba_scene_linear, cpicker->rgb_perceptual_slider);
335 ui_update_color_picker_buts_rgba(but->block, cpicker, true, rgba_scene_linear);
336 }
337
338 if (popup) {
340 }
341}
342
343static void ui_colorpicker_hsv_linear_slider_update_cb(bContext * /*C*/, void *bt1, void *bt2)
344{
345 uiBut *but = static_cast<uiBut *>(bt1);
346 uiPopupBlockHandle *popup = but->block->handle;
347 ColorPicker *cpicker = static_cast<ColorPicker *>(but->custom_data);
348
349 /* Get RNA ptr/prop from the original color datablock button (bt2) since the HSV buttons (bt1)
350 * do not directly point to it. */
351 uiBut *prop_but = static_cast<uiBut *>(bt2);
352 PointerRNA ptr = prop_but->rnapoin;
353 PropertyRNA *prop = prop_but->rnaprop;
354 float rgba_scene_linear[4];
355
356 if (prop) {
357 zero_v4(rgba_scene_linear);
358 /* Get the current RGBA color for its (optional) Alpha component,
359 * then update RGB components from the current HSV values. */
361 &ptr, prop, rgba_scene_linear, ARRAY_SIZE(rgba_scene_linear));
362 ui_color_picker_hsv_to_rgb(cpicker->hsv_linear_slider, rgba_scene_linear);
363 ui_update_color_picker_buts_rgba(but->block, cpicker, true, rgba_scene_linear);
364 }
365
366 if (popup) {
368 }
369}
370
371static void ui_colorpicker_rgb_perceptual_slider_update_cb(bContext * /*C*/, void *bt1, void *bt2)
372{
373 uiBut *but = static_cast<uiBut *>(bt1);
374 uiPopupBlockHandle *popup = but->block->handle;
375 ColorPicker *cpicker = static_cast<ColorPicker *>(but->custom_data);
376
377 /* Get RNA ptr/prop from the original color datablock button (bt2) since the HSV buttons (bt1)
378 * do not directly point to it. */
379 uiBut *prop_but = static_cast<uiBut *>(bt2);
380 PointerRNA ptr = prop_but->rnapoin;
381 PropertyRNA *prop = prop_but->rnaprop;
382 float rgba_scene_linear[4];
383
384 if (prop) {
385 zero_v4(rgba_scene_linear);
386 /* Get the current RGBA color for its (optional) Alpha component,
387 * then update RGB components from the current HSV values. */
389 &ptr, prop, rgba_scene_linear, ARRAY_SIZE(rgba_scene_linear));
390 copy_v3_v3(rgba_scene_linear, cpicker->rgb_perceptual_slider);
393 ui_update_color_picker_buts_rgba(but->block, cpicker, true, rgba_scene_linear);
394 }
395
396 if (popup) {
398 }
399}
400
401static void ui_colorpicker_hex_rna_cb(bContext * /*C*/, void *bt1, void *bt2)
402{
403 uiBut *but = static_cast<uiBut *>(bt1);
404 uiPopupBlockHandle *popup = but->block->handle;
405 ColorPicker *cpicker = static_cast<ColorPicker *>(but->custom_data);
406 char hexcol[128];
407 ui_but_string_get(but, hexcol, ARRAY_SIZE(hexcol));
408
409 /* In case the current color contains an Alpha component but the Hex string does not, get the
410 * current color to preserve the Alpha component.
411 * Like #ui_colorpicker_hsv_perceptual_slider_update_cb, the original color datablock button
412 * (bt2) is used since Hex Text Field button (bt1) doesn't directly point to it. */
413 uiBut *prop_but = static_cast<uiBut *>(bt2);
414 PointerRNA ptr = prop_but->rnapoin;
415 PropertyRNA *prop = prop_but->rnaprop;
416
417 float rgba[4];
418 if (prop) {
419 zero_v4(rgba);
421 }
422 /* Override current color with parsed the Hex string to preserve the original Alpha if the
423 * hex string doesn't contain it. */
424 hex_to_rgba(hexcol, rgba, rgba + 1, rgba + 2, rgba + 3);
425
426 /* Hex code is assumed to be in sRGB space (coming from other applications, web, etc...). */
427 if (!ui_but_is_color_gamma(but)) {
430 }
431
432 ui_update_color_picker_buts_rgba(but->block, cpicker, false, rgba);
433
434 if (popup) {
436 }
437}
438
439static void ui_popup_close_cb(bContext * /*C*/, void *bt1, void * /*arg*/)
440{
441 uiBut *but = (uiBut *)bt1;
442 uiPopupBlockHandle *popup = but->block->handle;
443
444 if (popup) {
445 ColorPicker *cpicker = static_cast<ColorPicker *>(but->custom_data);
446 BLI_assert(cpicker->is_init);
447 popup->menuretval = (equals_v3v3(cpicker->hsv_perceptual, cpicker->hsv_perceptual_init) ?
450 }
451}
452
454{
456 const ePickerSpace space = (block->is_color_gamma_picker) ? (type == PICKER_TYPE_RGB) ?
460
461 /* tag buttons */
462 for (const std::unique_ptr<uiBut> &bt : block->buttons) {
463 if ((bt->func == ui_colorpicker_rgba_update_cb) && (bt->type == ButType::NumSlider) &&
464 (bt->rnaindex != 3))
465 {
466 /* RGB sliders (color circle and alpha are always shown) */
468 bt->flag, !(type == PICKER_TYPE_RGB && space == PICKER_SPACE_LINEAR), UI_HIDDEN);
469 }
471 /* HSV sliders */
473 bt->flag, !(type == PICKER_TYPE_RGB && space == PICKER_SPACE_PERCEPTUAL), UI_HIDDEN);
474 }
476 /* HSV sliders */
478 bt->flag, !(type == PICKER_TYPE_HSV && space == PICKER_SPACE_PERCEPTUAL), UI_HIDDEN);
479 }
480 else if (bt->func == ui_colorpicker_hsv_linear_slider_update_cb) {
481 /* HSV sliders */
483 bt->flag, !(type == PICKER_TYPE_HSV && space == PICKER_SPACE_LINEAR), UI_HIDDEN);
484 }
485 }
486}
487
488static void ui_colorpicker_update_type_space_cb(bContext * /*C*/, void *picker_bt1, void *prop_bt1)
489{
490 uiBut *picker_but = static_cast<uiBut *>(picker_bt1);
491 uiBlock *block = picker_but->block;
492 ColorPicker *cpicker = static_cast<ColorPicker *>(picker_but->custom_data);
493
494 uiBut *prop_but = static_cast<uiBut *>(prop_bt1);
495 PointerRNA ptr = prop_but->rnapoin;
496 PropertyRNA *prop = prop_but->rnaprop;
497
498 float rgba_scene_linear[4];
499
500 zero_v4(rgba_scene_linear);
502 &ptr, prop, rgba_scene_linear, ARRAY_SIZE(rgba_scene_linear));
503 ui_update_color_picker_buts_rgba(block, cpicker, false, rgba_scene_linear);
504
506}
507
508#define PICKER_TOTAL_W (180.0f * UI_SCALE_FAC)
509#define PICKER_BAR ((8.0f * UI_SCALE_FAC) + (6 * U.pixelsize))
510#define PICKER_SPACE (8.0f * UI_SCALE_FAC)
511#define PICKER_W (PICKER_TOTAL_W - PICKER_BAR - PICKER_SPACE)
512#define PICKER_H PICKER_W
513
516 PropertyRNA *prop,
517 ColorPicker *cpicker)
518{
519 uiBut *bt;
520 uiButHSVCube *hsv_but;
521
522 /* HS circle */
523 bt = uiDefButR_prop(block,
525 0,
526 "",
527 0,
528 0,
529 PICKER_H,
530 PICKER_W,
531 ptr,
532 prop,
533 -1,
534 0.0,
535 0.0,
536 TIP_("Color"));
538 bt->custom_data = cpicker;
539
540 /* value */
541 if (U.color_picker_type == USER_CP_CIRCLE_HSL) {
542 hsv_but = (uiButHSVCube *)uiDefButR_prop(block,
544 0,
545 "",
547 0,
549 PICKER_H,
550 ptr,
551 prop,
552 -1,
553 0.0,
554 0.0,
555 "Lightness");
556 hsv_but->gradient_type = UI_GRAD_L_ALT;
557 UI_but_func_set(hsv_but, ui_colorpicker_rgba_update_cb, hsv_but, hsv_but);
558 }
559 else {
560 hsv_but = (uiButHSVCube *)uiDefButR_prop(block,
562 0,
563 "",
565 0,
567 PICKER_H,
568 ptr,
569 prop,
570 -1,
571 0.0,
572 0.0,
574 hsv_but->gradient_type = UI_GRAD_V_ALT;
575 UI_but_func_set(hsv_but, ui_colorpicker_rgba_update_cb, hsv_but, hsv_but);
576 }
577 hsv_but->custom_data = cpicker;
578}
579
582 PropertyRNA *prop,
583 eButGradientType type,
584 ColorPicker *cpicker)
585{
586 uiButHSVCube *hsv_but;
587
588 BLI_assert(type <= UI_GRAD_HS);
589
590 /* HS square */
591 hsv_but = (uiButHSVCube *)uiDefButR_prop(block,
593 0,
594 "",
595 0,
598 PICKER_H,
599 ptr,
600 prop,
601 -1,
602 0.0,
603 0.0,
604 TIP_("Color"));
605 hsv_but->gradient_type = type;
606 UI_but_func_set(hsv_but, ui_colorpicker_rgba_update_cb, hsv_but, hsv_but);
607 hsv_but->custom_data = cpicker;
608
609 /* value */
610 hsv_but = (uiButHSVCube *)uiDefButR_prop(block,
612 0,
613 "",
614 0,
615 0,
618 ptr,
619 prop,
620 -1,
621 0.0,
622 0.0,
624 hsv_but->gradient_type = (eButGradientType)(type + 3);
625 UI_but_func_set(hsv_but, ui_colorpicker_rgba_update_cb, hsv_but, hsv_but);
626 hsv_but->custom_data = cpicker;
627}
628
629/* a HS circle, V slider, rgb/hsv/hex sliders */
630static void ui_block_colorpicker(const bContext * /*C*/,
631 uiBlock *block,
632 uiBut *from_but,
633 float rgba_scene_linear[4],
634 bool show_picker)
635{
636 /* ePickerType */
637 uiBut *bt;
638 int picker_width;
639 float softmin, softmax, hardmin, hardmax, step, precision;
641 PointerRNA *ptr = &from_but->rnapoin;
642 PropertyRNA *prop = from_but->rnaprop;
643
644 picker_width = PICKER_TOTAL_W;
645
646 RNA_property_float_ui_range(ptr, prop, &softmin, &softmax, &step, &precision);
647 RNA_property_float_range(ptr, prop, &hardmin, &hardmax);
648 RNA_property_float_get_array_at_most(ptr, prop, rgba_scene_linear, 4);
649
651 cpicker, block->is_color_gamma_picker, false, rgba_scene_linear);
652 cpicker->has_alpha = ui_but_color_has_alpha(from_but);
653
654 /* when the softmax isn't defined in the RNA,
655 * using very large numbers causes sRGB/linear round trip to fail. */
656 if (softmax == FLT_MAX) {
657 softmax = 1.0f;
658 }
659
660 switch (U.color_picker_type) {
662 ui_colorpicker_square(block, ptr, prop, UI_GRAD_SV, cpicker);
663 break;
665 ui_colorpicker_square(block, ptr, prop, UI_GRAD_HS, cpicker);
666 break;
668 ui_colorpicker_square(block, ptr, prop, UI_GRAD_HV, cpicker);
669 break;
670
671 /* user default */
674 default:
675 ui_colorpicker_circle(block, ptr, prop, cpicker);
676 break;
677 }
678
679 /* mode */
680 int yco = -0.5f * UI_UNIT_Y;
681
682 if (!block->is_color_gamma_picker) {
683 auto colorspace_tip_func = [](bContext & /*C*/, uiTooltipData &tip, uiBut *but, void *space) {
686 IFACE_("Color Space: ") +
687 std::string(static_cast<const char *>(space)),
688 {},
691 false);
692 };
693
695
696 bt = uiDefButC(block,
698 0,
699 IFACE_("Linear"),
700 0,
701 yco -= UI_UNIT_Y,
702 picker_width * 0.5,
703 UI_UNIT_Y,
705 0.0,
706 float(PICKER_TYPE_RGB),
707 TIP_("Scene linear values in the working color space"));
712 bt,
713 colorspace_tip_func,
715 nullptr);
716 bt->custom_data = cpicker;
717
718 bt = uiDefButC(block,
720 0,
721 IFACE_("Perceptual"),
722 picker_width * 0.5,
723 yco,
724 picker_width * 0.5,
725 UI_UNIT_Y,
727 0.0,
728 float(PICKER_TYPE_HSV),
729 TIP_("Perceptually uniform values, matching the color picker"));
734 bt,
735 colorspace_tip_func,
737 nullptr);
738
739 bt->custom_data = cpicker;
740
741 UI_block_align_end(block);
742
743 yco -= 0.5f * UI_UNIT_X;
744 }
745
747
748 bt = uiDefButC(block,
750 0,
751 IFACE_("RGB"),
752 0,
753 yco -= UI_UNIT_Y,
754 picker_width * 0.5,
755 UI_UNIT_Y,
757 0.0,
758 float(PICKER_TYPE_RGB),
759 TIP_("RGB values"));
763 bt->custom_data = cpicker;
764
765 bt = uiDefButC(block,
767 0,
768 (U.color_picker_type == USER_CP_CIRCLE_HSL) ? IFACE_("HSL") : IFACE_("HSV"),
769 picker_width * 0.5,
770 yco,
771 picker_width * 0.5,
772 UI_UNIT_Y,
774 0.0,
775 float(PICKER_TYPE_HSV),
776 (U.color_picker_type == USER_CP_CIRCLE_HSL) ? TIP_("Hue, Saturation, Lightness") :
777 TIP_("Hue, Saturation, Value"));
781 bt->custom_data = cpicker;
782
783 UI_block_align_end(block);
784
785 const int slider_yco = yco - 1.1f * UI_UNIT_Y;
786
787 /* NOTE: don't disable UI_BUT_UNDO for RGBA values, since these don't add undo steps. */
788
789 /* RGB values */
791 const auto add_rgb_perceptual_slider =
792 [&](const char *str, const char *tip, const int index, const int y) {
793 bt = uiDefButR_prop(block,
795 0,
796 str,
797 0,
798 y,
799 picker_width,
800 UI_UNIT_Y,
801 ptr,
802 prop,
803 index,
804 0.0,
805 0.0,
806 tip);
810 bt->custom_data = cpicker;
811 };
812
813 yco = slider_yco;
814 add_rgb_perceptual_slider(IFACE_("Red:"), TIP_("Red"), 0, yco);
815 add_rgb_perceptual_slider(IFACE_("Green:"), TIP_("Green"), 1, yco -= UI_UNIT_Y);
816 add_rgb_perceptual_slider(IFACE_("Blue:"), TIP_("Blue"), 2, yco -= UI_UNIT_Y);
817
818 /* HSV values */
819 const auto add_hsv_perceptual_slider =
820 [&](const char *str, const char *tip, const int index, const int y, const bool linear) {
821 float *hsv_values = linear ? cpicker->hsv_linear_slider : cpicker->hsv_perceptual_slider;
822 bt = uiDefButF(block,
824 0,
825 str,
826 0,
827 y,
828 picker_width,
829 UI_UNIT_Y,
830 hsv_values + index,
831 0.0,
832 1.0,
833 tip);
834 if (index == 2) {
835 bt->hardmax = hardmax; /* Not common but RGB may be over 1.0. */
836 }
843 bt,
844 from_but);
845 bt->custom_data = cpicker;
846 };
847
848 yco = slider_yco;
849 add_hsv_perceptual_slider(IFACE_("Hue:"), TIP_("Hue"), 0, yco, !block->is_color_gamma_picker);
850 add_hsv_perceptual_slider(IFACE_("Saturation:"),
851 TIP_("Saturation"),
852 1,
853 yco -= UI_UNIT_Y,
854 !block->is_color_gamma_picker);
855 if (U.color_picker_type == USER_CP_CIRCLE_HSL) {
856 add_hsv_perceptual_slider(IFACE_("Lightness:"),
857 TIP_("Lightness"),
858 2,
859 yco -= UI_UNIT_Y,
860 !block->is_color_gamma_picker);
861 }
862 else {
863 add_hsv_perceptual_slider(IFACE_("Value:"),
865 2,
866 yco -= UI_UNIT_Y,
867 !block->is_color_gamma_picker);
868 }
869
870 /* Could use:
871 * col->prop(ptr, prop, -1, 0, UI_ITEM_R_EXPAND | UI_ITEM_R_SLIDER, "", ICON_NONE);
872 * but need to use UI_but_func_set for updating other fake buttons */
873
874 if (!block->is_color_gamma_picker) {
875 yco = slider_yco;
876
877 /* Display RGB values */
878 const auto add_rgb_perceptual_slider =
879 [&](const char *str, const char *tip, const int index, const int y) {
880 bt = uiDefButF(block,
882 0,
883 str,
884 0,
885 y,
886 picker_width,
887 UI_UNIT_Y,
888 cpicker->rgb_perceptual_slider + index,
889 hardmin,
890 hardmax,
891 tip);
894 bt->softmin = softmin;
895 bt->softmax = softmax;
898 bt->custom_data = cpicker;
899 };
900
901 add_rgb_perceptual_slider(IFACE_("Red:"), TIP_("Red"), 0, yco);
902 add_rgb_perceptual_slider(IFACE_("Green:"), TIP_("Green"), 1, yco -= UI_UNIT_Y);
903 add_rgb_perceptual_slider(IFACE_("Blue:"), TIP_("Blue"), 2, yco -= UI_UNIT_Y);
904
905 yco = slider_yco;
906 add_hsv_perceptual_slider(IFACE_("Hue:"), TIP_("Hue"), 0, yco, false);
907 add_hsv_perceptual_slider(
908 IFACE_("Saturation:"), TIP_("Saturation"), 1, yco -= UI_UNIT_Y, false);
909 if (U.color_picker_type == USER_CP_CIRCLE_HSL) {
910 add_hsv_perceptual_slider(
911 IFACE_("Lightness:"), TIP_("Lightness"), 2, yco -= UI_UNIT_Y, false);
912 }
913 else {
914 add_hsv_perceptual_slider(
915 IFACE_("Value:"), CTX_TIP_(BLT_I18NCONTEXT_COLOR, "Value"), 2, yco -= UI_UNIT_Y, false);
916 }
917 }
918
919 if (cpicker->has_alpha) {
920 bt = uiDefButR_prop(block,
922 0,
923 IFACE_("Alpha:"),
924 0,
925 yco -= UI_UNIT_Y,
926 picker_width,
927 UI_UNIT_Y,
928 ptr,
929 prop,
930 3,
931 0.0,
932 0.0,
933 TIP_("Alpha"));
937 bt->custom_data = cpicker;
938 }
939 else {
940 rgba_scene_linear[3] = 1.0f;
941 }
942
943 UI_block_align_end(block);
944
945 /* Hex color is in sRGB space. */
946 float rgba_hex[4];
947 uchar rgba_hex_uchar[4];
948
949 copy_v4_v4(rgba_hex, rgba_scene_linear);
950
951 if (!ui_but_is_color_gamma(from_but)) {
954 }
955
956 rgba_float_to_uchar(rgba_hex_uchar, rgba_hex);
957
958 if (cpicker->has_alpha) {
959 SNPRINTF_UTF8(cpicker->hexcol, "#%02X%02X%02X%02X", UNPACK4_EX((uint), rgba_hex_uchar, ));
960 }
961 else {
962 SNPRINTF_UTF8(cpicker->hexcol, "#%02X%02X%02X", UNPACK3_EX((uint), rgba_hex_uchar, ));
963 }
964
965 yco -= UI_UNIT_Y * 1.5f;
966
967 const int label_width = picker_width * 0.15f;
968 const int eyedropper_offset = show_picker ? UI_UNIT_X * 1.25f : 0;
969 const int text_width = picker_width - label_width - eyedropper_offset;
970
971 uiDefBut(block,
973 0,
974 IFACE_("Hex"),
975 0,
976 yco,
977 label_width,
978 UI_UNIT_Y,
979 nullptr,
980 0.0,
981 0.0,
982 std::nullopt);
983
984 bt = uiDefBut(block,
986 0,
987 "",
988 label_width,
989 yco,
990 text_width,
991 UI_UNIT_Y,
992 cpicker->hexcol,
993 0,
994 cpicker->has_alpha ? 10 : 8,
995 std::nullopt);
996 const auto bt_tooltip_func =
997 [](bContext & /*C*/, uiTooltipData &tip, uiBut * /*but*/, void *has_alpha_ptr) {
998 const bool *has_alpha = static_cast<bool *>(has_alpha_ptr);
999 if (*has_alpha) {
1001 "Hex triplet for color with alpha (#RRGGBBAA).",
1002 {},
1005 false);
1006 }
1007 else {
1009 "Hex triplet for color (#RRGGBB).",
1010 {},
1013 false);
1014 }
1016 tip, "Gamma corrected", {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL, false);
1017 };
1019 bt, bt_tooltip_func, static_cast<void *>(&cpicker->has_alpha), nullptr);
1021 UI_but_func_set(bt, ui_colorpicker_hex_rna_cb, bt, from_but);
1022 bt->custom_data = cpicker;
1023
1024 if (show_picker) {
1025 bt = uiDefIconButO(block,
1027 "UI_OT_eyedropper_color",
1029 ICON_EYEDROPPER,
1030 picker_width - UI_UNIT_X,
1031 yco,
1032 UI_UNIT_X,
1033 UI_UNIT_Y,
1034 std::nullopt);
1037 UI_but_func_set(bt, ui_popup_close_cb, bt, nullptr);
1038 bt->custom_data = cpicker;
1039 }
1040
1042}
1043
1044static int ui_colorpicker_wheel_cb(const bContext * /*C*/, uiBlock *block, const wmEvent *event)
1045{
1046 uiPopupBlockHandle *popup = block->handle;
1047 bool mouse_in_region = popup && BLI_rcti_isect_pt(&popup->region->winrct,
1048 float(event->xy[0]),
1049 float(event->xy[1]));
1050
1051 if (popup && !mouse_in_region && (ISMOUSE_WHEEL(event->type) || event->type == MOUSEPAN)) {
1052 /* Exit and save color if moving mouse wheel or trackpad panning while outside the popup. */
1053 popup->menuretval = UI_RETURN_OK;
1054 return 1;
1055 }
1056
1057 /* Increase/Decrease the Color HSV Value component using the mouse wheel. */
1058 float add = 0.0f;
1059
1060 switch (event->type) {
1061 case WHEELUPMOUSE:
1062 add = 0.05f;
1063 break;
1064 case WHEELDOWNMOUSE:
1065 add = -0.05f;
1066 break;
1067 case MOUSEPAN:
1068 add = 0.005f * WM_event_absolute_delta_y(event) / UI_SCALE_FAC;
1069 break;
1070 default:
1071 break;
1072 }
1073
1074 if (add != 0.0f) {
1075 for (const std::unique_ptr<uiBut> &but : block->buttons) {
1076 if (but->type == ButType::HsvCube && but->active == nullptr) {
1077 ColorPicker *cpicker = static_cast<ColorPicker *>(but->custom_data);
1078 float *hsv_perceptual = cpicker->hsv_perceptual;
1079
1080 /* Get the RGBA Color. */
1081 float rgba_perceptual[4];
1082 ui_but_v4_get(but.get(), rgba_perceptual);
1084
1085 /* Convert it to HSV. */
1086 ui_color_picker_rgb_to_hsv_compat(rgba_perceptual, hsv_perceptual);
1087
1088 /* Increment/Decrement its value from mouse wheel input. */
1089 hsv_perceptual[2] = clamp_f(hsv_perceptual[2] + add, 0.0f, 1.0f);
1090
1091 /* Convert it to linear space RGBA, and apply it back to the button. */
1092 float rgba_scene_linear[4];
1093 rgba_scene_linear[3] = rgba_perceptual[3]; /* Transfer Alpha component. */
1094 ui_color_picker_hsv_to_rgb(hsv_perceptual, rgba_scene_linear);
1095 ui_perceptual_to_scene_linear_space(but.get(), rgba_scene_linear);
1096 ui_but_v4_set(but.get(), rgba_scene_linear);
1097
1098 /* Update all other Color Picker buttons to reflect the color change. */
1099 ui_update_color_picker_buts_rgba(block, cpicker, false, rgba_scene_linear);
1100 if (popup) {
1102 }
1103
1104 return 1;
1105 }
1106 }
1107 }
1108 return 0;
1109}
1110
1112{
1113 uiBut *but = static_cast<uiBut *>(arg_but);
1114 uiBlock *block;
1115
1116 block = UI_block_begin(C, handle->region, __func__, blender::ui::EmbossType::Emboss);
1117
1118 if (ui_but_is_color_gamma(but)) {
1119 block->is_color_gamma_picker = true;
1120 }
1121
1122 copy_v3_v3(handle->retvec, but->editvec);
1123
1124 ui_block_colorpicker(C, block, but, handle->retvec, true);
1125
1129
1131 block->direction = UI_DIR_UP;
1132
1133 return block;
1134}
1135
1137{
1138 ColorPicker *cpicker = MEM_callocN<ColorPicker>(__func__);
1139 BLI_addhead(&block->color_pickers.list, cpicker);
1140
1141 return cpicker;
1142}
1143
#define BLI_assert(a)
Definition BLI_assert.h:46
void BLI_addhead(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:91
MINLINE float clamp_f(float value, float min, float max)
void hsv_to_rgb_v(const float hsv[3], float r_rgb[3])
Definition math_color.cc:57
void rgb_to_hsv_v(const float rgb[3], float r_hsv[3])
void rgb_to_hsv_compat_v(const float rgb[3], float r_hsv[3])
void hex_to_rgba(const char *hexcol, float *r_r, float *r_g, float *r_b, float *r_a)
void hsl_to_rgb_v(const float hsl[3], float r_rgb[3])
Definition math_color.cc:62
MINLINE void rgba_float_to_uchar(unsigned char r_col[4], const float col_f[4])
void rgb_to_hsl_v(const float rgb[3], float r_hsl[3])
void rgb_to_hsl_compat_v(const float rgb[3], float r_hsl[3])
MINLINE void copy_v4_v4(float r[4], const float a[4])
MINLINE bool equals_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void zero_v4(float r[4])
bool BLI_rcti_isect_pt(const struct rcti *rect, int x, int y)
#define SNPRINTF_UTF8(dst, format,...)
#define SNPRINTF_UTF8_RLEN(dst, format,...)
unsigned char uchar
unsigned int uint
#define UNPACK3_EX(pre, a, post)
#define ARRAY_SIZE(arr)
#define UNPACK4_EX(pre, a, post)
#define SET_FLAG_FROM_TEST(value, test, flag)
#define ELEM(...)
#define TIP_(msgid)
#define IFACE_(msgid)
#define CTX_TIP_(context, msgid)
#define BLT_I18NCONTEXT_COLOR
#define UI_SCALE_FAC
@ USER_CP_SQUARE_SV
@ USER_CP_CIRCLE_HSL
@ USER_CP_SQUARE_HS
@ USER_CP_SQUARE_HV
@ USER_CP_CIRCLE_HSV
BLI_INLINE void IMB_colormanagement_srgb_to_scene_linear_v3(float scene_linear[3], const float srgb[3])
void IMB_colormanagement_color_picking_to_scene_linear_v3(float scene_linear[3], const float color_picking[3])
@ COLOR_ROLE_SCENE_LINEAR
@ COLOR_ROLE_COLOR_PICKING
const char * IMB_colormanagement_role_colorspace_name_get(int role)
void IMB_colormanagement_scene_linear_to_color_picking_v3(float color_picking[3], const float scene_linear[3])
BLI_INLINE void IMB_colormanagement_scene_linear_to_srgb_v3(float srgb[3], const float scene_linear[3])
Read Guarded memory(de)allocation.
PropertySubType
Definition RNA_types.hh:232
@ PROP_COLOR
Definition RNA_types.hh:260
@ PROP_COLOR_GAMMA
Definition RNA_types.hh:272
#define C
Definition RandGen.cpp:29
void UI_but_func_set(uiBut *but, std::function< void(bContext &)> func)
uiBut * uiDefButF(uiBlock *block, ButType type, int retval, blender::StringRef str, int x, int y, short width, short height, float *poin, float min, float max, std::optional< blender::StringRef > tip)
void UI_but_flag_disable(uiBut *but, int flag)
#define UI_UNIT_Y
@ UI_BLOCK_LOOP
@ UI_BLOCK_MOVEMOUSE_QUIT
@ UI_BLOCK_KEEP_OPEN
@ UI_BLOCK_OUT_1
void UI_block_theme_style_set(uiBlock *block, char theme_style)
void UI_but_func_tooltip_custom_set(uiBut *but, uiButToolTipCustomFunc func, void *arg, uiFreeArgFunc free_arg)
uiBlock * UI_block_begin(const bContext *C, ARegion *region, std::string name, blender::ui::EmbossType emboss)
@ UI_BUT_UNDO
void UI_block_bounds_set_normal(uiBlock *block, int addval)
Definition interface.cc:637
void UI_tooltip_text_field_add(uiTooltipData &data, std::string text, std::string suffix, const uiTooltipStyle style, const uiTooltipColorID color_id, const bool is_pad=false)
uiBut * uiDefIconButO(uiBlock *block, ButType type, blender::StringRefNull opname, blender::wm::OpCallContext opcontext, int icon, int x, int y, short width, short height, std::optional< blender::StringRef > tip)
uiBut * uiDefButR_prop(uiBlock *block, ButType type, int retval, std::optional< blender::StringRef > str, int x, int y, short width, short height, PointerRNA *ptr, PropertyRNA *prop, int index, float min, float max, std::optional< blender::StringRef > tip)
void UI_but_number_slider_precision_set(uiBut *but, float precision)
@ UI_TIP_STYLE_NORMAL
@ UI_TIP_STYLE_HEADER
void UI_but_number_slider_step_size_set(uiBut *but, float step_size)
void UI_but_drawflag_disable(uiBut *but, int flag)
@ UI_RETURN_UPDATE
@ UI_RETURN_CANCEL
@ UI_RETURN_OK
void UI_block_align_begin(uiBlock *block)
eButGradientType
@ UI_GRAD_L_ALT
@ UI_GRAD_SV
@ UI_GRAD_V_ALT
@ UI_GRAD_HV
@ UI_GRAD_HS
@ UI_TIP_LC_NORMAL
@ UI_TIP_LC_ACTIVE
@ UI_BLOCK_THEME_STYLE_POPUP
#define UI_UNIT_X
@ UI_BUT_ICON_LEFT
@ UI_BUT_TEXT_LEFT
uiBut * uiDefButC(uiBlock *block, ButType type, int retval, blender::StringRef str, int x, int y, short width, short height, char *poin, float min, float max, std::optional< blender::StringRef > tip)
uiBut * uiDefBut(uiBlock *block, uiButTypeWithPointerType but_and_ptr_type, int retval, blender::StringRef str, int x, int y, short width, short height, void *poin, float min, float max, std::optional< blender::StringRef > tip)
@ UI_DIR_UP
void UI_block_align_end(uiBlock *block)
#define U
#define str(s)
uint col
VecBase< float, D > step(VecOp< float, D >, VecOp< float, D >) RET
void ui_but_v4_get(uiBut *but, float vec[4])
void ui_but_v4_set(uiBut *but, const float vec[4])
void ui_but_update(uiBut *but)
void ui_but_string_get(uiBut *but, char *str, const size_t str_maxncpy)
void ui_but_v3_set(uiBut *but, const float vec[3])
@ UI_HIDDEN
static void ui_color_picker_rgb_round(float rgb[3])
static void ui_block_colorpicker(const bContext *, uiBlock *block, uiBut *from_but, float rgba_scene_linear[4], bool show_picker)
static void ui_colorpicker_square(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, eButGradientType type, ColorPicker *cpicker)
ColorPicker * ui_block_colorpicker_create(uiBlock *block)
void ui_color_picker_hsv_to_rgb(const float r_cp[3], float rgb[3])
static void ui_colorpicker_rgb_perceptual_slider_update_cb(bContext *, void *bt1, void *bt2)
static void ui_colorpicker_hex_rna_cb(bContext *, void *bt1, void *bt2)
static void ui_update_color_picker_buts_rgba(uiBlock *block, ColorPicker *cpicker, const bool is_editing_sliders, const float rgba_scene_linear[4])
static void ui_scene_linear_to_perceptual_space(const bool is_gamma, float rgb[3])
static void ui_colorpicker_hide_reveal(uiBlock *block)
static void ui_colorpicker_circle(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, ColorPicker *cpicker)
void ui_color_picker_rgb_to_hsv(const float rgb[3], float r_cp[3])
static char g_color_picker_space
static void ui_perceptual_to_scene_linear_space(const bool is_gamma, float rgb[3])
void ui_but_hsv_set(uiBut *but)
static char g_color_picker_type
static void ui_colorpicker_hsv_perceptual_slider_update_cb(bContext *, void *bt1, void *bt2)
uiBlock * ui_block_func_COLOR(bContext *C, uiPopupBlockHandle *handle, void *arg_but)
void ui_color_picker_rgb_to_hsv_compat(const float rgb[3], float r_cp[3])
static void ui_colorpicker_update_type_space_cb(bContext *, void *picker_bt1, void *prop_bt1)
static void ui_colorpicker_rgba_update_cb(bContext *, void *picker_bt1, void *prop_bt1)
bool ui_but_color_has_alpha(uiBut *but)
static void ui_color_picker_update_from_rgb_linear(ColorPicker *cpicker, const bool is_gamma, const bool is_editing_sliders, const float rgb_scene_linear[3])
bool ui_but_is_color_gamma(uiBut *but)
static int ui_colorpicker_wheel_cb(const bContext *, uiBlock *block, const wmEvent *event)
static void ui_popup_close_cb(bContext *, void *bt1, void *)
static void ui_colorpicker_hsv_linear_slider_update_cb(bContext *, void *bt1, void *bt2)
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
static void add(blender::Map< std::string, std::string > &messages, Message &msg)
Definition msgfmt.cc:222
#define fabsf
void RNA_property_float_ui_range(PointerRNA *ptr, PropertyRNA *prop, float *softmin, float *softmax, float *step, float *precision)
void RNA_property_float_range(PointerRNA *ptr, PropertyRNA *prop, float *hardmin, float *hardmax)
void RNA_property_float_get_array_at_most(PointerRNA *ptr, PropertyRNA *prop, float *values, int values_num)
int RNA_property_array_length(PointerRNA *ptr, PropertyRNA *prop)
PropertySubType RNA_property_subtype(PropertyRNA *prop)
#define FLT_MAX
Definition stdcycles.h:14
float hsv_linear_slider[3]
float hsv_perceptual_init[3]
float rgb_perceptual_slider[3]
float hsv_perceptual[3]
float hsv_perceptual_slider[3]
blender::Vector< std::unique_ptr< uiBut > > buttons
bool is_color_gamma_picker
ColorPickerData color_pickers
uiPopupBlockHandle * handle
int(* block_event_func)(const bContext *C, uiBlock *, const wmEvent *)
eButGradientType gradient_type
void * custom_data
float * editvec
PropertyRNA * rnaprop
uiBlock * block
PointerRNA rnapoin
wmEventType type
Definition WM_types.hh:757
int xy[2]
Definition WM_types.hh:761
i
Definition text_draw.cc:230
int WM_event_absolute_delta_y(const wmEvent *event)
#define ISMOUSE_WHEEL(event_type)
@ MOUSEPAN
@ WHEELUPMOUSE
@ WHEELDOWNMOUSE
PointerRNA * ptr
Definition wm_files.cc:4238