1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2008 Blender Foundation.
17  * All rights reserved.
18  */
19 
20 /** \file
21  * \ingroup edinterface
22  *
23  * Color Picker Region & Color Utils
24  */
25 
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include "MEM_guardedalloc.h"
31 
32 #include "DNA_userdef_types.h"
33 
34 #include "BLI_listbase.h"
35 #include "BLI_math.h"
36 #include "BLI_string.h"
37 #include "BLI_utildefines.h"
38 
39 #include "BKE_context.h"
40 
41 #include "WM_types.h"
42 
43 #include "RNA_access.h"
44 
45 #include "UI_interface.h"
46 
47 #include "BLT_translation.h"
48 
49 #include "ED_screen.h"
50 
51 #include "IMB_colormanagement.h"
52 
53 #include "interface_intern.h"
54 
55 enum ePickerType {
56   PICKER_TYPE_RGB = 0,
57   PICKER_TYPE_HSV = 1,
58   PICKER_TYPE_HEX = 2,
59 };
60 
61 /* -------------------------------------------------------------------- */
62 /** \name Color Conversion
63  * \{ */
64 
ui_color_picker_rgb_round(float rgb[3])65 static void ui_color_picker_rgb_round(float rgb[3])
66 {
67   /* Handle small rounding errors in color space conversions. Doing these for
68    * all color space conversions would be expensive, but for the color picker
69    * we can do the extra work. */
70   for (int i = 0; i < 3; i++) {
71     if (fabsf(rgb[i]) < 1e-6f) {
72       rgb[i] = 0.0f;
73     }
74     else if (fabsf(1.0f - rgb[i]) < 1e-6f) {
75       rgb[i] = 1.0f;
76     }
77   }
78 }
79 
ui_rgb_to_color_picker_compat_v(const float rgb[3],float r_cp[3])80 void ui_rgb_to_color_picker_compat_v(const float rgb[3], float r_cp[3])
81 {
82   switch (U.color_picker_type) {
83     case USER_CP_CIRCLE_HSL:
84       rgb_to_hsl_compat_v(rgb, r_cp);
85       break;
86     default:
87       rgb_to_hsv_compat_v(rgb, r_cp);
88       break;
89   }
90 }
91 
ui_rgb_to_color_picker_v(const float rgb[3],float r_cp[3])92 void ui_rgb_to_color_picker_v(const float rgb[3], float r_cp[3])
93 {
94   switch (U.color_picker_type) {
95     case USER_CP_CIRCLE_HSL:
96       rgb_to_hsl_v(rgb, r_cp);
97       break;
98     default:
99       rgb_to_hsv_v(rgb, r_cp);
100       break;
101   }
102 }
103 
ui_color_picker_to_rgb_v(const float r_cp[3],float rgb[3])104 void ui_color_picker_to_rgb_v(const float r_cp[3], float rgb[3])
105 {
106   switch (U.color_picker_type) {
107     case USER_CP_CIRCLE_HSL:
108       hsl_to_rgb_v(r_cp, rgb);
109       break;
110     default:
111       hsv_to_rgb_v(r_cp, rgb);
112       break;
113   }
114 }
115 
ui_color_picker_to_rgb(float r_cp0,float r_cp1,float r_cp2,float * r,float * g,float * b)116 void ui_color_picker_to_rgb(float r_cp0, float r_cp1, float r_cp2, float *r, float *g, float *b)
117 {
118   switch (U.color_picker_type) {
119     case USER_CP_CIRCLE_HSL:
120       hsl_to_rgb(r_cp0, r_cp1, r_cp2, r, g, b);
121       break;
122     default:
123       hsv_to_rgb(r_cp0, r_cp1, r_cp2, r, g, b);
124       break;
125   }
126 }
127 
128 /* Returns true if the button is for a color with gamma baked in,
129  * or if it's a color picker for such a button. */
ui_but_is_color_gamma(uiBut * but)130 bool ui_but_is_color_gamma(uiBut *but)
131 {
132   if (but->rnaprop) {
133     if (RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) {
134       return true;
135     }
136   }
137 
138   return but->block->is_color_gamma_picker;
139 }
140 
ui_scene_linear_to_color_picker_space(uiBut * but,float rgb[3])141 void ui_scene_linear_to_color_picker_space(uiBut *but, float rgb[3])
142 {
143   /* Map to color picking space for HSV values and HSV cube/circle,
144    * assuming it is more perceptually linear than the scene linear
145    * space for intuitive color picking. */
146   if (!ui_but_is_color_gamma(but)) {
147     IMB_colormanagement_scene_linear_to_color_picking_v3(rgb);
148     ui_color_picker_rgb_round(rgb);
149   }
150 }
151 
ui_color_picker_to_scene_linear_space(uiBut * but,float rgb[3])152 void ui_color_picker_to_scene_linear_space(uiBut *but, float rgb[3])
153 {
154   if (!ui_but_is_color_gamma(but)) {
155     IMB_colormanagement_color_picking_to_scene_linear_v3(rgb);
156     ui_color_picker_rgb_round(rgb);
157   }
158 }
159 
160 /** \} */
161 
162 /* -------------------------------------------------------------------- */
163 /** \name Color Picker
164  * \{ */
165 
166 /* for picker, while editing hsv */
ui_but_hsv_set(uiBut * but)167 void ui_but_hsv_set(uiBut *but)
168 {
169   float col[3];
170   ColorPicker *cpicker = but->custom_data;
171   float *hsv = cpicker->color_data;
172 
173   ui_color_picker_to_rgb_v(hsv, col);
174 
175   ui_but_v3_set(but, col);
176 }
177 
178 /* Updates all buttons who share the same color picker as the one passed
179  * also used by small picker, be careful with name checks below... */
ui_update_color_picker_buts_rgb(uiBut * from_but,uiBlock * block,ColorPicker * cpicker,const float rgb[3])180 static void ui_update_color_picker_buts_rgb(uiBut *from_but,
181                                             uiBlock *block,
182                                             ColorPicker *cpicker,
183                                             const float rgb[3])
184 {
185   float *hsv = cpicker->color_data;
186 
187   /* Convert from RGB to HSV in perceptually linear space. */
188   float tmp[3];
189   copy_v3_v3(tmp, rgb);
190   if (from_but) {
191     ui_scene_linear_to_color_picker_space(from_but, tmp);
192   }
193   ui_rgb_to_color_picker_compat_v(tmp, hsv);
194 
195   /* this updates button strings,
196    * is hackish... but button pointers are on stack of caller function */
197   LISTBASE_FOREACH (uiBut *, bt, &block->buttons) {
198     if (bt->custom_data != cpicker) {
199       continue;
200     }
201 
202     if (bt->rnaprop) {
203       ui_but_v3_set(bt, rgb);
204 
205       /* original button that created the color picker already does undo
206        * push, so disable it on RNA buttons in the color picker block */
207       UI_but_flag_disable(bt, UI_BUT_UNDO);
208     }
209     else if (STREQ(bt->str, "Hex: ")) {
210       float rgb_hex[3];
211       uchar rgb_hex_uchar[3];
212       char col[16];
213 
214       /* Hex code is assumed to be in sRGB space
215        * (coming from other applications, web, etc) */
216       copy_v3_v3(rgb_hex, rgb);
217       if (from_but && !ui_but_is_color_gamma(from_but)) {
218         IMB_colormanagement_scene_linear_to_srgb_v3(rgb_hex);
219         ui_color_picker_rgb_round(rgb_hex);
220       }
221 
222       rgb_float_to_uchar(rgb_hex_uchar, rgb_hex);
223       BLI_snprintf(col, sizeof(col), "%02X%02X%02X", UNPACK3_EX((uint), rgb_hex_uchar, ));
224 
225       strcpy(bt->poin, col);
226     }
227     else if (bt->str[1] == ' ') {
228       if (bt->str[0] == 'R') {
229         ui_but_value_set(bt, rgb[0]);
230       }
231       else if (bt->str[0] == 'G') {
232         ui_but_value_set(bt, rgb[1]);
233       }
234       else if (bt->str[0] == 'B') {
235         ui_but_value_set(bt, rgb[2]);
236       }
237       else if (bt->str[0] == 'H') {
238         ui_but_value_set(bt, hsv[0]);
239       }
240       else if (bt->str[0] == 'S') {
241         ui_but_value_set(bt, hsv[1]);
242       }
243       else if (bt->str[0] == 'V') {
244         ui_but_value_set(bt, hsv[2]);
245       }
246       else if (bt->str[0] == 'L') {
247         ui_but_value_set(bt, hsv[2]);
248       }
249     }
250 
251     ui_but_update(bt);
252   }
253 }
254 
ui_colorpicker_rna_cb(bContext * UNUSED (C),void * bt1,void * UNUSED (arg))255 static void ui_colorpicker_rna_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
256 {
257   uiBut *but = (uiBut *)bt1;
258   uiPopupBlockHandle *popup = but->block->handle;
259   PropertyRNA *prop = but->rnaprop;
260   PointerRNA ptr = but->rnapoin;
261   float rgb[4];
262 
263   if (prop) {
264     RNA_property_float_get_array(&ptr, prop, rgb);
265     ui_update_color_picker_buts_rgb(but, but->block, but->custom_data, rgb);
266   }
267 
268   if (popup) {
269     popup->menuretval = UI_RETURN_UPDATE;
270   }
271 }
272 
ui_color_wheel_rna_cb(bContext * UNUSED (C),void * bt1,void * UNUSED (arg))273 static void ui_color_wheel_rna_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
274 {
275   uiBut *but = (uiBut *)bt1;
276   uiPopupBlockHandle *popup = but->block->handle;
277   float rgb[3];
278   ColorPicker *cpicker = but->custom_data;
279   float *hsv = cpicker->color_data;
280 
281   ui_color_picker_to_rgb_v(hsv, rgb);
282 
283   /* hsv is saved in perceptually linear space so convert back */
284   ui_color_picker_to_scene_linear_space(but, rgb);
285 
286   ui_update_color_picker_buts_rgb(but, but->block, cpicker, rgb);
287 
288   if (popup) {
289     popup->menuretval = UI_RETURN_UPDATE;
290   }
291 }
292 
ui_colorpicker_hex_rna_cb(bContext * UNUSED (C),void * bt1,void * hexcl)293 static void ui_colorpicker_hex_rna_cb(bContext *UNUSED(C), void *bt1, void *hexcl)
294 {
295   uiBut *but = (uiBut *)bt1;
296   uiPopupBlockHandle *popup = but->block->handle;
297   ColorPicker *cpicker = but->custom_data;
298   char *hexcol = (char *)hexcl;
299   float rgb[3];
300 
301   hex_to_rgb(hexcol, rgb, rgb + 1, rgb + 2);
302 
303   /* Hex code is assumed to be in sRGB space (coming from other applications, web, etc) */
304   if (!ui_but_is_color_gamma(but)) {
305     IMB_colormanagement_srgb_to_scene_linear_v3(rgb);
306     ui_color_picker_rgb_round(rgb);
307   }
308 
309   ui_update_color_picker_buts_rgb(but, but->block, cpicker, rgb);
310 
311   if (popup) {
312     popup->menuretval = UI_RETURN_UPDATE;
313   }
314 }
315 
ui_popup_close_cb(bContext * UNUSED (C),void * bt1,void * UNUSED (arg))316 static void ui_popup_close_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
317 {
318   uiBut *but = (uiBut *)bt1;
319   uiPopupBlockHandle *popup = but->block->handle;
320 
321   if (popup) {
322     ColorPicker *cpicker = but->custom_data;
323     BLI_assert(cpicker->is_init);
324     popup->menuretval = (equals_v3v3(cpicker->color_data, cpicker->color_data_init) ?
325                              UI_RETURN_CANCEL :
326                              UI_RETURN_OK);
327   }
328 }
329 
ui_colorpicker_hide_reveal(uiBlock * block,enum ePickerType colormode)330 static void ui_colorpicker_hide_reveal(uiBlock *block, enum ePickerType colormode)
331 {
332   /* tag buttons */
333   LISTBASE_FOREACH (uiBut *, bt, &block->buttons) {
334     if ((bt->func == ui_colorpicker_rna_cb) && (bt->type == UI_BTYPE_NUM_SLIDER) &&
335         (bt->rnaindex != 3)) {
336       /* RGB sliders (color circle and alpha are always shown) */
337       SET_FLAG_FROM_TEST(bt->flag, (colormode != PICKER_TYPE_RGB), UI_HIDDEN);
338     }
339     else if (bt->func == ui_color_wheel_rna_cb) {
340       /* HSV sliders */
341       SET_FLAG_FROM_TEST(bt->flag, (colormode != PICKER_TYPE_HSV), UI_HIDDEN);
342     }
343     else if (bt->func == ui_colorpicker_hex_rna_cb || bt->type == UI_BTYPE_LABEL) {
344       /* HEX input or gamma correction status label */
345       SET_FLAG_FROM_TEST(bt->flag, (colormode != PICKER_TYPE_HEX), UI_HIDDEN);
346     }
347   }
348 }
349 
ui_colorpicker_create_mode_cb(bContext * UNUSED (C),void * bt1,void * UNUSED (arg))350 static void ui_colorpicker_create_mode_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg))
351 {
352   uiBut *bt = bt1;
353   const short colormode = ui_but_value_get(bt);
354   ui_colorpicker_hide_reveal(bt->block, colormode);
355 }
356 
357 #define PICKER_H (7.5f * U.widget_unit)
358 #define PICKER_W (7.5f * U.widget_unit)
359 #define PICKER_SPACE (0.3f * U.widget_unit)
360 #define PICKER_BAR (0.7f * U.widget_unit)
361 
362 #define PICKER_TOTAL_W (PICKER_W + PICKER_SPACE + PICKER_BAR)
363 
ui_colorpicker_circle(uiBlock * block,PointerRNA * ptr,PropertyRNA * prop,ColorPicker * cpicker)364 static void ui_colorpicker_circle(uiBlock *block,
365                                   PointerRNA *ptr,
366                                   PropertyRNA *prop,
367                                   ColorPicker *cpicker)
368 {
369   uiBut *bt;
370   uiButHSVCube *hsv_but;
371 
372   /* HS circle */
373   bt = uiDefButR_prop(block,
374                       UI_BTYPE_HSVCIRCLE,
375                       0,
376                       "",
377                       0,
378                       0,
379                       PICKER_H,
380                       PICKER_W,
381                       ptr,
382                       prop,
383                       -1,
384                       0.0,
385                       0.0,
386                       0.0,
387                       0,
388                       TIP_("Color"));
389   UI_but_func_set(bt, ui_colorpicker_rna_cb, bt, NULL);
390   bt->custom_data = cpicker;
391 
392   /* value */
393   if (U.color_picker_type == USER_CP_CIRCLE_HSL) {
394     hsv_but = (uiButHSVCube *)uiDefButR_prop(block,
395                                              UI_BTYPE_HSVCUBE,
396                                              0,
397                                              "",
398                                              PICKER_W + PICKER_SPACE,
399                                              0,
400                                              PICKER_BAR,
401                                              PICKER_H,
402                                              ptr,
403                                              prop,
404                                              -1,
405                                              0.0,
406                                              0.0,
407                                              0,
408                                              0,
409                                              "Lightness");
410     hsv_but->gradient_type = UI_GRAD_L_ALT;
411     UI_but_func_set(&hsv_but->but, ui_colorpicker_rna_cb, &hsv_but->but, NULL);
412   }
413   else {
414     hsv_but = (uiButHSVCube *)uiDefButR_prop(block,
415                                              UI_BTYPE_HSVCUBE,
416                                              0,
417                                              "",
418                                              PICKER_W + PICKER_SPACE,
419                                              0,
420                                              PICKER_BAR,
421                                              PICKER_H,
422                                              ptr,
423                                              prop,
424                                              -1,
425                                              0.0,
426                                              0.0,
427                                              0,
428                                              0,
429                                              TIP_("Value"));
430     hsv_but->gradient_type = UI_GRAD_V_ALT;
431     UI_but_func_set(&hsv_but->but, ui_colorpicker_rna_cb, &hsv_but->but, NULL);
432   }
433   hsv_but->but.custom_data = cpicker;
434 }
435 
ui_colorpicker_square(uiBlock * block,PointerRNA * ptr,PropertyRNA * prop,eButGradientType type,ColorPicker * cpicker)436 static void ui_colorpicker_square(uiBlock *block,
437                                   PointerRNA *ptr,
438                                   PropertyRNA *prop,
439                                   eButGradientType type,
440                                   ColorPicker *cpicker)
441 {
442   uiButHSVCube *hsv_but;
443 
444   BLI_assert(type <= UI_GRAD_HS);
445 
446   /* HS square */
447   hsv_but = (uiButHSVCube *)uiDefButR_prop(block,
448                                            UI_BTYPE_HSVCUBE,
449                                            0,
450                                            "",
451                                            0,
452                                            PICKER_BAR + PICKER_SPACE,
453                                            PICKER_TOTAL_W,
454                                            PICKER_H,
455                                            ptr,
456                                            prop,
457                                            -1,
458                                            0.0,
459                                            0.0,
460                                            0,
461                                            0,
462                                            TIP_("Color"));
463   hsv_but->gradient_type = type;
464   UI_but_func_set(&hsv_but->but, ui_colorpicker_rna_cb, &hsv_but->but, NULL);
465   hsv_but->but.custom_data = cpicker;
466 
467   /* value */
468   hsv_but = (uiButHSVCube *)uiDefButR_prop(block,
469                                            UI_BTYPE_HSVCUBE,
470                                            0,
471                                            "",
472                                            0,
473                                            0,
474                                            PICKER_TOTAL_W,
475                                            PICKER_BAR,
476                                            ptr,
477                                            prop,
478                                            -1,
479                                            0.0,
480                                            0.0,
481                                            0,
482                                            0,
483                                            TIP_("Value"));
484   hsv_but->gradient_type = type + 3;
485   UI_but_func_set(&hsv_but->but, ui_colorpicker_rna_cb, &hsv_but->but, NULL);
486   hsv_but->but.custom_data = cpicker;
487 }
488 
489 /* a HS circle, V slider, rgb/hsv/hex sliders */
ui_block_colorpicker(uiBlock * block,uiBut * from_but,float rgba[4],bool show_picker)490 static void ui_block_colorpicker(uiBlock *block, uiBut *from_but, float rgba[4], bool show_picker)
491 {
492   /* ePickerType */
493   static char colormode = 1;
494   uiBut *bt;
495   int width, butwidth;
496   static char hexcol[128];
497   float softmin, softmax, hardmin, hardmax, step, precision;
498   int yco;
499   ColorPicker *cpicker = ui_block_colorpicker_create(block);
500   float *hsv = cpicker->color_data;
501   PointerRNA *ptr = &from_but->rnapoin;
502   PropertyRNA *prop = from_but->rnaprop;
503 
504   width = PICKER_TOTAL_W;
505   butwidth = width - 1.5f * UI_UNIT_X;
506 
507   /* sneaky way to check for alpha */
508   rgba[3] = FLT_MAX;
509 
510   RNA_property_float_ui_range(ptr, prop, &softmin, &softmax, &step, &precision);
511   RNA_property_float_range(ptr, prop, &hardmin, &hardmax);
512   RNA_property_float_get_array(ptr, prop, rgba);
513 
514   float rgb_perceptual[3];
515   copy_v3_v3(rgb_perceptual, rgba);
516   ui_scene_linear_to_color_picker_space(from_but, rgb_perceptual);
517   ui_rgb_to_color_picker_v(rgb_perceptual, hsv);
518   if (cpicker->is_init == false) {
519     copy_v3_v3(cpicker->color_data_init, cpicker->color_data);
520     cpicker->is_init = true;
521   }
522 
523   /* when the softmax isn't defined in the RNA,
524    * using very large numbers causes sRGB/linear round trip to fail. */
525   if (softmax == FLT_MAX) {
526     softmax = 1.0f;
527   }
528 
529   switch (U.color_picker_type) {
530     case USER_CP_SQUARE_SV:
531       ui_colorpicker_square(block, ptr, prop, UI_GRAD_SV, cpicker);
532       break;
533     case USER_CP_SQUARE_HS:
534       ui_colorpicker_square(block, ptr, prop, UI_GRAD_HS, cpicker);
535       break;
536     case USER_CP_SQUARE_HV:
537       ui_colorpicker_square(block, ptr, prop, UI_GRAD_HV, cpicker);
538       break;
539 
540     /* user default */
541     case USER_CP_CIRCLE_HSV:
542     case USER_CP_CIRCLE_HSL:
543     default:
544       ui_colorpicker_circle(block, ptr, prop, cpicker);
545       break;
546   }
547 
548   /* mode */
549   yco = -1.5f * UI_UNIT_Y;
550   UI_block_align_begin(block);
551   bt = uiDefButC(block,
552                  UI_BTYPE_ROW,
553                  0,
554                  IFACE_("RGB"),
555                  0,
556                  yco,
557                  width / 3,
558                  UI_UNIT_Y,
559                  &colormode,
560                  0.0,
561                  (float)PICKER_TYPE_RGB,
562                  0,
563                  0,
564                  "");
565   UI_but_flag_disable(bt, UI_BUT_UNDO);
566   UI_but_func_set(bt, ui_colorpicker_create_mode_cb, bt, NULL);
567   bt->custom_data = cpicker;
568   bt = uiDefButC(block,
569                  UI_BTYPE_ROW,
570                  0,
571                  IFACE_((U.color_picker_type == USER_CP_CIRCLE_HSL) ? "HSL" : "HSV"),
572                  width / 3,
573                  yco,
574                  width / 3,
575                  UI_UNIT_Y,
576                  &colormode,
577                  0.0,
578                  PICKER_TYPE_HSV,
579                  0,
580                  0,
581                  "");
582   UI_but_flag_disable(bt, UI_BUT_UNDO);
583   UI_but_func_set(bt, ui_colorpicker_create_mode_cb, bt, NULL);
584   bt->custom_data = cpicker;
585   bt = uiDefButC(block,
586                  UI_BTYPE_ROW,
587                  0,
588                  IFACE_("Hex"),
589                  2 * width / 3,
590                  yco,
591                  width / 3,
592                  UI_UNIT_Y,
593                  &colormode,
594                  0.0,
595                  PICKER_TYPE_HEX,
596                  0,
597                  0,
598                  "");
599   UI_but_flag_disable(bt, UI_BUT_UNDO);
600   UI_but_func_set(bt, ui_colorpicker_create_mode_cb, bt, NULL);
601   bt->custom_data = cpicker;
602   UI_block_align_end(block);
603 
604   yco = -3.0f * UI_UNIT_Y;
605   if (show_picker) {
606     bt = uiDefIconButO(block,
607                        UI_BTYPE_BUT,
608                        "UI_OT_eyedropper_color",
609                        WM_OP_INVOKE_DEFAULT,
610                        ICON_EYEDROPPER,
611                        butwidth + 10,
612                        yco,
613                        UI_UNIT_X,
614                        UI_UNIT_Y,
615                        NULL);
616     UI_but_flag_disable(bt, UI_BUT_UNDO);
617     UI_but_drawflag_disable(bt, UI_BUT_ICON_LEFT);
618     UI_but_func_set(bt, ui_popup_close_cb, bt, NULL);
619     bt->custom_data = cpicker;
620   }
621 
622   /* Note: don't disable UI_BUT_UNDO for RGBA values, since these don't add undo steps. */
623 
624   /* RGB values */
625   UI_block_align_begin(block);
626   bt = uiDefButR_prop(block,
627                       UI_BTYPE_NUM_SLIDER,
628                       0,
629                       IFACE_("R:"),
630                       0,
631                       yco,
632                       butwidth,
633                       UI_UNIT_Y,
634                       ptr,
635                       prop,
636                       0,
637                       0.0,
638                       0.0,
639                       0,
640                       3,
641                       TIP_("Red"));
642   UI_but_func_set(bt, ui_colorpicker_rna_cb, bt, NULL);
643   bt->custom_data = cpicker;
644   bt = uiDefButR_prop(block,
645                       UI_BTYPE_NUM_SLIDER,
646                       0,
647                       IFACE_("G:"),
648                       0,
649                       yco -= UI_UNIT_Y,
650                       butwidth,
651                       UI_UNIT_Y,
652                       ptr,
653                       prop,
654                       1,
655                       0.0,
656                       0.0,
657                       0,
658                       3,
659                       TIP_("Green"));
660   UI_but_func_set(bt, ui_colorpicker_rna_cb, bt, NULL);
661   bt->custom_data = cpicker;
662   bt = uiDefButR_prop(block,
663                       UI_BTYPE_NUM_SLIDER,
664                       0,
665                       IFACE_("B:"),
666                       0,
667                       yco -= UI_UNIT_Y,
668                       butwidth,
669                       UI_UNIT_Y,
670                       ptr,
671                       prop,
672                       2,
673                       0.0,
674                       0.0,
675                       0,
676                       3,
677                       TIP_("Blue"));
678   UI_but_func_set(bt, ui_colorpicker_rna_cb, bt, NULL);
679   bt->custom_data = cpicker;
680 
681   /* Could use:
682    * uiItemFullR(col, ptr, prop, -1, 0, UI_ITEM_R_EXPAND | UI_ITEM_R_SLIDER, "", ICON_NONE);
683    * but need to use UI_but_func_set for updating other fake buttons */
684 
685   /* HSV values */
686   yco = -3.0f * UI_UNIT_Y;
687   UI_block_align_begin(block);
688   bt = uiDefButF(block,
689                  UI_BTYPE_NUM_SLIDER,
690                  0,
691                  IFACE_("H:"),
692                  0,
693                  yco,
694                  butwidth,
695                  UI_UNIT_Y,
696                  hsv,
697                  0.0,
698                  1.0,
699                  10,
700                  3,
701                  TIP_("Hue"));
702   UI_but_flag_disable(bt, UI_BUT_UNDO);
703   UI_but_func_set(bt, ui_color_wheel_rna_cb, bt, hsv);
704   bt->custom_data = cpicker;
705   bt = uiDefButF(block,
706                  UI_BTYPE_NUM_SLIDER,
707                  0,
708                  IFACE_("S:"),
709                  0,
710                  yco -= UI_UNIT_Y,
711                  butwidth,
712                  UI_UNIT_Y,
713                  hsv + 1,
714                  0.0,
715                  1.0,
716                  10,
717                  3,
718                  TIP_("Saturation"));
719   UI_but_flag_disable(bt, UI_BUT_UNDO);
720   UI_but_func_set(bt, ui_color_wheel_rna_cb, bt, hsv);
721   bt->custom_data = cpicker;
722   if (U.color_picker_type == USER_CP_CIRCLE_HSL) {
723     bt = uiDefButF(block,
724                    UI_BTYPE_NUM_SLIDER,
725                    0,
726                    IFACE_("L:"),
727                    0,
728                    yco -= UI_UNIT_Y,
729                    butwidth,
730                    UI_UNIT_Y,
731                    hsv + 2,
732                    0.0,
733                    1.0,
734                    10,
735                    3,
736                    TIP_("Lightness"));
737   }
738   else {
739     bt = uiDefButF(block,
740                    UI_BTYPE_NUM_SLIDER,
741                    0,
742                    IFACE_("V:"),
743                    0,
744                    yco -= UI_UNIT_Y,
745                    butwidth,
746                    UI_UNIT_Y,
747                    hsv + 2,
748                    0.0,
749                    softmax,
750                    10,
751                    3,
752                    TIP_("Value"));
753   }
754   UI_but_flag_disable(bt, UI_BUT_UNDO);
755 
756   bt->hardmax = hardmax; /* not common but rgb  may be over 1.0 */
757   UI_but_func_set(bt, ui_color_wheel_rna_cb, bt, hsv);
758   bt->custom_data = cpicker;
759 
760   UI_block_align_end(block);
761 
762   if (rgba[3] != FLT_MAX) {
763     bt = uiDefButR_prop(block,
764                         UI_BTYPE_NUM_SLIDER,
765                         0,
766                         IFACE_("A: "),
767                         0,
768                         yco -= UI_UNIT_Y,
769                         butwidth,
770                         UI_UNIT_Y,
771                         ptr,
772                         prop,
773                         3,
774                         0.0,
775                         0.0,
776                         0,
777                         3,
778                         TIP_("Alpha"));
779     UI_but_func_set(bt, ui_colorpicker_rna_cb, bt, NULL);
780     bt->custom_data = cpicker;
781   }
782   else {
783     rgba[3] = 1.0f;
784   }
785 
786   /* Hex color is in sRGB space. */
787   float rgb_hex[3];
788   uchar rgb_hex_uchar[3];
789 
790   copy_v3_v3(rgb_hex, rgba);
791 
792   if (!ui_but_is_color_gamma(from_but)) {
793     IMB_colormanagement_scene_linear_to_srgb_v3(rgb_hex);
794     ui_color_picker_rgb_round(rgb_hex);
795   }
796 
797   rgb_float_to_uchar(rgb_hex_uchar, rgb_hex);
798   BLI_snprintf(hexcol, sizeof(hexcol), "%02X%02X%02X", UNPACK3_EX((uint), rgb_hex_uchar, ));
799 
800   yco = -3.0f * UI_UNIT_Y;
801   bt = uiDefBut(block,
802                 UI_BTYPE_TEXT,
803                 0,
804                 IFACE_("Hex: "),
805                 0,
806                 yco,
807                 butwidth,
808                 UI_UNIT_Y,
809                 hexcol,
810                 0,
811                 8,
812                 0,
813                 0,
814                 TIP_("Hex triplet for color (#RRGGBB)"));
815   UI_but_flag_disable(bt, UI_BUT_UNDO);
816   UI_but_func_set(bt, ui_colorpicker_hex_rna_cb, bt, hexcol);
817   bt->custom_data = cpicker;
818   uiDefBut(block,
819            UI_BTYPE_LABEL,
820            0,
821            IFACE_("(Gamma Corrected)"),
822            0,
823            yco - UI_UNIT_Y,
824            butwidth,
825            UI_UNIT_Y,
826            NULL,
827            0.0,
828            0.0,
829            0,
830            0,
831            "");
832 
833   ui_colorpicker_hide_reveal(block, colormode);
834 }
835 
ui_colorpicker_small_wheel_cb(const bContext * UNUSED (C),uiBlock * block,const wmEvent * event)836 static int ui_colorpicker_small_wheel_cb(const bContext *UNUSED(C),
837                                          uiBlock *block,
838                                          const wmEvent *event)
839 {
840   float add = 0.0f;
841 
842   if (event->type == WHEELUPMOUSE) {
843     add = 0.05f;
844   }
845   else if (event->type == WHEELDOWNMOUSE) {
846     add = -0.05f;
847   }
848 
849   if (add != 0.0f) {
850     LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
851       if (but->type == UI_BTYPE_HSVCUBE && but->active == NULL) {
852         uiPopupBlockHandle *popup = block->handle;
853         float rgb[3];
854         ColorPicker *cpicker = but->custom_data;
855         float *hsv = cpicker->color_data;
856 
857         ui_but_v3_get(but, rgb);
858         ui_scene_linear_to_color_picker_space(but, rgb);
859         ui_rgb_to_color_picker_compat_v(rgb, hsv);
860 
861         hsv[2] = clamp_f(hsv[2] + add, 0.0f, 1.0f);
862 
863         ui_color_picker_to_rgb_v(hsv, rgb);
864         ui_color_picker_to_scene_linear_space(but, rgb);
865         ui_but_v3_set(but, rgb);
866 
867         ui_update_color_picker_buts_rgb(but, block, cpicker, rgb);
868         if (popup) {
869           popup->menuretval = UI_RETURN_UPDATE;
870         }
871 
872         return 1;
873       }
874     }
875   }
876   return 0;
877 }
878 
ui_block_func_COLOR(bContext * C,uiPopupBlockHandle * handle,void * arg_but)879 uiBlock *ui_block_func_COLOR(bContext *C, uiPopupBlockHandle *handle, void *arg_but)
880 {
881   uiBut *but = arg_but;
882   uiBlock *block;
883   bool show_picker = true;
884 
885   block = UI_block_begin(C, handle->region, __func__, UI_EMBOSS);
886 
887   if (ui_but_is_color_gamma(but)) {
888     block->is_color_gamma_picker = true;
889   }
890 
891   if (but->block) {
892     /* if color block is invoked from a popup we wouldn't be able to set color properly
893      * this is because color picker will close popups first and then will try to figure
894      * out active button RNA, and of course it'll fail
895      */
896     show_picker = (but->block->flag & UI_BLOCK_POPUP) == 0;
897   }
898 
899   copy_v3_v3(handle->retvec, but->editvec);
900 
901   ui_block_colorpicker(block, but, handle->retvec, show_picker);
902 
903   block->flag = UI_BLOCK_LOOP | UI_BLOCK_KEEP_OPEN | UI_BLOCK_OUT_1 | UI_BLOCK_MOVEMOUSE_QUIT;
904   UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
905   UI_block_bounds_set_normal(block, 0.5 * UI_UNIT_X);
906 
907   block->block_event_func = ui_colorpicker_small_wheel_cb;
908 
909   /* and lets go */
910   block->direction = UI_DIR_UP;
911 
912   return block;
913 }
914 
ui_block_colorpicker_create(struct uiBlock * block)915 ColorPicker *ui_block_colorpicker_create(struct uiBlock *block)
916 {
917   ColorPicker *cpicker = MEM_callocN(sizeof(ColorPicker), "color_picker");
918   BLI_addhead(&block->color_pickers.list, cpicker);
919 
920   return cpicker;
921 }
922 
923 /** \} */
924