37#define DT_DRAWLAYER_PICKER_U_MAX 0.70710678f
38#define DT_DRAWLAYER_PICKER_V_MAX 0.81649658f
39#define DT_DRAWLAYER_PICKER_C_MAX 0.81649658f
84 return fminf(fmaxf(
value, 0.0f), 1.0f);
90 return fabsf(a[0] - b[0]) <= 1e-6f && fabsf(a[1] - b[1]) <= 1e-6f && fabsf(a[2] - b[2]) <= 1e-6f;
112 const float r =
m + u * 0.70710678f +
v * 0.40824829f;
113 const float g =
m - u * 0.70710678f +
v * 0.40824829f;
114 const float b =
m -
v * 0.81649658f;
116 if(!isfinite(
r) || !isfinite(
g) || !isfinite(b))
return 1;
117 if(r < 0.0f || r > 1.0f || g < 0.0f || g > 1.0f || b < 0.0f || b > 1.0f)
return 1;
128 const float ku = cosf(hue);
129 const float kv = sinf(hue);
131 ku * 0.70710678f + kv * 0.40824829f,
132 -ku * 0.70710678f + kv * 0.40824829f,
136 if(m <= 0.0f || m >= 1.0f)
return 0.0f;
138 float limit = FLT_MAX;
139 for(
int c = 0; c < 3; c++)
142 limit = fminf(limit, (1.0f -
m) /
k[c]);
143 else if(
k[c] < -1e-6f)
144 limit = fminf(limit,
m / -
k[c]);
147 if(!isfinite(limit) || limit < 0.0f)
return 0.0f;
171 widgets->
picker_m = (display_rgb[0] + display_rgb[1] + display_rgb[2]) / 3.0f;
172 widgets->
picker_u = (display_rgb[0] - display_rgb[1]) * 0.70710678f;
173 widgets->
picker_v = (display_rgb[0] + display_rgb[1] - 2.0f * display_rgb[2]) * 0.40824829f;
209 float *gap,
int *cell_count)
211 const float margin = 0.0f;
214 const float widget_w = gtk_widget_get_allocated_width((
GtkWidget *)widget);
215 const float widget_h = gtk_widget_get_allocated_height((
GtkWidget *)widget);
228 float row_x = 0.0f, row_y = 0.0f, row_w = 0.0f, row_h = 0.0f, gap = 0.0f;
231 if(index < 0 || index >= count)
return FALSE;
233 const float cell_w = fmaxf(8.0f, (row_w - gap * (count - 1)) / (
float)count);
245 const float srgb = (
v <= 0.0031308f) ? (12.92f *
v) : (1.055f * powf(
v, 1.0f / 2.4f) - 0.055f);
246 return (uint8_t)CLAMP((
int)lrintf(255.0f *
_clamp01(srgb)), 0, 255);
256 memset(dst, 0, (
size_t)stride *
height);
257 for(
int py = 0; py <
height; py++)
259 unsigned char *
row = dst + (size_t)py * stride;
260 for(
int px = 0; px <
width; px++)
262 row[4 * px + 1] = 0xff;
263 row[4 * px + 2] = 0xff;
264 row[4 * px + 3] = 0xff;
265 row[4 * px + 0] = 0xff;
269 const float radius = 0.36f * fminf((
float)
width, (
float)
height);
275 .radius = fmaxf(radius, 1.0f),
284 .color = { 0.0f, 0.0f, 0.0f, 1.0f },
285 .display_color = { 0.0f, 0.0f, 0.0f },
292 const float bg[3] = { 1.0f, 1.0f, 1.0f };
294 0.5f * (
float)
width, 0.5f * (
float)
height, 1.0f, bg);
296 for(
int y = 0; y <
height; y++)
298 unsigned char *
row = dst + (size_t)y * stride;
301 const float *pixel = rgba_scratch + 4 * ((size_t)y *
width +
x);
305 row[4 *
x + 3] = 255;
312 float *plane_x,
float *plane_y,
float *plane_w,
float *plane_h)
314 const float width = gtk_widget_get_allocated_width((
GtkWidget *)widget);
315 const float height = gtk_widget_get_allocated_height((
GtkWidget *)widget);
318 const float usable_w = fmaxf(40.0f,
width - 2.0f * margin - gap);
319 const float usable_h = fmaxf(40.0f,
height - 2.0f * margin);
320 const float size = fmaxf(40.0f, fminf(usable_h, 0.5f * usable_w));
325 if(!
IS_NULL_PTR(plane_y)) *plane_y = margin + 0.5f * (usable_h -
size);
339 const float rw,
const float rh,
const float margin)
341 return x >= rx - margin && x <= rx + rw + margin && y >= ry - margin && y <= ry + rh + margin;
379 if(!widgets || !display_rgb)
return FALSE;
395 if(
IS_NULL_PTR(widgets) || !history || !valid)
return;
410 if(
IS_NULL_PTR(widgets) || !history || !valid)
return;
423 if(!widgets || !display_rgb)
return FALSE;
444 float x,
float y,
float display_rgb[3])
446 if(!widgets || !widget || !display_rgb)
return FALSE;
448 float uv_x = 0.0f, uv_y = 0.0f, uv_size = 0.0f;
449 float plane_x = 0.0f, plane_y = 0.0f, plane_w = 0.0f, plane_h = 0.0f;
451 const float hit_margin
464 const float tx = 2.0f *
_clamp01((
x - uv_x) / fmaxf(uv_size, 1.0f)) - 1.0f;
465 const float ty = 1.0f - 2.0f *
_clamp01((y - uv_y) / fmaxf(uv_size, 1.0f));
473 const float tx =
_clamp01((
x - plane_x) / fmaxf(plane_w, 1.0f));
474 const float ty =
_clamp01((y - plane_y) / fmaxf(plane_h, 1.0f));
475 const float new_m = 1.0f - ty;
477 const float new_u = new_chroma * cosf(widgets->
picker_hue);
478 const float new_v = new_chroma * sinf(widgets->
picker_hue);
518 float x,
float y,
float display_rgb[3])
520 if(!widgets || !widget || !display_rgb)
return FALSE;
522 const int width = gtk_widget_get_allocated_width(widget);
523 const int height = gtk_widget_get_allocated_height(widget);
541 double pixels_per_dip)
545 const int width = gtk_widget_get_allocated_width(widget);
546 const int height = gtk_widget_get_allocated_height(widget);
549 const double ppd = (pixels_per_dip > 0.0) ? pixels_per_dip : 1.0;
550 const int width_px =
MAX(1, (
int)ceil(
width * ppd));
551 const int height_px =
MAX(1, (
int)ceil(
height * ppd));
559 widgets->
color_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width_px, height_px);
560 if(cairo_surface_status(widgets->
color_surface) != CAIRO_STATUS_SUCCESS)
565 cairo_surface_set_device_scale(widgets->
color_surface, ppd, ppd);
572 float uv_x = 0.0f, uv_y = 0.0f, uv_size = 0.0f;
573 float plane_x = 0.0f, plane_y = 0.0f, plane_w = 0.0f, plane_h = 0.0f;
578 unsigned char *data = cairo_image_surface_get_data(widgets->
color_surface);
579 const int stride = cairo_image_surface_get_stride(widgets->
color_surface);
580 memset(data, 0, (
size_t)stride * height_px);
582 for(
int py = 0; py < height_px; py++)
584 for(
int px = 0; px < width_px; px++)
586 float rgb[3] = { 0.12f, 0.12f, 0.12f };
587 gboolean paint =
TRUE;
588 const float fx = ((float)px + 0.5f) / ppd;
589 const float fy = ((float)py + 0.5f) / ppd;
591 if(
fx >= uv_x && fx <= uv_x + uv_size && fy >= uv_y && fy <= uv_y + uv_size)
593 const float tx = 2.0f *
_clamp01((
fx - uv_x) / fmaxf(uv_size, 1.0f)) - 1.0f;
594 const float ty = 1.0f - 2.0f *
_clamp01((fy - uv_y) / fmaxf(uv_size, 1.0f));
599 else if(
fx >= plane_x && fx <= plane_x + plane_w && fy >= plane_y && fy <= plane_y + plane_h)
601 const float tx =
_clamp01((
fx - plane_x) / fmaxf(plane_w, 1.0f));
602 const float ty =
_clamp01((fy - plane_y) / fmaxf(plane_h, 1.0f));
603 const float m = 1.0f - ty;
605 const float u = chroma * cosf(widgets->
picker_hue);
606 const float v = chroma * sinf(widgets->
picker_hue);
614 unsigned char *pixel = data + (size_t)py * stride + 4 * px;
617 pixel[0] = (
unsigned char)CLAMP(roundf(255.0f *
_clamp01(
rgb[2])), 0, 255);
618 pixel[1] = (
unsigned char)CLAMP(roundf(255.0f *
_clamp01(
rgb[1])), 0, 255);
619 pixel[2] = (
unsigned char)CLAMP(roundf(255.0f *
_clamp01(
rgb[0])), 0, 255);
624 pixel[0] = pixel[1] = pixel[2] = 0;
633 cairo_set_source_surface(cr, widgets->
color_surface, 0.0, 0.0);
636 const float uv_mark_x
638 const float uv_mark_y
641 const float plane_mark_y = plane_y + (1.0f -
_clamp01(widgets->
picker_m)) * plane_h;
642 const float mark_r = 5.0f;
644 cairo_set_line_width(cr, 2.0);
645 cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
646 cairo_arc(cr, uv_mark_x, uv_mark_y, mark_r + 1.5f, 0.0, 2.0 * G_PI);
648 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
649 cairo_arc(cr, uv_mark_x, uv_mark_y, mark_r, 0.0, 2.0 * G_PI);
652 cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
653 cairo_arc(cr, plane_mark_x, plane_mark_y, mark_r + 1.5f, 0.0, 2.0 * G_PI);
655 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
656 cairo_arc(cr, plane_mark_x, plane_mark_y, mark_r, 0.0, 2.0 * G_PI);
667 const int width = gtk_widget_get_allocated_width(widget);
668 const int height = gtk_widget_get_allocated_height(widget);
675 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.12);
683 const float x = col * cell_w;
684 const float y =
row * cell_h;
689 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
691 cairo_rectangle(cr,
x + gap * 0.5f, y + gap * 0.5f,
MAX(1.0f, cell_w - gap),
MAX(1.0f, cell_h - gap));
699 const float hardness,
700 const float sprinkles,
701 const float sprinkle_size,
702 const float sprinkle_coarseness,
703 const int selected_shape)
707 const float new_opacity =
_clamp01(opacity);
708 const float new_hardness =
_clamp01(hardness);
709 const float new_sprinkles =
_clamp01(sprinkles);
710 const float new_size = fmaxf(1.0f, sprinkle_size);
711 const float new_coarseness =
_clamp01(sprinkle_coarseness);
736 cairo_t *cr,
const double pixels_per_dip)
740 const int width = gtk_widget_get_allocated_width(widget);
741 const int height = gtk_widget_get_allocated_height(widget);
744 const double ppd = (pixels_per_dip > 0.0) ? pixels_per_dip : 1.0;
745 const int width_px =
MAX(1, (
int)ceil(
width * ppd));
746 const int height_px =
MAX(1, (
int)ceil(
height * ppd));
753 widgets->
profile_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width_px, height_px);
754 if(cairo_surface_status(widgets->
profile_surface) != CAIRO_STATUS_SUCCESS)
772 unsigned char *data = cairo_image_surface_get_data(widgets->
profile_surface);
773 const int stride = cairo_image_surface_get_stride(widgets->
profile_surface);
774 memset(data, 0, (
size_t)stride * height_px);
775 int max_cell_w_px = 0;
776 int max_cell_h_px = 0;
778 for(
int i = 0;
i < count;
i++)
780 float cell_x = 0.0f, cell_y = 0.0f, cell_w = 0.0f, cell_h = 0.0f;
782 const int x0 =
MAX(0, (
int)floor(cell_x * ppd));
783 const int y0 =
MAX(0, (
int)floor(cell_y * ppd));
784 const int x1 =
MIN(width_px, (
int)ceil((cell_x + cell_w) * ppd));
785 const int y1 =
MIN(height_px, (
int)ceil((cell_y + cell_h) * ppd));
786 max_cell_w_px =
MAX(max_cell_w_px,
MAX(1, x1 - x0));
787 max_cell_h_px =
MAX(max_cell_h_px,
MAX(1, y1 - y0));
790 unsigned char *cell = g_malloc0((
size_t)max_cell_w_px * max_cell_h_px * 4);
791 float *rgba_scratch = g_malloc((
size_t)max_cell_w_px * max_cell_h_px * 4 *
sizeof(
float));
793 for(
int i = 0;
i < count;
i++)
795 float cell_x = 0.0f, cell_y = 0.0f, cell_w = 0.0f, cell_h = 0.0f;
798 const int x0 =
MAX(0, (
int)floor(cell_x * ppd));
799 const int y0 =
MAX(0, (
int)floor(cell_y * ppd));
800 const int x1 =
MIN(width_px, (
int)ceil((cell_x + cell_w) * ppd));
801 const int y1 =
MIN(height_px, (
int)ceil((cell_y + cell_h) * ppd));
802 const int cell_w_px =
MAX(1, x1 - x0);
803 const int cell_h_px =
MAX(1, y1 - y0);
805 memset(cell, 0, (
size_t)max_cell_w_px * max_cell_h_px * 4);
808 for(
int py = 0; py < cell_h_px; py++)
810 memcpy(data + (
size_t)(y0 + py) * stride + 4 * x0,
811 cell + (
size_t)py * cell_w_px * 4,
812 (
size_t)cell_w_px * 4);
825 for(
int i = 0;
i < count;
i++)
827 float cell_x = 0.0f, cell_y = 0.0f, cell_w = 0.0f, cell_h = 0.0f;
831 cairo_rectangle(cr, cell_x, cell_y, cell_w, cell_h);
832 cairo_set_line_width(cr, selected ? 2.5 : 1.0);
833 if(selected) cairo_set_source_rgb(cr, 0.12, 0.45, 0.85);
834 else cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.18);
841 const float x,
const float y,
int *shape)
843 if(!widgets || !widget)
return FALSE;
847 float cell_x = 0.0f, cell_y = 0.0f, cell_w = 0.0f, cell_h = 0.0f;
849 if(
x >= cell_x && x <= cell_x + cell_w && y >= cell_y && y <= cell_y + cell_h)
853 if(shape) *shape =
i;
@ DT_DRAWLAYER_BRUSH_MODE_PAINT
@ DT_DRAWLAYER_BRUSH_SHAPE_SIGMOIDAL
@ DT_DRAWLAYER_BRUSH_SHAPE_LINEAR
static dt_aligned_pixel_t rgb
static const dt_aligned_pixel_simd_t value
#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_PIXEL_APPLY_DPI(value)
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
struct _GtkWidget GtkWidget
struct dt_gui_gtk_t * gui
Fully resolved input dab descriptor.