From 3641e85c1412a524ed541360d9fa35268a6e83e0 Mon Sep 17 00:00:00 2001 From: Pavel Charvat Date: Wed, 12 Apr 2006 22:34:57 +0200 Subject: [PATCH] First try of libjpeg... much faster than graphicsmagick --- images/Makefile | 4 +- images/image-idx.c | 36 ++++++----- images/image-sig.c | 29 ++++++--- images/image-thumb.c | 149 +++++++++++++++++++++++++++++++++++++++++++ images/image-thumb.h | 19 ++++++ images/images.h | 13 +++- 6 files changed, 221 insertions(+), 29 deletions(-) create mode 100644 images/image-thumb.c create mode 100644 images/image-thumb.h diff --git a/images/Makefile b/images/Makefile index e19e8343..de77b745 100644 --- a/images/Makefile +++ b/images/Makefile @@ -6,7 +6,7 @@ PROGS+=$(addprefix $(o)/images/,image-idx image-test) $(o)/images/image-sig.o $(o)/images/image-sig.oo: CFLAGS+=-I/usr/include/GraphicsMagick $(o)/images/image-idx.o $(o)/images/image-idx.oo: CFLAGS+=-I/usr/include/GraphicsMagick -$(o)/images/image-idx: $(o)/images/images.o $(o)/images/image-idx.o $(o)/indexer/iconfig.o $(o)/images/image-sig.o $(LIBSH) $(LIBLANG) $(LIBCHARSET) -$(o)/images/image-idx: LIBS+=-lGraphicsMagick +$(o)/images/image-idx: $(o)/images/images.o $(o)/images/image-idx.o $(o)/images/image-thumb.o $(o)/indexer/iconfig.o $(o)/images/image-sig.o $(LIBSH) $(LIBLANG) $(LIBCHARSET) +$(o)/images/image-idx: LIBS+=-lGraphicsMagick -ljpeg $(o)/images/image-test: $(o)/images/images.o $(o)/images/image-test.o $(LIBSH) diff --git a/images/image-idx.c b/images/image-idx.c index 074b60bc..058c7774 100644 --- a/images/image-idx.c +++ b/images/image-idx.c @@ -22,6 +22,7 @@ #include "lib/bbuf.h" #include "images/images.h" +#include "images/image-thumb.h" #include #include @@ -58,7 +59,7 @@ generate_signatures(uns limit) if (attr = obj_find_attr(obj, 'N')) { DBG("Reading oid=%d url=%s", oid, obj_find_aval(obj_find_attr(obj, 'U' + OBJ_ATTR_SON)->son, 'U')); - bb_t buf; + /*bb_t buf; uns buf_len = 0; bb_init(&buf); for (; attr; attr = attr->same) @@ -69,24 +70,27 @@ generate_signatures(uns limit) buf_len += len; } byte thumb[buf_len]; - uns thumb_len = base224_decode(thumb, buf.ptr, buf_len); - - int err = compute_image_signature(thumb, thumb_len, &sig); - if (!err) + uns thumb_len = base224_decode(thumb, buf.ptr, buf_len);*/ + struct image thumb; + int err; + if (!(err = decompress_thumbnail(obj, pool, &thumb))) { - bwrite(fb_signatures, &oid, sizeof(oid)); - bwrite(fb_signatures, &sig.vec, sizeof(struct image_vector)); - bputc(fb_signatures, sig.len); - if (sig.len) - bwrite(fb_signatures, sig.reg, sig.len * sizeof(struct image_region)); - count++; - if (count >= limit) - break; + if (!(err = compute_image_signature(&thumb, &sig))) + { + bwrite(fb_signatures, &oid, sizeof(oid)); + bwrite(fb_signatures, &sig.vec, sizeof(struct image_vector)); + bputc(fb_signatures, sig.len); + if (sig.len) + bwrite(fb_signatures, sig.reg, sig.len * sizeof(struct image_region)); + count++; + if (count >= limit) + break; + } + else + DBG("Cannot create signature, error=%d", err); } else - DBG("Cannot create signature, error=%d", err); - - bb_done(&buf); + DBG("Cannot decompress thumbnail, error=%d", err); } } brewind(fb_signatures); diff --git a/images/image-sig.c b/images/image-sig.c index a32b8016..c240bd3a 100644 --- a/images/image-sig.c +++ b/images/image-sig.c @@ -60,8 +60,11 @@ struct block { }; static void -compute_image_area_signature(PixelPacket *pixels, uns width, uns height, struct image_signature *sig) +compute_image_area_signature(struct image *image, struct image_signature *sig) { + uns width = image->width; + uns height = image->height; + ASSERT(width >= 4 && height >= 4); uns w = width >> 2; @@ -71,9 +74,9 @@ compute_image_area_signature(PixelPacket *pixels, uns width, uns height, struct struct block *blocks = xmalloc(blocks_count * sizeof(struct block)), *block = blocks; /* FIXME: use mempool */ /* Every 4x4 block (FIXME: deal with smaller blocks near the edges) */ - PixelPacket *p = pixels; - for (uns block_y = 0; block_y < h; block_y++, p += (width & 3) + width * 3) - for (uns block_x = 0; block_x < w; block_x++, p -= 4 * width - 4, block++) + byte *p = image->pixels; + for (uns block_y = 0; block_y < h; block_y++, p += 3 * ((width & 3) + width * 3)) + for (uns block_x = 0; block_x < w; block_x++, p -= 3 * (4 * width - 4), block++) { int t[16], s[16], *tp = t; @@ -84,13 +87,13 @@ compute_image_area_signature(PixelPacket *pixels, uns width, uns height, struct uns l_sum = 0; uns u_sum = 0; uns v_sum = 0; - for (uns y = 0; y < 4; y++, p += width - 4) - for (uns x = 0; x < 4; x++, p++) + for (uns y = 0; y < 4; y++, p += 3 * (width - 4)) + for (uns x = 0; x < 4; x++, p += 3) { double rgb[3], luv[3], xyz[3]; - rgb[0] = (p->red >> (QuantumDepth - 8)) / 255.; - rgb[1] = (p->green >> (QuantumDepth - 8)) / 255.; - rgb[2] = (p->blue >> (QuantumDepth - 8)) / 255.; + rgb[0] = p[0] / 255.; + rgb[1] = p[1] / 255.; + rgb[2] = p[2] / 255.; srgb_to_xyz_slow(rgb, xyz); xyz_to_luv_slow(xyz, luv); l_sum += *tp++ = luv[0]; @@ -199,8 +202,13 @@ compute_image_signature_finish(void) } int -compute_image_signature(void *data, uns len, struct image_signature *sig) +compute_image_signature(struct image *image, struct image_signature *sig) { + if ((image->flags & IMAGE_GRAYSCALE) || (image->width < 4) || (image->height < 4)) + return -1; + compute_image_area_signature(image, sig); + return 0; +#if 0 Image *image = BlobToImage(image_info, data, len, &exception); /* Damn slow... takes most of CPU time :-/ */ if (!image) die("Invalid image format"); @@ -215,5 +223,6 @@ compute_image_signature(void *data, uns len, struct image_signature *sig) compute_image_area_signature(pixels, image->columns, image->rows, sig); DestroyImage(image); return 0; +#endif } diff --git a/images/image-thumb.c b/images/image-thumb.c new file mode 100644 index 00000000..6c64c0e8 --- /dev/null +++ b/images/image-thumb.c @@ -0,0 +1,149 @@ +/* + * Image Library -- Thumbnails + * + * (c) 2006 Pavel Charvat + * + * This software may be freely distributed and used according to the terms + * of the GNU Lesser General Public License. + * + * FIXME: + * - support for PNG (libpng seems to be fast) + * - improve thumbnail creation in gatherer... faster compression, + * only grayscale/RGB colorspaces and maybe fixed headers (abbreviated datastreams in libjpeg) + * - hook memory allocation managers, get rid of multiple initializations + * - optimize decompression parameters + */ + +#undef LOCAL_DEBUG + +#include "sherlock/sherlock.h" +#include "lib/base224.h" +#include "lib/mempool.h" +#include "sherlock/object.h" +#include "images/images.h" +#include "images/image-thumb.h" + +#include +#include +#include + +struct hook_error_mgr { + struct jpeg_error_mgr pub; + jmp_buf setjmp_buf; +}; + +#define LONGJMP(cinfo) do{ longjmp(((struct hook_error_mgr *)(cinfo)->err)->setjmp_buf, 1); }while(0) + +static void NONRET +hook_error_exit(j_common_ptr cinfo) +{ + LONGJMP(cinfo); +} + +static void +hook_init_source(j_decompress_ptr cinfo UNUSED) +{ +} + +static boolean NONRET +hook_fill_input_buffer(j_decompress_ptr cinfo) +{ + LONGJMP(cinfo); +} + +static void +hook_skip_input_data(j_decompress_ptr cinfo, long num_bytes) +{ + if (num_bytes > 0) + { + cinfo->src->next_input_byte += (size_t) num_bytes; + cinfo->src->bytes_in_buffer -= (size_t) num_bytes; + } +} + +int +decompress_thumbnail(struct odes *obj, struct mempool *pool, struct image *image) +{ + DBG("Decompressing thumbnail from URL %s", obj_find_aval(obj_find_attr(obj, 'U' + OBJ_ATTR_SON)->son, 'U')); + + /* Find the attribute */ + struct oattr *attr = obj_find_attr(obj, 'N'); + if (!attr) + { + DBG("There is no thumbnail"); + return -1; + } + + /* Merge all instances of the attribute to continuous buffer */ + uns attr_count = 0; + for (struct oattr *a = attr; a; a = a->same) + attr_count++; + byte b224[attr_count * MAX_ATTR_SIZE], *b = b224; + for (struct oattr *a = attr; a; a = a->same) + for (byte *s = a->val; *s; ) + *b++ = *s++; + ASSERT(b != b224); + + /* Decode base-224 data */ + byte buf[b - b224]; + uns len = base224_decode(buf, b224, b - b224); + DBG("Data length is %d", len); + + /* Initialize libjpeg decompression object */ + DBG("Creating libjpeg decompression object"); + struct jpeg_decompress_struct cinfo; + struct hook_error_mgr err; + cinfo.err = jpeg_std_error(&err.pub); // FIXME: we need to hide error messages + err.pub.error_exit = hook_error_exit; + if (setjmp(err.setjmp_buf)) + { + DBG("Error during the decompression, longjump saved us"); + jpeg_destroy_decompress(&cinfo); + return -2; + } + jpeg_create_decompress(&cinfo); + + /* Initialize source manager */ + struct jpeg_source_mgr src; + cinfo.src = &src; + src.next_input_byte = buf; + src.bytes_in_buffer = len; + src.init_source = hook_init_source; + src.fill_input_buffer = hook_fill_input_buffer; + src.skip_input_data = hook_skip_input_data; + src.resync_to_restart = jpeg_resync_to_restart; + src.term_source = hook_init_source; + + /* Read JPEG header and setup decompression options */ + DBG("Reading image header"); + jpeg_read_header(&cinfo, TRUE); + image->flags = 0; + if (cinfo.out_color_space == JCS_GRAYSCALE) + image->flags |= IMAGE_GRAYSCALE; + else + cinfo.out_color_space = JCS_RGB; + + /* Decompress the image */ + DBG("Reading image data"); + jpeg_start_decompress(&cinfo); + ASSERT(sizeof(JSAMPLE) == 1 && ( + (cinfo.out_color_space == JCS_GRAYSCALE && cinfo.output_components == 1) || + (cinfo.out_color_space == JCS_RGB && cinfo.output_components == 3))); + image->width = cinfo.output_width; + image->height = cinfo.output_height; + uns scanline = cinfo.output_width * cinfo.output_components; + uns size = scanline * cinfo.output_height; + ASSERT(size); + byte *pixels = image->pixels = mp_alloc(pool, size); + while (cinfo.output_scanline < cinfo.output_height) + { + jpeg_read_scanlines(&cinfo, (JSAMPLE **)&pixels, 1); + pixels += scanline; + } + jpeg_finish_decompress(&cinfo); + + /* Destroy libjpeg object and leave */ + jpeg_destroy_decompress(&cinfo); + DBG("Thumbnail decompressed successfully"); + return 0; +} diff --git a/images/image-thumb.h b/images/image-thumb.h new file mode 100644 index 00000000..e4b79900 --- /dev/null +++ b/images/image-thumb.h @@ -0,0 +1,19 @@ +/* + * Image Library -- Thumbnails + * + * (c) 2006 Pavel Charvat + * + * This software may be freely distributed and used according to the terms + * of the GNU Lesser General Public License. + */ + +#ifndef _IMAGE_THUMB_H +#define _IMAGE_THUMB_H + +struct odes; +struct mempool; +struct image; + +int decompress_thumbnail(struct odes *obj, struct mempool *pool, struct image *image); + +#endif diff --git a/images/images.h b/images/images.h index 763006d9..64c273d1 100644 --- a/images/images.h +++ b/images/images.h @@ -4,6 +4,17 @@ #include #include +enum image_flag { + IMAGE_GRAYSCALE = 0x1, /* RGB otherwise */ +}; + +struct image { + uns flags; /* enum thumbnail_flags */ + uns width; /* number of columns */ + uns height; /* number of rows */ + byte *pixels; /* 3 bytes per pixel for RGB, 1 byte for grayscale */ +}; + #define IMAGE_VEC_K 6 #define IMAGE_REG_K 9 #define IMAGE_REG_MAX 4 @@ -67,7 +78,7 @@ void image_tree_done(void); void compute_image_signature_prepare(void); void compute_image_signature_finish(void); -int compute_image_signature(void *data, uns len, struct image_signature *sig); +int compute_image_signature(struct image *image, struct image_signature *sig); #endif -- 2.39.2