39#define DT_DRAWLAYER_PICKER_U_MAX 0.70710678f
40#define DT_DRAWLAYER_PICKER_V_MAX 0.81649658f
41#define DT_DRAWLAYER_PICKER_C_MAX 0.81649658f
86 return fminf(fmaxf(
value, 0.0f), 1.0f);
92 return fabsf(a[0] - b[0]) <= 1e-6f && fabsf(a[1] - b[1]) <= 1e-6f && fabsf(a[2] - b[2]) <= 1e-6f;
114 const float r =
m + u * 0.70710678f +
v * 0.40824829f;
115 const float g =
m - u * 0.70710678f +
v * 0.40824829f;
116 const float b =
m -
v * 0.81649658f;
118 if(!isfinite(
r) || !isfinite(
g) || !isfinite(b))
return 1;
119 if(r < 0.0f || r > 1.0f || g < 0.0f || g > 1.0f || b < 0.0f || b > 1.0f)
return 1;
130 const float ku = cosf(hue);
131 const float kv = sinf(hue);
133 ku * 0.70710678f + kv * 0.40824829f,
134 -ku * 0.70710678f + kv * 0.40824829f,
138 if(m <= 0.0f || m >= 1.0f)
return 0.0f;
140 float limit = FLT_MAX;
141 for(
int c = 0; c < 3; c++)
144 limit = fminf(limit, (1.0f -
m) /
k[c]);
145 else if(
k[c] < -1e-6f)
146 limit = fminf(limit,
m / -
k[c]);
149 if(!isfinite(limit) || limit < 0.0f)
return 0.0f;
173 widgets->
picker_m = (display_rgb[0] + display_rgb[1] + display_rgb[2]) / 3.0f;
174 widgets->
picker_u = (display_rgb[0] - display_rgb[1]) * 0.70710678f;
175 widgets->
picker_v = (display_rgb[0] + display_rgb[1] - 2.0f * display_rgb[2]) * 0.40824829f;
211 float *gap,
int *cell_count)
213 const float margin = 0.0f;
216 const float widget_w = gtk_widget_get_allocated_width((
GtkWidget *)widget);
217 const float widget_h = gtk_widget_get_allocated_height((
GtkWidget *)widget);
230 float row_x = 0.0f, row_y = 0.0f, row_w = 0.0f, row_h = 0.0f, gap = 0.0f;
233 if(index < 0 || index >= count)
return FALSE;
235 const float cell_w = fmaxf(8.0f, (row_w - gap * (count - 1)) / (
float)count);
247 const float srgb = (
v <= 0.0031308f) ? (12.92f *
v) : (1.055f * powf(
v, 1.0f / 2.4f) - 0.055f);
248 return (uint8_t)CLAMP((
int)lrintf(255.0f *
_clamp01(srgb)), 0, 255);
258 memset(dst, 0, (
size_t)stride *
height);
259 for(
int py = 0; py <
height; py++)
261 unsigned char *
row = dst + (size_t)py * stride;
262 for(
int px = 0; px <
width; px++)
264 row[4 * px + 1] = 0xff;
265 row[4 * px + 2] = 0xff;
266 row[4 * px + 3] = 0xff;
267 row[4 * px + 0] = 0xff;
271 const float radius = 0.36f * fminf((
float)
width, (
float)
height);
277 .radius = fmaxf(radius, 1.0f),
286 .color = { 0.0f, 0.0f, 0.0f, 1.0f },
287 .display_color = { 0.0f, 0.0f, 0.0f },
294 const float bg[3] = { 1.0f, 1.0f, 1.0f };
296 0.5f * (
float)
width, 0.5f * (
float)
height, 1.0f, bg);
298 for(
int y = 0; y <
height; y++)
300 unsigned char *
row = dst + (size_t)y * stride;
303 const float *pixel = rgba_scratch + 4 * ((size_t)y *
width +
x);
307 row[4 *
x + 3] = 255;
314 float *plane_x,
float *plane_y,
float *plane_w,
float *plane_h)
316 const float width = gtk_widget_get_allocated_width((
GtkWidget *)widget);
317 const float height = gtk_widget_get_allocated_height((
GtkWidget *)widget);
320 const float usable_w = fmaxf(40.0f,
width - 2.0f * margin - gap);
321 const float usable_h = fmaxf(40.0f,
height - 2.0f * margin);
322 const float size = fmaxf(40.0f, fminf(usable_h, 0.5f * usable_w));
327 if(!
IS_NULL_PTR(plane_y)) *plane_y = margin + 0.5f * (usable_h -
size);
341 const float rw,
const float rh,
const float margin)
343 return x >= rx - margin && x <= rx + rw + margin && y >= ry - margin && y <= ry + rh + margin;
381 if(!widgets || !display_rgb)
return FALSE;
397 if(
IS_NULL_PTR(widgets) || !history || !valid)
return;
412 if(
IS_NULL_PTR(widgets) || !history || !valid)
return;
425 if(!widgets || !display_rgb)
return FALSE;
446 float x,
float y,
float display_rgb[3])
448 if(!widgets || !widget || !display_rgb)
return FALSE;
450 float uv_x = 0.0f, uv_y = 0.0f, uv_size = 0.0f;
451 float plane_x = 0.0f, plane_y = 0.0f, plane_w = 0.0f, plane_h = 0.0f;
453 const float hit_margin
466 const float tx = 2.0f *
_clamp01((
x - uv_x) / fmaxf(uv_size, 1.0f)) - 1.0f;
467 const float ty = 1.0f - 2.0f *
_clamp01((y - uv_y) / fmaxf(uv_size, 1.0f));
475 const float tx =
_clamp01((
x - plane_x) / fmaxf(plane_w, 1.0f));
476 const float ty =
_clamp01((y - plane_y) / fmaxf(plane_h, 1.0f));
477 const float new_m = 1.0f - ty;
479 const float new_u = new_chroma * cosf(widgets->
picker_hue);
480 const float new_v = new_chroma * sinf(widgets->
picker_hue);
520 float x,
float y,
float display_rgb[3])
522 if(!widgets || !widget || !display_rgb)
return FALSE;
524 const int width = gtk_widget_get_allocated_width(widget);
525 const int height = gtk_widget_get_allocated_height(widget);
543 double pixels_per_dip)
547 const int width = gtk_widget_get_allocated_width(widget);
548 const int height = gtk_widget_get_allocated_height(widget);
551 const double ppd = (pixels_per_dip > 0.0) ? pixels_per_dip : 1.0;
552 const int width_px =
MAX(1, (
int)ceil(
width * ppd));
553 const int height_px =
MAX(1, (
int)ceil(
height * ppd));
561 widgets->
color_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width_px, height_px);
562 if(cairo_surface_status(widgets->
color_surface) != CAIRO_STATUS_SUCCESS)
567 cairo_surface_set_device_scale(widgets->
color_surface, ppd, ppd);
574 float uv_x = 0.0f, uv_y = 0.0f, uv_size = 0.0f;
575 float plane_x = 0.0f, plane_y = 0.0f, plane_w = 0.0f, plane_h = 0.0f;
580 unsigned char *data = cairo_image_surface_get_data(widgets->
color_surface);
581 const int stride = cairo_image_surface_get_stride(widgets->
color_surface);
582 memset(data, 0, (
size_t)stride * height_px);
584 for(
int py = 0; py < height_px; py++)
586 for(
int px = 0; px < width_px; px++)
588 float rgb[3] = { 0.12f, 0.12f, 0.12f };
589 gboolean paint =
TRUE;
590 const float fx = ((float)px + 0.5f) / ppd;
591 const float fy = ((float)py + 0.5f) / ppd;
593 if(
fx >= uv_x && fx <= uv_x + uv_size && fy >= uv_y && fy <= uv_y + uv_size)
595 const float tx = 2.0f *
_clamp01((
fx - uv_x) / fmaxf(uv_size, 1.0f)) - 1.0f;
596 const float ty = 1.0f - 2.0f *
_clamp01((fy - uv_y) / fmaxf(uv_size, 1.0f));
601 else if(
fx >= plane_x && fx <= plane_x + plane_w && fy >= plane_y && fy <= plane_y + plane_h)
603 const float tx =
_clamp01((
fx - plane_x) / fmaxf(plane_w, 1.0f));
604 const float ty =
_clamp01((fy - plane_y) / fmaxf(plane_h, 1.0f));
605 const float m = 1.0f - ty;
607 const float u = chroma * cosf(widgets->
picker_hue);
608 const float v = chroma * sinf(widgets->
picker_hue);
616 unsigned char *pixel = data + (size_t)py * stride + 4 * px;
619 pixel[0] = (
unsigned char)CLAMP(roundf(255.0f *
_clamp01(
rgb[2])), 0, 255);
620 pixel[1] = (
unsigned char)CLAMP(roundf(255.0f *
_clamp01(
rgb[1])), 0, 255);
621 pixel[2] = (
unsigned char)CLAMP(roundf(255.0f *
_clamp01(
rgb[0])), 0, 255);
626 pixel[0] = pixel[1] = pixel[2] = 0;
635 cairo_set_source_surface(cr, widgets->
color_surface, 0.0, 0.0);
638 const float uv_mark_x
640 const float uv_mark_y
643 const float plane_mark_y = plane_y + (1.0f -
_clamp01(widgets->
picker_m)) * plane_h;
644 const float mark_r = 5.0f;
646 cairo_set_line_width(cr, 2.0);
647 cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
648 cairo_arc(cr, uv_mark_x, uv_mark_y, mark_r + 1.5f, 0.0, 2.0 * G_PI);
650 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
651 cairo_arc(cr, uv_mark_x, uv_mark_y, mark_r, 0.0, 2.0 * G_PI);
654 cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
655 cairo_arc(cr, plane_mark_x, plane_mark_y, mark_r + 1.5f, 0.0, 2.0 * G_PI);
657 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
658 cairo_arc(cr, plane_mark_x, plane_mark_y, mark_r, 0.0, 2.0 * G_PI);
669 const int width = gtk_widget_get_allocated_width(widget);
670 const int height = gtk_widget_get_allocated_height(widget);
677 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.12);
685 const float x = col * cell_w;
686 const float y =
row * cell_h;
691 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
693 cairo_rectangle(cr,
x + gap * 0.5f, y + gap * 0.5f,
MAX(1.0f, cell_w - gap),
MAX(1.0f, cell_h - gap));
701 const float hardness,
702 const float sprinkles,
703 const float sprinkle_size,
704 const float sprinkle_coarseness,
705 const int selected_shape)
709 const float new_opacity =
_clamp01(opacity);
710 const float new_hardness =
_clamp01(hardness);
711 const float new_sprinkles =
_clamp01(sprinkles);
712 const float new_size = fmaxf(1.0f, sprinkle_size);
713 const float new_coarseness =
_clamp01(sprinkle_coarseness);
738 cairo_t *cr,
const double pixels_per_dip)
742 const int width = gtk_widget_get_allocated_width(widget);
743 const int height = gtk_widget_get_allocated_height(widget);
746 const double ppd = (pixels_per_dip > 0.0) ? pixels_per_dip : 1.0;
747 const int width_px =
MAX(1, (
int)ceil(
width * ppd));
748 const int height_px =
MAX(1, (
int)ceil(
height * ppd));
755 widgets->
profile_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width_px, height_px);
756 if(cairo_surface_status(widgets->
profile_surface) != CAIRO_STATUS_SUCCESS)
774 unsigned char *data = cairo_image_surface_get_data(widgets->
profile_surface);
775 const int stride = cairo_image_surface_get_stride(widgets->
profile_surface);
776 memset(data, 0, (
size_t)stride * height_px);
777 int max_cell_w_px = 0;
778 int max_cell_h_px = 0;
780 for(
int i = 0;
i < count;
i++)
782 float cell_x = 0.0f, cell_y = 0.0f, cell_w = 0.0f, cell_h = 0.0f;
784 const int x0 =
MAX(0, (
int)floor(cell_x * ppd));
785 const int y0 =
MAX(0, (
int)floor(cell_y * ppd));
786 const int x1 =
MIN(width_px, (
int)ceil((cell_x + cell_w) * ppd));
787 const int y1 =
MIN(height_px, (
int)ceil((cell_y + cell_h) * ppd));
788 max_cell_w_px =
MAX(max_cell_w_px,
MAX(1, x1 - x0));
789 max_cell_h_px =
MAX(max_cell_h_px,
MAX(1, y1 - y0));
792 unsigned char *cell = g_malloc0((
size_t)max_cell_w_px * max_cell_h_px * 4);
793 float *rgba_scratch = g_malloc((
size_t)max_cell_w_px * max_cell_h_px * 4 *
sizeof(
float));
795 for(
int i = 0;
i < count;
i++)
797 float cell_x = 0.0f, cell_y = 0.0f, cell_w = 0.0f, cell_h = 0.0f;
800 const int x0 =
MAX(0, (
int)floor(cell_x * ppd));
801 const int y0 =
MAX(0, (
int)floor(cell_y * ppd));
802 const int x1 =
MIN(width_px, (
int)ceil((cell_x + cell_w) * ppd));
803 const int y1 =
MIN(height_px, (
int)ceil((cell_y + cell_h) * ppd));
804 const int cell_w_px =
MAX(1, x1 - x0);
805 const int cell_h_px =
MAX(1, y1 - y0);
807 memset(cell, 0, (
size_t)max_cell_w_px * max_cell_h_px * 4);
810 for(
int py = 0; py < cell_h_px; py++)
812 memcpy(data + (
size_t)(y0 + py) * stride + 4 * x0,
813 cell + (
size_t)py * cell_w_px * 4,
814 (
size_t)cell_w_px * 4);
827 for(
int i = 0;
i < count;
i++)
829 float cell_x = 0.0f, cell_y = 0.0f, cell_w = 0.0f, cell_h = 0.0f;
833 cairo_rectangle(cr, cell_x, cell_y, cell_w, cell_h);
834 cairo_set_line_width(cr,
selected ? 2.5 : 1.0);
835 if(
selected) cairo_set_source_rgb(cr, 0.12, 0.45, 0.85);
836 else cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.18);
843 const float x,
const float y,
int *shape)
845 if(!widgets || !widget)
return FALSE;
849 float cell_x = 0.0f, cell_y = 0.0f, cell_w = 0.0f, cell_h = 0.0f;
851 if(
x >= cell_x && x <= cell_x + cell_w && y >= cell_y && y <= cell_y + cell_h)
855 if(shape) *shape =
i;
@ DT_DRAWLAYER_BRUSH_MODE_PAINT
@ DT_DRAWLAYER_BRUSH_SHAPE_SIGMOIDAL
@ DT_DRAWLAYER_BRUSH_SHAPE_LINEAR
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
static dt_aligned_pixel_t rgb
struct dt_gui_gtk_t * dt_gui_get_global(void)
GHashTable * selected
set of checked row labels, mirrored to conf on every change
gboolean dt_drawlayer_brush_rasterize_dab_rgbaf(const dt_drawlayer_brush_dab_t *dab, float *rgba, const int width, const int height, const float center_x, const float center_y, const float opacity_multiplier, const float background_rgb[3])
Rasterize a single dab preview in linear float RGBA over an opaque background.
Stroke-level path sampling and runtime-state API for drawlayer.
float *const restrict const size_t k
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
static const dt_aligned_pixel_simd_t value
Fully resolved input dab descriptor.