$(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)
#include "lib/bbuf.h"
#include "images/images.h"
+#include "images/image-thumb.h"
#include <stdlib.h>
#include <fcntl.h>
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)
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);
};
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;
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;
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];
}
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");
compute_image_area_signature(pixels, image->columns, image->rows, sig);
DestroyImage(image);
return 0;
+#endif
}
--- /dev/null
+/*
+ * Image Library -- Thumbnails
+ *
+ * (c) 2006 Pavel Charvat <pchar@ucw.cz>
+ *
+ * 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 <stdio.h>
+#include <jpeglib.h>
+#include <setjmp.h>
+
+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;
+}
--- /dev/null
+/*
+ * Image Library -- Thumbnails
+ *
+ * (c) 2006 Pavel Charvat <pchar@ucw.cz>
+ *
+ * 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
#include <alloca.h>
#include <stdio.h>
+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
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