From 17d8f7156814e16b2f24adffae8b43bd2eba20fa Mon Sep 17 00:00:00 2001 From: Pavel Charvat Date: Fri, 3 Nov 2006 00:06:50 +0100 Subject: [PATCH] added support for some color conversions; we should be able to load cmyk jpegs now... --- images/Makefile | 2 +- images/alpha.c | 78 ------ images/color.c | 601 ++++++++++++++++++++++++++++++++++++++++-- images/color.h | 73 +++-- images/image-tool.c | 2 +- images/image-walk.h | 8 +- images/image.c | 2 +- images/images.h | 26 +- images/io-libjpeg.c | 35 ++- images/io-libmagick.c | 8 +- images/io-libpng.c | 65 ++--- images/io-libungif.c | 18 +- images/io-main.c | 19 +- images/math.h | 10 - 14 files changed, 726 insertions(+), 221 deletions(-) delete mode 100644 images/alpha.c diff --git a/images/Makefile b/images/Makefile index 5953519a..db62a9a7 100644 --- a/images/Makefile +++ b/images/Makefile @@ -4,7 +4,7 @@ DIRS+=images PROGS+=$(o)/images/image-tool $(o)/images/color-tool CONFIGS+=images -LIBIMAGES_MODS=math config context image scale color alpha io-main object +LIBIMAGES_MODS=math config context image scale color io-main object ifdef CONFIG_IMAGES_DUP PROGS+=$(o)/images/image-dup-test diff --git a/images/alpha.c b/images/alpha.c deleted file mode 100644 index d97207e8..00000000 --- a/images/alpha.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Image Library -- Alpha channels - * - * (c) 2006 Pavel Charvat - * - * This software may be freely distributed and used according to the terms - * of the GNU Lesser General Public License. - */ - -#undef LOCAL_DEBUG - -#include "lib/lib.h" -#include "images/images.h" -#include "images/color.h" - -static inline uns -merge_func(uns value, uns alpha, uns acoef, uns bcoef) -{ - return ((uns)(acoef + (int)alpha * (int)(value - bcoef)) * (0xffffffffU / 255 / 255)) >> 24; -} - -int -image_apply_background(struct image_context *ctx UNUSED, struct image *dest, struct image *src, struct color *background) -{ - DBG("image_apply_background()"); - - /* Grayscale */ - if (src->pixel_size == 2) - { - ASSERT(dest->pixel_size == 1); - byte bg; - if (background->color_space) - color_put_grayscale(&bg, background); - else - bg = 0; - uns a = 255 * bg, b = bg; -# define IMAGE_WALK_PREFIX(x) walk_##x -# define IMAGE_WALK_INLINE -# define IMAGE_WALK_DOUBLE -# define IMAGE_WALK_UNROLL 4 -# define IMAGE_WALK_IMAGE dest -# define IMAGE_WALK_SEC_IMAGE src -# define IMAGE_WALK_COL_STEP 1 -# define IMAGE_WALK_SEC_COL_STEP 2 -# define IMAGE_WALK_DO_STEP do{ walk_pos[0] = merge_func(walk_sec_pos[0], walk_sec_pos[1], a, b); }while(0) -# include "images/image-walk.h" - } - - /* RGBA to RGB or aligned RGB */ - else if (src->pixel_size == 4) - { - ASSERT((src->flags & IMAGE_ALPHA) && dest->pixel_size >= 3 && !(dest->flags & IMAGE_ALPHA)); - byte bg[3]; - if (background->color_space) - color_put_rgb(bg, background); - else - bg[0] = bg[1] = bg[2] = 0; - uns a0 = 255 * bg[0], b0 = bg[0]; - uns a1 = 255 * bg[1], b1 = bg[1]; - uns a2 = 255 * bg[2], b2 = bg[2]; -# define IMAGE_WALK_PREFIX(x) walk_##x -# define IMAGE_WALK_INLINE -# define IMAGE_WALK_DOUBLE -# define IMAGE_WALK_UNROLL 2 -# define IMAGE_WALK_IMAGE dest -# define IMAGE_WALK_SEC_IMAGE src -# define IMAGE_WALK_SEC_COL_STEP 4 -# define IMAGE_WALK_DO_STEP do{ \ - walk_pos[0] = merge_func(walk_sec_pos[0], walk_sec_pos[3], a0, b0); \ - walk_pos[1] = merge_func(walk_sec_pos[1], walk_sec_pos[3], a1, b1); \ - walk_pos[2] = merge_func(walk_sec_pos[2], walk_sec_pos[3], a2, b2); \ - }while(0) -# include "images/image-walk.h" - } - else - ASSERT(0); - return 1; -} diff --git a/images/color.c b/images/color.c index 7dd3773c..193a0f13 100644 --- a/images/color.c +++ b/images/color.c @@ -13,6 +13,8 @@ #include "lib/math.h" #include "images/images.h" #include "images/color.h" +#include "images/error.h" +#include "images/math.h" #include @@ -57,7 +59,7 @@ uns color_space_name_to_id(byte *name) { for (uns i = 1; i < COLOR_SPACE_MAX; i++) - if (!strcasecmp(name, color_space_name[i])) + if (color_space_name[i] && !strcasecmp(name, color_space_name[i])) return i; return 0; } @@ -65,54 +67,571 @@ color_space_name_to_id(byte *name) struct color color_black = { .color_space = COLOR_SPACE_GRAYSCALE }; struct color color_white = { .c = { 255 }, .color_space = COLOR_SPACE_GRAYSCALE }; -inline void -color_put_grayscale(byte *dest, struct color *color) +int +color_get(struct color *color, byte *src, uns src_space) +{ + color->color_space = src_space; + memcpy(color->c, src, color_space_channels[src_space]); + return 1; +} + +int +color_put(struct image_context *ctx, struct color *color, byte *dest, uns dest_space) { - switch (color->color_space) + switch (dest_space) { case COLOR_SPACE_GRAYSCALE: - dest[0] = color->c[0]; + switch (color->color_space) + { + case COLOR_SPACE_GRAYSCALE: + dest[0] = color->c[0]; + return 1; + case COLOR_SPACE_RGB: + dest[0] = rgb_to_gray_func(color->c[0], color->c[1], color->c[2]); + return 1; + } break; case COLOR_SPACE_RGB: - dest[0] = rgb_to_gray_func(color->c[0], color->c[1], color->c[2]); + switch (color->color_space) + { + case COLOR_SPACE_GRAYSCALE: + dest[0] = dest[1] = dest[2] = color->c[0]; + return 1; + case COLOR_SPACE_RGB: + dest[0] = color->c[0]; + dest[1] = color->c[1]; + dest[2] = color->c[2]; + return 1; + case COLOR_SPACE_CMYK: + { + double rgb[3], cmyk[4]; + for (uns i = 0; i < 4; i++) + cmyk[i] = color->c[i] * (1.0 / 255); + cmyk_to_rgb_exact(rgb, cmyk); + for (uns i = 0; i < 3; i++) + dest[i] = CLAMP(rgb[i] * 255, 0, 255); + } + return 1; + } + break; + case COLOR_SPACE_CMYK: + switch (color->color_space) + { + case COLOR_SPACE_GRAYSCALE: + dest[0] = dest[1] = dest[2] = 0; + dest[3] = 255 - color->c[0]; + return 1; + case COLOR_SPACE_RGB: + { + double rgb[3], cmyk[4]; + for (uns i = 0; i < 3; i++) + rgb[i] = color->c[i] * (1.0 / 255); + rgb_to_cmyk_exact(cmyk, rgb); + for (uns i = 0; i < 4; i++) + dest[i] = CLAMP(cmyk[i] * 255, 0, 255); + } + return 1; + } break; - default: - ASSERT(0); } + if (dest_space != COLOR_SPACE_RGB ) + { + /* Try to convert the color via RGB */ + struct color rgb; + if (!color_put(ctx, color, rgb.c, COLOR_SPACE_RGB)) + return 0; + rgb.color_space = COLOR_SPACE_RGB; + return color_put(ctx, &rgb, dest, dest_space); + } + IMAGE_ERROR(ctx, IMAGE_ERROR_INVALID_PIXEL_FORMAT, "Conversion from %s to %s is not supported", + color_space_id_to_name(color->color_space), color_space_id_to_name(color->color_space)); + return 0; } -inline void -color_put_rgb(byte *dest, struct color *color) + +/********************* IMAGE CONVERSION ROUTINES **********************/ + +struct image_conv_options image_conv_defaults = { + .flags = IMAGE_CONV_COPY_ALPHA | IMAGE_CONV_FILL_ALPHA | IMAGE_CONV_APPLY_ALPHA, + .background = { .color_space = COLOR_SPACE_GRAYSCALE } }; + +/* Grayscale <-> RGB */ + +#define IMAGE_WALK_PREFIX(x) walk_##x +#define IMAGE_WALK_FUNC_NAME image_conv_gray_1_to_rgb_n +#define IMAGE_WALK_DOUBLE +#define IMAGE_WALK_SEC_COL_STEP 1 +#define IMAGE_WALK_UNROLL 4 +#define IMAGE_WALK_DO_STEP do{ walk_pos[0] = walk_pos[1] = walk_pos[2] = walk_sec_pos[0]; }while(0) +#include "images/image-walk.h" + +#define IMAGE_WALK_PREFIX(x) walk_##x +#define IMAGE_WALK_FUNC_NAME image_conv_rgb_n_to_gray_1 +#define IMAGE_WALK_DOUBLE +#define IMAGE_WALK_COL_STEP 1 +#define IMAGE_WALK_UNROLL 2 +#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) +#include "images/image-walk.h" + +/* YCbCr <-> RGB */ + +static inline void +pixel_conv_ycbcr_to_rgb(byte *dest, byte *src) +{ + /* R = Y + 1.40200 * Cr + * G = Y - 0.34414 * Cb - 0.71414 * Cr + * B = Y + 1.77200 * Cb */ + int y = src[0], cb = src[1] - 128, cr = src[2] - 128; + dest[0] = CLAMP(y + (91881 * cr) / 0x10000, 0, 255); + dest[1] = CLAMP(y - (22553 * cb + 46801 * cr) / 0x10000, 0, 255); + dest[2] = CLAMP(y + (116129 * cb) / 0x10000, 0, 255); +} + +#define IMAGE_WALK_PREFIX(x) walk_##x +#define IMAGE_WALK_FUNC_NAME image_conv_ycbcr_n_to_rgb_n +#define IMAGE_WALK_DOUBLE +#define IMAGE_WALK_DO_STEP do{ pixel_conv_ycbcr_to_rgb(walk_pos, walk_sec_pos); }while(0) +#include "images/image-walk.h" + +static inline void +pixel_conv_rgb_to_ycbcr(byte *dest, byte *src) +{ + /* Y = 0.29900 * R + 0.58700 * G + 0.11400 * B + * Cb = -0.16874 * R - 0.33126 * G + 0.50000 * B + CENTER + * Cr = 0.50000 * R - 0.41869 * G - 0.08131 * B + CENTER */ + uns r = src[0], g = src[1], b = src[2]; + dest[0] = (19595 * r + 38470 * g + 7471 * b) / 0x10000; + dest[1] = (0x800000 + 0x8000 * b - 11058 * r - 21710 * g) / 0x10000; + dest[2] = (0x800000 + 0x8000 * r - 27439 * g - 5329 * b) / 0x10000; +} + +#define IMAGE_WALK_PREFIX(x) walk_##x +#define IMAGE_WALK_FUNC_NAME image_conv_rgb_n_to_ycbcr_n +#define IMAGE_WALK_DOUBLE +#define IMAGE_WALK_DO_STEP do{ pixel_conv_rgb_to_ycbcr(walk_pos, walk_sec_pos); }while(0) +#include "images/image-walk.h" + +/* CMYK <-> RGB */ + +static inline void +pixel_conv_cmyk_to_rgb(byte *dest, byte *src) { - switch (color->color_space) + uns d = (255 - src[3]) * (0xffffffffU / 255 /255); + dest[0] = d * (255 - src[0]) >> 24; + dest[1] = d * (255 - src[1]) >> 24; + dest[2] = d * (255 - src[2]) >> 24; +} + +#define IMAGE_WALK_PREFIX(x) walk_##x +#define IMAGE_WALK_FUNC_NAME image_conv_cmyk_4_to_rgb_n +#define IMAGE_WALK_DOUBLE +#define IMAGE_WALK_SEC_COL_STEP 4 +#define IMAGE_WALK_DO_STEP do{ pixel_conv_cmyk_to_rgb(walk_pos, walk_sec_pos); }while(0) +#include "images/image-walk.h" + +static inline void +pixel_conv_rgb_to_cmyk(byte *dest, byte *src) +{ + uns k = MAX(src[0], src[1]); + k = MAX(k, src[2]); + uns d = fast_div_u32_u8(0x7fffffffU, k); /* == 0 for zero K */ + dest[0] = (d * (k - src[0])) >> 23; + dest[1] = (d * (k - src[1])) >> 23; + dest[2] = (d * (k - src[2])) >> 23; + dest[3] = 255 - k; +} + +#define IMAGE_WALK_PREFIX(x) walk_##x +#define IMAGE_WALK_FUNC_NAME image_conv_rgb_n_to_cmyk_4 +#define IMAGE_WALK_DOUBLE +#define IMAGE_WALK_COL_STEP 4 +#define IMAGE_WALK_DO_STEP do{ pixel_conv_rgb_to_cmyk(walk_pos, walk_sec_pos); }while(0) +#include "images/image-walk.h" + +/* Main functions */ + +static int +image_conv_color_space(struct image_context *ctx UNUSED, struct image *dest, struct image *src, struct image_conv_options *opt UNUSED) +{ + switch (dest->flags & IMAGE_COLOR_SPACE) { case COLOR_SPACE_GRAYSCALE: - dest[0] = dest[1] = dest[2] = color->c[0]; + switch (src->flags & IMAGE_COLOR_SPACE) + { + case COLOR_SPACE_RGB: + if (dest->pixel_size == 1) + { + image_conv_rgb_n_to_gray_1(dest, src); + return 1; + } + break; + } break; case COLOR_SPACE_RGB: - dest[0] = color->c[0]; - dest[1] = color->c[1]; - dest[2] = color->c[2]; + switch (src->flags & IMAGE_CHANNELS_FORMAT) + { + case COLOR_SPACE_GRAYSCALE: + if (src->pixel_size == 1) + { + image_conv_gray_1_to_rgb_n(dest, src); + return 1; + } + break; + case COLOR_SPACE_YCBCR: + image_conv_ycbcr_n_to_rgb_n(dest, src); + return 1; + case COLOR_SPACE_CMYK: + if (src->pixel_size == 4) + { + image_conv_cmyk_4_to_rgb_n(dest, src); + return 1; + } + break; + } break; - default: - ASSERT(0); + case COLOR_SPACE_YCBCR: + switch (src->flags & IMAGE_CHANNELS_FORMAT) + { + case COLOR_SPACE_RGB: + image_conv_rgb_n_to_ycbcr_n(dest, src); + return 1; + } + break; + case COLOR_SPACE_CMYK: + switch (src->flags & IMAGE_CHANNELS_FORMAT) + { + case COLOR_SPACE_RGB: + if (dest->pixel_size == 4) + { + image_conv_rgb_n_to_cmyk_4(dest, src); + return 1; + } + break; + } + break; } + return 0; } -void -color_put_color_space(byte *dest, struct color *color, uns color_space) +static void +image_conv_copy(struct image *dest, struct image *src) +{ + if (dest->pixels == src->pixels) + return; + else if (dest->pixel_size != src->pixel_size) + { + uns channels = MIN(dest->channels, src->channels); + switch (channels) + { + case 1: + { +# define IMAGE_WALK_PREFIX(x) walk_##x +# define IMAGE_WALK_INLINE +# define IMAGE_WALK_DOUBLE +# define IMAGE_WALK_IMAGE dest +# define IMAGE_WALK_SEC_IMAGE src +# define IMAGE_WALK_UNROLL 4 +# define IMAGE_WALK_DO_STEP do{ walk_pos[0] = walk_sec_pos[0]; }while(0) +# include "images/image-walk.h" + } + return; + case 2: +# define IMAGE_WALK_PREFIX(x) walk_##x +# define IMAGE_WALK_INLINE +# define IMAGE_WALK_DOUBLE +# define IMAGE_WALK_IMAGE dest +# define IMAGE_WALK_SEC_IMAGE src +# define IMAGE_WALK_UNROLL 4 +# define IMAGE_WALK_DO_STEP do{ walk_pos[0] = walk_sec_pos[0]; walk_pos[1] = walk_sec_pos[1]; }while(0) +# include "images/image-walk.h" + return; + case 3: +# define IMAGE_WALK_PREFIX(x) walk_##x +# define IMAGE_WALK_INLINE +# define IMAGE_WALK_DOUBLE +# define IMAGE_WALK_IMAGE dest +# define IMAGE_WALK_SEC_IMAGE src +# define IMAGE_WALK_UNROLL 2 +# 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) +# include "images/image-walk.h" + return; + case 4: +# define IMAGE_WALK_PREFIX(x) walk_##x +# define IMAGE_WALK_INLINE +# define IMAGE_WALK_DOUBLE +# define IMAGE_WALK_IMAGE dest +# define IMAGE_WALK_SEC_IMAGE src +# define IMAGE_WALK_UNROLL 2 +# 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) +# include "images/image-walk.h" + return; + default: +# define IMAGE_WALK_PREFIX(x) walk_##x +# define IMAGE_WALK_INLINE +# define IMAGE_WALK_DOUBLE +# define IMAGE_WALK_IMAGE dest +# define IMAGE_WALK_SEC_IMAGE src +# define IMAGE_WALK_DO_STEP do{ for (uns i = 0; i < channels; i++) walk_pos[i] = walk_sec_pos[i]; }while(0) +# include "images/image-walk.h" + return; + } + } + else if (dest->row_size != src->row_size || ((dest->flags | src->flags) & IMAGE_GAPS_PROTECTED)) + { + byte *s = src->pixels; + byte *d = dest->pixels; + for (uns row = src->rows; row--; ) + { + memcpy(d, s, src->row_pixels_size); + d += dest->row_size; + s += src->row_size; + } + } + else if (dest->pixels != src->pixels) + memcpy(dest->pixels, src->pixels, src->image_size); +} + +static void +image_conv_fill_alpha(struct image *dest) { - switch (color_space) + switch (dest->channels) { - case COLOR_SPACE_GRAYSCALE: - color_put_grayscale(dest, color); + case 2: + if (dest->pixel_size == 2) + { +# define IMAGE_WALK_PREFIX(x) walk_##x +# define IMAGE_WALK_INLINE +# define IMAGE_WALK_IMAGE dest +# define IMAGE_WALK_COL_STEP 2 +# define IMAGE_WALK_UNROLL 4 +# define IMAGE_WALK_DO_STEP do{ walk_pos[1] = 255; }while(0) +# include "images/image-walk.h" + return; + } break; - case COLOR_SPACE_RGB: - color_put_rgb(dest, color); + case 4: + if (dest->pixel_size == 4) + { +# define IMAGE_WALK_PREFIX(x) walk_##x +# define IMAGE_WALK_INLINE +# define IMAGE_WALK_IMAGE dest +# define IMAGE_WALK_COL_STEP 4 +# define IMAGE_WALK_UNROLL 4 +# define IMAGE_WALK_DO_STEP do{ walk_pos[3] = 255; }while(0) +# include "images/image-walk.h" + return; + } break; - default: - ASSERT(0); } + { +# define IMAGE_WALK_PREFIX(x) walk_##x +# define IMAGE_WALK_INLINE +# define IMAGE_WALK_IMAGE dest +# define IMAGE_WALK_UNROLL 4 +# define IMAGE_WALK_DO_STEP do{ walk_pos[dest->channels - 1] = 255; }while(0) +# include "images/image-walk.h" + } +} + +static void +image_conv_copy_alpha(struct image *dest, struct image *src) +{ + if (dest->pixels != src->pixels || dest->channels != src->channels) + { +# define IMAGE_WALK_PREFIX(x) walk_##x +# define IMAGE_WALK_INLINE +# define IMAGE_WALK_DOUBLE +# define IMAGE_WALK_IMAGE dest +# define IMAGE_WALK_SEC_IMAGE src +# define IMAGE_WALK_UNROLL 4 +# define IMAGE_WALK_DO_STEP do{ walk_pos[dest->channels - 1] = walk_sec_pos[src->channels - 1]; }while(0) +# include "images/image-walk.h" + } +} + +static inline uns +image_conv_alpha_func(uns value, uns alpha, uns acoef, uns bcoef) +{ + return ((uns)(acoef + (int)alpha * (int)(value - bcoef)) * (0xffffffffU / 255 / 255)) >> 24; +} + +static int +image_conv_apply_alpha_from(struct image_context *ctx, struct image *dest, struct image *src, struct image_conv_options *opt) +{ + if (!opt->background.color_space) + return 1; + byte background[IMAGE_MAX_CHANNELS]; + if (unlikely(!color_put(ctx, &opt->background, background, dest->flags & IMAGE_COLOR_SPACE))) + return 0; + uns a[IMAGE_MAX_CHANNELS], b[IMAGE_MAX_CHANNELS]; + for (uns i = 0; i < dest->channels; i++) + a[i] = 255 * (b[i] = background[i]); + switch (dest->channels) + { + case 1: + { +# define IMAGE_WALK_PREFIX(x) walk_##x +# define IMAGE_WALK_INLINE +# define IMAGE_WALK_IMAGE dest +# define IMAGE_WALK_SEC_IMAGE src +# define IMAGE_WALK_DOUBLE +# define IMAGE_WALK_UNROLL 2 +# define IMAGE_WALK_DO_STEP do{ \ + walk_pos[0] = image_conv_alpha_func(walk_pos[0], walk_sec_pos[src->channels - 1], a[0], b[0]); }while(0) +# include "images/image-walk.h" + } + return 1; + case 3: + { +# define IMAGE_WALK_PREFIX(x) walk_##x +# define IMAGE_WALK_INLINE +# define IMAGE_WALK_IMAGE dest +# define IMAGE_WALK_SEC_IMAGE src +# define IMAGE_WALK_DOUBLE +# define IMAGE_WALK_DO_STEP do{ \ + walk_pos[0] = image_conv_alpha_func(walk_pos[0], walk_sec_pos[src->channels - 1], a[0], b[0]); \ + walk_pos[1] = image_conv_alpha_func(walk_pos[1], walk_sec_pos[src->channels - 1], a[1], b[1]); \ + walk_pos[2] = image_conv_alpha_func(walk_pos[2], walk_sec_pos[src->channels - 1], a[2], b[2]); }while(0) +# include "images/image-walk.h" + } + return 1; + } + { +# define IMAGE_WALK_PREFIX(x) walk_##x +# define IMAGE_WALK_INLINE +# define IMAGE_WALK_IMAGE dest +# define IMAGE_WALK_SEC_IMAGE src +# define IMAGE_WALK_DOUBLE +# define IMAGE_WALK_DO_STEP do{ for (uns i = 0; i < dest->channels; i++) \ + walk_pos[i] = image_conv_alpha_func(walk_pos[i], walk_sec_pos[src->channels - 1], a[i], b[i]); }while(0) +# include "images/image-walk.h" + } + return 1; +} + +static int +image_conv_apply_alpha_to(struct image_context *ctx, struct image *dest, struct image *src, struct image_conv_options *opt) +{ + if (!opt->background.color_space) + { + image_conv_copy(dest, src); + return 1; + } + byte background[IMAGE_MAX_CHANNELS]; + if (unlikely(!color_put(ctx, &opt->background, background, dest->flags & IMAGE_COLOR_SPACE))) + return 0; + uns a[IMAGE_MAX_CHANNELS], b[IMAGE_MAX_CHANNELS]; + for (uns i = 0; i < dest->channels; i++) + a[i] = 255 * (b[i] = background[i]); + switch (dest->channels) + { + case 1: + { +# define IMAGE_WALK_PREFIX(x) walk_##x +# define IMAGE_WALK_INLINE +# define IMAGE_WALK_IMAGE dest +# define IMAGE_WALK_SEC_IMAGE src +# define IMAGE_WALK_DOUBLE +# define IMAGE_WALK_UNROLL 2 +# define IMAGE_WALK_DO_STEP do{ \ + walk_pos[0] = image_conv_alpha_func(walk_sec_pos[0], walk_sec_pos[src->channels - 1], a[0], b[0]); }while(0) +# include "images/image-walk.h" + } + return 1; + case 3: + { +# define IMAGE_WALK_PREFIX(x) walk_##x +# define IMAGE_WALK_INLINE +# define IMAGE_WALK_IMAGE dest +# define IMAGE_WALK_SEC_IMAGE src +# define IMAGE_WALK_DOUBLE +# define IMAGE_WALK_DO_STEP do{ \ + walk_pos[0] = image_conv_alpha_func(walk_sec_pos[0], walk_sec_pos[src->channels - 1], a[0], b[0]); \ + walk_pos[1] = image_conv_alpha_func(walk_sec_pos[1], walk_sec_pos[src->channels - 1], a[1], b[1]); \ + walk_pos[2] = image_conv_alpha_func(walk_sec_pos[2], walk_sec_pos[src->channels - 1], a[2], b[2]); }while(0) +# include "images/image-walk.h" + } + return 1; + } + { +# define IMAGE_WALK_PREFIX(x) walk_##x +# define IMAGE_WALK_INLINE +# define IMAGE_WALK_IMAGE dest +# define IMAGE_WALK_SEC_IMAGE src +# define IMAGE_WALK_DOUBLE +# define IMAGE_WALK_DO_STEP do{ for (uns i = 0; i < dest->channels; i++) \ + walk_pos[i] = image_conv_alpha_func(walk_sec_pos[i], walk_sec_pos[src->channels - 1], a[i], b[i]); }while(0) +# include "images/image-walk.h" + } + return 1; +} + +int +image_conv(struct image_context *ctx, struct image *dest, struct image *src, struct image_conv_options *opt) +{ + ASSERT(dest->cols == src->cols && dest->rows == src->rows); + if (!((dest->flags ^ src->flags) & IMAGE_COLOR_SPACE)) + { + if (!(src->flags & IMAGE_ALPHA) || (dest->flags & IMAGE_ALPHA)) + image_conv_copy(dest, src); + else if (unlikely(!image_conv_apply_alpha_to(ctx, dest, src, opt))) + return 0; + } + else + { + if (!(src->flags & IMAGE_ALPHA)) + { + if (unlikely(!image_conv_color_space(ctx, dest, src, opt))) + goto error; + if ((dest->flags & IMAGE_ALPHA) && (opt->flags & IMAGE_CONV_FILL_ALPHA)) + image_conv_fill_alpha(dest); + } + else + { + if (dest->flags & IMAGE_ALPHA) + { + if (dest->channels <= src->channels) + { + if (unlikely(!image_conv_color_space(ctx, dest, src, opt))) + goto error; + if (opt->flags & IMAGE_CONV_COPY_ALPHA) + image_conv_copy_alpha(dest, src); + else if (opt->flags & IMAGE_CONV_FILL_ALPHA) + image_conv_fill_alpha(dest); + } + else + { + if (opt->flags & IMAGE_CONV_COPY_ALPHA) + image_conv_copy_alpha(dest, src); + else + image_conv_fill_alpha(dest); + if (unlikely(!image_conv_color_space(ctx, dest, src, opt))) + goto error; + } + } + else + { + if (dest->channels <= src->channels) + { + if (unlikely(!image_conv_color_space(ctx, dest, src, opt))) + goto error; + if (unlikely(!image_conv_apply_alpha_from(ctx, dest, src, opt))) + return 0; + } + else + { + if (unlikely(!image_conv_apply_alpha_to(ctx, dest, src, opt))) + return 0; + if (unlikely(!image_conv_color_space(ctx, dest, dest, opt))) + goto error; + } + } + } + } + return 1; +error: + IMAGE_ERROR(ctx, IMAGE_ERROR_INVALID_PIXEL_FORMAT, "Image conversion not supported for such pixel formats"); + return 0; } /********************* EXACT CONVERSION ROUTINES **********************/ @@ -365,6 +884,36 @@ luv_to_xyz_exact(double xyz[3], double luv[3]) xyz[2] = (9 * xyz[1] - 15 * var_v * xyz[1] - var_v * xyz[0]) / (3 * var_v); } +/* RGB to CMYK - a very simple version, not too accureate */ +void +rgb_to_cmyk_exact(double cmyk[4], double rgb[3]) +{ + cmyk[0] = 1 - rgb[0]; + cmyk[1] = 1 - rgb[1]; + cmyk[2] = 1 - rgb[2]; + cmyk[3] = MIN(cmyk[0], cmyk[1]); + cmyk[3] = MIN(cmyk[3], cmyk[2]); + if (cmyk[3] > 0.9999) + { + cmyk[3] = 1; + cmyk[0] = cmyk[1] = cmyk[2] = 0; + } + else + { + double d = 1 / (1 - cmyk[3]); + for (uns i = 0; i < 3; i++) + cmyk[i] = d * (cmyk[i] - cmyk[3]); + } +} + +/* CMYK to RGB */ +void +cmyk_to_rgb_exact(double rgb[3], double cmyk[4]) +{ + double d = 1 - cmyk[1]; + for (uns i = 0; i < 3; i++) + rgb[i] = d * (1 - cmyk[i]); +} /***************** OPTIMIZED SRGB -> LUV CONVERSION *********************/ diff --git a/images/color.h b/images/color.h index 7ec32acc..a148e6c3 100644 --- a/images/color.h +++ b/images/color.h @@ -27,12 +27,13 @@ #include "images/images.h" +/* Basic color spaces */ enum { COLOR_SPACE_UNKNOWN = 0, - COLOR_SPACE_UNKNOWN_1 = 1, - COLOR_SPACE_UNKNOWN_2 = 2, - COLOR_SPACE_UNKNOWN_3 = 3, - COLOR_SPACE_UNKNOWN_4 = 4, + COLOR_SPACE_UNKNOWN_1 = 1, /* unknown 1-channel color space */ + COLOR_SPACE_UNKNOWN_2 = 2, /* unknown 2-channels color space */ + COLOR_SPACE_UNKNOWN_3 = 3, /* unknown 3-channels color space */ + COLOR_SPACE_UNKNOWN_4 = 4, /* unknown 4-channels color space */ COLOR_SPACE_UNKNOWN_MAX = 4, COLOR_SPACE_GRAYSCALE, COLOR_SPACE_RGB, @@ -48,9 +49,49 @@ enum { extern uns color_space_channels[COLOR_SPACE_MAX]; extern byte *color_space_name[COLOR_SPACE_MAX]; +/* Color space ID <-> name conversions */ byte *color_space_id_to_name(uns id); uns color_space_name_to_id(byte *name); +/* Struct color manipulation */ +int color_get(struct color *color, byte *src, uns src_space); +int color_put(struct image_context *ctx, struct color *color, byte *dest, uns dest_space); + +static inline void +color_make_gray(struct color *color, uns gray) +{ + color->c[0] = gray; + color->color_space = COLOR_SPACE_GRAYSCALE; +} + +static inline void +color_make_rgb(struct color *color, uns r, uns g, uns b) +{ + color->c[0] = r; + color->c[1] = g; + color->c[2] = b; + color->color_space = COLOR_SPACE_RGB; +} + +extern struct color color_black, color_white; + +/* Conversion between various pixel formats */ + +enum { + IMAGE_CONV_FILL_ALPHA = 1, + IMAGE_CONV_COPY_ALPHA = 2, + IMAGE_CONV_APPLY_ALPHA = 4, +}; + +struct image_conv_options { + uns flags; + struct color background; +}; + +extern struct image_conv_options image_conv_defaults; + +int image_conv(struct image_context *ctx, struct image *dest, struct image *src, struct image_conv_options *opt); + /* Color spaces in the CIE 1931 chromacity diagram */ struct color_space_chromacity_info { @@ -98,33 +139,13 @@ rgb_to_gray_func(uns r, uns g, uns b) return (r * 19660 + g * 38666 + b * 7210) >> 16; } -extern struct color color_black, color_white; - -static inline void -color_make_gray(struct color *color, uns gray) -{ - color->c[0] = gray; - color->color_space = COLOR_SPACE_GRAYSCALE; -} - -static inline void -color_make_rgb(struct color *color, uns r, uns g, uns b) -{ - color->c[0] = r; - color->c[1] = g; - color->c[2] = b; - color->color_space = COLOR_SPACE_RGB; -} - -void color_put_color_space(byte *dest, struct color *color, uns color_space); -void color_put_grayscale(byte *dest, struct color *color); -void color_put_rgb(byte *dest, struct color *color); - /* Exact slow conversion routines */ void srgb_to_xyz_exact(double dest[3], double src[3]); void xyz_to_srgb_exact(double dest[3], double src[3]); void xyz_to_luv_exact(double dest[3], double src[3]); void luv_to_xyz_exact(double dest[3], double src[3]); +void rgb_to_cmyk_exact(double dest[4], double src[3]); +void cmyk_to_rgb_exact(double dest[3], double src[4]); /* Reference white */ #define REF_WHITE_X 0.96422 diff --git a/images/image-tool.c b/images/image-tool.c index 5063d9b6..e1717569 100644 --- a/images/image-tool.c +++ b/images/image-tool.c @@ -184,7 +184,7 @@ main(int argc, char **argv) if (io.background_color.color_space) { byte rgb[3]; - color_put_rgb(rgb, &io.background_color); + TRY(color_put(&ctx, &io.background_color, rgb, COLOR_SPACE_RGB)); printf("Background: %02x%02x%02x\n", rgb[0], rgb[1], rgb[2]); } if (io.exif_size) diff --git a/images/image-walk.h b/images/image-walk.h index dfc72a19..96bfd5cb 100644 --- a/images/image-walk.h +++ b/images/image-walk.h @@ -83,7 +83,12 @@ #endif #ifndef IMAGE_WALK_INLINE -static void P(walk) +static void +#ifdef IMAGE_WALK_FUNC_NAME +IMAGE_WALK_FUNC_NAME +#else +P(walk) +#endif (struct image *P(img) # ifdef IMAGE_WALK_DOUBLE , struct image *P(sec_img) @@ -143,6 +148,7 @@ static void P(walk) } #undef IMAGE_WALK_PREFIX +#undef IMAGE_WALK_FUNC_NAME #undef IMAGE_WALK_INLINE #undef IMAGE_WALK_UNROLL #undef IMAGE_WALK_DOUBLE diff --git a/images/image.c b/images/image.c index 26445aa3..058b173e 100644 --- a/images/image.c +++ b/images/image.c @@ -41,7 +41,7 @@ image_new(struct image_context *ctx, uns cols, uns rows, uns flags, struct mempo pixel_size = channels = flags_to_pixel_size(flags); if (!channels || channels > 4) { - IMAGE_ERROR(ctx, IMAGE_ERROR_INVALID_PIXEL_FORMAT, "Invalid number of color channels"); + IMAGE_ERROR(ctx, IMAGE_ERROR_INVALID_PIXEL_FORMAT, "Invalid number of color channels (%u)", channels); return NULL; } switch (channels) diff --git a/images/images.h b/images/images.h index f35caf59..5449519c 100644 --- a/images/images.h +++ b/images/images.h @@ -68,12 +68,22 @@ enum image_flag { IMAGE_INTERNAL_FLAGS = IMAGE_NEED_DESTROY | IMAGE_GAPS_PROTECTED, }; +#define IMAGE_MAX_CHANNELS 4 +#define IMAGE_CHANNELS_FORMAT_MAX_SIZE 128 +byte *image_channels_format_to_name(uns format, byte *buf); +uns image_name_to_channels_format(byte *name); + +struct color { + byte c[IMAGE_MAX_CHANNELS]; + byte color_space; +}; + struct image { byte *pixels; /* aligned top left pixel, there are at least sizeof(uns) unused bytes after the buffer (possible optimizations) */ uns cols; /* number of columns */ uns rows; /* number of rows */ - uns channels; /* number of color channels */ + uns channels; /* number of color channels including the alpha channel */ uns pixel_size; /* size of pixel in bytes (1, 2, 3 or 4) */ uns row_size; /* scanline size in bytes */ uns row_pixels_size; /* scanline size in bytes excluding rows gaps */ @@ -93,25 +103,11 @@ image_dimensions_valid(uns cols, uns rows) { return cols && rows && cols <= image_max_dim && rows <= image_max_dim; } - -#define IMAGE_CHANNELS_FORMAT_MAX_SIZE 128 -byte *image_channels_format_to_name(uns format, byte *buf); -uns image_name_to_channels_format(byte *name); - -struct color { - byte c[3]; - byte color_space; -} PACKED; - /* scale.c */ int image_scale(struct image_context *ctx, struct image *dest, struct image *src); void image_dimensions_fit_to_box(uns *cols, uns *rows, uns max_cols, uns max_rows, uns upsample); -/* alpha.c */ - -int image_apply_background(struct image_context *ctx, struct image *dest, struct image *src, struct color *background); - /* image-io.c */ enum image_format { diff --git a/images/io-libjpeg.c b/images/io-libjpeg.c index f4a72e29..03952519 100644 --- a/images/io-libjpeg.c +++ b/images/io-libjpeg.c @@ -7,7 +7,7 @@ * of the GNU Lesser General Public License. */ -#define LOCAL_DEBUG +#undef LOCAL_DEBUG #include "lib/lib.h" #include "lib/mempool.h" @@ -52,7 +52,7 @@ libjpeg_read_error_exit(j_common_ptr cinfo) struct libjpeg_err *e = (struct libjpeg_err *)cinfo->err; byte buf[JMSG_LENGTH_MAX]; e->pub.format_message(cinfo, buf); - IMAGE_ERROR(e->io->context, IMAGE_ERROR_READ_FAILED, "%s", buf); + IMAGE_ERROR(e->io->context, IMAGE_ERROR_READ_FAILED, "libjpeg: %s", buf); longjmp(e->setjmp_buf, 1); } @@ -63,7 +63,7 @@ libjpeg_write_error_exit(j_common_ptr cinfo) struct libjpeg_err *e = (struct libjpeg_err *)cinfo->err; byte buf[JMSG_LENGTH_MAX]; e->pub.format_message(cinfo, buf); - IMAGE_ERROR(e->io->context, IMAGE_ERROR_WRITE_FAILED, "%s", buf); + IMAGE_ERROR(e->io->context, IMAGE_ERROR_WRITE_FAILED, "libjpeg: %s", buf); longjmp(e->setjmp_buf, 1); } @@ -336,16 +336,14 @@ libjpeg_read_data(struct image_io *io) DBG("libjpeg_read_data()"); struct libjpeg_read_internals *i = io->read_data; + uns read_flags = io->flags; /* Select color space */ - switch (io->flags & IMAGE_COLOR_SPACE) + switch (read_flags & IMAGE_COLOR_SPACE) { case COLOR_SPACE_GRAYSCALE: i->cinfo.out_color_space = JCS_GRAYSCALE; break; - case COLOR_SPACE_RGB: - i->cinfo.out_color_space = JCS_RGB; - break; case COLOR_SPACE_YCBCR: i->cinfo.out_color_space = JCS_YCbCr; break; @@ -356,9 +354,18 @@ libjpeg_read_data(struct image_io *io) i->cinfo.out_color_space = JCS_YCCK; break; default: - jpeg_destroy_decompress(&i->cinfo); - IMAGE_ERROR(io->context, IMAGE_ERROR_INVALID_PIXEL_FORMAT, "Unsupported color space."); - return 0; + switch (i->cinfo.jpeg_color_space) + { + case JCS_CMYK: + read_flags = (read_flags & ~IMAGE_COLOR_SPACE & IMAGE_CHANNELS_FORMAT) | COLOR_SPACE_CMYK; + i->cinfo.out_color_space = JCS_YCbCr; + break; + default: + read_flags = (read_flags & ~IMAGE_COLOR_SPACE & IMAGE_CHANNELS_FORMAT) | COLOR_SPACE_RGB; + i->cinfo.out_color_space = JCS_RGB; + break; + } + break; } /* Prepare the image */ @@ -383,7 +390,7 @@ libjpeg_read_data(struct image_io *io) } jpeg_calc_output_dimensions(&i->cinfo); DBG("Output dimensions %ux%u", (uns)i->cinfo.output_width, (uns)i->cinfo.output_height); - if (unlikely(!image_io_read_data_prepare(&rdi, io, i->cinfo.output_width, i->cinfo.output_height, io->flags))) + if (unlikely(!image_io_read_data_prepare(&rdi, io, i->cinfo.output_width, i->cinfo.output_height, read_flags))) { jpeg_destroy_decompress(&i->cinfo); return 0; @@ -401,7 +408,7 @@ libjpeg_read_data(struct image_io *io) /* Decompress the image */ struct image *img = rdi.image; jpeg_start_decompress(&i->cinfo); - if ((int)img->pixel_size == i->cinfo.num_components) + if ((int)img->pixel_size == i->cinfo.output_components) { byte *pixels = img->pixels; for (uns r = img->rows; r--; ) @@ -416,7 +423,7 @@ libjpeg_read_data(struct image_io *io) { case 2: /* Grayscale -> Grayscale+Alpha */ { - ASSERT(i->cinfo.num_components == 1); + ASSERT(i->cinfo.output_components == 1); byte buf[img->cols], *src; # define IMAGE_WALK_PREFIX(x) walk_##x # define IMAGE_WALK_INLINE @@ -430,7 +437,7 @@ libjpeg_read_data(struct image_io *io) break; case 4: /* * -> *+Alpha or aligned * */ { - ASSERT(i->cinfo.num_components == 3); + ASSERT(i->cinfo.output_components == 3); byte buf[img->cols * 3], *src; # define IMAGE_WALK_PREFIX(x) walk_##x # define IMAGE_WALK_INLINE diff --git a/images/io-libmagick.c b/images/io-libmagick.c index cdf58048..60d36159 100644 --- a/images/io-libmagick.c +++ b/images/io-libmagick.c @@ -170,8 +170,11 @@ libmagick_read_data(struct image_io *io) /* Prepare the image */ struct image_io_read_data_internals rdi; uns read_flags = io->flags; + uns cs = read_flags & IMAGE_COLOR_SPACE; + if (cs != COLOR_SPACE_GRAYSCALE && cs != COLOR_SPACE_RGB) + read_flags = (read_flags & ~IMAGE_COLOR_SPACE & IMAGE_PIXEL_FORMAT) | COLOR_SPACE_RGB; if ((read_flags & IMAGE_IO_USE_BACKGROUND) && !(read_flags & IMAGE_ALPHA)) - read_flags = (read_flags | IMAGE_ALPHA) & IMAGE_CHANNELS_FORMAT; + read_flags = (read_flags & IMAGE_CHANNELS_FORMAT) | IMAGE_ALPHA; if (unlikely(!image_io_read_data_prepare(&rdi, io, rd->image->columns, rd->image->rows, read_flags))) { libmagick_destroy_read_data(rd); @@ -279,7 +282,8 @@ libmagick_write(struct image_io *io) info->colorspace = RGBColorspace; break; default: - ASSERT(0); + IMAGE_ERROR(io->context, IMAGE_ERROR_WRITE_FAILED, "Unsupported color space."); + goto err; } switch (io->format) { diff --git a/images/io-libpng.c b/images/io-libpng.c index 76b2d247..2095065f 100644 --- a/images/io-libpng.c +++ b/images/io-libpng.c @@ -188,18 +188,6 @@ libpng_read_data(struct image_io *io) struct libpng_read_data *rd = io->read_data; - /* Test supported pixel formats */ - switch (io->flags & IMAGE_COLOR_SPACE) - { - case COLOR_SPACE_GRAYSCALE: - case COLOR_SPACE_RGB: - break; - default: - png_destroy_read_struct(&rd->png_ptr, &rd->info_ptr, &rd->end_ptr); - IMAGE_ERROR(io->context, IMAGE_ERROR_INVALID_PIXEL_FORMAT, "Unsupported color space."); - return 0; - } - struct image_io_read_data_internals rdi; rdi.image = NULL; @@ -220,21 +208,24 @@ libpng_read_data(struct image_io *io) switch (rd->color_type) { case PNG_COLOR_TYPE_PALETTE: - if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE) + if ((read_flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE) { png_set_palette_to_rgb(rd->png_ptr); png_set_rgb_to_gray_fixed(rd->png_ptr, 1, 21267, 71514); } else - png_set_palette_to_rgb(rd->png_ptr); - if (!(io->flags & IMAGE_ALPHA)) + { + png_set_palette_to_rgb(rd->png_ptr); + read_flags = (read_flags & ~IMAGE_COLOR_SPACE & IMAGE_CHANNELS_FORMAT) | COLOR_SPACE_RGB; + } + if (!(read_flags & IMAGE_ALPHA)) { if (io->flags & IMAGE_IO_USE_BACKGROUND) { png_set_add_alpha(rd->png_ptr, 255, PNG_FILLER_AFTER); - read_flags = (read_flags | IMAGE_ALPHA) & IMAGE_CHANNELS_FORMAT; + read_flags = (read_flags & IMAGE_CHANNELS_FORMAT) | IMAGE_ALPHA; } - else if ((io->flags & IMAGE_PIXEL_FORMAT) == (COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED)) + else if ((read_flags & IMAGE_PIXEL_FORMAT) == (COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED)) png_set_add_alpha(rd->png_ptr, 255, PNG_FILLER_AFTER); else png_set_strip_alpha(rd->png_ptr); @@ -243,35 +234,45 @@ libpng_read_data(struct image_io *io) png_set_add_alpha(rd->png_ptr, 255, PNG_FILLER_AFTER); break; case PNG_COLOR_TYPE_GRAY: - if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_RGB) - png_set_gray_to_rgb(rd->png_ptr); - if (io->flags & IMAGE_ALPHA) + if ((read_flags & IMAGE_COLOR_SPACE) != COLOR_SPACE_GRAYSCALE) + { + png_set_gray_to_rgb(rd->png_ptr); + read_flags = (read_flags & ~IMAGE_COLOR_SPACE & IMAGE_CHANNELS_FORMAT) | COLOR_SPACE_RGB; + } + if (read_flags & IMAGE_ALPHA) png_set_add_alpha(rd->png_ptr, 255, PNG_FILLER_AFTER); break; case PNG_COLOR_TYPE_GRAY_ALPHA: - if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_RGB) - png_set_gray_to_rgb(rd->png_ptr); - if (!(io->flags & IMAGE_ALPHA)) + if ((read_flags & IMAGE_COLOR_SPACE) != COLOR_SPACE_GRAYSCALE) + { + png_set_gray_to_rgb(rd->png_ptr); + read_flags = (read_flags & ~IMAGE_COLOR_SPACE & IMAGE_CHANNELS_FORMAT) | COLOR_SPACE_RGB; + } + if (!(read_flags & IMAGE_ALPHA)) { if (io->flags & IMAGE_IO_USE_BACKGROUND) - read_flags = (read_flags | IMAGE_ALPHA) & IMAGE_CHANNELS_FORMAT; + read_flags = (read_flags & IMAGE_CHANNELS_FORMAT) | IMAGE_ALPHA; else png_set_strip_alpha(rd->png_ptr); } break; case PNG_COLOR_TYPE_RGB: - if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE) + if ((read_flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE) png_set_rgb_to_gray_fixed(rd->png_ptr, 1, 21267, 71514); - if ((io->flags & IMAGE_ALPHA) || (io->flags & IMAGE_PIXEL_FORMAT) == (COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED)) + else + read_flags = (read_flags & ~IMAGE_COLOR_SPACE & IMAGE_CHANNELS_FORMAT) | COLOR_SPACE_RGB; + if ((read_flags & IMAGE_ALPHA) || (read_flags & IMAGE_PIXEL_FORMAT) == (COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED)) png_set_add_alpha(rd->png_ptr, 255, PNG_FILLER_AFTER); break; case PNG_COLOR_TYPE_RGB_ALPHA: - if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE) + if ((read_flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE) png_set_rgb_to_gray_fixed(rd->png_ptr, 1, 21267, 71514); - if (!(io->flags & IMAGE_ALPHA)) + else + read_flags = (read_flags & ~IMAGE_COLOR_SPACE & IMAGE_CHANNELS_FORMAT) | COLOR_SPACE_RGB; + if (!(read_flags & IMAGE_ALPHA)) if (io->flags & IMAGE_IO_USE_BACKGROUND) - read_flags = (read_flags | IMAGE_ALPHA) & IMAGE_CHANNELS_FORMAT; - else if ((io->flags & IMAGE_PIXEL_FORMAT) != (COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED)) + read_flags = (read_flags & IMAGE_CHANNELS_FORMAT) | IMAGE_ALPHA; + else if ((read_flags & IMAGE_PIXEL_FORMAT) != (COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED)) png_set_strip_alpha(rd->png_ptr); break; default: @@ -361,7 +362,9 @@ libpng_write(struct image_io *io) png_set_filler(png_ptr, 0, PNG_FILLER_AFTER); break; default: - ASSERT(0); + IMAGE_ERROR(io->context, IMAGE_ERROR_WRITE_FAILED, "Libpng does not support this pixel format (0x%x)", img->flags & IMAGE_PIXEL_FORMAT); + png_destroy_write_struct(&png_ptr, &info_ptr); + return 0; } png_write_info(png_ptr, info_ptr); diff --git a/images/io-libungif.c b/images/io-libungif.c index c4648431..9e19de26 100644 --- a/images/io-libungif.c +++ b/images/io-libungif.c @@ -147,7 +147,11 @@ libungif_read_data(struct image_io *io) /* Prepare image */ struct image_io_read_data_internals rdi; - if (unlikely(!image_io_read_data_prepare(&rdi, io, image->ImageDesc.Width, image->ImageDesc.Height, io->flags))) + uns read_flags = io->flags; + uns cs = read_flags & IMAGE_COLOR_SPACE; + if (cs != COLOR_SPACE_GRAYSCALE && cs != COLOR_SPACE_RGB) + read_flags = (read_flags & ~IMAGE_COLOR_SPACE & IMAGE_CHANNELS_FORMAT) | COLOR_SPACE_RGB; + if (unlikely(!image_io_read_data_prepare(&rdi, io, image->ImageDesc.Width, image->ImageDesc.Height, read_flags))) { DGifCloseFile(gif); return 0; @@ -180,7 +184,11 @@ libungif_read_data(struct image_io *io) if (pal_pos != pal_end) bzero(pal_pos, pal_end - pal_pos); if (rd->transparent_index >= 0 && (io->flags & IMAGE_IO_USE_BACKGROUND)) - color_put_grayscale(pal + rd->transparent_index, &io->background_color); + if (!color_put(io->context, &io->background_color, pal + rd->transparent_index, COLOR_SPACE_GRAYSCALE)) + { + DGifCloseFile(gif); + return 0; + } # define DO_ROW_END do{ \ walk_row_start += dein_step; \ if (walk_row_start >= img_end) \ @@ -232,7 +240,11 @@ libungif_read_data(struct image_io *io) if (pal_pos != pal_end) bzero(pal_pos, pal_end - pal_pos); if (rd->transparent_index >= 0 && (io->flags & IMAGE_IO_USE_BACKGROUND)) - color_put_rgb(pal + 4 * rd->transparent_index, &io->background_color); + if (!color_put(io->context, &io->background_color, pal + 4 * rd->transparent_index, COLOR_SPACE_RGB)) + { + DGifCloseFile(gif); + return 0; + } # define IMAGE_WALK_PREFIX(x) walk_##x # define IMAGE_WALK_INLINE # define IMAGE_WALK_IMAGE (rdi.image) diff --git a/images/io-main.c b/images/io-main.c index 7b176b70..3d02a9e5 100644 --- a/images/io-main.c +++ b/images/io-main.c @@ -14,6 +14,7 @@ #include "images/images.h" #include "images/error.h" #include "images/io-main.h" +#include "images/color.h" #include @@ -341,20 +342,18 @@ image_io_read_data_finish(struct image_io_read_data_internals *rdi, struct image rdi->image = img; } - /* Merge with background */ - if ((io->flags ^ rdi->image->flags) & IMAGE_ALPHA) + /* Convert pixel format */ + if (io->flags != rdi->image->flags) { - DBG("Applying background"); - uns flags = rdi->image->flags & ~IMAGE_ALPHA; - if (!(rdi->need_transformations = (flags ^ io->flags) & (IMAGE_NEW_FLAGS & ~IMAGE_PIXELS_ALIGNED))) - flags = io->flags; - struct image *img = image_new(io->context, io->cols, io->rows, flags, rdi->need_transformations ? NULL : io->pool); + struct image *img = image_new(io->context, io->cols, io->rows, io->flags, io->pool); if (unlikely(!img)) { image_destroy(rdi->image); return 0; } - if (unlikely(!image_apply_background(io->context, img, rdi->image, &io->background_color))) + struct image_conv_options opt = image_conv_defaults; + opt.background = io->background_color; + if (unlikely(!image_conv(io->context, img, rdi->image, &opt))) { image_destroy(rdi->image); image_destroy(img); @@ -363,10 +362,6 @@ image_io_read_data_finish(struct image_io_read_data_internals *rdi, struct image image_destroy(rdi->image); rdi->image = img; } - - // FIXME: support for various color spaces - - ASSERT(!rdi->need_transformations); } /* Success */ diff --git a/images/math.h b/images/math.h index 1824c95b..a4d56167 100644 --- a/images/math.h +++ b/images/math.h @@ -13,17 +13,7 @@ isqr(int x) static inline uns fast_div_u32_u8(uns x, uns y) { -#ifdef CPU_I386 - int ret, dmy; - asm volatile ( - "mull %3" - :"=d"(ret),"=a"(dmy) - :"1"(x),"g"(fast_div_tab[y]) - ); - return ret; -#else return ((u64)(x) * fast_div_tab[y]) >> 32; -#endif } static inline uns -- 2.39.2