2 * Image Library -- Color Spaces
4 * (c) 2006 Pavel Charvat <pchar@ucw.cz>
6 * This software may be freely distributed and used according to the terms
7 * of the GNU Lesser General Public License.
12 #include "sherlock/sherlock.h"
14 #include "images/images.h"
15 #include "images/color.h"
16 #include "images/error.h"
17 #include "images/math.h"
21 uns color_space_channels[COLOR_SPACE_MAX] = {
22 [COLOR_SPACE_UNKNOWN] = 0,
23 [COLOR_SPACE_UNKNOWN_1] = 1,
24 [COLOR_SPACE_UNKNOWN_2] = 2,
25 [COLOR_SPACE_UNKNOWN_3] = 3,
26 [COLOR_SPACE_UNKNOWN_4] = 4,
27 [COLOR_SPACE_GRAYSCALE] = 1,
28 [COLOR_SPACE_RGB] = 3,
29 [COLOR_SPACE_XYZ] = 3,
30 [COLOR_SPACE_LAB] = 3,
31 [COLOR_SPACE_YCBCR] = 3,
32 [COLOR_SPACE_CMYK] = 4,
33 [COLOR_SPACE_YCCK] = 4,
36 byte *color_space_name[COLOR_SPACE_MAX] = {
37 [COLOR_SPACE_UNKNOWN] = "Unknown",
38 [COLOR_SPACE_UNKNOWN_1] = "1-channel",
39 [COLOR_SPACE_UNKNOWN_2] = "2-channels",
40 [COLOR_SPACE_UNKNOWN_3] = "3-channels",
41 [COLOR_SPACE_UNKNOWN_4] = "4-channels",
42 [COLOR_SPACE_GRAYSCALE] = "Grayscale",
43 [COLOR_SPACE_RGB] = "RGB",
44 [COLOR_SPACE_XYZ] = "XYZ",
45 [COLOR_SPACE_LAB] = "LAB",
46 [COLOR_SPACE_YCBCR] = "YCbCr",
47 [COLOR_SPACE_CMYK] = "CMYK",
48 [COLOR_SPACE_YCCK] = "YCCK",
52 color_space_id_to_name(uns id)
54 ASSERT(id < COLOR_SPACE_MAX);
55 return color_space_name[id];
59 color_space_name_to_id(byte *name)
61 for (uns i = 1; i < COLOR_SPACE_MAX; i++)
62 if (color_space_name[i] && !strcasecmp(name, color_space_name[i]))
67 struct color color_black = { .color_space = COLOR_SPACE_GRAYSCALE };
68 struct color color_white = { .c = { 255 }, .color_space = COLOR_SPACE_GRAYSCALE };
71 color_get(struct color *color, byte *src, uns src_space)
73 color->color_space = src_space;
74 memcpy(color->c, src, color_space_channels[src_space]);
79 color_put(struct image_context *ctx, struct color *color, byte *dest, uns dest_space)
83 case COLOR_SPACE_GRAYSCALE:
84 switch (color->color_space)
86 case COLOR_SPACE_GRAYSCALE:
87 dest[0] = color->c[0];
90 dest[0] = rgb_to_gray_func(color->c[0], color->c[1], color->c[2]);
95 switch (color->color_space)
97 case COLOR_SPACE_GRAYSCALE:
98 dest[0] = dest[1] = dest[2] = color->c[0];
100 case COLOR_SPACE_RGB:
101 dest[0] = color->c[0];
102 dest[1] = color->c[1];
103 dest[2] = color->c[2];
105 case COLOR_SPACE_CMYK:
107 double rgb[3], cmyk[4];
108 for (uns i = 0; i < 4; i++)
109 cmyk[i] = color->c[i] * (1.0 / 255);
110 cmyk_to_rgb_exact(rgb, cmyk);
111 for (uns i = 0; i < 3; i++)
112 dest[i] = CLAMP(rgb[i] * 255, 0, 255);
117 case COLOR_SPACE_CMYK:
118 switch (color->color_space)
120 case COLOR_SPACE_GRAYSCALE:
121 dest[0] = dest[1] = dest[2] = 0;
122 dest[3] = 255 - color->c[0];
124 case COLOR_SPACE_RGB:
126 double rgb[3], cmyk[4];
127 for (uns i = 0; i < 3; i++)
128 rgb[i] = color->c[i] * (1.0 / 255);
129 rgb_to_cmyk_exact(cmyk, rgb);
130 for (uns i = 0; i < 4; i++)
131 dest[i] = CLAMP(cmyk[i] * 255, 0, 255);
137 if (dest_space != COLOR_SPACE_RGB )
139 /* Try to convert the color via RGB */
141 if (!color_put(ctx, color, rgb.c, COLOR_SPACE_RGB))
143 rgb.color_space = COLOR_SPACE_RGB;
144 return color_put(ctx, &rgb, dest, dest_space);
146 IMAGE_ERROR(ctx, IMAGE_ERROR_INVALID_PIXEL_FORMAT, "Conversion from %s to %s is not supported",
147 color_space_id_to_name(color->color_space), color_space_id_to_name(color->color_space));
152 /********************* IMAGE CONVERSION ROUTINES **********************/
154 struct image_conv_options image_conv_defaults = {
155 .flags = IMAGE_CONV_COPY_ALPHA | IMAGE_CONV_FILL_ALPHA | IMAGE_CONV_APPLY_ALPHA,
156 .background = { .color_space = COLOR_SPACE_GRAYSCALE } };
158 /* Grayscale <-> RGB */
160 #define IMAGE_WALK_PREFIX(x) walk_##x
161 #define IMAGE_WALK_FUNC_NAME image_conv_gray_1_to_rgb_n
162 #define IMAGE_WALK_DOUBLE
163 #define IMAGE_WALK_SEC_COL_STEP 1
164 #define IMAGE_WALK_UNROLL 4
165 #define IMAGE_WALK_DO_STEP do{ walk_pos[0] = walk_pos[1] = walk_pos[2] = walk_sec_pos[0]; }while(0)
166 #include "images/image-walk.h"
168 #define IMAGE_WALK_PREFIX(x) walk_##x
169 #define IMAGE_WALK_FUNC_NAME image_conv_rgb_n_to_gray_1
170 #define IMAGE_WALK_DOUBLE
171 #define IMAGE_WALK_COL_STEP 1
172 #define IMAGE_WALK_UNROLL 2
173 #define IMAGE_WALK_DO_STEP do{ walk_pos[0] = rgb_to_gray_func(walk_sec_pos[0], walk_sec_pos[1], walk_sec_pos[2]); }while(0)
174 #include "images/image-walk.h"
179 pixel_conv_ycbcr_to_rgb(byte *dest, byte *src)
181 /* R = Y + 1.40200 * Cr
182 * G = Y - 0.34414 * Cb - 0.71414 * Cr
183 * B = Y + 1.77200 * Cb */
184 int y = src[0], cb = src[1] - 128, cr = src[2] - 128;
185 dest[0] = CLAMP(y + (91881 * cr) / 0x10000, 0, 255);
186 dest[1] = CLAMP(y - (22553 * cb + 46801 * cr) / 0x10000, 0, 255);
187 dest[2] = CLAMP(y + (116129 * cb) / 0x10000, 0, 255);
190 #define IMAGE_WALK_PREFIX(x) walk_##x
191 #define IMAGE_WALK_FUNC_NAME image_conv_ycbcr_n_to_rgb_n
192 #define IMAGE_WALK_DOUBLE
193 #define IMAGE_WALK_DO_STEP do{ pixel_conv_ycbcr_to_rgb(walk_pos, walk_sec_pos); }while(0)
194 #include "images/image-walk.h"
197 pixel_conv_rgb_to_ycbcr(byte *dest, byte *src)
199 /* Y = 0.29900 * R + 0.58700 * G + 0.11400 * B
200 * Cb = -0.16874 * R - 0.33126 * G + 0.50000 * B + CENTER
201 * Cr = 0.50000 * R - 0.41869 * G - 0.08131 * B + CENTER */
202 uns r = src[0], g = src[1], b = src[2];
203 dest[0] = (19595 * r + 38470 * g + 7471 * b) / 0x10000;
204 dest[1] = (0x800000 + 0x8000 * b - 11058 * r - 21710 * g) / 0x10000;
205 dest[2] = (0x800000 + 0x8000 * r - 27439 * g - 5329 * b) / 0x10000;
208 #define IMAGE_WALK_PREFIX(x) walk_##x
209 #define IMAGE_WALK_FUNC_NAME image_conv_rgb_n_to_ycbcr_n
210 #define IMAGE_WALK_DOUBLE
211 #define IMAGE_WALK_DO_STEP do{ pixel_conv_rgb_to_ycbcr(walk_pos, walk_sec_pos); }while(0)
212 #include "images/image-walk.h"
217 pixel_conv_cmyk_to_rgb(byte *dest, byte *src)
219 uns d = (255 - src[3]) * (0xffffffffU / 255 /255);
220 dest[0] = d * (255 - src[0]) >> 24;
221 dest[1] = d * (255 - src[1]) >> 24;
222 dest[2] = d * (255 - src[2]) >> 24;
225 #define IMAGE_WALK_PREFIX(x) walk_##x
226 #define IMAGE_WALK_FUNC_NAME image_conv_cmyk_4_to_rgb_n
227 #define IMAGE_WALK_DOUBLE
228 #define IMAGE_WALK_SEC_COL_STEP 4
229 #define IMAGE_WALK_DO_STEP do{ pixel_conv_cmyk_to_rgb(walk_pos, walk_sec_pos); }while(0)
230 #include "images/image-walk.h"
233 pixel_conv_rgb_to_cmyk(byte *dest, byte *src)
235 uns k = MAX(src[0], src[1]);
237 uns d = fast_div_u32_u8(0x7fffffffU, k); /* == 0 for zero K */
238 dest[0] = (d * (k - src[0])) >> 23;
239 dest[1] = (d * (k - src[1])) >> 23;
240 dest[2] = (d * (k - src[2])) >> 23;
244 #define IMAGE_WALK_PREFIX(x) walk_##x
245 #define IMAGE_WALK_FUNC_NAME image_conv_rgb_n_to_cmyk_4
246 #define IMAGE_WALK_DOUBLE
247 #define IMAGE_WALK_COL_STEP 4
248 #define IMAGE_WALK_DO_STEP do{ pixel_conv_rgb_to_cmyk(walk_pos, walk_sec_pos); }while(0)
249 #include "images/image-walk.h"
254 pixel_conv_ycck_to_rgb(byte *dest, byte *src)
256 int y = src[0], cb = src[1] - 128, cr = src[2] - 128;
257 uns d = (255 - src[3]) * (0xffffffffU / 255 /255);
258 dest[0] = (d * CLAMP(y + (91881 * cr) / 0x10000, 0, 255) >> 24);
259 dest[1] = (d * CLAMP(y - (22553 * cb + 46801 * cr) / 0x10000, 0, 255) >> 24);
260 dest[2] = (d * CLAMP(y + (116129 * cb) / 0x10000, 0, 255) >> 24);
263 #define IMAGE_WALK_PREFIX(x) walk_##x
264 #define IMAGE_WALK_FUNC_NAME image_conv_ycck_4_to_rgb_n
265 #define IMAGE_WALK_DOUBLE
266 #define IMAGE_WALK_SEC_COL_STEP 4
267 #define IMAGE_WALK_DO_STEP do{ pixel_conv_ycck_to_rgb(walk_pos, walk_sec_pos); }while(0)
268 #include "images/image-walk.h"
271 pixel_conv_rgb_to_ycck(byte *dest, byte *src)
273 uns k = MAX(src[0], src[1]);
275 uns d = fast_div_u32_u8(0x7fffffffU, k); /* == 0 for zero K */
276 uns r = 255 - ((d * (k - src[0])) >> 23);
277 uns g = 255 - ((d * (k - src[1])) >> 23);
278 uns b = 255 - ((d * (k - src[2])) >> 23);
279 dest[0] = (19595 * r + 38470 * g + 7471 * b) / 0x10000;
280 dest[1] = (0x800000 + 0x8000 * b - 11058 * r - 21710 * g) / 0x10000;
281 dest[2] = (0x800000 + 0x8000 * r - 27439 * g - 5329 * b) / 0x10000;
285 #define IMAGE_WALK_PREFIX(x) walk_##x
286 #define IMAGE_WALK_FUNC_NAME image_conv_rgb_n_to_ycck_4
287 #define IMAGE_WALK_DOUBLE
288 #define IMAGE_WALK_COL_STEP 4
289 #define IMAGE_WALK_DO_STEP do{ pixel_conv_rgb_to_ycck(walk_pos, walk_sec_pos); }while(0)
290 #include "images/image-walk.h"
295 image_conv_color_space(struct image_context *ctx UNUSED, struct image *dest, struct image *src, struct image_conv_options *opt UNUSED)
297 switch (dest->flags & IMAGE_COLOR_SPACE)
299 case COLOR_SPACE_GRAYSCALE:
300 switch (src->flags & IMAGE_COLOR_SPACE)
302 case COLOR_SPACE_RGB:
303 if (dest->pixel_size == 1)
305 image_conv_rgb_n_to_gray_1(dest, src);
311 case COLOR_SPACE_RGB:
312 switch (src->flags & IMAGE_CHANNELS_FORMAT)
314 case COLOR_SPACE_GRAYSCALE:
315 if (src->pixel_size == 1)
317 image_conv_gray_1_to_rgb_n(dest, src);
321 case COLOR_SPACE_YCBCR:
322 image_conv_ycbcr_n_to_rgb_n(dest, src);
324 case COLOR_SPACE_CMYK:
325 if (src->pixel_size == 4)
327 image_conv_cmyk_4_to_rgb_n(dest, src);
331 case COLOR_SPACE_YCCK:
332 if (src->pixel_size == 4)
334 image_conv_ycck_4_to_rgb_n(dest, src);
340 case COLOR_SPACE_YCBCR:
341 switch (src->flags & IMAGE_CHANNELS_FORMAT)
343 case COLOR_SPACE_RGB:
344 image_conv_rgb_n_to_ycbcr_n(dest, src);
348 case COLOR_SPACE_CMYK:
349 switch (src->flags & IMAGE_CHANNELS_FORMAT)
351 case COLOR_SPACE_RGB:
352 if (dest->pixel_size == 4)
354 image_conv_rgb_n_to_cmyk_4(dest, src);
360 case COLOR_SPACE_YCCK:
361 switch (src->flags & IMAGE_CHANNELS_FORMAT)
363 case COLOR_SPACE_RGB:
364 if (dest->pixel_size == 4)
366 image_conv_rgb_n_to_ycck_4(dest, src);
377 image_conv_copy(struct image *dest, struct image *src)
379 if (dest->pixels == src->pixels)
381 else if (dest->pixel_size != src->pixel_size)
383 uns channels = MIN(dest->channels, src->channels);
388 # define IMAGE_WALK_PREFIX(x) walk_##x
389 # define IMAGE_WALK_INLINE
390 # define IMAGE_WALK_DOUBLE
391 # define IMAGE_WALK_IMAGE dest
392 # define IMAGE_WALK_SEC_IMAGE src
393 # define IMAGE_WALK_UNROLL 4
394 # define IMAGE_WALK_DO_STEP do{ walk_pos[0] = walk_sec_pos[0]; }while(0)
395 # include "images/image-walk.h"
399 # define IMAGE_WALK_PREFIX(x) walk_##x
400 # define IMAGE_WALK_INLINE
401 # define IMAGE_WALK_DOUBLE
402 # define IMAGE_WALK_IMAGE dest
403 # define IMAGE_WALK_SEC_IMAGE src
404 # define IMAGE_WALK_UNROLL 4
405 # define IMAGE_WALK_DO_STEP do{ walk_pos[0] = walk_sec_pos[0]; walk_pos[1] = walk_sec_pos[1]; }while(0)
406 # include "images/image-walk.h"
409 # define IMAGE_WALK_PREFIX(x) walk_##x
410 # define IMAGE_WALK_INLINE
411 # define IMAGE_WALK_DOUBLE
412 # define IMAGE_WALK_IMAGE dest
413 # define IMAGE_WALK_SEC_IMAGE src
414 # define IMAGE_WALK_UNROLL 2
415 # define IMAGE_WALK_DO_STEP do{ walk_pos[0] = walk_sec_pos[0]; walk_pos[1] = walk_sec_pos[1]; walk_pos[2] = walk_sec_pos[2]; }while(0)
416 # include "images/image-walk.h"
419 # define IMAGE_WALK_PREFIX(x) walk_##x
420 # define IMAGE_WALK_INLINE
421 # define IMAGE_WALK_DOUBLE
422 # define IMAGE_WALK_IMAGE dest
423 # define IMAGE_WALK_SEC_IMAGE src
424 # define IMAGE_WALK_UNROLL 2
425 # define IMAGE_WALK_DO_STEP do{ walk_pos[0] = walk_sec_pos[0]; walk_pos[1] = walk_sec_pos[1]; walk_pos[2] = walk_sec_pos[2]; walk_pos[3] = walk_sec_pos[3]; }while(0)
426 # include "images/image-walk.h"
429 # define IMAGE_WALK_PREFIX(x) walk_##x
430 # define IMAGE_WALK_INLINE
431 # define IMAGE_WALK_DOUBLE
432 # define IMAGE_WALK_IMAGE dest
433 # define IMAGE_WALK_SEC_IMAGE src
434 # define IMAGE_WALK_DO_STEP do{ for (uns i = 0; i < channels; i++) walk_pos[i] = walk_sec_pos[i]; }while(0)
435 # include "images/image-walk.h"
439 else if (dest->row_size != src->row_size || ((dest->flags | src->flags) & IMAGE_GAPS_PROTECTED))
441 byte *s = src->pixels;
442 byte *d = dest->pixels;
443 for (uns row = src->rows; row--; )
445 memcpy(d, s, src->row_pixels_size);
450 else if (dest->pixels != src->pixels)
451 memcpy(dest->pixels, src->pixels, src->image_size);
455 image_conv_fill_alpha(struct image *dest)
457 switch (dest->channels)
460 if (dest->pixel_size == 2)
462 # define IMAGE_WALK_PREFIX(x) walk_##x
463 # define IMAGE_WALK_INLINE
464 # define IMAGE_WALK_IMAGE dest
465 # define IMAGE_WALK_COL_STEP 2
466 # define IMAGE_WALK_UNROLL 4
467 # define IMAGE_WALK_DO_STEP do{ walk_pos[1] = 255; }while(0)
468 # include "images/image-walk.h"
473 if (dest->pixel_size == 4)
475 # define IMAGE_WALK_PREFIX(x) walk_##x
476 # define IMAGE_WALK_INLINE
477 # define IMAGE_WALK_IMAGE dest
478 # define IMAGE_WALK_COL_STEP 4
479 # define IMAGE_WALK_UNROLL 4
480 # define IMAGE_WALK_DO_STEP do{ walk_pos[3] = 255; }while(0)
481 # include "images/image-walk.h"
487 # define IMAGE_WALK_PREFIX(x) walk_##x
488 # define IMAGE_WALK_INLINE
489 # define IMAGE_WALK_IMAGE dest
490 # define IMAGE_WALK_UNROLL 4
491 # define IMAGE_WALK_DO_STEP do{ walk_pos[dest->channels - 1] = 255; }while(0)
492 # include "images/image-walk.h"
497 image_conv_copy_alpha(struct image *dest, struct image *src)
499 if (dest->pixels != src->pixels || dest->channels != src->channels)
501 # define IMAGE_WALK_PREFIX(x) walk_##x
502 # define IMAGE_WALK_INLINE
503 # define IMAGE_WALK_DOUBLE
504 # define IMAGE_WALK_IMAGE dest
505 # define IMAGE_WALK_SEC_IMAGE src
506 # define IMAGE_WALK_UNROLL 4
507 # define IMAGE_WALK_DO_STEP do{ walk_pos[dest->channels - 1] = walk_sec_pos[src->channels - 1]; }while(0)
508 # include "images/image-walk.h"
513 image_conv_alpha_func(uns value, uns alpha, uns acoef, uns bcoef)
515 return ((uns)(acoef + (int)alpha * (int)(value - bcoef)) * (0xffffffffU / 255 / 255)) >> 24;
519 image_conv_apply_alpha_from(struct image_context *ctx, struct image *dest, struct image *src, struct image_conv_options *opt)
521 if (!opt->background.color_space)
523 byte background[IMAGE_MAX_CHANNELS];
524 if (unlikely(!color_put(ctx, &opt->background, background, dest->flags & IMAGE_COLOR_SPACE)))
526 uns a[IMAGE_MAX_CHANNELS], b[IMAGE_MAX_CHANNELS];
527 for (uns i = 0; i < dest->channels; i++)
528 a[i] = 255 * (b[i] = background[i]);
529 switch (dest->channels)
533 # define IMAGE_WALK_PREFIX(x) walk_##x
534 # define IMAGE_WALK_INLINE
535 # define IMAGE_WALK_IMAGE dest
536 # define IMAGE_WALK_SEC_IMAGE src
537 # define IMAGE_WALK_DOUBLE
538 # define IMAGE_WALK_UNROLL 2
539 # define IMAGE_WALK_DO_STEP do{ \
540 walk_pos[0] = image_conv_alpha_func(walk_pos[0], walk_sec_pos[src->channels - 1], a[0], b[0]); }while(0)
541 # include "images/image-walk.h"
546 # define IMAGE_WALK_PREFIX(x) walk_##x
547 # define IMAGE_WALK_INLINE
548 # define IMAGE_WALK_IMAGE dest
549 # define IMAGE_WALK_SEC_IMAGE src
550 # define IMAGE_WALK_DOUBLE
551 # define IMAGE_WALK_DO_STEP do{ \
552 walk_pos[0] = image_conv_alpha_func(walk_pos[0], walk_sec_pos[src->channels - 1], a[0], b[0]); \
553 walk_pos[1] = image_conv_alpha_func(walk_pos[1], walk_sec_pos[src->channels - 1], a[1], b[1]); \
554 walk_pos[2] = image_conv_alpha_func(walk_pos[2], walk_sec_pos[src->channels - 1], a[2], b[2]); }while(0)
555 # include "images/image-walk.h"
560 # define IMAGE_WALK_PREFIX(x) walk_##x
561 # define IMAGE_WALK_INLINE
562 # define IMAGE_WALK_IMAGE dest
563 # define IMAGE_WALK_SEC_IMAGE src
564 # define IMAGE_WALK_DOUBLE
565 # define IMAGE_WALK_DO_STEP do{ for (uns i = 0; i < dest->channels; i++) \
566 walk_pos[i] = image_conv_alpha_func(walk_pos[i], walk_sec_pos[src->channels - 1], a[i], b[i]); }while(0)
567 # include "images/image-walk.h"
573 image_conv_apply_alpha_to(struct image_context *ctx, struct image *dest, struct image *src, struct image_conv_options *opt)
575 if (!opt->background.color_space)
577 image_conv_copy(dest, src);
580 byte background[IMAGE_MAX_CHANNELS];
581 if (unlikely(!color_put(ctx, &opt->background, background, dest->flags & IMAGE_COLOR_SPACE)))
583 uns a[IMAGE_MAX_CHANNELS], b[IMAGE_MAX_CHANNELS];
584 for (uns i = 0; i < dest->channels; i++)
585 a[i] = 255 * (b[i] = background[i]);
586 switch (dest->channels)
590 # define IMAGE_WALK_PREFIX(x) walk_##x
591 # define IMAGE_WALK_INLINE
592 # define IMAGE_WALK_IMAGE dest
593 # define IMAGE_WALK_SEC_IMAGE src
594 # define IMAGE_WALK_DOUBLE
595 # define IMAGE_WALK_UNROLL 2
596 # define IMAGE_WALK_DO_STEP do{ \
597 walk_pos[0] = image_conv_alpha_func(walk_sec_pos[0], walk_sec_pos[src->channels - 1], a[0], b[0]); }while(0)
598 # include "images/image-walk.h"
603 # define IMAGE_WALK_PREFIX(x) walk_##x
604 # define IMAGE_WALK_INLINE
605 # define IMAGE_WALK_IMAGE dest
606 # define IMAGE_WALK_SEC_IMAGE src
607 # define IMAGE_WALK_DOUBLE
608 # define IMAGE_WALK_DO_STEP do{ \
609 walk_pos[0] = image_conv_alpha_func(walk_sec_pos[0], walk_sec_pos[src->channels - 1], a[0], b[0]); \
610 walk_pos[1] = image_conv_alpha_func(walk_sec_pos[1], walk_sec_pos[src->channels - 1], a[1], b[1]); \
611 walk_pos[2] = image_conv_alpha_func(walk_sec_pos[2], walk_sec_pos[src->channels - 1], a[2], b[2]); }while(0)
612 # include "images/image-walk.h"
617 # define IMAGE_WALK_PREFIX(x) walk_##x
618 # define IMAGE_WALK_INLINE
619 # define IMAGE_WALK_IMAGE dest
620 # define IMAGE_WALK_SEC_IMAGE src
621 # define IMAGE_WALK_DOUBLE
622 # define IMAGE_WALK_DO_STEP do{ for (uns i = 0; i < dest->channels; i++) \
623 walk_pos[i] = image_conv_alpha_func(walk_sec_pos[i], walk_sec_pos[src->channels - 1], a[i], b[i]); }while(0)
624 # include "images/image-walk.h"
630 image_conv(struct image_context *ctx, struct image *dest, struct image *src, struct image_conv_options *opt)
632 ASSERT(dest->cols == src->cols && dest->rows == src->rows);
633 if (!((dest->flags ^ src->flags) & IMAGE_COLOR_SPACE))
635 if (!(src->flags & IMAGE_ALPHA) || (dest->flags & IMAGE_ALPHA))
636 image_conv_copy(dest, src);
637 else if (unlikely(!image_conv_apply_alpha_to(ctx, dest, src, opt)))
642 if (!(src->flags & IMAGE_ALPHA))
644 if (unlikely(!image_conv_color_space(ctx, dest, src, opt)))
646 if ((dest->flags & IMAGE_ALPHA) && (opt->flags & IMAGE_CONV_FILL_ALPHA))
647 image_conv_fill_alpha(dest);
651 if (dest->flags & IMAGE_ALPHA)
653 if (dest->channels <= src->channels)
655 if (unlikely(!image_conv_color_space(ctx, dest, src, opt)))
657 if (opt->flags & IMAGE_CONV_COPY_ALPHA)
658 image_conv_copy_alpha(dest, src);
659 else if (opt->flags & IMAGE_CONV_FILL_ALPHA)
660 image_conv_fill_alpha(dest);
664 if (opt->flags & IMAGE_CONV_COPY_ALPHA)
665 image_conv_copy_alpha(dest, src);
667 image_conv_fill_alpha(dest);
668 if (unlikely(!image_conv_color_space(ctx, dest, src, opt)))
674 if (dest->channels <= src->channels)
676 if (unlikely(!image_conv_color_space(ctx, dest, src, opt)))
678 if (unlikely(!image_conv_apply_alpha_from(ctx, dest, src, opt)))
683 if (unlikely(!image_conv_apply_alpha_to(ctx, dest, src, opt)))
685 if (unlikely(!image_conv_color_space(ctx, dest, dest, opt)))
693 IMAGE_ERROR(ctx, IMAGE_ERROR_INVALID_PIXEL_FORMAT, "Image conversion not supported for such pixel formats");
697 /********************* EXACT CONVERSION ROUTINES **********************/
699 /* Reference whites */
700 #define COLOR_ILLUMINANT_A 0.44757, 0.40744
701 #define COLOR_ILLUMINANT_B 0.34840, 0.35160
702 #define COLOR_ILLUMINANT_C 0.31006, 0.31615
703 #define COLOR_ILLUMINANT_D50 0.34567, 0.35850
704 #define COLOR_ILLUMINANT_D55 0.33242, 0.34743
705 #define COLOR_ILLUMINANT_D65 0.31273, 0.32902
706 #define COLOR_ILLUMINANT_D75 0.29902, 0.31485
707 #define COLOR_ILLUMINANT_9300K 0.28480, 0.29320
708 #define COLOR_ILLUMINANT_E (1./3.), (1./3.)
709 #define COLOR_ILLUMINANT_F2 0.37207, 0.37512
710 #define COLOR_ILLUMINANT_F7 0.31285, 0.32918
711 #define COLOR_ILLUMINANT_F11 0.38054, 0.37691
714 color_illuminant_d50[2] = {COLOR_ILLUMINANT_D50},
715 color_illuminant_d65[2] = {COLOR_ILLUMINANT_D65},
716 color_illuminant_e[2] = {COLOR_ILLUMINANT_E};
718 /* RGB profiles (many missing) */
719 const struct color_space_info
720 color_adobe_rgb_info = {"Adobe RGB", {{0.6400, 0.3300}, {0.2100, 0.7100}, {0.1500, 0.0600}, {COLOR_ILLUMINANT_D65}}, {0.45, 0.45, 0, 0, 0}},
721 color_apple_rgb_info = {"Apple RGB", {{0.6250, 0.3400}, {0.2800, 0.5950}, {0.1550, 0.0700}, {COLOR_ILLUMINANT_D65}}, {0.56, 0.56, 0, 0, 0}},
722 color_cie_rgb_info = {"CIE RGB", {{0.7350, 0.2650}, {0.2740, 0.7170}, {0.1670, 0.0090}, {COLOR_ILLUMINANT_E}}, {0.45, 0.45, 0, 0, 0}},
723 color_color_match_rgb_info = {"ColorMatch RGB", {{0.6300, 0.3400}, {0.2950, 0.6050}, {0.1500, 0.0750}, {COLOR_ILLUMINANT_D50}}, {0.56, 0.56, 0, 0, 0}},
724 color_srgb_info = {"sRGB", {{0.6400, 0.3300}, {0.3000, 0.6000}, {0.1500, 0.0600}, {COLOR_ILLUMINANT_D65}}, {0.45, 0.42, 0.055, 0.003, 12.92}};
726 #define CLIP(x, min, max) (((x) < (min)) ? (min) : ((x) > (max)) ? (max) : (x))
731 a[0] = CLIP(a[0], 0, 1);
732 a[1] = CLIP(a[1], 0, 1);
733 a[2] = CLIP(a[2], 0, 1);
737 correct_gamma_simple(double dest[3], double src[3], const struct color_space_gamma_info *info)
739 dest[0] = pow(src[0], info->simple_gamma);
740 dest[1] = pow(src[1], info->simple_gamma);
741 dest[2] = pow(src[2], info->simple_gamma);
745 invert_gamma_simple(double dest[3], double src[3], const struct color_space_gamma_info *info)
747 dest[0] = pow(src[0], 1 / info->simple_gamma);
748 dest[1] = pow(src[1], 1 / info->simple_gamma);
749 dest[2] = pow(src[2], 1 / info->simple_gamma);
753 correct_gamma_detailed(double dest[3], double src[3], const struct color_space_gamma_info *info)
755 for (uns i = 0; i < 3; i++)
756 if (src[i] > info->transition)
757 dest[i] = (1 + info->offset) * pow(src[i], info->detailed_gamma) - info->offset;
759 dest[i] = info->slope * src[i];
763 invert_gamma_detailed(double dest[3], double src[3], const struct color_space_gamma_info *info)
765 for (uns i = 0; i < 3; i++)
766 if (src[i] > info->transition * info->slope)
767 dest[i] = pow((src[i] + info->offset) / (1 + info->offset), 1 / info->detailed_gamma);
769 dest[i] = src[i] / info->slope;
773 apply_matrix(double dest[3], double src[3], double matrix[9])
775 dest[0] = src[0] * matrix[0] + src[1] * matrix[1] + src[2] * matrix[2];
776 dest[1] = src[0] * matrix[3] + src[1] * matrix[4] + src[2] * matrix[5];
777 dest[2] = src[0] * matrix[6] + src[1] * matrix[7] + src[2] * matrix[8];
781 color_invert_matrix(double dest[9], double matrix[9])
783 double *i = dest, *m = matrix;
784 double a0 = m[4] * m[8] - m[5] * m[7];
785 double a1 = m[3] * m[8] - m[5] * m[6];
786 double a2 = m[3] * m[7] - m[4] * m[6];
787 double d = 1 / (m[0] * a0 - m[1] * a1 + m[2] * a2);
791 i[1] = -d * (m[1] * m[8] - m[2] * m[7]);
792 i[4] = d * (m[0] * m[8] - m[2] * m[6]);
793 i[7] = -d * (m[0] * m[7] - m[1] * m[6]);
794 i[2] = d * (m[1] * m[5] - m[2] * m[4]);
795 i[5] = -d * (m[0] * m[5] - m[2] * m[3]);
796 i[8] = d * (m[0] * m[4] - m[1] * m[3]);
800 mul_matrices(double r[9], double a[9], double b[9])
802 r[0] = a[0] * b[0] + a[1] * b[3] + a[2] * b[6];
803 r[1] = a[0] * b[1] + a[1] * b[4] + a[2] * b[7];
804 r[2] = a[0] * b[2] + a[1] * b[5] + a[2] * b[8];
805 r[3] = a[3] * b[0] + a[4] * b[3] + a[5] * b[6];
806 r[4] = a[3] * b[1] + a[4] * b[4] + a[5] * b[7];
807 r[5] = a[3] * b[2] + a[4] * b[5] + a[5] * b[8];
808 r[6] = a[6] * b[0] + a[7] * b[3] + a[8] * b[6];
809 r[7] = a[6] * b[1] + a[7] * b[4] + a[8] * b[7];
810 r[8] = a[6] * b[2] + a[7] * b[5] + a[8] * b[8];
813 /* computes conversion matrix from a given color space to CIE XYZ */
815 color_compute_color_space_to_xyz_matrix(double matrix[9], const struct color_space_chromacity_info *space)
817 double wX = space->white[0] / space->white[1];
818 double wZ = (1 - space->white[0] - space->white[1]) / space->white[1];
820 a[0] = space->prim1[0]; a[3] = space->prim1[1]; a[6] = 1 - a[0] - a[3];
821 a[1] = space->prim2[0]; a[4] = space->prim2[1]; a[7] = 1 - a[1] - a[4];
822 a[2] = space->prim3[0]; a[5] = space->prim3[1]; a[8] = 1 - a[2] - a[5];
823 color_invert_matrix(b, a);
824 double ra = wX * b[0] + b[1] + wZ * b[2];
825 double rb = wX * b[3] + b[4] + wZ * b[5];
826 double rc = wX * b[6] + b[7] + wZ * b[8];
827 matrix[0] = a[0] * ra;
828 matrix[1] = a[1] * rb;
829 matrix[2] = a[2] * rc;
830 matrix[3] = a[3] * ra;
831 matrix[4] = a[4] * rb;
832 matrix[5] = a[5] * rc;
833 matrix[6] = a[6] * ra;
834 matrix[7] = a[7] * rb;
835 matrix[8] = a[8] * rc;
838 /* computes matrix to join transformations with different reference whites */
840 color_compute_bradford_matrix(double matrix[9], const double source[2], const double dest[2])
842 /* cone response matrix and its inversion */
843 static double r[9] = {
844 0.8951, 0.2664, -0.1614,
845 -0.7502, 1.7135, 0.0367,
846 0.0389, -0.0685, 1.0296};
847 //static double i[9] = {0.9870, -0.1471, 0.1600, 0.4323, 0.5184, 0.0493, -0.0085, 0.0400, 0.9685};
849 color_invert_matrix(i, r);
850 double aX = source[0] / source[1];
851 double aZ = (1 - source[0] - source[1]) / source[1];
852 double bX = dest[0] / dest[1];
853 double bZ = (1 - dest[0] - dest[1]) / dest[1];
854 double x = (r[0] * bX + r[1] + r[2] * bZ) / (r[0] * aX + r[1] + r[2] * aZ);
855 double y = (r[3] * bX + r[4] + r[5] * bZ) / (r[3] * aX + r[4] + r[5] * aZ);
856 double z = (r[6] * bX + r[7] + r[8] * bZ) / (r[6] * aX + r[7] + r[8] * aZ);
858 m[0] = i[0] * x; m[1] = i[1] * y; m[2] = i[2] * z;
859 m[3] = i[3] * x; m[4] = i[4] * y; m[5] = i[5] * z;
860 m[6] = i[6] * x; m[7] = i[7] * y; m[8] = i[8] * z;
861 mul_matrices(matrix, m, r);
865 color_compute_color_spaces_conversion_matrix(double matrix[9], const struct color_space_chromacity_info *src, const struct color_space_chromacity_info *dest)
867 double a_to_xyz[9], b_to_xyz[9], xyz_to_b[9], bradford[9], m[9];
868 color_compute_color_space_to_xyz_matrix(a_to_xyz, src);
869 color_compute_color_space_to_xyz_matrix(b_to_xyz, dest);
870 color_invert_matrix(xyz_to_b, b_to_xyz);
871 if (src->white[0] == dest->white[0] && src->white[1] == dest->white[1])
872 mul_matrices(matrix, a_to_xyz, xyz_to_b);
875 color_compute_bradford_matrix(bradford, src->white, dest->white);
876 mul_matrices(m, a_to_xyz, bradford);
877 mul_matrices(matrix, m, xyz_to_b);
883 srgb_to_xyz_exact(double xyz[3], double srgb[3])
885 static double matrix[9] = {
886 0.41248031, 0.35756952, 0.18043951,
887 0.21268516, 0.71513904, 0.07217580,
888 0.01933501, 0.11918984, 0.95031473};
890 invert_gamma_detailed(srgb_lin, srgb, &color_srgb_info.gamma);
891 apply_matrix(xyz, srgb_lin, matrix);
892 xyz_to_srgb_exact(srgb_lin, xyz);
897 xyz_to_srgb_exact(double srgb[3], double xyz[3])
899 static double matrix[9] = {
900 3.24026666, -1.53704957, -0.49850256,
901 -0.96928381, 1.87604525, 0.04155678,
902 0.05564281, -0.20402363, 1.05721334};
904 apply_matrix(srgb_lin, xyz, matrix);
906 correct_gamma_detailed(srgb, srgb_lin, &color_srgb_info.gamma);
911 xyz_to_luv_exact(double luv[3], double xyz[3])
913 double sum = xyz[0] + 15 * xyz[1] + 3 * xyz[2];
915 luv[0] = luv[1] = luv[2] = 0;
918 double var_u = 4 * xyz[0] / sum;
919 double var_v = 9 * xyz[1] / sum;
920 if (xyz[1] > 0.008856)
921 luv[0] = 116 * pow(xyz[1], 1 / 3.) - 16;
923 luv[0] = (116 * 7.787) * xyz[1];
924 luv[1] = luv[0] * (13 * (var_u - 4 * REF_WHITE_X / (REF_WHITE_X + 15 * REF_WHITE_Y + 3 * REF_WHITE_Z)));
925 luv[2] = luv[0] * (13 * (var_v - 9 * REF_WHITE_Y / (REF_WHITE_X + 15 * REF_WHITE_Y + 3 * REF_WHITE_Z)));
926 /* intervals [0..100], [-134..220], [-140..122] */
932 luv_to_xyz_exact(double xyz[3], double luv[3])
934 double var_u = luv[1] / (13 * luv[0]) + (4 * REF_WHITE_X / (REF_WHITE_X + 15 * REF_WHITE_Y + 3 * REF_WHITE_Z));
935 double var_v = luv[2] / (13 * luv[0]) + (9 * REF_WHITE_Y / (REF_WHITE_X + 15 * REF_WHITE_Y + 3 * REF_WHITE_Z));
936 double var_y = (luv[0] + 16) / 116;
937 double pow_y = var_y * var_y * var_y;
938 if (pow_y > 0.008856)
941 var_y = (var_y - 16 / 116) / 7.787;
943 xyz[0] = -(9 * xyz[1] * var_u) / ((var_u - 4) * var_v - var_u * var_v);
944 xyz[2] = (9 * xyz[1] - 15 * var_v * xyz[1] - var_v * xyz[0]) / (3 * var_v);
947 /* RGB to CMYK - a very simple version, not too accureate */
949 rgb_to_cmyk_exact(double cmyk[4], double rgb[3])
951 cmyk[0] = 1 - rgb[0];
952 cmyk[1] = 1 - rgb[1];
953 cmyk[2] = 1 - rgb[2];
954 cmyk[3] = MIN(cmyk[0], cmyk[1]);
955 cmyk[3] = MIN(cmyk[3], cmyk[2]);
956 if (cmyk[3] > 0.9999)
959 cmyk[0] = cmyk[1] = cmyk[2] = 0;
963 double d = 1 / (1 - cmyk[3]);
964 for (uns i = 0; i < 3; i++)
965 cmyk[i] = d * (cmyk[i] - cmyk[3]);
971 cmyk_to_rgb_exact(double rgb[3], double cmyk[4])
973 double d = 1 - cmyk[1];
974 for (uns i = 0; i < 3; i++)
975 rgb[i] = d * (1 - cmyk[i]);
978 /***************** OPTIMIZED SRGB -> LUV CONVERSION *********************/
980 u16 srgb_to_luv_tab1[256];
981 u16 srgb_to_luv_tab2[9 << SRGB_TO_LUV_TAB2_SIZE];
982 u32 srgb_to_luv_tab3[20 << SRGB_TO_LUV_TAB3_SIZE];
985 srgb_to_luv_init(void)
987 DBG("Initializing sRGB -> Luv table");
988 for (uns i = 0; i < 256; i++)
992 t = pow((t + 0.055) * (1 / 1.055), 2.4);
995 srgb_to_luv_tab1[i] = CLAMP(t * 0xfff + 0.5, 0, 0xfff);
997 for (uns i = 0; i < (9 << SRGB_TO_LUV_TAB2_SIZE); i++)
999 double t = i / (double)((9 << SRGB_TO_LUV_TAB2_SIZE) - 1);
1001 t = 1.16 * pow(t, 1 / 3.) - 0.16;
1003 t = (1.16 * 7.787) * t;
1004 srgb_to_luv_tab2[i] =
1005 CLAMP(t * ((1 << SRGB_TO_LUV_TAB2_SCALE) - 1) + 0.5,
1006 0, (1 << SRGB_TO_LUV_TAB2_SCALE) - 1);
1008 for (uns i = 0; i < (20 << SRGB_TO_LUV_TAB3_SIZE); i++)
1010 srgb_to_luv_tab3[i] = i ? (13 << (SRGB_TO_LUV_TAB3_SCALE + SRGB_TO_LUV_TAB3_SIZE)) / i : 0;
1015 srgb_to_luv_pixels(byte *dest, byte *src, uns count)
1019 srgb_to_luv_pixel(dest, src);
1026 /************************ GRID INTERPOLATION ALGORITHM ************************/
1028 struct color_grid_node *srgb_to_luv_grid;
1029 struct color_interpolation_node *color_interpolation_table;
1031 /* Returns volume of a given tetrahedron multiplied by 6 */
1033 tetrahedron_volume(uns *v1, uns *v2, uns *v3, uns *v4)
1035 int a[3], b[3], c[3];
1036 for (uns i = 0; i < 3; i++)
1038 a[i] = v2[i] - v1[i];
1039 b[i] = v3[i] - v1[i];
1040 c[i] = v4[i] - v1[i];
1043 a[0] * (b[1] * c[2] - b[2] * c[1]) -
1044 a[1] * (b[0] * c[2] - b[2] * c[0]) +
1045 a[2] * (b[0] * c[1] - b[1] * c[0]);
1046 return (result > 0) ? result : -result;
1050 interpolate_tetrahedron(struct color_interpolation_node *n, uns *p, const uns *c)
1053 for (uns i = 0; i < 4; i++)
1055 v[i][0] = (c[i] & 0001) ? (1 << COLOR_CONV_OFS) : 0;
1056 v[i][1] = (c[i] & 0010) ? (1 << COLOR_CONV_OFS) : 0;
1057 v[i][2] = (c[i] & 0100) ? (1 << COLOR_CONV_OFS) : 0;
1059 ((c[i] & 0001) ? 1 : 0) +
1060 ((c[i] & 0010) ? (1 << COLOR_CONV_SIZE) : 0) +
1061 ((c[i] & 0100) ? (1 << (COLOR_CONV_SIZE * 2)) : 0);
1063 uns vol = tetrahedron_volume(v[0], v[1], v[2], v[3]);
1064 n->mul[0] = ((tetrahedron_volume(p, v[1], v[2], v[3]) << 8) + (vol >> 1)) / vol;
1065 n->mul[1] = ((tetrahedron_volume(v[0], p, v[2], v[3]) << 8) + (vol >> 1)) / vol;
1066 n->mul[2] = ((tetrahedron_volume(v[0], v[1], p, v[3]) << 8) + (vol >> 1)) / vol;
1067 n->mul[3] = ((tetrahedron_volume(v[0], v[1], v[2], p) << 8) + (vol >> 1)) / vol;
1069 for (j = 0; j < 4; j++)
1072 for (uns i = 0; i < 4; i++)
1074 n->ofs[i] = n->ofs[j];
1078 interpolation_table_init(void)
1080 DBG("Initializing color interpolation table");
1081 struct color_interpolation_node *n = color_interpolation_table =
1082 xmalloc(sizeof(struct color_interpolation_node) << (COLOR_CONV_OFS * 3));
1084 for (p[2] = 0; p[2] < (1 << COLOR_CONV_OFS); p[2]++)
1085 for (p[1] = 0; p[1] < (1 << COLOR_CONV_OFS); p[1]++)
1086 for (p[0] = 0; p[0] < (1 << COLOR_CONV_OFS); p[0]++)
1089 static const uns tetrahedra[5][4] = {
1090 {0000, 0001, 0010, 0100},
1091 {0110, 0111, 0100, 0010},
1092 {0101, 0100, 0111, 0001},
1093 {0011, 0010, 0001, 0111},
1094 {0111, 0001, 0010, 0100}};
1095 if (p[0] + p[1] + p[2] <= (1 << COLOR_CONV_OFS))
1097 else if ((1 << COLOR_CONV_OFS) + p[0] <= p[1] + p[2])
1099 else if ((1 << COLOR_CONV_OFS) + p[1] <= p[0] + p[2])
1101 else if ((1 << COLOR_CONV_OFS) + p[2] <= p[0] + p[1])
1105 interpolate_tetrahedron(n, p, tetrahedra[index]);
1110 typedef void color_conv_func(double dest[3], double src[3]);
1113 conv_grid_init(struct color_grid_node **grid, color_conv_func func)
1117 struct color_grid_node *g = *grid = xmalloc((sizeof(struct color_grid_node)) << (COLOR_CONV_SIZE * 3));
1118 double src[3], dest[3];
1119 for (uns k = 0; k < (1 << COLOR_CONV_SIZE); k++)
1121 src[2] = k * (255 / (double)((1 << COLOR_CONV_SIZE) - 1));
1122 for (uns j = 0; j < (1 << COLOR_CONV_SIZE); j++)
1124 src[1] = j * (255/ (double)((1 << COLOR_CONV_SIZE) - 1));
1125 for (uns i = 0; i < (1 << COLOR_CONV_SIZE); i++)
1127 src[0] = i * (255 / (double)((1 << COLOR_CONV_SIZE) - 1));
1129 g->val[0] = CLAMP(dest[0] + 0.5, 0, 255);
1130 g->val[1] = CLAMP(dest[1] + 0.5, 0, 255);
1131 g->val[2] = CLAMP(dest[2] + 0.5, 0, 255);
1139 srgb_to_luv_func(double dest[3], double src[3])
1141 double srgb[3], xyz[3], luv[3];
1142 srgb[0] = src[0] / 255.;
1143 srgb[1] = src[1] / 255.;
1144 srgb[2] = src[2] / 255.;
1145 srgb_to_xyz_exact(xyz, srgb);
1146 xyz_to_luv_exact(luv, xyz);
1147 dest[0] = luv[0] * 2.55;
1148 dest[1] = luv[1] * (2.55 / 4) + 128;
1149 dest[2] = luv[2] * (2.55 / 4) + 128;
1153 color_conv_init(void)
1155 interpolation_table_init();
1156 conv_grid_init(&srgb_to_luv_grid, srgb_to_luv_func);
1160 color_conv_pixels(byte *dest, byte *src, uns count, struct color_grid_node *grid)
1164 color_conv_pixel(dest, src, grid);
1171 /**************************** TESTS *******************************/
1177 conv_error(u32 color, struct color_grid_node *grid, color_conv_func func)
1179 byte src[3], dest[3];
1180 src[0] = color & 255;
1181 src[1] = (color >> 8) & 255;
1182 src[2] = (color >> 16) & 255;
1183 color_conv_pixel(dest, src, grid);
1184 double src2[3], dest2[3];
1185 for (uns i = 0; i < 3; i++)
1189 for (uns i = 0; i < 3; i++)
1190 err += (dest[i] - dest2[i]) * (dest[i] - dest2[i]);
1194 typedef void test_fn(byte *dest, byte *src);
1197 func_error(u32 color, test_fn test, color_conv_func func)
1199 byte src[3], dest[3];
1200 src[0] = color & 255;
1201 src[1] = (color >> 8) & 255;
1202 src[2] = (color >> 16) & 255;
1204 double src2[3], dest2[3];
1205 for (uns i = 0; i < 3; i++)
1209 for (uns i = 0; i < 3; i++)
1210 err += (dest[i] - dest2[i]) * (dest[i] - dest2[i]);
1215 test_grid(byte *name, struct color_grid_node *grid, color_conv_func func)
1217 double max_err = 0, sum_err = 0;
1219 for (uns i = 0; i < count; i++)
1221 double err = conv_error(random_max(0x1000000), grid, func);
1222 max_err = MAX(err, max_err);
1225 DBG("%s: error max=%f avg=%f", name, max_err, sum_err / count);
1227 die("Too large error in %s conversion", name);
1231 test_func(byte *name, test_fn test, color_conv_func func)
1233 double max_err = 0, sum_err = 0;
1235 for (uns i = 0; i < count; i++)
1237 double err = func_error(random_max(0x1000000), test, func);
1238 max_err = MAX(err, max_err);
1241 DBG("%s: error max=%f avg=%f", name, max_err, sum_err / count);
1243 die("Too large error in %s conversion", name);
1250 test_func("func sRGB -> Luv", srgb_to_luv_pixel, srgb_to_luv_func);
1252 test_grid("grid sRGB -> Luv", srgb_to_luv_grid, srgb_to_luv_func);
1256 byte *a = xmalloc(3 * CNT), *b = xmalloc(3 * CNT);
1257 for (uns i = 0; i < 3 * CNT; i++)
1258 a[i] = random_max(256);
1260 for (uns i = 0; i < TESTS; i++)
1261 memcpy(b, a, CNT * 3);
1262 DBG("memcpy time=%d", (uns)get_timer());
1264 for (uns i = 0; i < TESTS; i++)
1265 srgb_to_luv_pixels(b, a, CNT);
1266 DBG("direct time=%d", (uns)get_timer());
1268 for (uns i = 0; i < TESTS; i++)
1269 color_conv_pixels(b, a, CNT, srgb_to_luv_grid);
1270 DBG("grid time=%d", (uns)get_timer());