Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
colorchart.c
Go to the documentation of this file.
1/*
2 * This file is part of darktable,
3 * Copyright (C) 2016 johannes hanika.
4 * Copyright (C) 2016-2017 Peter Budai.
5 * Copyright (C) 2016 Roman Lebedev.
6 * Copyright (C) 2016-2017 Tobias Ellinghaus.
7 * Copyright (C) 2018 jothalha.
8 * Copyright (C) 2020, 2023 Aurélien PIERRE.
9 * Copyright (C) 2020 Heiko Bauke.
10 * Copyright (C) 2020-2021 Pascal Obry.
11 * Copyright (C) 2021 Ralf Brown.
12 * Copyright (C) 2022 Martin Bařinka.
13 *
14 * darktable is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation, either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * darktable is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with darktable. If not, see <http://www.gnu.org/licenses/>.
26 */
27
28#include "system/macros.h"
29#include "system/mem_alloc.h"
30#include "system/simd.h"
31#include <glib/gstdio.h>
32#include <lcms2.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36
37#pragma GCC diagnostic ignored "-Wshadow"
38
39#include "colorchart.h"
40
41#define MAX_LINE_LENGTH 512
42
52
53void free_chart(chart_t *chart)
54{
55 if(IS_NULL_PTR(chart)) return;
56 g_list_free_full(chart->f_list, dt_free_gpointer);
57 chart->f_list = NULL;
58 if(chart->d_table) g_hash_table_unref(chart->d_table);
59 if(chart->box_table) g_hash_table_unref(chart->box_table);
60 if(chart->patch_sets) g_hash_table_unref(chart->patch_sets);
61 dt_free(chart);
62}
63
64static char *parse_string(char **c)
65{
66 while(**c == ' ' || **c == '\t') (*c)++;
67 char *result = *c;
68 while(**c != ' ' && **c != '\t' && **c != '\0' && **c != '\n') (*c)++;
69 *(*c)++ = '\0';
70 return result;
71}
72
73static double parse_double(char **c)
74{
75 while(**c == ' ' || **c == '\t') (*c)++;
76 double result = g_ascii_strtod(*c, c);
77 *((*c) - 1) = '\0';
78 return result;
79}
80
81// this is not the code from argyll but a rewrite!
82static int strinc(char *label, size_t buffer_size)
83{
84 size_t label_len = strlen(label);
85 char *c = label + label_len - 1;
86 while(c >= label)
87 {
88 char carry_over = 0;
89 switch(*c)
90 {
91 case 'z':
92 case 'Z':
93 *c -= 25;
94 carry_over = *c;
95 break;
96 case '9':
97 *c = '0';
98 carry_over = '1';
99 break;
100 default:
101 (*c)++;
102 }
103 if(IS_NULL_PTR(carry_over))
104 break;
105 else if(c == label)
106 {
107 if(label_len + 1 >= buffer_size) return 0;
108 memmove(c + 1, c, label_len + 1);
109 *c = carry_over;
110 }
111 c--;
112 }
113 return 1;
114}
115
117{
119 box->color[0] = c0;
120 box->color[1] = c1;
121 box->color[2] = c2;
122
123 dt_aligned_pixel_t Lab = { c0, c1, c2 };
124 dt_aligned_pixel_t XYZ = { c0 * 0.01, c1 * 0.01, c2 * 0.01 };
125
126 switch(color_space)
127 {
128 default:
130 for(int c = 0; c < 3; c++) box->rgb[c] = 0.0;
131 break;
135 dt_XYZ_to_sRGB_clipped(XYZ, box->rgb);
136 break;
137 }
138}
139
140/* box_t embeds 64-aligned members and is allocated with dt_calloc_align; on Windows that
141 * memory must go back through _aligned_free, which dt_free_gpointer (plain g_free) is not. */
142static void _free_box_gpointer(gpointer ptr)
143{
145}
146
147static void free_labels_list(gpointer data)
148{
149 g_list_free_full((GList *)data, dt_free_gpointer);
150}
151
152// In some environments ERROR is already defined, ie: WIN32
153#if defined(ERROR)
154#undef ERROR
155#endif // defined (ERROR)
156
157#define ERROR \
158 { \
159 lineno = __LINE__; \
160 goto error; \
161 }
162// according to cht_format.html from argyll:
163// "The keywords and associated data must be used in the following order: BOXES, BOX_SHRINK, REF_ROTATION,
164// XLIST, YLIST and EXPECTED."
165chart_t *parse_cht(const char *filename)
166{
167 chart_t *result = (chart_t *)calloc(1, sizeof(chart_t));
168 int lineno = 0;
169
170 FILE *fp = g_fopen(filename, "rb");
171 if(IS_NULL_PTR(fp))
172 {
173 fprintf(stderr, "error opening `%s'\n", filename);
174 ERROR;
175 }
176
177 // parser control
178 char line[MAX_LINE_LENGTH] = { 0 };
179 parser_state_t last_block = BLOCK_NONE;
180 int skip_block = 0;
181
182 // data gathered from the CHT file
183 unsigned int n_boxes;
184 result->d_table = g_hash_table_new_full(g_str_hash, g_str_equal, dt_free_gpointer, dt_free_gpointer);
185 result->box_table = g_hash_table_new_full(g_str_hash, g_str_equal, dt_free_gpointer, _free_box_gpointer);
186 result->patch_sets = g_hash_table_new_full(g_str_hash, g_str_equal, dt_free_gpointer, free_labels_list);
187
188 float x_min = FLT_MAX, x_max = FLT_MIN, y_min = FLT_MAX, y_max = FLT_MIN;
189
190 // main loop over the input file
191 while(fgets(line, MAX_LINE_LENGTH, fp))
192 {
193 if(line[0] == '\0' || line[0] == '\n')
194 {
195 skip_block = 0;
196 continue;
197 }
198 if(skip_block) continue;
199
200 // we should be at the start of a block now
201 char *c = line;
202 ssize_t len = strlen(line);
203 char *keyword = parse_string(&c);
204
205 if(!g_strcmp0(keyword, "BOXES") && last_block < BLOCK_BOXES)
206 {
207 last_block = BLOCK_BOXES;
208 if(c - line >= len) ERROR;
209 n_boxes = parse_double(&c);
210
211 // let's have another loop reading from the file.
212 while(fgets(line, MAX_LINE_LENGTH, fp))
213 {
214 if(line[0] == '\0' || line[0] == '\n') break;
215
216 char *c = line;
217 ssize_t len = strlen(line);
218 while(*c == ' ') c++;
219 if(*c == 'F')
220 {
221 float x0, y0, x1, y1, x2, y2, x3, y3;
222 // using sscanf would be nice, but parsing floats does only work with LANG=C
223 // if(sscanf(line, " F _ _ %f %f %f %f %f %f %f %f", &x0, &y0, &x1, &y1, &x2, &y2, &x3, &y3) != 8)
224 // ERROR;
225 c++;
226 while(*c == ' ') c++;
227 if(*c++ != '_') ERROR;
228 while(*c == ' ') c++;
229 if(*c++ != '_') ERROR;
230 while(*c == ' ') c++;
231 if(c - line >= len) ERROR;
232 x0 = parse_double(&c);
233 if(c - line >= len) ERROR;
234 y0 = parse_double(&c);
235 if(c - line >= len) ERROR;
236 x1 = parse_double(&c);
237 if(c - line >= len) ERROR;
238 y1 = parse_double(&c);
239 if(c - line >= len) ERROR;
240 x2 = parse_double(&c);
241 if(c - line >= len) ERROR;
242 y2 = parse_double(&c);
243 if(c - line >= len) ERROR;
244 x3 = parse_double(&c);
245 if(c - line >= len) ERROR;
246 y3 = parse_double(&c);
247
248 x_min = MIN(x_min, x0);
249 x_min = MIN(x_min, x1);
250 x_min = MIN(x_min, x2);
251 x_min = MIN(x_min, x3);
252
253 y_min = MIN(y_min, y0);
254 y_min = MIN(y_min, y1);
255 y_min = MIN(y_min, y2);
256 y_min = MIN(y_min, y3);
257
258 x_max = MAX(x_max, x0);
259 x_max = MAX(x_max, x1);
260 x_max = MAX(x_max, x2);
261 x_max = MAX(x_max, x3);
262
263 y_max = MAX(y_max, y0);
264 y_max = MAX(y_max, y1);
265 y_max = MAX(y_max, y2);
266 y_max = MAX(y_max, y3);
267
268 f_line_t *l = (f_line_t *)malloc(sizeof(f_line_t));
269
270 l->p[0].x = x0;
271 l->p[0].y = y0;
272 l->p[1].x = x1;
273 l->p[1].y = y1;
274 l->p[2].x = x2;
275 l->p[2].y = y2;
276 l->p[3].x = x3;
277 l->p[3].y = y3;
278
279 result->f_list = g_list_append(result->f_list, l);
280 }
281 // these get parsed the same way
282 else if((*c == 'D') || (*c == 'X') || (*c == 'Y'))
283 {
284 char kl, *lxs, *lxe, *lys, *lye;
285 float w, h, xo, yo, xi, yi;
286 kl = *c;
287 *c++ = '\0';
288
289 if(c - line >= len) ERROR;
290 lxs = parse_string(&c);
291 if(c - line >= len) ERROR;
292 lxe = parse_string(&c);
293 if(c - line >= len) ERROR;
294 lys = parse_string(&c);
295 if(c - line >= len) ERROR;
296 lye = parse_string(&c);
297
298 if(c - line >= len) ERROR;
299 w = parse_double(&c);
300 if(c - line >= len) ERROR;
301 h = parse_double(&c);
302 if(c - line >= len) ERROR;
303 xo = parse_double(&c);
304 if(c - line >= len) ERROR;
305 yo = parse_double(&c);
306 if(c - line >= len) ERROR;
307 xi = parse_double(&c);
308 if(c - line >= len) ERROR;
309 yi = parse_double(&c);
310
311 x_min = MIN(x_min, xo);
312 y_min = MIN(y_min, yo);
313
314 int y_steps = 1;
315 size_t lxs_len = strlen(lxs), lxe_len = strlen(lxe), lys_len = strlen(lys), lye_len = strlen(lye);
316 if(lxs_len > lxe_len || lys_len > lye_len) ERROR;
317
318 // make sure there is enough room to add another char in the beginning
319 const size_t x_label_size = lxe_len + 1;
320 const size_t y_label_size = lye_len + 1;
321
322 char *x_label = malloc(x_label_size);
323 char *y_label = malloc(y_label_size);
324
325 char *first_label = NULL, *last_label = NULL;
326 GList *labels = NULL;
327
328 float y = yo;
329 memcpy(y_label, lys, lys_len + 1);
330 while(1)
331 {
332 float x = xo;
333 memcpy(x_label, lxs, lxs_len + 1);
334 while(1)
335 {
336 // build the label of the box
337 char *label;
338 if(!g_strcmp0(x_label, "_"))
339 label = g_strdup(y_label);
340 else if(!g_strcmp0(y_label, "_"))
341 label = g_strdup(x_label);
342 else
343 {
344 if(kl == 'Y')
345 label = g_strconcat(y_label, x_label, NULL);
346 else
347 label = g_strconcat(x_label, y_label, NULL);
348 }
349
350 if(IS_NULL_PTR(first_label)) first_label = label;
351 dt_free(last_label);
352 last_label = label;
353
354 // store it
355 box_t *box = dt_calloc_align(sizeof(box_t)); // box_t is 64-aligned, see #1212
356 box->p.x = x;
357 box->p.y = y;
358 box->w = w;
359 box->h = h;
360 box->color_space = DT_COLORSPACE_NONE; // no color for this box yet
361 if(kl == 'D')
362 g_hash_table_insert(result->d_table, label, box);
363 else
364 g_hash_table_insert(result->box_table, label, box);
365 if(kl == 'X' || kl == 'Y') labels = g_list_append(labels, g_strdup(label));
366
367 // increment in x direction
368 if(!g_strcmp0(x_label, lxe)) break;
369 x += xi;
370 if(!strinc(x_label, x_label_size))
371 {
372 dt_free(y_label);
373 dt_free(x_label);
374 ERROR;
375 }
376 }
377 x_max = MAX(x_max, x + w);
378 // increment in y direction
379 if(!g_strcmp0(y_label, lye)) break;
380 y += yi;
381 y_steps++;
382 if(!strinc(y_label, y_label_size))
383 {
384 dt_free(y_label);
385 dt_free(x_label);
386 ERROR;
387 }
388 }
389 y_max = MAX(y_max, y + h);
390 if((kl == 'X' || kl == 'Y') && first_label && last_label)
391 g_hash_table_insert(result->patch_sets, g_strdup_printf("%s .. %s", first_label, last_label), labels);
392
393 dt_free(last_label);
394 dt_free(y_label);
395 dt_free(x_label);
396 }
397 else
398 ERROR;
399 }
400
401 if(n_boxes != g_hash_table_size(result->d_table) + g_hash_table_size(result->box_table)) ERROR;
402
403 // all the box lines are read and we know the bounding box,
404 // so let's scale all the values to have a bounding box with the longer side having length 1 and start
405 // at (0, 0)
406
407 result->bb_w = x_max - x_min;
408 result->bb_h = y_max - y_min;
409
410#define SCALE_X(x) x = (x - x_min) / result->bb_w
411#define SCALE_Y(y) y = (y - y_min) / result->bb_h
412
413 for(GList *iter = result->f_list; iter; iter = g_list_next(iter))
414 {
415 f_line_t *f = iter->data;
416 for(int i = 0; i < 4; i++)
417 {
418 SCALE_X(f->p[i].x);
419 SCALE_Y(f->p[i].y);
420 }
421 }
422
423 GHashTableIter table_iter;
424 gpointer key, value;
425
426 g_hash_table_iter_init(&table_iter, result->d_table);
427 while(g_hash_table_iter_next(&table_iter, &key, &value))
428 {
429 box_t *box = (box_t *)value;
430 SCALE_X(box->p.x);
431 SCALE_Y(box->p.y);
432 box->w /= result->bb_w;
433 box->h /= result->bb_h;
434 }
435
436 g_hash_table_iter_init(&table_iter, result->box_table);
437 while(g_hash_table_iter_next(&table_iter, &key, &value))
438 {
439 box_t *box = (box_t *)value;
440 SCALE_X(box->p.x);
441 SCALE_Y(box->p.y);
442 box->w /= result->bb_w;
443 box->h /= result->bb_h;
444 }
445
446#undef SCALE_X
447#undef SCALE_Y
448 }
449 else if(!g_strcmp0(keyword, "BOX_SHRINK") && last_block < BLOCK_BOX_SHRINK)
450 {
451 last_block = BLOCK_BOX_SHRINK;
452 if(c - line >= len) ERROR;
453 result->box_shrink = parse_double(&c);
454 }
455 else if(!g_strcmp0(keyword, "REF_ROTATION") && last_block < BLOCK_REF_ROTATION)
456 {
457 last_block = BLOCK_REF_ROTATION;
458 if(c - line >= len) ERROR;
459 result->ref_rotation = parse_double(&c);
460 }
461 else if(!g_strcmp0(keyword, "XLIST") && last_block < BLOCK_XLIST)
462 {
463 last_block = BLOCK_XLIST;
464 // skip until empty line, we don't care about these
465 skip_block = 1;
466 }
467 else if(!g_strcmp0(keyword, "YLIST") && last_block < BLOCK_YLIST)
468 {
469 last_block = BLOCK_YLIST;
470 // skip until empty line, we don't care about these
471 skip_block = 1;
472 }
473 else if(!g_strcmp0(keyword, "EXPECTED") && last_block < BLOCK_EXPECTED)
474 {
475 last_block = BLOCK_EXPECTED;
477 if(c - line >= len) ERROR;
478 char *cs = parse_string(&c);
479 if(c - line >= len) ERROR;
480 unsigned int n_colors = parse_double(&c);
481
482 if(!g_strcmp0(cs, "XYZ"))
484 else if(!g_strcmp0(cs, "LAB"))
486 else
487 ERROR;
488
489 // read and store the numbers.
490 // we use them 1) to draw visual hints on the grid and 2) as a fallback reference set
491
492 // let's have another loop reading from the file.
493 while(fgets(line, MAX_LINE_LENGTH, fp))
494 {
495 if(line[0] == '\0' || line[0] == '\n') break;
496 n_colors--;
497 ssize_t len = strlen(line);
498 char *c = line;
499
500 char *label = parse_string(&c);
501 box_t *box = (box_t *)g_hash_table_lookup(result->box_table, label);
502 if(IS_NULL_PTR(box)) ERROR;
503
504 if(c - line >= len) ERROR;
505 float c0 = parse_double(&c);
506 if(c - line >= len) ERROR;
507 float c1 = parse_double(&c);
508 if(c - line >= len) ERROR;
509 float c2 = parse_double(&c);
511 }
512 if(n_colors != 0) ERROR;
513 }
514 else
515 {
516 fprintf(stderr, "unknown keyword `%s'\n", keyword);
517 ERROR;
518 }
519 }
520
521 fprintf(stderr, "cht `%s' done\n", filename);
522 goto end;
523
524error:
525 fprintf(stderr, "error parsing CHT file, (%s:%d)\n", __FUNCTION__, lineno);
526 // clean up
527 free_chart(result);
528 result = NULL;
529
530end:
531 if(fp) fclose(fp);
532 return result;
533}
534
535int parse_it8(const char *filename, chart_t *chart)
536{
537 int result = 1;
538 cmsHANDLE hIT8 = cmsIT8LoadFromFile(NULL, filename);
539 if(IS_NULL_PTR(hIT8))
540 {
541 fprintf(stderr, "error loading IT8 file `%s'\n", filename);
542 goto error;
543 }
544
545 if(cmsIT8TableCount(hIT8) != 1)
546 {
547 fprintf(stderr, "error with the IT8 file, we only support files with one table at the moment\n");
548 goto error;
549 }
550
552 int column_SAMPLE_ID = -1, column_X = -1, column_Y = -1, column_Z = -1, column_L = -1, column_a = -1,
553 column_b = -1;
554 char **sample_names = NULL;
555 int n_columns = cmsIT8EnumDataFormat(hIT8, &sample_names);
556
557 if(n_columns == -1)
558 {
559 fprintf(stderr, "error with the IT8 file, can't get column types\n");
560 goto error;
561 }
562
563 for(int i = 0; i < n_columns; i++)
564 {
565 if(!g_strcmp0(sample_names[i], "SAMPLE_ID"))
566 column_SAMPLE_ID = i;
567 else if(!g_strcmp0(sample_names[i], "XYZ_X"))
568 column_X = i;
569 else if(!g_strcmp0(sample_names[i], "XYZ_Y"))
570 column_Y = i;
571 else if(!g_strcmp0(sample_names[i], "XYZ_Z"))
572 column_Z = i;
573 else if(!g_strcmp0(sample_names[i], "LAB_L"))
574 column_L = i;
575 else if(!g_strcmp0(sample_names[i], "LAB_A"))
576 column_a = i;
577 else if(!g_strcmp0(sample_names[i], "LAB_B"))
578 column_b = i;
579 }
580
581 if(column_SAMPLE_ID == -1)
582 {
583 fprintf(stderr, "error with the IT8 file, can't find the SAMPLE_ID column\n");
584 goto error;
585 }
586
587 char *columns[3] = { 0 };
588 if(column_X != -1 && column_Y != -1 && column_Z != -1)
589 {
591 columns[0] = "XYZ_X";
592 columns[1] = "XYZ_Y";
593 columns[2] = "XYZ_Z";
594 }
595 else if(column_L != -1 && column_a != -1 && column_b != -1)
596 {
598 columns[0] = "LAB_L";
599 columns[1] = "LAB_A";
600 columns[2] = "LAB_B";
601 }
602 else
603 {
604 fprintf(stderr, "error with the IT8 file, can't find XYZ or Lab columns\n");
605 goto error;
606 }
607
608 GHashTableIter table_iter;
609 gpointer key, value;
610
611 g_hash_table_iter_init(&table_iter, chart->box_table);
612 while(g_hash_table_iter_next(&table_iter, &key, &value))
613 {
614 box_t *box = (box_t *)value;
615
616 if(cmsIT8GetData(hIT8, key, "SAMPLE_ID") == NULL)
617 {
618 fprintf(stderr, "error with the IT8 file, can't find sample `%s'\n", (char *)key);
619 goto error;
620 }
621
622 checker_set_color(box, color_space, cmsIT8GetDataDbl(hIT8, key, columns[0]), cmsIT8GetDataDbl(hIT8, key, columns[1]),
623 cmsIT8GetDataDbl(hIT8, key, columns[2]));
624 }
625
626 fprintf(stderr, "it8 `%s' done\n", filename);
627 goto end;
628
629error:
630 result = 0;
631end:
632 if(hIT8) cmsIT8Free(hIT8);
633 return result;
634}
635
636#undef MAX_LINE_LENGTH
637
638// clang-format off
639// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
640// vim: shiftwidth=2 expandtab tabstop=2 cindent
641// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
642// clang-format on
static void error(char *msg)
Definition ashift_lsd.c:202
static char * parse_string(char **c)
Definition colorchart.c:64
static int strinc(char *label, size_t buffer_size)
Definition colorchart.c:82
static double parse_double(char **c)
Definition colorchart.c:73
chart_t * parse_cht(const char *filename)
Definition colorchart.c:165
void free_chart(chart_t *chart)
Definition colorchart.c:53
static void free_labels_list(gpointer data)
Definition colorchart.c:147
static void _free_box_gpointer(gpointer ptr)
Definition colorchart.c:142
int parse_it8(const char *filename, chart_t *chart)
Definition colorchart.c:535
#define ERROR
Definition colorchart.c:157
void checker_set_color(box_t *box, dt_colorspaces_color_profile_type_t color_space, float c0, float c1, float c2)
Definition colorchart.c:116
#define SCALE_X(x)
#define SCALE_Y(y)
parser_state_t
Definition colorchart.c:43
@ BLOCK_BOX_SHRINK
Definition colorchart.c:46
@ BLOCK_NONE
Definition colorchart.c:44
@ BLOCK_YLIST
Definition colorchart.c:49
@ BLOCK_REF_ROTATION
Definition colorchart.c:47
@ BLOCK_XLIST
Definition colorchart.c:48
@ BLOCK_BOXES
Definition colorchart.c:45
@ BLOCK_EXPECTED
Definition colorchart.c:50
#define MAX_LINE_LENGTH
Definition colorchart.c:41
static const float x
const float f
dt_Lab_to_XYZ(Lab, XYZ)
static dt_aligned_pixel_t XYZ
static dt_aligned_pixel_t Lab
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
static void * dt_calloc_align(size_t size)
dt_alloc_align() followed by a zero fill.
Definition mem_alloc.h:225
static void dt_free_align_ptr(void *mem)
Platform back-end for dt_free_align(). Call dt_free_align() instead: this one takes the pointer by va...
Definition mem_alloc.h:193
static void dt_free_gpointer(gpointer ptr)
g_free() one pointer, with the signature GDestroyNotify wants.
Definition mem_alloc.h:184
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
char * key
dt_colorspaces_color_profile_type_t color_space
Definition mipmap_cache.c:5
dt_colorspaces_color_profile_type_t
@ DT_COLORSPACE_LAB
@ DT_COLORSPACE_NONE
No profile / "take it from the image settings". Never matches a list entry.
@ DT_COLORSPACE_XYZ
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
Definition simd.h:53
static const dt_aligned_pixel_simd_t value
Definition simd.h:144
float h
Definition colorchart.h:41
float w
Definition colorchart.h:41
dt_colorspaces_color_profile_type_t color_space
Definition colorchart.h:43
dt_aligned_pixel_t color
Definition colorchart.h:44
dt_aligned_pixel_t rgb
Definition colorchart.h:45
point_t p
Definition colorchart.h:40
float bb_w
Definition colorchart.h:59
GHashTable * patch_sets
Definition colorchart.h:57
float bb_h
Definition colorchart.h:59
float box_shrink
Definition colorchart.h:61
GHashTable * box_table
Definition colorchart.h:53
GHashTable * d_table
Definition colorchart.h:53
float ref_rotation
Definition colorchart.h:61
GList * f_list
Definition colorchart.h:51
point_t p[4]
Definition colorchart.h:34
size_t y
Definition censorize.c:78
size_t x
Definition censorize.c:78
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29