$(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-obj.o $(o)/images/image-obj.oo: CFLAGS+=-I/usr/include/GraphicsMagick
-$(o)/images/image-idx: $(o)/images/image-idx.o $(o)/images/image-obj.o $(o)/indexer/iconfig.o $(o)/images/image-sig.o $(LIBSH) $(LIBLANG) $(LIBCHARSET)
+$(o)/images/image-idx: $(o)/images/image-idx.o $(o)/images/image-obj.o $(o)/images/image-dup.o $(o)/indexer/iconfig.o $(o)/images/image-sig.o $(LIBSH) $(LIBLANG) $(LIBCHARSET)
$(o)/images/image-idx: LIBS+=-lGraphicsMagick -ljpeg -lpng
$(o)/images/image-test: $(o)/images/image-test.o $(LIBSH)
--- /dev/null
+/*
+ * Image Library -- Duplicates Comparison
+ *
+ * (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:
+ * - many possible optimization
+ * - compare normalized pictures (brightness, ...)
+ * - better image scale... now it can completely miss some rows/cols of pixels
+ * - maybe better/slower last step
+ * - different thresholds for various transformations
+ * - do not test all transformations for symetric pictures
+ * - ... secret ideas :-)
+ */
+
+#undef LOCAL_DEBUG
+
+#include "sherlock/sherlock.h"
+#include "lib/mempool.h"
+#include "images/images.h"
+#include "images/image-dup.h"
+
+static uns image_dup_scale_min_size = 8;
+static uns image_dup_ratio_threshold = 140;
+static uns image_dup_error_threshold = 2000;
+
+static inline byte *
+image_dup_block(struct image_dup *dup, uns col, uns row)
+{
+ ASSERT(col <= dup->cols && row <= dup->rows);
+ return dup->buf + (dup->line << row) + (3 << (row + col));
+}
+
+static inline void
+pixels_average(byte *dest, byte *src1, byte *src2)
+{
+ dest[0] = ((uns)src1[0] + (uns)src2[0]) >> 1;
+ dest[1] = ((uns)src1[1] + (uns)src2[1]) >> 1;
+ dest[2] = ((uns)src1[2] + (uns)src2[2]) >> 1;
+}
+
+void
+image_dup_init(struct image_dup *dup, struct image *image, struct mempool *pool)
+{
+ ASSERT(image->width && image->height);
+
+ dup->image = image;
+ dup->width = image->width;
+ dup->height = image->height;
+ for (dup->cols = 0; (uns)(1 << dup->cols) < image->width; dup->cols++);
+ for (dup->rows = 0; (uns)(1 << dup->rows) < image->height; dup->rows++);
+ dup->buf = mp_alloc(pool, 12 << (dup->cols + dup->rows));
+ dup->line = 6 << dup->cols;
+ dup->flags = 0;
+ if (image->width >= image_dup_scale_min_size && image->height >= image_dup_scale_min_size)
+ dup->flags |= IMAGE_DUP_FLAG_SCALE;
+
+ /* Scale original image to right bottom block */
+ {
+ byte *d = image_dup_block(dup, dup->cols, dup->rows);
+ uns width = 1 << dup->cols;
+ uns height = 1 << dup->rows;
+ uns line_size = 3 * image->width;
+ uns src_y = 0;
+ for (uns y = 0; y < height; y++)
+ {
+ byte *line = image->pixels + line_size * (src_y >> dup->rows);
+ uns src_x = 0;
+ for (uns x = 0; x < width; x++)
+ {
+ byte *s = line + 3 * (src_x >> dup->cols);
+ d[0] = s[0];
+ d[1] = s[1];
+ d[2] = s[2];
+ d += 3;
+ src_x += image->width;
+ }
+ src_y += image->height;
+ }
+ }
+
+ /* Complete bottom row */
+ for (uns i = dup->cols; i--; )
+ {
+ byte *d = image_dup_block(dup, i, dup->rows);
+ byte *s = image_dup_block(dup, i + 1, dup->rows);
+ for (uns y = 0; y < (uns)(1 << dup->rows); y++)
+ for (uns x = 0; x < (uns)(1 << i); x++)
+ {
+ pixels_average(d, s, s + 3);
+ d += 3;
+ s += 6;
+ }
+ }
+
+ /* Complete remaining blocks */
+ for (uns i = 0; i <= dup->cols; i++)
+ {
+ uns line_size = (3 << i);
+ for (uns j = dup->rows; j--; )
+ {
+ byte *d = image_dup_block(dup, i, j);
+ byte *s = image_dup_block(dup, i, j + 1);
+ for (uns y = 0; y < (uns)(1 << j); y++)
+ {
+ for (uns x = 0; x < (uns)(1 << i); x++)
+ {
+ pixels_average(d, s, s + line_size);
+ d += 3;
+ s += 3;
+ }
+ s += line_size;
+ }
+ }
+ }
+}
+
+static inline uns
+err (int a, int b)
+{
+ a -= b;
+ return a * a;
+}
+
+static inline uns
+err_sum(byte *pos1, byte *end1, byte *pos2)
+{
+ uns e = 0;
+ while (pos1 != end1)
+ e += err(*pos1++, *pos2++);
+ return e;
+}
+
+static inline uns
+err_sum_transformed(byte *pos1, byte *end1, byte *pos2, uns width, int add1, int add2)
+{
+ DBG("err_sum_transformed(): %p %p %p %d %d %d", pos1, end1, pos2, width, add1, add2);
+ uns e = 0;
+ while (pos1 != end1)
+ {
+ for (uns i = 0; i < width; i++, pos2 += add1)
+ {
+ e += err(pos1[0], pos2[0]);
+ e += err(pos1[1], pos2[1]);
+ e += err(pos1[2], pos2[2]);
+ pos1 += 3;
+ }
+ pos2 += add2;
+ }
+ return e;
+}
+
+static inline int
+aspect_ratio_test(uns width1, uns height1, uns width2, uns height2)
+{
+ uns r1 = width1 * height2;
+ uns r2 = height1 * width2;
+ return
+ r1 <= ((r2 * image_dup_ratio_threshold) >> 5) &&
+ r2 <= ((r1 * image_dup_ratio_threshold) >> 5);
+}
+
+static inline int
+average_compare(struct image_dup *dup1, struct image_dup *dup2)
+{
+ byte *block1 = image_dup_block(dup1, 0, 0);
+ byte *block2 = image_dup_block(dup2, 0, 0);
+ uns e =
+ err(block1[0], block2[0]) +
+ err(block1[1], block2[1]) +
+ err(block1[2], block2[2]);
+ return e <= image_dup_error_threshold;
+}
+
+static int
+blocks_compare(struct image_dup *dup1, struct image_dup *dup2, uns col, uns row, uns trans)
+{
+ DBG("blocks_compare(): col=%d row=%d trans=%d", col, row, trans);
+ byte *block1 = image_dup_block(dup1, col, row);
+ byte *block2 = (trans < 4) ? image_dup_block(dup2, col, row) : image_dup_block(dup2, row, col);
+ int add1, add2;
+ switch (trans)
+ {
+ case 0: ;
+ uns err = (err_sum(block1, block1 + (3 << (col + row)), block2) >> (col + row));
+ DBG("average error=%d", err);
+ return err <= image_dup_error_threshold;
+ case 1:
+ add1 = -3;
+ add2 = 6 << col;
+ block2 += (3 << col) - 3;
+ break;
+ case 2:
+ add1 = 1;
+ add2 = -(6 << col);
+ block2 += (3 << (col + row)) - (3 << col);
+ break;
+ case 3:
+ add1 = -3;
+ add2 = 0;
+ block2 += (3 << (col + row)) - 3;
+ break;
+ case 4:
+ add1 = (3 << col);
+ add2 = -(3 << (col + row)) + 3;
+ break;
+ case 5:
+ add1 = -(3 << col);
+ add2 = (3 << (col + row)) + 3;
+ block2 += (3 << (col + row)) - (3 << col);
+ break;
+ case 6:
+ add1 = (3 << col);
+ add2 = -(3 << (col + row)) - 3;
+ block2 += (3 << col) - 3;
+ break;
+ case 7:
+ add1 = -(3 << col);
+ add2 = (3 << (col + row)) - 3;
+ block2 += (3 << (col + row)) - 3;
+ break;
+ default:
+ ASSERT(0);
+ }
+ uns err = (err_sum_transformed(block1, block1 + (3 << (col + row)), block2, (1 << col), add1, add2) >> (col + row));
+ DBG("average error=%d", err);
+ return err <= image_dup_error_threshold;
+}
+
+static int
+same_size_compare(struct image_dup *dup1, struct image_dup *dup2, uns trans)
+{
+ byte *block1 = dup1->image->pixels;
+ byte *block2 = dup2->image->pixels;
+ DBG("same_size_compare(): trans=%d", trans);
+ int add1, add2;
+ switch (trans)
+ {
+ case 0: ;
+ uns err = (err_sum(block1, block1 + 3 * dup1->width * dup1->height, block2) / (dup1->width * dup1->height));
+ DBG("average error=%d", err);
+ return err <= image_dup_error_threshold;
+ case 1:
+ add1 = -3;
+ add2 = 6 * dup1->width;
+ block2 += 3 * (dup1->width - 1);
+ break;
+ case 2:
+ add1 = 1;
+ add2 = -6 * dup1->width;
+ block2 += 3 * dup1->width * (dup1->height - 1);
+ break;
+ case 3:
+ add1 = -3;
+ add2 = 0;
+ block2 += 3 * (dup1->width * dup1->height - 1);
+ break;
+ case 4:
+ add1 = 3 * dup1->width;
+ add2 = -3 * (dup1->width * dup1->height - 1);
+ break;
+ case 5:
+ add1 = -3 * dup1->width;
+ add2 = 3 * (dup1->width * dup1->height + 1);
+ block2 += 3 * dup1->width * (dup1->height - 1);
+ break;
+ case 6:
+ add1 = 3 * dup1->width;
+ add2 = -3 * (dup1->width * dup1->height + 1);
+ block2 += 3 * (dup1->width - 1);
+ break;
+ case 7:
+ add1 = -3 * dup1->width;
+ add2 = 3 * (dup1->width * dup1->height - 1);
+ block2 += 3 * (dup1->width * dup1->height - 1);
+ break;
+ default:
+ ASSERT(0);
+ }
+ uns err = (err_sum_transformed(block1, block1 + 3 * dup1->width * dup1->height, block2, dup1->width, add1, add2) / (dup1->width * dup1->height));
+ DBG("average error=%d", err);
+ return err <= image_dup_error_threshold;
+}
+
+int
+image_dup_compare(struct image_dup *dup1, struct image_dup *dup2, uns trans)
+{
+ if (!average_compare(dup1, dup2))
+ return 0;
+ if ((dup1->flags & dup2->flags) & IMAGE_DUP_FLAG_SCALE)
+ {
+ DBG("Scale support");
+ if (!aspect_ratio_test(dup1->width, dup1->height, dup2->width, dup2->height))
+ trans &= 0xf0;
+ if (!aspect_ratio_test(dup1->width, dup1->height, dup2->height, dup2->width))
+ trans &= 0x0f;
+ }
+ else
+ {
+ DBG("No scale support");
+ if (!(dup1->width == dup2->width && dup1->height == dup2->height))
+ trans &= 0xf0;
+ if (!(dup1->width == dup2->height && dup1->height == dup2->width))
+ trans &= 0x0f;
+ }
+ if (!trans)
+ return 0;
+ if (trans & 0x0f)
+ {
+ uns cols = MIN(dup1->cols, dup2->cols);
+ uns rows = MIN(dup1->rows, dup2->rows);
+ for (uns t = 0; t < 4; t++)
+ if (trans & (1 << t))
+ {
+ DBG("Testing trans %d", t);
+ for (uns i = MAX(cols, rows); i--; )
+ {
+ uns col = MAX(0, (int)(cols - i));
+ uns row = MAX(0, (int)(rows - i));
+ if (!blocks_compare(dup1, dup2, col, row, t))
+ break;
+ if (!i &&
+ (dup1->width != dup2->width || dup1->height != dup2->height ||
+ same_size_compare(dup1, dup2, t)))
+ return 1;
+ }
+ }
+ }
+ if (trans & 0xf0)
+ {
+ uns cols = MIN(dup1->cols, dup2->rows);
+ uns rows = MIN(dup1->rows, dup2->cols);
+ for (uns t = 4; t < 8; t++)
+ if (trans & (1 << t))
+ {
+ DBG("Testing trans %d", t);
+ for (uns i = MAX(cols, rows); i--; )
+ {
+ uns col = MAX(0, (int)(cols - i));
+ uns row = MAX(0, (int)(rows - i));
+ if (!blocks_compare(dup1, dup2, col, row, t))
+ break;
+ if (!i &&
+ (dup1->width != dup2->height || dup1->height != dup2->width ||
+ same_size_compare(dup1, dup2, t)) )
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
--- /dev/null
+#ifndef _IMAGES_IMAGE_DUP_H
+#define _IMAGES_IMAGE_DUP_H
+
+struct image;
+
+struct image_dup {
+ struct image *image;
+ byte *buf;
+ uns flags;
+ uns cols;
+ uns rows;
+ uns line;
+ uns width;
+ uns height;
+};
+
+#define IMAGE_DUP_FLAG_SCALE 0x1
+
+#define IMAGE_DUP_TRANS_ID 0x01
+#define IMAGE_DUP_TRANS_ALL 0xff
+
+void image_dup_init(struct image_dup *dup, struct image *image, struct mempool *pool);
+int image_dup_compare(struct image_dup *dup1, struct image_dup *dup2, uns trans);
+
+#endif
#include "images/images.h"
#include "images/image-obj.h"
#include "images/image-sig.h"
+#include "images/image-dup.h"
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
/* This should happen in gatherer or scanner */
-UNUSED static void
+static void
generate_signatures(uns limit)
{
struct fastbuf *fb_cards = index_bopen("cards", O_RDONLY);
die("Failed to read card");
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'));
+ byte *url = obj_find_aval(obj_find_attr(obj, 'U' + OBJ_ATTR_SON)->son, 'U');
+ DBG("Reading oid=%d url=%s", oid, url);
struct image_obj imo;
imo_init(&imo, pool, obj);
if (imo_decompress_thumbnail(&imo))
{
- int err;
- if (!(err = compute_image_signature(&imo.thumb, &sig)))
+ if (compute_image_signature(&imo.thumb, &sig))
{
bwrite(fb_signatures, &oid, sizeof(oid));
bwrite(fb_signatures, &sig.vec, sizeof(struct image_vector));
break;
}
else
- DBG("Cannot create signature, error=%d", err);
+ DBG("Cannot create signature");
}
else
DBG("Cannot decompress thumbnail");
bclose(fb_signatures);
}
-UNUSED static void
-generate_random_signatures(uns count)
-{
- log(L_INFO, "Generating %d random signatures", count);
- struct fastbuf *fb_signatures = index_bopen("image-sig", O_CREAT | O_WRONLY | O_TRUNC);
- bputl(fb_signatures, count);
- for (uns i = 0; i < count; i++)
- {
- oid_t oid = i;
- struct image_vector vec;
- for (uns j = 0; j < IMAGE_VEC_K; j++)
- vec.f[j] = random_max(256);
- bwrite(fb_signatures, &oid, sizeof(oid));
- bwrite(fb_signatures, &vec, sizeof(vec));
- bputc(fb_signatures, 0);
- }
- bclose(fb_signatures);
-}
-
struct signature_record {
oid_t oid;
struct image_vector vec;
usage("Invalid usage");
generate_signatures(~0U);
- //generate_random_signatures(1000000);
build_search_tree();
return 0;
* - 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
- * - alpha channel is propably useless (we could assume white background)
+ * - supply background color to transparent PNG images
* - optimize decompression parameters
* - create interface for thumbnail compression (for gatherer) and reading (MUX)
* - benchmatk libraries
{
case PNG_COLOR_TYPE_PALETTE:
png_set_palette_to_rgb(png_ptr);
- if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
- {
- png_set_tRNS_to_alpha(png_ptr);
- imo->thumb.flags |= IMAGE_ALPHA;
- }
- else
- png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
+ png_set_strip_alpha(png_ptr);
break;
case PNG_COLOR_TYPE_GRAY:
imo->thumb.flags |= IMAGE_GRAYSCALE;
png_set_gray_to_rgb(png_ptr);
- png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
+ png_set_strip_alpha(png_ptr);
break;
case PNG_COLOR_TYPE_GRAY_ALPHA:
- imo->thumb.flags |= IMAGE_GRAYSCALE | IMAGE_ALPHA;
+ imo->thumb.flags |= IMAGE_GRAYSCALE;
png_set_gray_to_rgb(png_ptr);
break;
case PNG_COLOR_TYPE_RGB:
- png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
break;
case PNG_COLOR_TYPE_RGB_ALPHA:
- imo->thumb.flags |= IMAGE_ALPHA;
+ png_set_strip_alpha(png_ptr);
break;
default:
ASSERT(0);
}
png_read_update_info(png_ptr, info_ptr);
- ASSERT(png_get_channels(png_ptr, info_ptr) == sizeof(struct pixel));
- ASSERT(png_get_rowbytes(png_ptr, info_ptr) == sizeof(struct pixel) * width);
+ ASSERT(png_get_channels(png_ptr, info_ptr) == 3);
/* Read image data */
DBG("Reading image data");
- struct pixel *pixels = imo->thumb.pixels = mp_alloc(imo->pool, width * height * sizeof(struct pixel));
+ byte *pixels = imo->thumb.pixels = mp_alloc(imo->pool, width * height * 3);
png_bytep rows[height];
- for (uns i = 0; i < height; i++, pixels += width)
+ for (uns i = 0; i < height; i++, pixels += width * 3)
rows[i] = (png_bytep)pixels;
png_read_image(png_ptr, rows);
png_read_end(png_ptr, end_ptr);
DBG("Reading image data");
jpeg_start_decompress(&cinfo);
ASSERT(imo->thumb.width == cinfo.output_width && imo->thumb.height == cinfo.output_height);
- struct pixel *pixels = imo->thumb.pixels = mp_alloc(imo->pool, cinfo.output_width * cinfo.output_height * sizeof(struct pixel));
+ ASSERT(sizeof(JSAMPLE) == 1);
+ byte *pixels = imo->thumb.pixels = mp_alloc(imo->pool, cinfo.output_width * cinfo.output_height * 3);
if (cinfo.out_color_space == JCS_RGB)
{ /* Read RGB pixels */
uns size = cinfo.output_width * 3;
- JSAMPLE buf[size], *buf_end = buf + size;
while (cinfo.output_scanline < cinfo.output_height)
{
- JSAMPLE *p = buf;
- jpeg_read_scanlines(&cinfo, &p, 1);
- for (; p != buf_end; p += 3)
- {
- pixels->r = p[0] >> (sizeof(JSAMPLE) * 8 - 8);
- pixels->g = p[1] >> (sizeof(JSAMPLE) * 8 - 8);
- pixels->b = p[2] >> (sizeof(JSAMPLE) * 8 - 8);
- pixels->a = 255;
- pixels++;
- }
+ jpeg_read_scanlines(&cinfo, (JSAMPLE **)&pixels, 1);
+ pixels += size;
}
}
else
jpeg_read_scanlines(&cinfo, &p, 1);
for (; p != buf_end; p++)
{
- pixels->r = pixels->g = pixels->b = p[0] >> (sizeof(JSAMPLE) * 8 - 8);
- pixels->a = 255;
- pixels++;
+ pixels[0] = pixels[1] = pixels[2] = p[0];
+ pixels += 3;
}
}
}
PixelPacket *pixels = (PixelPacket *)AcquireImagePixels(image, 0, 0, image->columns, image->rows, &magick_exception);
ASSERT(pixels);
uns size = image->columns * image->rows;
- struct pixel *p = imo->thumb.pixels = mp_alloc(imo->pool, size * sizeof(struct pixel));
- for (uns i = 0; i < size; i++, p++, pixels++)
+ byte *p = imo->thumb.pixels = mp_alloc(imo->pool, size * 3);
+ for (uns i = 0; i < size; i++)
{
- p->r = pixels->red >> (QuantumDepth - 8);
- p->g = pixels->green >> (QuantumDepth - 8);
- p->b = pixels->blue >> (QuantumDepth - 8);
- p->a = 255;
+ p[0] = pixels->red >> (QuantumDepth - 8);
+ p[1] = pixels->green >> (QuantumDepth - 8);
+ p[2] = pixels->blue >> (QuantumDepth - 8);
+ p += 3;
+ pixels++;
}
DestroyImage(image);
return 1;
extract_image_info(struct image_obj *imo)
{
DBG("Parsing image info attribute");
- imo->flags |= IMAGE_OBJ_INFO;
+ ASSERT(!(imo->flags & IMAGE_OBJ_VALID_INFO));
+ imo->flags |= IMAGE_OBJ_VALID_INFO;
byte *info = obj_find_aval(imo->obj, 'G');
if (!info)
{
byte color_space[MAX_ATTR_SIZE], thumb_format[MAX_ATTR_SIZE];
UNUSED uns cnt = sscanf(info, "%d%d%s%d%d%d%s", &imo->width, &imo->height, color_space, &colors, &imo->thumb.width, &imo->thumb.height, thumb_format);
ASSERT(cnt == 7);
- if (*thumb_format == 'j')
- imo->flags |= IMAGE_OBJ_THUMB_JPEG;
+ switch (*thumb_format)
+ {
+ case 'j':
+ imo->thumb_format = IMAGE_OBJ_FORMAT_JPEG;
+ break;
+ case 'p':
+ imo->thumb_format = IMAGE_OBJ_FORMAT_PNG;
+ break;
+ default:
+ ASSERT(0);
+ }
return 1;
}
extract_thumb_data(struct image_obj *imo)
{
DBG("Extracting thumbnail data");
- imo->flags |= IMAGE_OBJ_THUMB_DATA;
+ ASSERT(!(imo->flags & IMAGE_OBJ_VALID_DATA) &&
+ (imo->flags & IMAGE_OBJ_VALID_INFO));
+ imo->flags |= IMAGE_OBJ_VALID_DATA;
struct oattr *attr = obj_find_attr(imo->obj, 'N');
if (!attr)
{
extract_thumb_image(struct image_obj *imo)
{
DBG("Decompressing thumbnail image");
- imo->flags |= IMAGE_OBJ_THUMB_IMAGE;
-
- /* JPEG format */
- if (imo->flags & IMAGE_OBJ_THUMB_JPEG)
+ ASSERT(!(imo->flags & IMAGE_OBJ_VALID_IMAGE) &&
+ (imo->flags & IMAGE_OBJ_VALID_INFO) &&
+ (imo->flags & IMAGE_OBJ_VALID_DATA));
+ imo->flags |= IMAGE_OBJ_VALID_IMAGE;
+ switch (imo->thumb_format)
{
+ case IMAGE_OBJ_FORMAT_JPEG:
#if defined(USE_LIBJPEG)
- return libjpeg_decompress_thumbnail(imo);
+ return libjpeg_decompress_thumbnail(imo);
#elif defined(USE_MAGICK)
- return magick_decompress_thumbnail(imo);
+ return magick_decompress_thumbnail(imo);
#else
- DBG("JPEG not supported");
- return 0;
-#endif
- }
- /* PNG format */
- else
- {
+ DBG("JPEG not supported");
+ return 0;
+#endif
+ case IMAGE_OBJ_FORMAT_PNG:
#if defined(USE_LIBPNG)
- return libpng_decompress_thumbnail(imo);
+ return libpng_decompress_thumbnail(imo);
#elif defined(USE_MAGICK)
- return magick_decompress_thumbnail(imo);
+ return magick_decompress_thumbnail(imo);
#else
- DBG("PNG not supported");
- return 0;
+ DBG("PNG not supported");
+ return 0;
#endif
+ default:
+ ASSERT(0);
}
}
struct mempool;
struct odes;
+enum image_obj_format {
+ IMAGE_OBJ_FORMAT_JPEG,
+ IMAGE_OBJ_FORMAT_PNG
+};
+
enum image_obj_flag {
- IMAGE_OBJ_INFO = 0x1,
- IMAGE_OBJ_THUMB_JPEG = 0x2,
- IMAGE_OBJ_THUMB_DATA = 0x4,
- IMAGE_OBJ_THUMB_IMAGE = 0x8
+ IMAGE_OBJ_VALID_INFO = 0x1,
+ IMAGE_OBJ_VALID_DATA = 0x2,
+ IMAGE_OBJ_VALID_IMAGE = 0x4
};
struct image_obj {
uns flags;
uns width;
uns height;
+ uns thumb_format;
byte *thumb_data;
uns thumb_size;
struct image thumb;
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) */
- struct pixel *p = image->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 += 1)
+ 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->r / 255.;
- rgb[1] = p->g / 255.;
- rgb[2] = p->b / 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];
enum image_flag {
IMAGE_GRAYSCALE = 0x1, /* grayscale image */
- IMAGE_ALPHA = 0x2, /* alpha present */
};
-struct pixel {
- byte r, g, b, a;
-} PACKED;
-
struct image {
uns flags; /* enum image_flag */
uns width; /* number of columns */
uns height; /* number of rows */
- struct pixel *pixels; /* RGBA */
+ byte *pixels; /* RGB */
};
#endif