From 6a4b173e7d57907f20a4397e68d277d74a2bc840 Mon Sep 17 00:00:00 2001 From: Pavel Charvat Date: Tue, 25 Jul 2006 08:23:41 +0200 Subject: [PATCH] some bugfixes reading of images via GraphicsMagick --- images/image.c | 16 +-- images/images.h | 5 +- images/io-libjpeg.c | 4 +- images/io-libmagick.c | 276 +++++++++++++++++++++++++++++++----------- images/io-main.c | 6 +- images/scale.c | 4 +- 6 files changed, 225 insertions(+), 86 deletions(-) diff --git a/images/image.c b/images/image.c index 4fb349de..1db02b10 100644 --- a/images/image.c +++ b/images/image.c @@ -14,7 +14,7 @@ #include "images/images.h" #include -#define MAX_IMAGE_SIZE (1 << 30) +#define MAX_IMAGE_BYTES (1 << 30) void image_thread_init(struct image_thread *it) @@ -45,7 +45,7 @@ struct image * image_new(struct image_thread *it, uns cols, uns rows, uns flags, struct mempool *pool) { DBG("image_new(cols=%u rows=%u flags=0x%x pool=%p)", cols, rows, flags, pool); - if (cols >= 0x10000 || rows >= 0x10000) + if (cols > IMAGE_MAX_SIZE || rows > IMAGE_MAX_SIZE) { image_thread_err(it, IMAGE_ERR_INVALID_DIMENSIONS, "Image dimension(s) too large"); return NULL; @@ -80,15 +80,16 @@ image_new(struct image_thread *it, uns cols, uns rows, uns flags, struct mempool ASSERT(0); } if (flags & IMAGE_SSE_ALIGNED) - align = MAX(16, sizeof(uns)); + align = IMAGE_SSE_ALIGN_SIZE; else if (flags & IMAGE_PIXELS_ALIGNED) align = pixel_size; else align = 1; row_size = cols * pixel_size; row_size = ALIGN(row_size, align); - u64 image_size_64 = row_size * rows; - if (image_size_64 > MAX_IMAGE_SIZE) + u64 image_size_64 = (u64)row_size * rows; + u64 bytes_64 = image_size_64 + (sizeof(struct image) + IMAGE_SSE_ALIGN_SIZE - 1 + sizeof(uns)); + if (bytes_64 > MAX_IMAGE_BYTES) { image_thread_err(it, IMAGE_ERR_INVALID_DIMENSIONS, "Image does not fit in memory"); return NULL; @@ -98,11 +99,10 @@ image_new(struct image_thread *it, uns cols, uns rows, uns flags, struct mempool image_thread_err(it, IMAGE_ERR_INVALID_DIMENSIONS, "Zero dimension(s)"); return NULL; } - uns size = sizeof(struct image) + image_size + MAX(16, sizeof(uns)) - 1 + sizeof(uns); - img = pool ? mp_alloc(pool, size) : xmalloc(size); + img = pool ? mp_alloc(pool, (uns)bytes_64) : xmalloc((uns)bytes_64); bzero(img, sizeof(struct image)); byte *p = (byte *)img + sizeof(struct image); - img->pixels = ALIGN_PTR(p, MAX(16, sizeof(uns))); + img->pixels = ALIGN_PTR(p, IMAGE_SSE_ALIGN_SIZE); img->flags = flags; img->pixel_size = pixel_size; img->cols = cols; diff --git a/images/images.h b/images/images.h index ee68a52b..91c43d19 100644 --- a/images/images.h +++ b/images/images.h @@ -47,6 +47,9 @@ void image_thread_err_format(struct image_thread *thread, uns code, char *msg, . /* basic image manupulation */ +#define IMAGE_MAX_SIZE 0xffffU /* maximum number of cols/rows, must be <(1<<16) */ +#define IMAGE_SSE_ALIGN_SIZE (MAX(16, sizeof(uns))) + enum color_space { COLOR_SPACE_UNKNOWN, COLOR_SPACE_GRAYSCALE, @@ -92,7 +95,7 @@ void image_dimensions_fit_to_box(u32 *cols, u32 *rows, u32 max_cols, u32 max_row /* image-io.c */ enum image_format { - IMAGE_FORMAT_UNKNOWN, + IMAGE_FORMAT_UNDEFINED, IMAGE_FORMAT_JPEG, IMAGE_FORMAT_PNG, IMAGE_FORMAT_GIF, diff --git a/images/io-libjpeg.c b/images/io-libjpeg.c index dfe44ddf..bcfa135f 100644 --- a/images/io-libjpeg.c +++ b/images/io-libjpeg.c @@ -435,12 +435,12 @@ libjpeg_write(struct image_io *io) /* RGBA (ignore alpha) or aligned RGB */ case 4: { - byte buf[img->cols * 3 + 1], *dest = buf; + byte buf[img->cols * 3], *dest = buf; # define IMAGE_WALK_INLINE # define IMAGE_WALK_UNROLL 4 # define IMAGE_WALK_COL_STEP 4 # define IMAGE_WALK_DO_ROW_END do{ dest = buf; jpeg_write_scanlines(&i.cinfo, (JSAMPLE **)&dest, 1); }while(0) -# define IMAGE_WALK_DO_STEP do{ *(u32 *)dest = *(u32 *)pos; dest += 3; }while(0) +# define IMAGE_WALK_DO_STEP do{ *dest++ = pos[0]; *dest++ = pos[1]; *dest++ = pos[2]; }while(0) # include "images/image-walk.h" } break; diff --git a/images/io-libmagick.c b/images/io-libmagick.c index d48553a4..7ec37930 100644 --- a/images/io-libmagick.c +++ b/images/io-libmagick.c @@ -1,5 +1,5 @@ /* - * Image Library -- GrapgicsMagick + * Image Library -- GraphicsMagick * * (c) 2006 Pavel Charvat * @@ -10,105 +10,241 @@ #define LOCAL_DEBUG #include "lib/lib.h" +#include "lib/mempool.h" +#include "lib/fastbuf.h" #include "images/images.h" #include #include #include #include -int -libmagick_read_header(struct image_io *io) -{ - image_thread_err(io->thread, IMAGE_ERR_NOT_IMPLEMENTED, "GraphicsMagick read not implemented."); - return 0; -} - -int -libmagick_read_data(struct image_io *io UNUSED) -{ - ASSERT(0); -} - -int -libmagick_write(struct image_io *io) -{ - image_thread_err(io->thread, IMAGE_ERR_NOT_IMPLEMENTED, "GraphicsMagick write not implemented."); - return 0; -} +#define MAX_FILE_SIZE (1 << 30) +#define QUANTUM_SCALE (QuantumDepth - 8) +#define QUANTUM_TO_BYTE(x) ((uns)(x) >> QUANTUM_SCALE) -#if 0 -struct magick_internals { +struct magick_read_data { ExceptionInfo exception; - QuantizeInfo quantize; ImageInfo *info; + Image *image; }; static inline void -magick_cleanup(struct image_io *io) +libmagick_destroy_read_data(struct magick_read_data *rd) { - DestroyImageInfo(io->internals->info); - DestroyExceptionInfo(&io->internals->exception); + if (rd->image) + DestroyImage(rd->image); + DestroyImageInfo(rd->info); + DestroyExceptionInfo(&rd->exception); DestroyMagick(); } -static int -magick_read_header(struct image_io *io) +static void +libmagick_read_cancel(struct image_io *io) { - DBG("magick_read_header()"); - struct magick_internals *i = io->internals = mp_alloc(io->pool, sizeof(*i)); + DBG("libmagick_read_cancel()"); - InitializeMagick(NULL); - GetExceptionInfo(&i->exception); - i->info = CloneImageInfo(NULL); - i->info->subrange = 1; - GetQuantizeInfo(&i->quantize); - i->quantize.colorspace = RGBColorspace; + struct magick_read_data *rd = io->read_data; + + DestroyImage(rd->image); + libmagick_destroy_read_data(rd); +} - uns len = bfilesize(io->fastbuf); - byte *buf = mp_alloc(io->pool, len); - len = bread(io->fastbuf, buf, len); +int +libmagick_read_header(struct image_io *io) +{ + DBG("libmagick_read_header()"); - Image *image = BlobToImage(magick_info, imo->thumb_data, imo->thumb_size, &magick_exception); - if (unlikely(!image)) - goto error; + /* Read entire stream */ + sh_off_t file_size = bfilesize(io->fastbuf) - btell(io->fastbuf); + if (file_size > MAX_FILE_SIZE) + { + image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Too long stream."); + return 0; + } + uns buf_size = file_size; + byte *buf = xmalloc(buf_size); + bread(io->fastbuf, buf, buf_size); + + /* Allocate read structure */ + struct magick_read_data *rd = io->read_data = mp_alloc(io->internal_pool, sizeof(*rd)); + + /* Initialize GraphicsMagick */ + InitializeMagick(NULL); + GetExceptionInfo(&rd->exception); + rd->info = CloneImageInfo(NULL); + rd->info->subrange = 1; + + /* Read the image */ + rd->image = BlobToImage(rd->info, buf, buf_size, &rd->exception); + xfree(buf); + if (!rd->image) + { + image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "GraphicsMagick failed to read the image."); + goto err; + } + if (rd->image->columns > IMAGE_MAX_SIZE || rd->image->rows > IMAGE_MAX_SIZE) + { + image_thread_err(io->thread, IMAGE_ERR_INVALID_DIMENSIONS, "Image too large."); + goto err; + } - // FIXME + /* Fill image parameters */ + if (!io->cols) + io->cols = rd->image->columns; + if (!io->rows) + io->rows = rd->image->rows; + if (!(io->flags & IMAGE_CHANNELS_FORMAT)) + { + switch (rd->image->colorspace) + { + case GRAYColorspace: + io->flags |= COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA; + break; + default: + io->flags |= COLOR_SPACE_RGB | IMAGE_ALPHA; + break; + } + } + + io->read_cancel = libmagick_read_cancel; return 1; -error: - magick_cleanup(io); + +err: + libmagick_destroy_read_data(rd); return 0; } -static int -magick_read_data(struct image_io *io) +static inline byte +libmagick_pixel_to_gray(PixelPacket *pixel) { - DBG("magick_read_data()"); - - // FIXME - - magick_cleanup(io); - return 1; + return ((uns)pixel->red * 19660 + (uns)pixel->green * 38666 + (uns)pixel->blue * 7210) >> (16 + QUANTUM_SCALE); } -static int -magick_decompress_thumbnail(struct image_obj *imo) +int +libmagick_read_data(struct image_io *io) { - DBG("Quantizing image"); - QuantizeImage(&magick_quantize, image); - DBG("Converting pixels"); - PixelPacket *pixels = (PixelPacket *)AcquireImagePixels(image, 0, 0, image->columns, image->rows, &magick_exception); - ASSERT(pixels); - uns size = image->columns * image->rows; - byte *p = imo->thumb.pixels = mp_alloc(imo->pool, imo->thumb.size = size * 3); - for (uns i = 0; i < size; i++) + DBG("libmagick_read_data()"); + + struct magick_read_data *rd = io->read_data; + + /* Quantize image */ + switch (rd->image->colorspace) + { + case RGBColorspace: + case GRAYColorspace: + break; + default: ; + QuantizeInfo quantize; + GetQuantizeInfo(&quantize); + quantize.colorspace = RGBColorspace; + QuantizeImage(&quantize, rd->image); + break; + } + + /* Allocate image for conversion */ + int need_scale = io->cols != rd->image->columns || io->rows != rd->image->rows; + int need_destroy = need_scale || !io->pool; + struct image *img = need_scale ? + image_new(io->thread, rd->image->columns, rd->image->rows, io->flags & IMAGE_CHANNELS_FORMAT, NULL) : + image_new(io->thread, io->cols, io->rows, io->flags, io->pool); + if (!img) + goto err; + + /* Acquire pixels */ + PixelPacket *src = (PixelPacket *)AcquireImagePixels(rd->image, 0, 0, rd->image->columns, rd->image->rows, &rd->exception); + if (!src) + { + image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Cannot acquire image pixels."); + goto err; + } + + /* Convert pixels */ + switch (img->pixel_size) + { + case 1: +# define IMAGE_WALK_INLINE +# define IMAGE_WALK_UNROLL 4 +# define IMAGE_WALK_COL_STEP 1 +# define IMAGE_WALK_DO_STEP do{ \ + pos[0] = libmagick_pixel_to_gray(src); \ + src++; }while(0) +# include "images/image-walk.h" + break; + + case 2: +# define IMAGE_WALK_INLINE +# define IMAGE_WALK_UNROLL 4 +# define IMAGE_WALK_COL_STEP 2 +# define IMAGE_WALK_DO_STEP do{ \ + pos[0] = libmagick_pixel_to_gray(src); \ + pos[1] = QUANTUM_TO_BYTE(src->opacity); \ + src++; }while(0) +# include "images/image-walk.h" + break; + + case 3: +# define IMAGE_WALK_INLINE +# define IMAGE_WALK_UNROLL 4 +# define IMAGE_WALK_COL_STEP 3 +# define IMAGE_WALK_DO_STEP do{ \ + pos[0] = QUANTUM_TO_BYTE(src->red); \ + pos[1] = QUANTUM_TO_BYTE(src->green); \ + pos[2] = QUANTUM_TO_BYTE(src->blue); \ + src++; }while(0) +# include "images/image-walk.h" + break; + + case 4: +# define IMAGE_WALK_INLINE +# define IMAGE_WALK_UNROLL 4 +# define IMAGE_WALK_COL_STEP 4 +# define IMAGE_WALK_DO_STEP do{ \ + pos[0] = QUANTUM_TO_BYTE(src->red); \ + pos[1] = QUANTUM_TO_BYTE(src->green); \ + pos[2] = QUANTUM_TO_BYTE(src->blue); \ + pos[3] = QUANTUM_TO_BYTE(src->opacity); \ + src++; }while(0) +# include "images/image-walk.h" + break; + + default: + ASSERT(0); + } + + /* Free GraphicsMagick structures */ + libmagick_destroy_read_data(rd); + + /* Scale image */ + if (need_scale) { - p[0] = pixels->red >> (QuantumDepth - 8); - p[1] = pixels->green >> (QuantumDepth - 8); - p[2] = pixels->blue >> (QuantumDepth - 8); - p += 3; - pixels++; + struct image *img2 = image_new(io->thread, io->cols, io->rows, io->flags, io->pool); + if (!img2) + goto err2; + int result = image_scale(io->thread, img2, img); + image_destroy(io->thread, img); + img = img2; + need_destroy = !io->pool; + if (!result) + goto err2; } - DestroyImage(image); + + /* Success */ + io->image = img; + io->image_destroy = need_destroy; return 1; + + /* Free structures */ +err: + libmagick_destroy_read_data(rd); +err2: + if (need_destroy) + image_destroy(io->thread, img); + return 0; +} + +int +libmagick_write(struct image_io *io) +{ + image_thread_err(io->thread, IMAGE_ERR_NOT_IMPLEMENTED, "GraphicsMagick write not implemented."); + return 0; } -#endif diff --git a/images/io-main.c b/images/io-main.c index c45864fa..eb04faa9 100644 --- a/images/io-main.c +++ b/images/io-main.c @@ -95,7 +95,7 @@ image_io_read_header(struct image_io *io) #endif break; - case IMAGE_FORMAT_UNKNOWN: + case IMAGE_FORMAT_UNDEFINED: // FIXME: auto-detect break; @@ -217,12 +217,12 @@ image_extension_to_format(byte *extension) return IMAGE_FORMAT_PNG; if (!strcasecmp(extension, "gif")) return IMAGE_FORMAT_GIF; - return IMAGE_FORMAT_UNKNOWN; + return IMAGE_FORMAT_UNDEFINED; } enum image_format image_file_name_to_format(byte *file_name) { byte *extension = strrchr(file_name, '.'); - return extension ? image_extension_to_format(extension + 1) : IMAGE_FORMAT_UNKNOWN; + return extension ? image_extension_to_format(extension + 1) : IMAGE_FORMAT_UNDEFINED; } diff --git a/images/scale.c b/images/scale.c index f617b01e..4eceac67 100644 --- a/images/scale.c +++ b/images/scale.c @@ -68,8 +68,8 @@ image_scale(struct image_thread *it, struct image *dest, struct image *src) void image_dimensions_fit_to_box(u32 *cols, u32 *rows, u32 max_cols, u32 max_rows, uns upsample) { - ASSERT(*cols && *rows && *cols <= 0xffff && *rows <= 0xffff); - ASSERT(max_cols && max_rows && max_cols <= 0xffff && max_rows <= 0xffff); + ASSERT(*cols && *rows && *cols <= IMAGE_MAX_SIZE && *rows <= IMAGE_MAX_SIZE); + ASSERT(max_cols && max_rows && max_cols <= IMAGE_MAX_SIZE && max_rows <= IMAGE_MAX_SIZE); if (*cols <= max_cols && *rows <= max_rows) { if (!upsample) -- 2.39.2