47 const float len = dt_fast_hypotf(dx, dy);
58 border[0] = centre[0] + radius * dx;
59 border[1] = centre[1] + radius * dy;
65 const float length = dt_fast_hypotf(dx, dy);
72 const float l = 1.0f / length;
74 float along = -radius_rate * l;
76 const float across = sqrtf(1.0f - along * along);
77 const float tx = dx * l;
78 const float ty = dy * l;
79 out[0] = centre[0] + radius * (along * tx + across * ty);
80 out[1] = centre[1] + radius * (along * ty - across * tx);
96 const gboolean default_clockwise)
98 const float a1 = atan2f(from[1] - centre[1], from[0] - centre[0]);
99 const float a2 = atan2f(to[1] - centre[1], to[0] - centre[0]);
100 float sweep_cw = a2 - a1;
101 if(sweep_cw < 0.0f) sweep_cw += 2.0f *
M_PI;
103 const float tie = 0.05f;
104 if(default_clockwise && sweep_cw >
M_PI + tie)
return FALSE;
105 if(!default_clockwise && sweep_cw <
M_PI - tie)
return TRUE;
106 return default_clockwise;
190#define OUTLINE_BLOCK 8
259#define OUTLINE_REPEAT_MIN_WALK 4.0f
280 memset(b, 0,
sizeof(*b));
308 return ((
unsigned)cx * 73856093u) ^ ((unsigned)cy * 19349663u);
317 while(
size < 2u * (
unsigned)
n)
size <<= 1;
324 b->cells.head = NULL;
325 b->cells.next = NULL;
328 memset(b->cells.head, 0xff, (
size_t)
size *
sizeof(
int));
329 b->cells.mask =
size - 1;
330 for(
int i = 0;
i <
n;
i++)
332 const int cx = (int)floorf(b->border_h[2 *
i]);
333 const int cy = (int)floorf(b->border_h[2 *
i + 1]);
335 b->cells.next[
i] = b->cells.head[h];
336 b->cells.head[h] =
i;
344 const float by,
const float walk_limit)
346 for(
int k = b->cells.head[h];
k >= 0;
k = b->cells.next[
k])
348 if(b->walk[
k] > walk_limit)
continue;
349 const float ex = b->border_h[2 *
k] - bx;
350 const float ey = b->border_h[2 *
k + 1] - by;
351 if(ex * ex + ey * ey <= 0.5625f)
return TRUE;
364 const int cx = (int)floorf(bx);
365 const int cy = (int)floorf(by);
366 for(
int neighbour = 0; neighbour < 9; neighbour++)
368 const unsigned h =
_outline_cell_hash(cx - 1 + neighbour % 3, cy - 1 + neighbour / 3) & b->cells.mask;
377 const float bx,
const float by)
379 if(lo > hi)
return FALSE;
382 for(
int blk = blk_lo; blk <= blk_hi; blk++)
384 const float reach = b->blocks.reach[blk];
385 if(reach <= 0.0f)
continue;
386 if(bx < b->blocks.minx[blk] - reach || bx > b->blocks.maxx[blk] + reach || by < b->blocks.miny[blk] - reach
387 || by > b->blocks.maxy[blk] + reach)
391 b->disc_tests += j1 - j0 + 1;
393 for(
int j = j0; j <= j1; j++)
395 const float ex = b->discs.cx[j] - bx;
396 const float ey = b->discs.cy[j] - by;
397 hit |= (ex * ex + ey * ey < b->discs.rin2[j]);
406 const int gx = CLAMP((
int)((
x -
g->minx) /
g->bucket) + 1, 0,
g->bw - 1);
407 const int gy = CLAMP((
int)((y -
g->miny) /
g->bucket) + 1, 0,
g->bh - 1);
408 return gy *
g->bw + gx;
417 g->bucket = fmaxf(r_max, 16.0f);
420 g->bw = (int)((
bbox[1] -
bbox[0]) /
g->bucket) + 3;
421 g->bh = (int)((
bbox[3] -
bbox[2]) /
g->bucket) + 3;
430 for(
int cell = 0; cell <
g->bw *
g->bh; cell++)
g->bucket_head[cell] = -1;
433 for(
int d = 0;
d < b->discs.count;
d++)
436 if(cell == last_cell)
438 g->run_end[nruns - 1] =
d;
441 g->run_start[nruns] =
d;
442 g->run_end[nruns] =
d;
443 g->run_next[nruns] =
g->bucket_head[cell];
444 g->bucket_head[cell] = nruns;
454 const float bx,
const float by)
457 const int gx = CLAMP((
int)((bx -
g->minx) /
g->bucket) + 1, 0,
g->bw - 1);
458 const int gy = CLAMP((
int)((by -
g->miny) /
g->bucket) + 1, 0,
g->bh - 1);
460 for(
int neighbour = 0; neighbour < 9; neighbour++)
462 const int xx = gx - 1 + neighbour % 3;
463 const int yy = gy - 1 + neighbour / 3;
464 if(xx < 0 || yy < 0 || xx >=
g->bw || yy >=
g->bh)
continue;
466 for(
int r =
g->bucket_head[yy *
g->bw + xx];
r >= 0;
r =
g->run_next[
r])
468 const int start =
g->run_start[
r];
469 const int end =
g->run_end[
r];
481 const float w0 = b->discs.dwalk[
d0];
482 const float w_lo = w0 - b->window;
487 const int m = (l + h) / 2;
488 if(b->discs.dwalk[
m] < w_lo) l =
m + 1;
492 const float w_hi = w0 + b->window;
494 h = b->discs.count - 1;
497 const int m = (l + h + 1) / 2;
498 if(b->discs.dwalk[
m] > w_hi) h =
m - 1;
523 const int to,
const int agreed)
525 if(agreed == 0)
return 0;
527 for(
int j = from; j < to; j++)
529 const gboolean inside
531 if(!inside)
continue;
554 for(
int i = 0;
i <
n;
i++)
556 const float px = points_h[
i * 2];
557 const float py = points_h[
i * 2 + 1];
558 const float bx = border_h[
i * 2];
559 const float by = border_h[
i * 2 + 1];
560 const float r = dt_fast_hypotf(bx - px, by - py);
561 bbox[0] = fminf(
bbox[0], fminf(bx, px));
562 bbox[1] = fmaxf(
bbox[1], fmaxf(bx, px));
563 bbox[2] = fminf(
bbox[2], fminf(by, py));
564 bbox[3] = fmaxf(
bbox[3], fmaxf(by, py));
567 gboolean new_disc = (ndisc == 0);
570 moved = dt_fast_hypotf(px - last_x, py - last_y);
571 new_disc = (moved > 0.5f || fabsf(
r - last_r) > 0.5f);
576 const float rin =
r - b->eps;
577 b->discs.cx[ndisc] = px;
578 b->discs.cy[ndisc] = py;
579 b->discs.rin2[ndisc] = (rin > 0.0f) ? rin * rin : -1.0f;
580 b->discs.dwalk[ndisc] = walked;
584 b->blocks.minx[blk] = px;
585 b->blocks.maxx[blk] = px;
586 b->blocks.miny[blk] = py;
587 b->blocks.maxy[blk] = py;
588 b->blocks.reach[blk] = fmaxf(rin, 0.0f);
592 b->blocks.minx[blk] = fminf(b->blocks.minx[blk], px);
593 b->blocks.maxx[blk] = fmaxf(b->blocks.maxx[blk], px);
594 b->blocks.miny[blk] = fminf(b->blocks.miny[blk], py);
595 b->blocks.maxy[blk] = fmaxf(b->blocks.maxy[blk], py);
596 b->blocks.reach[blk] = fmaxf(b->blocks.reach[blk], rin);
599 r_max = fmaxf(r_max,
r);
604 b->disc_of[
i] = ndisc - 1;
606 b->discs.count = ndisc;
608 b->border_h = border_h;
611 b->window = 2.0f * r_max + 1.0f;
619 int *
const from,
int *
const to)
622 while(
i <
n && !dropped[
i])
i++;
629 while(j <
n && dropped[j]) j++;
630 while(j + 2 <
n && !dropped[j] && (dropped[j + 1] || dropped[j + 2]))
632 while(j <
n && !dropped[j]) j++;
633 while(j <
n && dropped[j]) j++;
658 while(j <
n && dropped[j]) j++;
659 const gboolean kept_before = (
i > 0) && !dropped[
i - 1];
660 const gboolean kept_after = (j <
n) && !dropped[j];
661 if(j -
i <= 2 && kept_before && kept_after)
662 for(
int k =
i;
k < j;
k++) dropped[
k] = 0;
676 if(nskips == 0)
return 0;
703 const float *
const border_h = b->border_h;
706 gboolean last_inside =
FALSE;
707 float last_bx = 0.0f;
708 float last_by = 0.0f;
709 for(
int i = 0;
i <
n;
i++)
711 const float bx = border_h[
i * 2];
712 const float by = border_h[
i * 2 + 1];
713 const gboolean moved = (fabsf(bx - last_bx) > 0.5f || fabsf(by - last_by) > 0.5f);
714 if(last_probe >= 0 &&
i !=
n - 1 && !moved)
continue;
717 if(last_probe >= 0 &&
i > last_probe + 1)
719 const int agreed = (inside == last_inside) ? (
int)inside : -1;
728 last_inside = inside;
736 const int count,
const int header,
740 const int n = count - header;
742 const float *
const points_h = points + 2 * header;
743 const float *
const border_h = border + 2 * header;
753 memset(dropped, 0, (
size_t)
n);
756 for(
int i = 1;
i <
n;
i++)
757 b.walk[
i] = b.walk[
i - 1]
758 + dt_fast_hypotf(border_h[2 *
i] - border_h[2 *
i - 2], border_h[2 *
i + 1] - border_h[2 *
i - 1]);
769 "[masks] boundary pass: %d samples, %d discs, radius %.0f, %ld probes, %ld disc tests, %d dropped;"
770 " prepared in %.1f ms, probed in %.1f ms\n",
771 n, b.discs.count, r_max, b.probes, b.disc_tests, ndropped, 1000.0 * (prepared - start),
const dt_colormatrix_t dt_aligned_pixel_t out
void * dt_alloc_align(size_t size)
Allocate cacheline-aligned memory.
int32_t dt_get_debug_flags(void)
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
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...
The per-shape function table, private to the masks implementation.
#define DT_MASKS_OUTLINE_TILT_MAX
static gboolean _outline_sample_repeats(const _outline_boundary_t *const b, const float bx, const float by, const float walk)
#define OUTLINE_REPEAT_MIN_WALK
static void _outline_window(const _outline_boundary_t *const b, const int d0, int *const lo, int *const hi)
static int _outline_grid_cell(const _outline_disc_grid_t *const g, const float x, const float y)
static void _outline_keep_specks(uint8_t *const dropped, const int n)
static gboolean _outline_grid_build(_outline_boundary_t *const b, const float *const bbox, const float r_max)
static float _outline_discs_from_outline(const float *const points_h, const float *const border_h, const int n, _outline_boundary_t *const b, float *const bbox)
static gboolean _outline_far_contains(_outline_boundary_t *const b, const int lo, const int hi, const float bx, const float by)
void dt_masks_outline_envelope_offset(const float *centre, float dx, float dy, float radius, float radius_rate, float *out)
static gboolean _outline_cell_repeats(const _outline_boundary_t *const b, const unsigned h, const float bx, const float by, const float walk_limit)
int dt_masks_outline_boundary_skips(const float *const points, const float *const border, const int count, const int header, dt_masks_skip_range_t **skips_out)
static void _outline_boundary_free(_outline_boundary_t *const b)
static int _outline_probe_samples(_outline_boundary_t *const b, uint8_t *const dropped, const int n)
static gboolean _outline_sample_inside(_outline_boundary_t *const b, const int i, const float bx, const float by)
static gboolean _outline_cells_build(_outline_boundary_t *const b, const int n)
static int _outline_skips_from_dropped(const uint8_t *const dropped, const int n, const int header, dt_masks_skip_range_t **const skips_out)
static int _outline_settle_span(_outline_boundary_t *const b, uint8_t *const dropped, const int from, const int to, const int agreed)
static gboolean _outline_next_dropped_run(const uint8_t *const dropped, const int n, int *const cursor, int *const from, int *const to)
static gboolean _outline_boundary_alloc(_outline_boundary_t *const b, const int n)
void dt_masks_outline_offset_along(const float *const centre, float dx, float dy, const float radius, float *const border)
gboolean dt_masks_outline_short_way(const float *const centre, const float *const from, const float *const to, const gboolean default_clockwise)
static unsigned _outline_cell_hash(const int cx, const int cy)
static gboolean _outline_discs_contain(_outline_boundary_t *const b, const int lo, const int hi, const float bx, const float by)
The masks vocabulary: the enumerations a caller needs to NAME a shape or an operation,...
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
#define dt_pixelpipe_cache_alloc_align_cache(size, id)
_outline_disc_grid_t grid
One cut in a shape's border outline: while walking the border buffer forward, on reaching index jump_...
static double dt_get_wtime(void)