DIRS+=images
-PROGS+=$(addprefix $(o)/images/,image-idx image-test decomp)
+PROGS+=$(addprefix $(o)/images/,image-tool)
-$(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)/images/dup-cmp.o $(o)/indexer/iconfig.o $(o)/images/image-sig.o $(o)/images/kd-tree.o $(o)/images/color.o $(LIBSH) $(LIBLANG) $(LIBCHARSET)
-$(o)/images/image-idx: LIBS+=-lGraphicsMagick -ljpeg -lpng
-$(o)/images/image-test: $(o)/images/image-test.o $(o)/images/kd-tree.o $(LIBSH)
+LIBIMAGES_MODS=image scale io-main
+LIBIMAGES=$(o)/images/libimages.$(LS)
+LIBIMAGES_LIBS=
-$(o)/images/color-t: LIBS+=-lm
-$(o)/images/hilbert-test: LIBS+=-lm $(LIBSH)
+ifdef CONFIG_LIBJPEG
+LIBIMAGES_MODS+=io-libjpeg
+LIBIMAGES_LIBS+=-ljpeg
+endif
+
+ifdef CONFIG_LIBPNG
+LIBIMAGES_MODS+=io-libpng
+LIBIMAGES_LIBS+=-lpng
+endif
+
+ifdef CONFIG_LIBUNGIF
+LIBIMAGES_MODS+=io-libungif
+LIBIMAGES_LIBS+=-lungif
+endif
+
+ifdef CONFIG_LIBMAGICK
+LIBIMAGES_MODS+=io-libmagick
+MAGICK_LIBS:=$(shell GraphicsMagick-config --libs)
+LIBIMAGES_LIBS+=$(MAGICK_LIBS)
+$(o)/images/io-libmagick.o: CFLAGS+=-I/usr/include/GraphicsMagick
+endif
+
+$(o)/images/libimages.a: $(addsuffix .o,$(addprefix $(o)/images/,$(LIBIMAGES_MODS)))
+$(o)/images/libimages.so: $(addsuffix .oo,$(addprefix $(o)/images/,$(LIBIMAGES_MODS)))
-TESTS+=$(addprefix $(o)/images/,color.test hilbert-test.test)
+$(o)/images/image-tool: $(o)/images/image-tool.o $(LIBIMAGES) $(LIBUCW)
+$(o)/images/image-tool: LIBS+=$(LIBIMAGES_LIBS)
-$(o)/images/color.test: $(o)/images/color-t
+TESTS+=$(o)/images/hilbert-test.test
+$(o)/images/hilbert-test: LIBS+=-lm $(LIBSH)
$(o)/images/hilbert-test.test: $(o)/images/hilbert-test
+#$(o)/images/image-test: $(o)/images/image-test.o $(LIBIMAGES) $(LIBUCW)
+#$(o)/images/image-test: LIBS+=$(LIBIMAGES_LIBS)
+
+#$(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)/images/dup-cmp.o $(o)/indexer/iconfig.o $(o)/images/image-sig.o $(o)/images/kd-tree.o $(o)/images/color.o $(o)/images/image-io.o $(LIBSH) $(LIBLANG) $(LIBCHARSET)
+#$(o)/images/image-idx: LIBS+=-lGraphicsMagick -ljpeg -lpng
+
+#$(o)/images/color-t: LIBS+=-lm
+#$(o)/images/color.test: $(o)/images/color-t
+
# By :;DF
-$(o)/images/block_info.o $(o)/images/block_info.oo: CFLAGS+=-I/usr/include/GraphicsMagick
-$(o)/images/k_means.o $(o)/images/k_means.oo: CFLAGS+=-I/usr/include/GraphicsMagick
-$(o)/images/decomp.o $(o)/images/decomp.oo: CFLAGS+=-I/usr/include/GraphicsMagick
+#PROGS+=$(addprefix $(o)/images/,decomp)
+#
+#$(o)/images/block_info.o $(o)/images/block_info.oo: CFLAGS+=-I/usr/include/GraphicsMagick
+#$(o)/images/k_means.o $(o)/images/k_means.oo: CFLAGS+=-I/usr/include/GraphicsMagick
+#$(o)/images/decomp.o $(o)/images/decomp.oo: CFLAGS+=-I/usr/include/GraphicsMagick
-$(o)/images/decomp: $(o)/images/decomp.o $(o)/images/block_info.o $(o)/images/k_means.o $(LIBSH) $(LIBLANG) $(LIBCHARSET)
-$(o)/images/decomp: LIBS+=-lGraphicsMagick -ljpeg -lm
+#$(o)/images/decomp: $(o)/images/decomp.o $(o)/images/block_info.o $(o)/images/k_means.o $(LIBSH) $(LIBLANG) $(LIBCHARSET)
+#$(o)/images/decomp: LIBS+=-lGraphicsMagick -ljpeg -lm
struct image_data;
struct image_dup {
- struct image_data *image;
+ struct image *image;
byte *buf;
uns buf_size;
uns flags;
#define IMAGE_DUP_TRANS_ID 0x01
#define IMAGE_DUP_TRANS_ALL 0xff
-void image_dup_init(struct image_dup *dup, struct image_data *image, struct mempool *pool);
+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);
uns image_dup_estimate_size(uns width, uns height);
#include "lib/lib.h"
#include "lib/mempool.h"
#include "lib/math.h"
-
#include <stdlib.h>
#include <stdio.h>
/*********************************************************************************/
+#if 0
+
struct pass1_hilbert {
u32 index;
struct image_vector vec;
byte *buf;
oid_t oid;
byte *url;
- struct image_data image;
+ struct image image;
struct image_dup dup;
};
byte color_space[MAX_ATTR_SIZE];
sscanf(attr, "%d%d%s%d%d%d", &image_width, &image_height, color_space, &image_colors, &thumb_width, &thumb_height);
vectors[i].temp = image_dup_estimate_size(thumb_width, thumb_height) +
- sizeof(struct image_data) + thumb_width * thumb_height * 3;
+ sizeof(struct image) + thumb_width * thumb_height * 3;
}
buck2obj_free(bob);
mp_delete(pool);
random_clusters_cleanup();
vectors_cleanup();
}
+#endif
/*********************************************************************************/
srgb_to_luv_init();
+#if 0
+ while (1)
+ {
+ struct mempool *pool = mp_new(1024);
+ struct fastbuf *fb = bopen("a.jpg", O_RDONLY, 1024);
+ struct image_io io;
+ log(L_DEBUG, "opening");
+ image_open(&io, fb, pool);
+ io.format = IMAGE_FORMAT_JPEG;
+ log(L_DEBUG, "reading");
+ int i;
+ i = image_read(&io);
+ log(L_DEBUG, "done %d %d %d", i, io.image.width, io.image.height);
+ for (i = 0; i < 1000000000; i++)
+ ;
+ image_close(&io);
+ mp_delete(pool);
+ bclose(fb);
+ }
+#endif
+
+#if 0
generate_signatures(20000);
build_kd_tree();
//pass1();
pass2();
+#endif
return 0;
}
uns thumb_format;
byte *thumb_data;
uns thumb_size;
- struct image_data thumb;
+ struct image thumb;
};
static inline void
byte *_s = (byte *) alloca(IMAGE_VEC_K * 6), *_p = _s + sprintf(_s, "%d", _v->f[0]); \
for (uns _i = 1; _i < IMAGE_VEC_K; _i++) _p += sprintf(_p, " %d", _v->f[_i]); _s; })
-int compute_image_signature(struct image_data *image, struct image_signature *sig);
+int compute_image_signature(struct image *image, struct image_signature *sig);
#endif
#include "sherlock/sherlock.h"
#include "lib/fastbuf.h"
#include "images/images.h"
-#include "images/image-sig.h"
-#include "images/kd-tree.h"
+//#include "images/image-sig.h"
+//#include "images/kd-tree.h"
#include "sherlock/index.h"
#include "lib/mempool.h"
#include "sherlock/object.h"
#include "sherlock/sherlock.h"
#include "lib/fastbuf.h"
-#include "images/images.h"
+//#include "images/images.h"
#include "sherlock/index.h"
#include <stdio.h>
--- /dev/null
+/*
+ * Simple image manupulation utility
+ *
+ * (c) 2006 Pavel Charvat <pchar@ucw.cz>
+ *
+ * This software may be freely distributed and used according to the terms
+ * of the GNU General Public License.
+ */
+
+#include "lib/lib.h"
+#include "lib/getopt.h"
+#include "lib/fastbuf.h"
+#include "images/images.h"
+#include <stdlib.h>
+#include <fcntl.h>
+
+static void NONRET
+usage(void)
+{
+ fputs("\
+Usage: image-tool [options] infile [outfile]\n\
+\n\
+-q --quiet no progress messages\n\
+-f --input-format input image format (jpeg, gif, png)\n\
+-F --output-format output image format\n\
+-s --size force output dimensions (100x200)\n\
+-b --fit-box scale to fit the box (100x200)\n\
+-c --colorspace force output colorspace (gray, grayalpha, rgb, rgbalpha)\n\
+", stderr);
+ exit(1);
+}
+
+static char *shortopts = "qf:F:s:b:c:" CF_SHORT_OPTS;
+static struct option longopts[] =
+{
+ CF_LONG_OPTS
+ { "quiet", 0, 0, 'q' },
+ { "input-format", 0, 0, 'f' },
+ { "output-format", 0, 0, 'F' },
+ { "size", 0, 0, 's' },
+ { "fit-box", 0, 0, 'b' },
+ { "colorspace", 0, 0, 'c' },
+ { NULL, 0, 0, 0 }
+};
+
+static uns verbose = 1;
+static byte *input_file_name;
+static enum image_format input_format;
+static byte *output_file_name;
+static enum image_format output_format;
+static uns cols;
+static uns rows;
+static uns fit_box;
+static uns channels_format;
+
+#define MSG(x...) do{ if (verbose) log(L_INFO, ##x); }while(0)
+
+int
+main(int argc, char **argv)
+{
+ log_init(argv[0]);
+ int opt;
+ while ((opt = cf_getopt(argc, argv, shortopts, longopts, NULL)) >= 0)
+ switch (opt)
+ {
+ case 'q':
+ verbose = 0;
+ break;
+ case 'f':
+ if (!(input_format = image_extension_to_format(optarg)))
+ usage();
+ break;
+ case 'F':
+ if (!(output_format = image_extension_to_format(optarg)))
+ usage();
+ break;
+ case 's':
+ {
+ byte *r = strchr(optarg, 'x');
+ if (!r)
+ usage();
+ *r++ = 0;
+ if (!(cols = atoi(optarg)) || !(rows = atoi(r)))
+ usage();
+ fit_box = 0;
+ break;
+ }
+ case 'b':
+ {
+ byte *r = strchr(optarg, 'x');
+ if (!r)
+ usage();
+ *r++ = 0;
+ if (!(cols = atoi(optarg)) || !(rows = atoi(r)))
+ usage();
+ fit_box = 1;
+ break;
+ }
+ case 'c':
+ if (!(channels_format = image_name_to_channels_format(optarg)))
+ usage();
+ break;
+ default:
+ usage();
+ }
+
+ if (argc != optind + 1 && argc != optind + 2)
+ usage();
+ input_file_name = argv[optind++];
+ if (argc > optind)
+ output_file_name = argv[optind];
+
+#define TRY(x) do{ if (!(x)) die("Error: %s", it.err_msg); }while(0)
+ MSG("Initializing image library");
+ struct image_thread it;
+ struct image_io io;
+ image_thread_init(&it);
+ image_io_init(&it, &io);
+
+ MSG("Reading %s", input_file_name);
+ io.fastbuf = bopen(input_file_name, O_RDONLY, 1 << 18);
+ io.format = input_format ? : image_file_name_to_format(input_file_name);
+ TRY(image_io_read_header(&io));
+ if (!output_file_name)
+ {
+ bclose(io.fastbuf);
+ printf("Format: %s\n", image_format_to_extension(io.format) ? : (byte *)"?");
+ printf("Dimensions: %dx%d\n", io.cols, io.rows);
+ printf("Colorspace: %s\n", image_channels_format_to_name(io.flags & IMAGE_CHANNELS_FORMAT));
+ }
+ else
+ {
+ MSG("%s %dx%d %s", image_format_to_extension(io.format) ? : (byte *)"?", io.cols, io.rows,
+ image_channels_format_to_name(io.flags & IMAGE_CHANNELS_FORMAT));
+ if (cols)
+ if (fit_box)
+ {
+ image_dimensions_fit_to_box(&io.cols, &io.rows, MIN(cols, 0xffff), MIN(rows, 0xffff), 0);
+ }
+ else
+ {
+ io.cols = cols;
+ io.rows = rows;
+ }
+ if (channels_format)
+ io.flags = io.flags & ~IMAGE_PIXEL_FORMAT | channels_format;
+ TRY(image_io_read_data(&io, 0));
+ bclose(io.fastbuf);
+ MSG("Writing %s", output_file_name);
+ io.fastbuf = bopen(output_file_name, O_WRONLY | O_CREAT | O_TRUNC, 1 << 18);
+ io.format = output_format ? : image_file_name_to_format(output_file_name);
+ MSG("%s %dx%d %s", image_format_to_extension(io.format) ? : (byte *)"?", io.cols, io.rows,
+ image_channels_format_to_name(io.flags & IMAGE_CHANNELS_FORMAT));
+ TRY(image_io_write(&io));
+ bclose(io.fastbuf);
+ }
+
+ image_io_cleanup(&io);
+ image_thread_cleanup(&it);
+ MSG("Done.");
+ return 0;
+}
--- /dev/null
+/*
+ * Image Library -- Pixels iteration
+ *
+ * (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.
+ */
+
+#if !defined(IMAGE_WALK_INLINE) && !defined(IMAGE_WALK_STATIC)
+# error Missing IMAGE_WALK_INLINE or IMAGE_WALK_STATIC
+#endif
+
+#if !defined(IMAGE_WALK_UNROLL)
+# define IMAGE_WALK_UNROLL 1
+#elif IMAGE_WALK_UNROLL != 1 && IMAGE_WALK_UNROLL != 2 && IMAGE_WALK_UNROLL != 4
+# error IMAGE_WALK_UNROLL must be 1, 2 or 4
+#endif
+
+#ifndef IMAGE_WALK_PIXELS
+# define IMAGE_WALK_PIXELS (img->pixels)
+#endif
+#ifndef IMAGE_WALK_COLS
+# define IMAGE_WALK_COLS (img->cols)
+#endif
+#ifndef IMAGE_WALK_ROWS
+# define IMAGE_WALK_ROWS (img->rows)
+#endif
+#ifndef IMAGE_WALK_COL_STEP
+# define IMAGE_WALK_COL_STEP (img->pixel_size)
+#endif
+#ifndef IMAGE_WALK_ROW_STEP
+# define IMAGE_WALK_ROW_STEP (img->row_size)
+#endif
+
+#ifdef IMAGE_WALK_DOUBLE
+# ifndef IMAGE_WALK_SEC_PIXELS
+# define IMAGE_WALK_SEC_PIXELS (sec_img->pixels)
+# endif
+# ifndef IMAGE_WALK_SEC_COLS
+# define IMAGE_WALK_SEC_COLS (sec_img->cols)
+# endif
+# ifndef IMAGE_WALK_SEC_ROWS
+# define IMAGE_WALK_SEC_ROWS (sec_img->rows)
+# endif
+# ifndef IMAGE_WALK_SEC_COL_STEP
+# define IMAGE_WALK_SEC_COL_STEP (sec_img->pixel_size)
+# endif
+# ifndef IMAGE_WALK_SEC_ROW_STEP
+# define IMAGE_WALK_SEC_ROW_STEP (sec_img->row_size)
+# endif
+# define STEP IMAGE_WALK_DO_STEP; pos += col_step; sec_pos += sec_col_step
+#else
+# define STEP IMAGE_WALK_DO_STEP; pos += col_step
+#endif
+
+#ifndef IMAGE_WALK_DO_START
+# define IMAGE_WALK_DO_START
+#endif
+
+#ifndef IMAGE_WALK_DO_END
+# define IMAGE_WALK_DO_END
+#endif
+
+#ifndef IMAGE_WALK_DO_ROW_START
+# define IMAGE_WALK_DO_ROW_START
+#endif
+
+#ifndef IMAGE_WALK_DO_ROW_END
+# define IMAGE_WALK_DO_ROW_END
+#endif
+
+#ifndef IMAGE_WALK_DO_STEP
+# define IMAGE_WALK_DO_STEP
+#endif
+
+#ifndef IMAGE_WALK_INLINE
+static void IMAGE_WALK_STATIC
+ (struct image *img
+# ifdef IMAGE_WALK_DOUBLE
+ , struct image *sec_img
+# endif
+# ifdef IMAGE_WALK_EXTRA_ARGS
+ , IMAGE_WALK_EXTRA_ARGS
+# endif
+ )
+#endif
+{
+ uns cols = IMAGE_WALK_COLS;
+ uns rows = IMAGE_WALK_ROWS;
+# if IMAGE_WALK_UNROLL > 1
+ uns cols_unroll_block_count = cols / IMAGE_WALK_UNROLL;
+ uns cols_unroll_end_count = cols % IMAGE_WALK_UNROLL;
+# endif
+ byte *pos = IMAGE_WALK_PIXELS, *row_start = pos;
+ int col_step = IMAGE_WALK_COL_STEP;
+ int row_step = IMAGE_WALK_ROW_STEP;
+# ifdef IMAGE_WALK_DOUBLE
+ byte *sec_pos = IMAGE_WALK_SEC_PIXELS, *sec_row_start = sec_pos;
+ int sec_col_step = IMAGE_WALK_SEC_COL_STEP;
+ int sec_row_step = IMAGE_WALK_SEC_ROW_STEP;
+# endif
+ IMAGE_WALK_DO_START;
+ while (rows--)
+ {
+ IMAGE_WALK_DO_ROW_START;
+# if IMAGE_WALK_UNROLL == 1
+ for (uns i = cols; i--; )
+# else
+ for (uns i = cols_unroll_block_count; i--; )
+# endif
+ {
+# if IMAGE_WALK_UNROLL >= 4
+ STEP;
+ STEP;
+# endif
+# if IMAGE_WALK_UNROLL >= 2
+ STEP;
+# endif
+ STEP;
+ }
+# if IMAGE_WALK_UNROLL > 1
+ for (uns i = cols_unroll_end_count; i--; )
+ {
+ STEP;
+ }
+# endif
+ IMAGE_WALK_DO_ROW_END;
+ pos = (row_start += row_step);
+# ifdef IMAGE_WALK_DOUBLE
+ sec_pos = (sec_row_start += sec_row_step);
+# endif
+ }
+ IMAGE_WALK_DO_END;
+}
+
+#undef IMAGE_WALK_INLINE
+#undef IMAGE_WALK_STATIC
+#undef IMAGE_WALK_UNROLL
+#undef IMAGE_WALK_DOUBLE
+#undef IMAGE_WALK_EXTRA_ARGS
+#undef IMAGE_WALK_PIXELS
+#undef IMAGE_WALK_COLS
+#undef IMAGE_WALK_ROWS
+#undef IMAGE_WALK_COL_STEP
+#undef IMAGE_WALK_ROW_STEP
+#undef IMAGE_WALK_SEC_PIXELS
+#undef IMAGE_WALK_SEC_COLS
+#undef IMAGE_WALK_SEC_ROWS
+#undef IMAGE_WALK_SEC_COL_STEP
+#undef IMAGE_WALK_SEC_ROW_STEP
+#undef IMAGE_WALK_DO_START
+#undef IMAGE_WALK_DO_END
+#undef IMAGE_WALK_DO_ROW_START
+#undef IMAGE_WALK_DO_ROW_END
+#undef IMAGE_WALK_DO_STEP
+#undef STEP
--- /dev/null
+/*
+ * Image Library -- Basic image manipulation
+ *
+ * (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.
+ */
+
+#undef LOCAL_DEBUG
+
+#include "lib/lib.h"
+#include "lib/mempool.h"
+#include "images/images.h"
+#include <string.h>
+
+#define MAX_IMAGE_SIZE (1 << 30)
+
+void
+image_thread_init(struct image_thread *it)
+{
+ DBG("image_thread_init()");
+ bzero(it, sizeof(*it));
+ it->pool = mp_new(1024);
+}
+
+void
+image_thread_cleanup(struct image_thread *it)
+{
+ DBG("image_thread_cleanup()");
+ mp_delete(it->pool);
+}
+
+void
+image_thread_err_format(struct image_thread *it, uns code, char *msg, ...)
+{
+ va_list args;
+ va_start(args, msg);
+ it->err_code = code;
+ it->err_msg = mp_vprintf(it->pool, msg, args);
+ va_end(args);
+}
+
+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)
+ {
+ image_thread_err(it, IMAGE_ERR_INVALID_DIMENSIONS, "Image dimension(s) too large");
+ return NULL;
+ }
+ struct image *img;
+ uns pixel_size, row_size, image_size, align;
+ switch (flags & IMAGE_COLOR_SPACE)
+ {
+ case COLOR_SPACE_GRAYSCALE:
+ pixel_size = 1;
+ break;
+ case COLOR_SPACE_RGB:
+ pixel_size = 3;
+ break;
+ default:
+ ASSERT(0);
+ }
+ if (flags & IMAGE_ALPHA)
+ pixel_size++;
+ switch (pixel_size)
+ {
+ case 1:
+ case 2:
+ case 4:
+ flags |= IMAGE_PIXELS_ALIGNED;
+ break;
+ case 3:
+ if (flags & IMAGE_PIXELS_ALIGNED)
+ pixel_size = 4;
+ break;
+ default:
+ ASSERT(0);
+ }
+ if (flags & IMAGE_SSE_ALIGNED)
+ align = MAX(16, sizeof(uns));
+ 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)
+ {
+ image_thread_err(it, IMAGE_ERR_INVALID_DIMENSIONS, "Image does not fit in memory");
+ return NULL;
+ }
+ if (!(image_size = image_size_64))
+ {
+ 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);
+ bzero(img, sizeof(struct image));
+ byte *p = (byte *)img + sizeof(struct image);
+ img->pixels = ALIGN_PTR(p, MAX(16, sizeof(uns)));
+ img->flags = flags;
+ img->pixel_size = pixel_size;
+ img->cols = cols;
+ img->rows = rows;
+ img->row_size = row_size;
+ img->image_size = image_size;
+ DBG("img=%p flags=0x%x pixel_size=%u row_size=%u image_size=%u pixels=%p",
+ img, img->flags, img->pixel_size, img->row_size, img->image_size, img->pixels);
+ return img;
+}
+
+struct image *
+image_clone(struct image_thread *it, struct image *src, uns flags, struct mempool *pool)
+{
+ DBG("image_clone(src=%p flags=0x%x pool=%p)", src, src->flags, pool);
+ struct image *img;
+ flags &= ~IMAGE_CHANNELS_FORMAT;
+ flags |= src->flags & IMAGE_CHANNELS_FORMAT;
+ if (!(img = image_new(it, src->cols, src->rows, flags, pool)))
+ return NULL;
+ if (img->image_size)
+ {
+ if (src->pixel_size != img->pixel_size)
+ {
+ struct image *sec_img = src;
+# define IMAGE_WALK_INLINE
+# define IMAGE_WALK_DOUBLE
+# define IMAGE_WALK_DO_STEP do{ *(u32 *)pos = *(u32 *)sec_pos; }while(0)
+# include "images/image-walk.h"
+ }
+ else if (src->row_size != img->row_size)
+ {
+ byte *s = src->pixels;
+ byte *d = img->pixels;
+ uns bytes = src->cols * img->pixel_size;
+ for (uns row = src->rows; row--; )
+ {
+ memcpy(d, s, bytes);
+ d += img->row_size;
+ s += src->row_size;
+ }
+ }
+ else
+ memcpy(img->pixels, src->pixels, img->image_size);
+ }
+ return img;
+}
+
+void
+image_destroy(struct image_thread *it UNUSED, struct image *img)
+{
+ DBG("image_destroy(img=%p)", img);
+ xfree((byte *)img);
+}
+
+void
+image_clear(struct image_thread *it UNUSED, struct image *img)
+{
+ DBG("image_clear(img=%p)", img);
+ if (img->image_size)
+ bzero(img->pixels, img->image_size);
+}
+
+byte *
+color_space_to_name(enum color_space cs)
+{
+ return image_channels_format_to_name(cs);
+}
+
+byte *
+image_channels_format_to_name(uns format)
+{
+ switch (format)
+ {
+ case COLOR_SPACE_GRAYSCALE:
+ return "Gray";
+ case COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA:
+ return "GrayAlpha";
+ case COLOR_SPACE_RGB:
+ return "RGB";
+ case COLOR_SPACE_RGB | IMAGE_ALPHA:
+ return "RGBAlpha";
+ default:
+ return NULL;
+ }
+}
+
+uns
+image_name_to_channels_format(byte *name)
+{
+ if (!strcasecmp(name, "gray"))
+ return COLOR_SPACE_GRAYSCALE;
+ if (!strcasecmp(name, "grayscale"))
+ return COLOR_SPACE_GRAYSCALE;
+ if (!strcasecmp(name, "grayalpha"))
+ return COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA;
+ if (!strcasecmp(name, "grayscalealpha"))
+ return COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA;
+ if (!strcasecmp(name, "rgb"))
+ return COLOR_SPACE_RGB;
+ if (!strcasecmp(name, "rgbalpha"))
+ return COLOR_SPACE_RGB + IMAGE_ALPHA;
+ if (!strcasecmp(name, "rgba"))
+ return COLOR_SPACE_RGB + IMAGE_ALPHA;
+ return 0;
+}
#ifndef _IMAGES_IMAGES_H
#define _IMAGES_IMAGES_H
+#include "lib/mempool.h"
+
+/* image.c */
+
+/* error handling */
+
+enum image_error {
+ IMAGE_ERR_OK = 0,
+ IMAGE_ERR_UNSPECIFIED,
+ IMAGE_ERR_NOT_IMPLEMENTED,
+ IMAGE_ERR_INVALID_DIMENSIONS,
+ IMAGE_ERR_INVALID_FILE_FORMAT,
+ IMAGE_ERR_INVALID_PIXEL_FORMAT,
+ IMAGE_ERR_READ_FAILED,
+ IMAGE_ERR_WRITE_FAILED,
+ IMAGE_ERR_MAX
+};
+
+struct image_thread {
+ byte *err_msg;
+ enum image_error err_code;
+ struct mempool *pool;
+};
+
+void image_thread_init(struct image_thread *thread);
+void image_thread_cleanup(struct image_thread *thread);
+
+static inline void
+image_thread_flush(struct image_thread *thread)
+{
+ thread->err_code = 0;
+ thread->err_msg = NULL;
+ mp_flush(thread->pool);
+}
+
+static inline void
+image_thread_err(struct image_thread *thread, uns code, char *msg)
+{
+ thread->err_code = code;
+ thread->err_msg = (byte *)msg;
+}
+
+void image_thread_err_format(struct image_thread *thread, uns code, char *msg, ...);
+
+/* basic image manupulation */
+
+enum color_space {
+ COLOR_SPACE_UNKNOWN,
+ COLOR_SPACE_GRAYSCALE,
+ COLOR_SPACE_RGB,
+ COLOR_SPACE_MAX
+};
+
enum image_flag {
- IMAGE_GRAYSCALE = 0x1, /* grayscale image */
+ IMAGE_COLOR_SPACE = 0x7, /* mask for enum color_space */
+ IMAGE_ALPHA = 0x8, /* alpha channel */
+ IMAGE_PIXELS_ALIGNED = 0x10, /* align pixel size to the nearest power of two */
+ IMAGE_SSE_ALIGNED = 0x20, /* align scanlines to multiples of 16 bytes (both start and size) */
+ IMAGE_CHANNELS_FORMAT = IMAGE_COLOR_SPACE | IMAGE_ALPHA,
+ IMAGE_PIXEL_FORMAT = IMAGE_CHANNELS_FORMAT | IMAGE_PIXELS_ALIGNED,
+ IMAGE_ALIGNED = IMAGE_PIXELS_ALIGNED | IMAGE_SSE_ALIGNED,
};
-struct image_data {
- uns flags; /* enum image_flag */
- uns width; /* number of columns */
- uns height; /* number of rows */
- uns size; /* buffer size in bytes */
- byte *pixels; /* RGB */
+struct image {
+ byte *pixels; /* left top pixel, there are at least sizeof(uns)
+ unsed bytes after the buffer (possible optimizations) */
+ u32 cols; /* number of columns */
+ u32 rows; /* number of rows */
+ u32 pixel_size; /* size of pixel (1, 2, 3 or 4) */
+ u32 row_size; /* scanline size in bytes */
+ u32 image_size; /* size of pixels buffer (rows * rows_size) */
+ u32 flags; /* enum image_flag */
};
-#if 0
+struct image *image_new(struct image_thread *it, uns cols, uns rows, uns flags, struct mempool *pool);
+struct image *image_clone(struct image_thread *it, struct image *src, uns flags, struct mempool *pool);
+void image_destroy(struct image_thread *it, struct image *img); /* only with NULL mempool */
+void image_clear(struct image_thread *it, struct image *img);
+
+byte *color_space_to_name(enum color_space cs);
+byte *image_channels_format_to_name(uns format);
+uns image_name_to_channels_format(byte *name);
+
+/* scale.c */
+
+int image_scale(struct image_thread *thread, struct image *dest, struct image *src);
+void image_dimensions_fit_to_box(u32 *cols, u32 *rows, u32 max_cols, u32 max_rows, uns upsample);
+
+/* image-io.c */
enum image_format {
- IMAGE_FORMAT_UNDEFINED = 0,
+ IMAGE_FORMAT_UNKNOWN,
IMAGE_FORMAT_JPEG,
IMAGE_FORMAT_PNG,
- IMAGE_FORMAT_GIF
+ IMAGE_FORMAT_GIF,
+ IMAGE_FORMAT_MAX
};
struct image_io {
- struct mempool *pool;
- struct fastbuf *fb;
+ struct image *image;
+ struct fastbuf *fastbuf;
enum image_format format;
- struct image_data image;
- void *internals;
+ struct mempool *pool;
+ u32 cols;
+ u32 rows;
+ u32 flags;
+ /* internals */
+ struct image_thread *thread;
+ struct mempool *internal_pool;
+ int image_destroy;
+ void *read_data;
+ void (*read_cancel)(struct image_io *io);
union {
struct {
} jpeg;
};
};
-void image_open(struct image_io *io, struct fastbuf *fb, struct mempool *pool);
-void image_close(struct image_io *io);
-int image_read_header(struct image_io *io);
-int image_read_data(struct image_io *io);
-int image_read(struct image_io *io);
-int image_write(struct image_io *io);
+void image_io_init(struct image_thread *it, struct image_io *io);
+void image_io_cleanup(struct image_io *io);
+void image_io_reset(struct image_io *io);
+
+int image_io_read_header(struct image_io *io);
+struct image *image_io_read_data(struct image_io *io, int ref);
+struct image *image_io_read(struct image_io *io, int ref);
+
+int image_io_write(struct image_io *io);
+byte *image_format_to_extension(enum image_format format);
+enum image_format image_extension_to_format(byte *extension);
+enum image_format image_file_name_to_format(byte *file_name);
+
+/* internals */
+
+#ifdef CONFIG_LIBJPEG
+int libjpeg_read_header(struct image_io *io);
+int libjpeg_read_data(struct image_io *io);
+int libjpeg_write(struct image_io *io);
+#endif
+
+#ifdef CONFIG_LIBPNG
+int libpng_read_header(struct image_io *io);
+int libpng_read_data(struct image_io *io);
+int libpng_write(struct image_io *io);
#endif
+#ifdef CONFIG_LIBUNGIF
+int libungif_read_header(struct image_io *io);
+int libungif_read_data(struct image_io *io);
#endif
+#ifdef CONFIG_LIBMAGICK
+int libmagick_read_header(struct image_io *io);
+int libmagick_read_data(struct image_io *io);
+int libmagick_write(struct image_io *io);
+#endif
+
+#endif
--- /dev/null
+/*
+ * Image Library -- libjpeg
+ *
+ * (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.
+ */
+
+#undef LOCAL_DEBUG
+
+#include "lib/lib.h"
+#include "lib/mempool.h"
+#include "lib/fastbuf.h"
+#include "images/images.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <jpeglib.h>
+#include <setjmp.h>
+
+struct libjpeg_err {
+ struct jpeg_error_mgr pub;
+ jmp_buf setjmp_buf;
+ struct image_io *io;
+};
+
+struct libjpeg_read_internals {
+ struct jpeg_decompress_struct cinfo;
+ struct jpeg_source_mgr src;
+ struct libjpeg_err err;
+ struct fastbuf *fastbuf;
+ byte *fastbuf_pos;
+};
+
+struct libjpeg_write_internals {
+ struct jpeg_compress_struct cinfo;
+ struct jpeg_destination_mgr dest;
+ struct libjpeg_err err;
+ struct fastbuf *fastbuf;
+ byte *fastbuf_pos;
+};
+
+static void NONRET
+libjpeg_read_error_exit(j_common_ptr cinfo)
+{
+ DBG("libjpeg_error_exit()");
+ struct libjpeg_err *e = (struct libjpeg_err *)cinfo->err;
+ byte buf[JMSG_LENGTH_MAX];
+ e->pub.format_message(cinfo, buf);
+ image_thread_err(e->io->thread, IMAGE_ERR_READ_FAILED, buf);
+ longjmp(e->setjmp_buf, 1);
+}
+
+static void NONRET
+libjpeg_write_error_exit(j_common_ptr cinfo)
+{
+ DBG("libjpeg_error_exit()");
+ struct libjpeg_err *e = (struct libjpeg_err *)cinfo->err;
+ byte buf[JMSG_LENGTH_MAX];
+ e->pub.format_message(cinfo, buf);
+ image_thread_err(e->io->thread, IMAGE_ERR_WRITE_FAILED, buf);
+ longjmp(e->setjmp_buf, 1);
+}
+
+static void
+libjpeg_emit_message(j_common_ptr cinfo UNUSED, int msg_level UNUSED)
+{
+#ifdef LOCAL_DEBUG
+ byte buf[JMSG_LENGTH_MAX];
+ cinfo->err->format_message(cinfo, buf);
+ DBG("libjpeg_emit_message(): [%d] %s", msg_level, buf);
+#endif
+ if (unlikely(msg_level == -1))
+ longjmp(((struct libjpeg_err *)(cinfo)->err)->setjmp_buf, 1);
+}
+
+static inline uns
+libjpeg_fastbuf_read_prepare(struct libjpeg_read_internals *i)
+{
+ byte *start;
+ uns len = bdirect_read_prepare(i->fastbuf, &start);
+ i->fastbuf_pos = start + len;
+ i->src.next_input_byte = start;
+ i->src.bytes_in_buffer = len;
+ return len;
+}
+
+static inline void
+libjpeg_fastbuf_read_commit(struct libjpeg_read_internals *i)
+{
+ bdirect_read_commit(i->fastbuf, i->fastbuf_pos);
+}
+
+static void
+libjpeg_init_source(j_decompress_ptr cinfo)
+{
+ DBG("libjpeg_init_source()");
+ libjpeg_fastbuf_read_prepare((struct libjpeg_read_internals *)cinfo);
+}
+
+static void
+libjpeg_term_source(j_decompress_ptr cinfo UNUSED)
+{
+ DBG("libjpeg_term_source()");
+ //libjpeg_fastbuf_read_commit((struct libjpeg_read_internals *)cinfo);
+}
+
+static boolean
+libjpeg_fill_input_buffer(j_decompress_ptr cinfo)
+{
+ DBG("libjpeg_fill_input_buffer()");
+ struct libjpeg_read_internals *i = (struct libjpeg_read_internals *)cinfo;
+ libjpeg_fastbuf_read_commit(i);
+ return !!libjpeg_fastbuf_read_prepare(i);
+}
+
+static void
+libjpeg_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
+{
+ DBG("libjpeg_skip_input_data(num_bytes=%d)", (int)num_bytes);
+ if (num_bytes > 0)
+ {
+ struct libjpeg_read_internals *i = (struct libjpeg_read_internals *)cinfo;
+ if ((unsigned long)num_bytes <= i->src.bytes_in_buffer)
+ {
+ i->src.next_input_byte += num_bytes;
+ i->src.bytes_in_buffer -= num_bytes;
+ }
+ else
+ {
+ num_bytes -= i->src.bytes_in_buffer;
+ libjpeg_fastbuf_read_commit(i);
+ bskip(i->fastbuf, num_bytes);
+ libjpeg_fastbuf_read_prepare(i);
+ }
+ }
+}
+
+static inline void
+libjpeg_fastbuf_write_prepare(struct libjpeg_write_internals *i)
+{
+ byte *start;
+ uns len = bdirect_write_prepare(i->fastbuf, &start);
+ i->fastbuf_pos = start + len;
+ i->dest.next_output_byte = start;
+ i->dest.free_in_buffer = len;
+ if (!len)
+ {
+ image_thread_err(i->err.io->thread, IMAGE_ERR_WRITE_FAILED, "Unexpected end of stream");
+ longjmp(i->err.setjmp_buf, 1);
+ }
+}
+
+static void
+libjpeg_init_destination(j_compress_ptr cinfo)
+{
+ DBG("libjpeg_init_destination()");
+ libjpeg_fastbuf_write_prepare((struct libjpeg_write_internals *)cinfo);
+}
+
+static void
+libjpeg_term_destination(j_compress_ptr cinfo)
+{
+ DBG("libjpeg_term_destination()");
+ struct libjpeg_write_internals *i = (struct libjpeg_write_internals *)cinfo;
+ bdirect_write_commit(i->fastbuf, (byte *)i->dest.next_output_byte);
+}
+
+static boolean
+libjpeg_empty_output_buffer(j_compress_ptr cinfo)
+{
+ DBG("libjpeg_empty_output_buffer()");
+ struct libjpeg_write_internals *i = (struct libjpeg_write_internals *)cinfo;
+ bdirect_write_commit(i->fastbuf, i->fastbuf_pos);
+ libjpeg_fastbuf_write_prepare(i);
+ return TRUE;
+}
+
+static void
+libjpeg_read_cancel(struct image_io *io)
+{
+ DBG("libjpeg_read_cancel()");
+ struct libjpeg_read_internals *i = io->read_data;
+ jpeg_destroy_decompress(&i->cinfo);
+}
+
+int
+libjpeg_read_header(struct image_io *io)
+{
+ DBG("libjpeg_read_header()");
+ struct libjpeg_read_internals *i = io->read_data = mp_alloc(io->internal_pool, sizeof(*i));
+ i->fastbuf = io->fastbuf;
+
+ /* Create libjpeg read structure */
+ DBG("Creating libjpeg read structure");
+ i->cinfo.err = jpeg_std_error(&i->err.pub);
+ i->err.pub.error_exit = libjpeg_read_error_exit;
+ i->err.pub.emit_message = libjpeg_emit_message;
+ i->err.io = io;
+ if (setjmp(i->err.setjmp_buf))
+ {
+ DBG("Libjpeg failed to read the image, longjump saved us");
+ jpeg_destroy_decompress(&i->cinfo);
+ return 0;
+ }
+ jpeg_create_decompress(&i->cinfo);
+
+ /* Initialize source manager */
+ i->cinfo.src = &i->src;
+ i->src.init_source = libjpeg_init_source;
+ i->src.fill_input_buffer = libjpeg_fill_input_buffer;
+ i->src.skip_input_data = libjpeg_skip_input_data;
+ i->src.resync_to_restart = jpeg_resync_to_restart;
+ i->src.term_source = libjpeg_term_source;
+
+ /* Read JPEG header and setup decompression options */
+ DBG("Reading image header");
+ jpeg_read_header(&i->cinfo, TRUE);
+ if (!(io->flags & IMAGE_COLOR_SPACE))
+ switch (i->cinfo.jpeg_color_space)
+ {
+ case JCS_GRAYSCALE:
+ io->flags |= COLOR_SPACE_GRAYSCALE;
+ break;
+ default:
+ io->flags |= COLOR_SPACE_RGB;
+ break;
+ }
+ if (!io->cols)
+ io->cols = i->cinfo.image_width;
+ if (!io->rows)
+ io->rows = i->cinfo.image_height;
+
+ io->read_cancel = libjpeg_read_cancel;
+ return 1;
+}
+
+int
+libjpeg_read_data(struct image_io *io)
+{
+ DBG("libjpeg_read_data()");
+
+ struct libjpeg_read_internals *i = io->read_data;
+
+ /* Select color space */
+ switch (io->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;
+ default:
+ jpeg_destroy_decompress(&i->cinfo);
+ image_thread_err(io->thread, IMAGE_ERR_INVALID_PIXEL_FORMAT, "Unsupported color space.");
+ return 0;
+ }
+
+ /* Allocate the image... FIXME: use libjpeg feature to speed up downscale */
+ volatile int need_scale = io->cols != i->cinfo.image_width || io->rows != i->cinfo.image_height;
+ struct image * volatile img = need_scale ?
+ image_new(io->thread, i->cinfo.image_width, i->cinfo.image_height, io->flags & IMAGE_PIXEL_FORMAT, NULL) :
+ image_new(io->thread, i->cinfo.image_width, i->cinfo.image_height, io->flags, io->pool);
+ if (!img)
+ {
+ image_thread_err(io->thread, IMAGE_ERR_INVALID_PIXEL_FORMAT, "Unsupported color space.");
+ return 0;
+ }
+
+ /* Setup fallback */
+ if (setjmp(i->err.setjmp_buf))
+ {
+ DBG("Libjpeg failed to read the image, longjump saved us");
+ jpeg_destroy_decompress(&i->cinfo);
+ if (need_scale || !io->pool)
+ image_destroy(io->thread, img);
+ return 0;
+ }
+
+ /* Decompress the image */
+ jpeg_start_decompress(&i->cinfo);
+ switch (img->pixel_size)
+ {
+ /* grayscale or RGB */
+ case 1:
+ case 3:
+ {
+ byte *pixels = img->pixels;
+ for (uns r = img->rows; r--; )
+ {
+ jpeg_read_scanlines(&i->cinfo, (JSAMPLE **)&pixels, 1);
+ pixels += img->row_size;
+ }
+ }
+ break;
+ /* garscale with alpha */
+ case 2:
+ {
+ byte buf[img->cols], *src;
+# define IMAGE_WALK_INLINE
+# define IMAGE_WALK_UNROLL 4
+# define IMAGE_WALK_COL_STEP 2
+# define IMAGE_WALK_DO_ROW_START do{ src = buf; jpeg_read_scanlines(&i->cinfo, (JSAMPLE **)&src, 1); }while(0)
+# define IMAGE_WALK_DO_STEP do{ pos[0] = *src++; pos[1] = 255; }while(0)
+# include "images/image-walk.h"
+ }
+ break;
+ /* RGBA or aligned RGB */
+ case 4:
+ {
+ byte buf[img->cols * 3], *src;
+# define IMAGE_WALK_INLINE
+# define IMAGE_WALK_UNROLL 4
+# define IMAGE_WALK_COL_STEP 4
+# define IMAGE_WALK_DO_ROW_START do{ src = buf; jpeg_read_scanlines(&i->cinfo, (JSAMPLE **)&src, 1); }while(0)
+# define IMAGE_WALK_DO_STEP do{ *(u32 *)pos = *(u32 *)src; pos[3] = 255; src += 3; }while(0)
+# include "images/image-walk.h"
+ }
+ break;
+ default:
+ ASSERT(0);
+ }
+ ASSERT(i->cinfo.output_scanline == i->cinfo.output_height);
+
+ /* Destroy libjpeg object */
+ jpeg_finish_decompress(&i->cinfo);
+ jpeg_destroy_decompress(&i->cinfo);
+
+ /* Scale result if necessary */
+ if (need_scale)
+ {
+ DBG("Scaling image");
+ struct image *dest = image_new(io->thread, io->cols, io->rows, io->flags, io->pool);
+ if (!dest)
+ {
+ image_destroy(io->thread, img);
+ return 0;
+ }
+ if (!(image_scale(io->thread, dest, img)))
+ {
+ image_destroy(io->thread, img);
+ if (!io->pool)
+ image_destroy(io->thread, dest);
+ return 0;
+ }
+ image_destroy(io->thread, img);
+ io->image = dest;
+ }
+ else
+ io->image = img;
+ io->image_destroy = !io->pool;
+
+ DBG("Image readed");
+ return 1;
+}
+
+int
+libjpeg_write(struct image_io *io)
+{
+ DBG("libjpeg_write()");
+ struct libjpeg_write_internals i;
+ i.fastbuf = io->fastbuf;
+
+ /* Create libjpeg write structure */
+ DBG("Creating libjpeg write structure");
+ i.cinfo.err = jpeg_std_error(&i.err.pub);
+ i.err.pub.error_exit = libjpeg_write_error_exit;
+ i.err.pub.emit_message = libjpeg_emit_message;
+ i.err.io = io;
+ if (setjmp(i.err.setjmp_buf))
+ {
+ DBG("Libjpeg failed to write the image, longjump saved us");
+ jpeg_destroy_compress(&i.cinfo);
+ return 0;
+ }
+ jpeg_create_compress(&i.cinfo);
+
+ /* Initialize destination manager */
+ i.cinfo.dest = &i.dest;
+ i.dest.init_destination = libjpeg_init_destination;
+ i.dest.term_destination = libjpeg_term_destination;
+ i.dest.empty_output_buffer = libjpeg_empty_output_buffer;
+
+ /* Set output parameters */
+ struct image *img = io->image;
+ i.cinfo.image_width = img->cols;
+ i.cinfo.image_height = img->rows;
+ switch (io->flags & IMAGE_COLOR_SPACE)
+ {
+ case COLOR_SPACE_GRAYSCALE:
+ i.cinfo.input_components = 1;
+ i.cinfo.in_color_space = JCS_GRAYSCALE;
+ break;
+ case COLOR_SPACE_RGB:
+ i.cinfo.input_components = 3;
+ i.cinfo.in_color_space = JCS_RGB;
+ break;
+ default:
+ jpeg_destroy_compress(&i.cinfo);
+ image_thread_err(io->thread, IMAGE_ERR_INVALID_PIXEL_FORMAT, "Unsupported pixel format.");
+ return 0;
+ }
+ jpeg_set_defaults(&i.cinfo);
+
+ /* Compress the image */
+ jpeg_start_compress(&i.cinfo, TRUE);
+ switch (img->pixel_size)
+ {
+ /* grayscale or RGB */
+ case 1:
+ case 3:
+ {
+ byte *pixels = img->pixels;
+ for (uns r = img->rows; r--; )
+ {
+ jpeg_write_scanlines(&i.cinfo, (JSAMPLE **)&pixels, 1);
+ pixels += img->row_size;
+ }
+ }
+ break;
+ /* grayscale with alpha (ignore alpha) */
+ case 2:
+ {
+ byte buf[img->cols], *dest = buf;
+# define IMAGE_WALK_INLINE
+# define IMAGE_WALK_UNROLL 4
+# define IMAGE_WALK_COL_STEP 2
+# 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{ *dest++ = pos[0]; }while(0)
+# include "images/image-walk.h"
+ }
+ break;
+ /* RGBA (ignore alpha) or aligned RGB */
+ case 4:
+ {
+ byte buf[img->cols * 3 + 1], *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)
+# include "images/image-walk.h"
+ }
+ break;
+ default:
+ ASSERT(0);
+ }
+ ASSERT(i.cinfo.next_scanline == i.cinfo.image_height);
+ jpeg_finish_compress(&i.cinfo);
+ jpeg_destroy_compress(&i.cinfo);
+ return 1;
+}
--- /dev/null
+/*
+ * Image Library -- GrapgicsMagick
+ *
+ * (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.
+ */
+
+#define LOCAL_DEBUG
+
+#include "lib/lib.h"
+#include "images/images.h"
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <magick/api.h>
+
+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;
+}
+
+#if 0
+struct magick_internals {
+ ExceptionInfo exception;
+ QuantizeInfo quantize;
+ ImageInfo *info;
+};
+
+static inline void
+magick_cleanup(struct image_io *io)
+{
+ DestroyImageInfo(io->internals->info);
+ DestroyExceptionInfo(&io->internals->exception);
+ DestroyMagick();
+}
+
+static int
+magick_read_header(struct image_io *io)
+{
+ DBG("magick_read_header()");
+ struct magick_internals *i = io->internals = mp_alloc(io->pool, sizeof(*i));
+
+ InitializeMagick(NULL);
+ GetExceptionInfo(&i->exception);
+ i->info = CloneImageInfo(NULL);
+ i->info->subrange = 1;
+ GetQuantizeInfo(&i->quantize);
+ i->quantize.colorspace = RGBColorspace;
+
+ uns len = bfilesize(io->fastbuf);
+ byte *buf = mp_alloc(io->pool, len);
+ len = bread(io->fastbuf, buf, len);
+
+ Image *image = BlobToImage(magick_info, imo->thumb_data, imo->thumb_size, &magick_exception);
+ if (unlikely(!image))
+ goto error;
+
+ // FIXME
+ return 1;
+error:
+ magick_cleanup(io);
+ return 0;
+}
+
+static int
+magick_read_data(struct image_io *io)
+{
+ DBG("magick_read_data()");
+
+ // FIXME
+
+ magick_cleanup(io);
+ return 1;
+}
+
+static int
+magick_decompress_thumbnail(struct image_obj *imo)
+{
+ 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++)
+ {
+ 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;
+}
+#endif
--- /dev/null
+/*
+ * Image Library -- libpng
+ *
+ * (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.
+ */
+
+#undef LOCAL_DEBUG
+
+#include "lib/lib.h"
+#include "lib/mempool.h"
+#include "lib/fastbuf.h"
+#include "images/images.h"
+#include <png.h>
+#include <setjmp.h>
+
+struct libpng_internals {
+ png_structp png_ptr;
+ png_infop info_ptr;
+ png_infop end_ptr;
+ png_uint_32 cols;
+ png_uint_32 rows;
+ int bit_depth;
+ int color_type;
+};
+
+static png_voidp
+libpng_malloc(png_structp png_ptr, png_size_t size)
+{
+ DBG("libpng_malloc(size=%u)", (uns)size);
+ return mp_alloc(png_get_mem_ptr(png_ptr), size);
+}
+
+static void
+libpng_free(png_structp png_ptr UNUSED, png_voidp ptr UNUSED)
+{
+ DBG("libpng_free()");
+}
+
+static void NONRET
+libpng_error(png_structp png_ptr, png_const_charp msg)
+{
+ DBG("libpng_error()");
+ image_thread_err(png_get_error_ptr(png_ptr), IMAGE_ERR_READ_FAILED, (byte *)msg);
+ longjmp(png_jmpbuf(png_ptr), 1);
+}
+
+static void
+libpng_warning(png_structp png_ptr UNUSED, png_const_charp msg UNUSED)
+{
+ DBG("libpng_warning(): %s", (byte *)msg);
+}
+
+static void
+libpng_read(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+ DBG("libpng_read(): len=%d", (uns)length);
+ if (unlikely(bread(png_get_io_ptr(png_ptr), data, length) < length))
+ png_error(png_ptr, "Incomplete data");
+}
+
+static void
+libpng_read_cancel(struct image_io *io)
+{
+ DBG("libpng_read_cancel()");
+ struct libpng_internals *i = io->read_data;
+ png_destroy_read_struct(&i->png_ptr, &i->info_ptr, &i->end_ptr);
+}
+
+int
+libpng_read_header(struct image_io *io)
+{
+ DBG("libpng_read_header()");
+ struct libpng_internals *i = io->read_data = mp_alloc(io->internal_pool, sizeof(*i));
+ i->png_ptr = png_create_read_struct_2(PNG_LIBPNG_VER_STRING,
+ io->thread, libpng_error, libpng_warning,
+ io->internal_pool, libpng_malloc, libpng_free);
+ if (unlikely(!i->png_ptr))
+ goto err_create;
+ i->info_ptr = png_create_info_struct(i->png_ptr);
+ if (unlikely(!i->info_ptr))
+ {
+ png_destroy_read_struct(&i->png_ptr, NULL, NULL);
+ goto err_create;
+ }
+ i->end_ptr = png_create_info_struct(i->png_ptr);
+ if (unlikely(!i->end_ptr))
+ {
+ png_destroy_read_struct(&i->png_ptr, &i->info_ptr, NULL);
+ goto err_create;
+ }
+ if (setjmp(png_jmpbuf(i->png_ptr)))
+ {
+ DBG("Libpng failed to read the image, longjump saved us");
+ png_destroy_read_struct(&i->png_ptr, &i->info_ptr, &i->end_ptr);
+ return 0;
+ }
+ png_set_read_fn(i->png_ptr, io->fastbuf, libpng_read);
+ png_set_user_limits(i->png_ptr, 0xffff, 0xffff);
+
+ DBG("Reading image info");
+ png_read_info(i->png_ptr, i->info_ptr);
+ png_get_IHDR(i->png_ptr, i->info_ptr, &i->cols, &i->rows, &i->bit_depth, &i->color_type, NULL, NULL, NULL);
+
+ if (!io->cols)
+ io->cols = i->cols;
+ if (!io->rows)
+ io->rows = i->rows;
+ if (!(io->flags & IMAGE_CHANNELS_FORMAT))
+ switch (i->color_type)
+ {
+ case PNG_COLOR_TYPE_GRAY:
+ io->flags |= COLOR_SPACE_GRAYSCALE;
+ break;
+ case PNG_COLOR_TYPE_GRAY_ALPHA:
+ io->flags |= COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA;
+ break;
+ case PNG_COLOR_TYPE_RGB:
+ io->flags |= COLOR_SPACE_RGB;
+ break;
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ case PNG_COLOR_TYPE_PALETTE:
+ io->flags |= COLOR_SPACE_RGB | IMAGE_ALPHA;
+ break;
+ default:
+ png_destroy_read_struct(&i->png_ptr, &i->info_ptr, &i->end_ptr);
+ image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Unknown color type");
+ break;
+ }
+
+ io->read_cancel = libpng_read_cancel;
+ return 1;
+
+err_create:
+ image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Cannot create libpng read structure.");
+ return 0;
+}
+
+int
+libpng_read_data(struct image_io *io)
+{
+ DBG("libpng_read_data()");
+
+ struct libpng_internals *i = 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(&i->png_ptr, &i->info_ptr, &i->end_ptr);
+ image_thread_err(io->thread, IMAGE_ERR_INVALID_PIXEL_FORMAT, "Unsupported color space.");
+ return 0;
+ }
+
+ volatile int need_scale = io->cols != i->cols || io->rows != i->rows;
+ struct image * volatile img = need_scale ?
+ image_new(io->thread, i->cols, i->rows, io->flags & IMAGE_PIXEL_FORMAT, NULL) :
+ image_new(io->thread, i->cols, i->rows, io->flags, io->pool);
+ if (!img)
+ {
+ png_destroy_read_struct(&i->png_ptr, &i->info_ptr, &i->end_ptr);
+ return 0;
+ }
+
+ if (setjmp(png_jmpbuf(i->png_ptr)))
+ {
+ DBG("Libpng failed to read the image, longjump saved us");
+ png_destroy_read_struct(&i->png_ptr, &i->info_ptr, &i->end_ptr);
+ if (need_scale || !io->pool)
+ image_destroy(io->thread, img);
+ return 0;
+ }
+
+ /* Apply transformations */
+ if (i->bit_depth == 16)
+ png_set_strip_16(i->png_ptr);
+ switch (i->color_type)
+ {
+ case PNG_COLOR_TYPE_PALETTE:
+ if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE)
+ {
+ png_set_palette_to_rgb(i->png_ptr);
+ png_set_rgb_to_gray_fixed(i->png_ptr, 1, 21267, 71514);
+ }
+ else
+ png_set_palette_to_rgb(i->png_ptr);
+ if ((io->flags & IMAGE_ALPHA) || (io->flags & IMAGE_PIXEL_FORMAT) == (COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED))
+ png_set_add_alpha(i->png_ptr, 255, PNG_FILLER_AFTER);
+ else
+ png_set_strip_alpha(i->png_ptr);
+ break;
+ case PNG_COLOR_TYPE_GRAY:
+ if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_RGB)
+ png_set_gray_to_rgb(i->png_ptr);
+ if (io->flags & IMAGE_ALPHA)
+ png_set_add_alpha(i->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(i->png_ptr);
+ if (!(io->flags & IMAGE_ALPHA))
+ png_set_strip_alpha(i->png_ptr);
+ break;
+ case PNG_COLOR_TYPE_RGB:
+ if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE)
+ png_set_rgb_to_gray_fixed(i->png_ptr, 1, 21267, 71514);
+ if ((io->flags & IMAGE_ALPHA) || (io->flags & IMAGE_PIXEL_FORMAT) == (COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED))
+ png_set_add_alpha(i->png_ptr, 255, PNG_FILLER_AFTER);
+ break;
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE)
+ png_set_rgb_to_gray_fixed(i->png_ptr, 1, 21267, 71514);
+ if (!(io->flags & IMAGE_ALPHA) && (io->flags & IMAGE_PIXEL_FORMAT) != (COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED))
+ png_set_strip_alpha(i->png_ptr);
+ break;
+ default:
+ ASSERT(0);
+ }
+ png_read_update_info(i->png_ptr, i->info_ptr);
+
+ /* Read image data */
+ DBG("Reading image data");
+ byte *pixels = img->pixels;
+ png_bytep rows[img->rows];
+ for (uns r = 0; r < img->rows; r++, pixels += img->row_size)
+ rows[r] = (png_bytep)pixels;
+ png_read_image(i->png_ptr, rows);
+ png_read_end(i->png_ptr, i->end_ptr);
+
+ /* Destroy libpng read structure */
+ png_destroy_read_struct(&i->png_ptr, &i->info_ptr, &i->end_ptr);
+
+ /* Scale and store the resulting image */
+ if (need_scale)
+ {
+ struct image *dest = image_new(io->thread, io->cols, io->rows, io->flags, io->pool);
+ if (!dest)
+ {
+ image_destroy(io->thread, img);
+ return 0;
+ }
+ if (!image_scale(io->thread, dest, img))
+ {
+ image_destroy(io->thread, img);
+ if (!io->pool)
+ image_destroy(io->thread, dest);
+ return 0;
+ }
+ io->image = dest;
+ }
+ else
+ io->image = img;
+ io->image_destroy = !io->pool;
+
+ return 1;
+}
+
+int
+libpng_write(struct image_io *io)
+{
+ image_thread_err(io->thread, IMAGE_ERR_NOT_IMPLEMENTED, "Libpng writing not implemented.");
+ return 0;
+}
--- /dev/null
+/*
+ * Image Library -- libungif
+ *
+ * (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.
+ */
+
+#define LOCAL_DEBUG
+
+#include "lib/lib.h"
+#include "images/images.h"
+
+int
+libungif_read_header(struct image_io *io)
+{
+ image_thread_err(io->thread, IMAGE_ERR_NOT_IMPLEMENTED, "Libungif read not implemented.");
+ return 0;
+}
+
+int
+libungif_read_data(struct image_io *io UNUSED)
+{
+ ASSERT(0);
+}
--- /dev/null
+/*
+ * Image Library -- Image compression/decompression interface
+ *
+ * (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.
+ */
+
+#undef LOCAL_DEBUG
+
+#include "lib/lib.h"
+#include "images/images.h"
+#include <string.h>
+
+void
+image_io_init(struct image_thread *it, struct image_io *io)
+{
+ DBG("image_io_init()");
+ bzero(io, sizeof(*io));
+ io->thread = it;
+ io->internal_pool = mp_new(1024);
+}
+
+static inline void
+image_io_read_cancel(struct image_io *io)
+{
+ if (io->read_cancel)
+ {
+ io->read_cancel(io);
+ io->read_cancel = NULL;
+ }
+}
+
+static inline void
+image_io_image_destroy(struct image_io *io)
+{
+ if (io->image_destroy)
+ {
+ image_destroy(io->thread, io->image);
+ io->image_destroy = 0;
+ io->image = NULL;
+ }
+}
+
+void
+image_io_cleanup(struct image_io *io)
+{
+ DBG("image_io_cleanup()");
+ image_io_read_cancel(io);
+ image_io_image_destroy(io);
+ mp_delete(io->internal_pool);
+}
+
+void
+image_io_reset(struct image_io *io)
+{
+ DBG("image_io_reset()");
+ image_io_read_cancel(io);
+ image_io_image_destroy(io);
+ struct mempool *pool = io->internal_pool;
+ mp_flush(pool);
+ bzero(io, sizeof(*io));
+ io->internal_pool = pool;
+}
+
+int
+image_io_read_header(struct image_io *io)
+{
+ DBG("image_io_read_header()");
+ image_io_read_cancel(io);
+ image_io_image_destroy(io);
+ switch (io->format) {
+ case IMAGE_FORMAT_JPEG:
+#if defined(CONFIG_LIBJPEG)
+ return libjpeg_read_header(io);
+#elif defined(CONFIG_LIBMAGICK)
+ return libmagick_read_header(io);
+#endif
+ break;
+
+ case IMAGE_FORMAT_PNG:
+#if defined(CONFIG_LIBPNG)
+ return libpng_read_header(io);
+#elif defined(CONFIG_LIBMAGICK)
+ return libmagick_read_header(io);
+#endif
+ break;
+
+ case IMAGE_FORMAT_GIF:
+#if defined(CONFIG_LIBUNGIG)
+ return libungif_read_header(io);
+#elif defined(CONFIG_LIBMAGICK)
+ return libmagick_read_header(io);
+#endif
+ break;
+
+ case IMAGE_FORMAT_UNKNOWN:
+ // FIXME: auto-detect
+ break;
+
+ default:
+ ASSERT(0);
+ }
+ image_thread_err(io->thread, IMAGE_ERR_INVALID_FILE_FORMAT, "Image format not supported.");
+ return 0;
+}
+
+struct image *
+image_io_read_data(struct image_io *io, int ref)
+{
+ DBG("image_io_read_data()");
+ ASSERT(io->read_cancel);
+ io->read_cancel = NULL;
+ int result;
+ switch (io->format) {
+ case IMAGE_FORMAT_JPEG:
+#if defined(CONFIG_LIBJPEG)
+ result = libjpeg_read_data(io);
+#elif defined(CONFIG_LIBMAGICK)
+ result = libmagick_read_data(io);
+#else
+ ASSERT(0);
+#endif
+ break;
+
+ case IMAGE_FORMAT_PNG:
+#if defined(CONFIG_LIBPNG)
+ result = libpng_read_data(io);
+#elif defined(CONFIG_LIBMAGICK)
+ result = libmagick_read_data(io);
+#else
+ ASSERT(0);
+#endif
+ break;
+
+ case IMAGE_FORMAT_GIF:
+#if defined(CONFIG_LIBUNGIF)
+ result = libungif_read_data(io);
+#elif defined(CONFIG_LIBMAGICK)
+ result = libmagick_read_data(io);
+#else
+ ASSERT(0);
+#endif
+ break;
+
+ default:
+ ASSERT(0);
+ }
+ if (result)
+ {
+ if (ref)
+ io->image_destroy = 0;
+ return io->image;
+ }
+ else
+ return NULL;
+}
+
+struct image *
+image_io_read(struct image_io *io, int ref)
+{
+ if (!image_io_read_header(io))
+ return NULL;
+ return image_io_read_data(io, ref);
+}
+
+int
+image_io_write(struct image_io *io)
+{
+ DBG("image_io_write()");
+ image_io_read_cancel(io);
+ switch (io->format) {
+ case IMAGE_FORMAT_JPEG:
+#if defined(CONFIG_LIBJPEG)
+ return libjpeg_write(io);
+#endif
+ break;
+
+ case IMAGE_FORMAT_PNG:
+ break;
+
+ case IMAGE_FORMAT_GIF:
+ break;
+
+ default:
+ break;
+ }
+ image_thread_err(io->thread, IMAGE_ERR_INVALID_FILE_FORMAT, "Image format not supported.");
+ return 0;
+}
+
+byte *
+image_format_to_extension(enum image_format format)
+{
+ switch (format)
+ {
+ case IMAGE_FORMAT_JPEG:
+ return "jpg";
+ case IMAGE_FORMAT_PNG:
+ return "png";
+ case IMAGE_FORMAT_GIF:
+ return "gif";
+ default:
+ return NULL;
+ }
+}
+
+enum image_format
+image_extension_to_format(byte *extension)
+{
+ if (!strcasecmp(extension, "jpg"))
+ return IMAGE_FORMAT_JPEG;
+ if (!strcasecmp(extension, "jpeg"))
+ return IMAGE_FORMAT_JPEG;
+ if (!strcasecmp(extension, "png"))
+ return IMAGE_FORMAT_PNG;
+ if (!strcasecmp(extension, "gif"))
+ return IMAGE_FORMAT_GIF;
+ return IMAGE_FORMAT_UNKNOWN;
+}
+
+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;
+}
--- /dev/null
+/*
+ * Image Library -- Image scaling algorithms
+ *
+ * (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_SCALE_CHANNELS
+# define IMAGE_SCALE_CHANNELS IMAGE_SCALE_PIXEL_SIZE
+#endif
+
+static void
+IMAGE_SCALE_PREFIX(downsample)(struct image *dest, struct image *src)
+{
+ /* FIXME slow */
+ byte *rsrc = src->pixels, *psrc;
+ byte *rdest = dest->pixels, *pdest;
+ uns x_inc = (dest->cols << 16) / src->cols, x_pos, x_inc_frac = 0xffffff / x_inc;
+ uns y_inc = (dest->rows << 16) / src->rows, y_pos = 0, y_inc_frac = 0xffffff / y_inc;
+ uns final_mul = ((u64)x_inc * y_inc) >> 16;
+ uns buf_size = dest->cols * IMAGE_SCALE_CHANNELS;
+ u32 buf[buf_size], *pbuf;
+ buf_size *= sizeof(u32);
+ bzero(buf, buf_size);
+ for (uns rows_counter = src->rows; rows_counter--; )
+ {
+ pbuf = buf;
+ psrc = rsrc;
+ rsrc += src->row_size;
+ x_pos = 0;
+ y_pos += y_inc;
+ if (y_pos <= 0x10000)
+ {
+ for (uns cols_counter = src->cols; cols_counter--; )
+ {
+ x_pos += x_inc;
+ if (x_pos <= 0x10000)
+ {
+ pbuf[0] += psrc[0];
+# if IMAGE_SCALE_CHANNELS >= 2
+ pbuf[1] += psrc[1];
+# endif
+# if IMAGE_SCALE_CHANNELS >= 3
+ pbuf[2] += psrc[2];
+# endif
+# if IMAGE_SCALE_CHANNELS >= 4
+ pbuf[3] += psrc[3];
+# endif
+ }
+ else
+ {
+ x_pos -= 0x10000;
+ uns mul2 = x_pos * x_inc_frac;
+ uns mul1 = 0xffffff - mul2;
+ pbuf[0] += (psrc[0] * mul1) >> 24;
+ pbuf[0 + IMAGE_SCALE_CHANNELS] += (psrc[0] * mul2) >> 24;
+# if IMAGE_SCALE_CHANNELS >= 2
+ pbuf[1] += (psrc[1] * mul1) >> 24;
+ pbuf[1 + IMAGE_SCALE_CHANNELS] += (psrc[1] * mul2) >> 24;
+# endif
+# if IMAGE_SCALE_CHANNELS >= 3
+ pbuf[2] += (psrc[2] * mul1) >> 24;
+ pbuf[2 + IMAGE_SCALE_CHANNELS] += (psrc[2] * mul2) >> 24;
+# endif
+# if IMAGE_SCALE_CHANNELS >= 4
+ pbuf[3] += (psrc[3] * mul1) >> 24;
+ pbuf[3 + IMAGE_SCALE_CHANNELS] += (psrc[3] * mul2) >> 24;
+# endif
+ pbuf += IMAGE_SCALE_CHANNELS;
+ }
+ psrc += IMAGE_SCALE_PIXEL_SIZE;
+ }
+ }
+ else
+ {
+ y_pos -= 0x10000;
+ pdest = rdest;
+ rdest += dest->row_size;
+ uns mul2 = y_pos * y_inc_frac;
+ uns mul1 = 0xffffff - mul2;
+ uns a0 = 0;
+# if IMAGE_SCALE_CHANNELS >= 2
+ uns a1 = 0;
+# endif
+# if IMAGE_SCALE_CHANNELS >= 3
+ uns a2 = 0;
+# endif
+# if IMAGE_SCALE_CHANNELS >= 4
+ uns a3 = 0;
+# endif
+ for (uns cols_counter = src->cols; cols_counter--; )
+ {
+ x_pos += x_inc;
+ if (x_pos <= 0x10000)
+ {
+ pbuf[0] += ((psrc[0] * mul1) >> 24);
+ a0 += (psrc[0] * mul2) >> 24;
+# if IMAGE_SCALE_CHANNELS >= 2
+ pbuf[1] += ((psrc[1] * mul1) >> 24);
+ a1 += (psrc[1] * mul2) >> 24;
+# endif
+# if IMAGE_SCALE_CHANNELS >= 3
+ pbuf[2] += ((psrc[2] * mul1) >> 24);
+ a2 += (psrc[2] * mul2) >> 24;
+# endif
+# if IMAGE_SCALE_CHANNELS >= 4
+ pbuf[3] += ((psrc[3] * mul1) >> 24);
+ a3 += (psrc[3] * mul2) >> 24;
+# endif
+ }
+ else
+ {
+ x_pos -= 0x10000;
+ uns mul4 = x_pos * x_inc_frac;
+ uns mul3 = 0xffffff - mul4;
+ uns mul13 = ((u64)mul1 * mul3) >> 24;
+ uns mul23 = ((u64)mul2 * mul3) >> 24;
+ uns mul14 = ((u64)mul1 * mul4) >> 24;
+ uns mul24 = ((u64)mul2 * mul4) >> 24;
+ pdest[0] = ((((psrc[0] * mul13) >> 24) + pbuf[0]) * final_mul) >> 16;
+ pbuf[0] = ((psrc[0] * mul23) >> 24) + a0;
+ pbuf[0 + IMAGE_SCALE_CHANNELS] += ((psrc[0 + IMAGE_SCALE_PIXEL_SIZE] * mul14) >> 24);
+ a0 = ((psrc[0 + IMAGE_SCALE_PIXEL_SIZE] * mul24) >> 24);
+# if IMAGE_SCALE_CHANNELS >= 2
+ pdest[1] = ((((psrc[1] * mul13) >> 24) + pbuf[1]) * final_mul) >> 16;
+ pbuf[1] = ((psrc[1] * mul23) >> 24) + a1;
+ pbuf[1 + IMAGE_SCALE_CHANNELS] += ((psrc[1 + IMAGE_SCALE_PIXEL_SIZE] * mul14) >> 24);
+ a1 = ((psrc[1 + IMAGE_SCALE_PIXEL_SIZE] * mul24) >> 24);
+# endif
+# if IMAGE_SCALE_CHANNELS >= 3
+ pdest[2] = ((((psrc[2] * mul13) >> 24) + pbuf[2]) * final_mul) >> 16;
+ pbuf[2] = ((psrc[2] * mul23) >> 24) + a2;
+ pbuf[2 + IMAGE_SCALE_CHANNELS] += ((psrc[2 + IMAGE_SCALE_PIXEL_SIZE] * mul14) >> 24);
+ a2 = ((psrc[2 + IMAGE_SCALE_PIXEL_SIZE] * mul24) >> 24);
+# endif
+# if IMAGE_SCALE_CHANNELS >= 4
+ pdest[3] = ((((psrc[3] * mul13) >> 24) + pbuf[3]) * final_mul) >> 16;
+ pbuf[3] = ((psrc[3] * mul23) >> 24) + a3;
+ pbuf[3 + IMAGE_SCALE_CHANNELS] += ((psrc[3 + IMAGE_SCALE_PIXEL_SIZE] * mul14) >> 24);
+ a3 = ((psrc[3 + IMAGE_SCALE_PIXEL_SIZE] * mul24) >> 24);
+# endif
+ pbuf += IMAGE_SCALE_CHANNELS;
+ pdest += IMAGE_SCALE_PIXEL_SIZE;
+ }
+ psrc += IMAGE_SCALE_PIXEL_SIZE;
+ }
+ pdest[0] = (pbuf[0] * final_mul) >> 16;
+ pbuf[0] = a0;
+# if IMAGE_SCALE_CHANNELS >= 2
+ pdest[1] = (pbuf[1] * final_mul) >> 16;
+ pbuf[1] = a1;
+# endif
+# if IMAGE_SCALE_CHANNELS >= 3
+ pdest[2] = (pbuf[2] * final_mul) >> 16;
+ pbuf[2] = a2;
+# endif
+# if IMAGE_SCALE_CHANNELS >= 4
+ pdest[3] = (pbuf[3] * final_mul) >> 16;
+ pbuf[3] = a3;
+# endif
+ }
+ }
+ pdest = rdest;
+ pbuf = buf;
+ for (uns cols_counter = dest->cols; cols_counter--; )
+ {
+ pdest[0] = (pbuf[0] * final_mul) >> 16;
+# if IMAGE_SCALE_CHANNELS >= 2
+ pdest[1] = (pbuf[1] * final_mul) >> 16;
+# endif
+# if IMAGE_SCALE_CHANNELS >= 3
+ pdest[2] = (pbuf[2] * final_mul) >> 16;
+# endif
+# if IMAGE_SCALE_CHANNELS >= 4
+ pdest[3] = (pbuf[3] * final_mul) >> 16;
+# endif
+ pbuf += IMAGE_SCALE_CHANNELS;
+ pdest += IMAGE_SCALE_PIXEL_SIZE;
+ }
+}
+
+#undef IMAGE_SCALE_PREFIX
+#undef IMAGE_SCALE_PIXEL_SIZE
+#undef IMAGE_SCALE_CHANNELS
--- /dev/null
+/*
+ * Image Library -- Image scaling algorithms
+ *
+ * (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.
+ */
+
+#undef LOCAL_DEBUG
+
+#include "lib/lib.h"
+#include "images/images.h"
+#include <string.h>
+
+#define IMAGE_SCALE_PREFIX(x) image_scale_1_##x
+#define IMAGE_SCALE_PIXEL_SIZE 1
+#include "images/scale-gen.h"
+
+#define IMAGE_SCALE_PREFIX(x) image_scale_2_##x
+#define IMAGE_SCALE_PIXEL_SIZE 2
+#include "images/scale-gen.h"
+
+#define IMAGE_SCALE_PREFIX(x) image_scale_3_##x
+#define IMAGE_SCALE_PIXEL_SIZE 3
+#include "images/scale-gen.h"
+
+#define IMAGE_SCALE_PREFIX(x) image_scale_4_##x
+#define IMAGE_SCALE_PIXEL_SIZE 4
+#include "images/scale-gen.h"
+
+int
+image_scale(struct image_thread *it, struct image *dest, struct image *src)
+{
+ if (src->cols < dest->cols || src->rows < dest->rows)
+ {
+ image_thread_err(it, IMAGE_ERR_INVALID_DIMENSIONS, "Upsampling not supported.");
+ return 0;
+ }
+ if ((src->flags & IMAGE_PIXEL_FORMAT) != (dest->flags & IMAGE_PIXEL_FORMAT))
+ {
+ image_thread_err(it, IMAGE_ERR_INVALID_PIXEL_FORMAT, "Different pixel format not supported.");
+ return 0;
+ }
+ switch (src->pixel_size)
+ {
+ /* Gray */
+ case 1:
+ image_scale_1_downsample(dest, src);
+ return 1;
+ /* GrayA */
+ case 2:
+ image_scale_2_downsample(dest, src);
+ return 1;
+ /* RGB */
+ case 3:
+ image_scale_3_downsample(dest, src);
+ return 1;
+ /* RGBA or aligned RGB */
+ case 4:
+ image_scale_4_downsample(dest, src);
+ return 1;
+ default:
+ ASSERT(0);
+ }
+}
+
+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);
+ if (*cols <= max_cols && *rows <= max_rows)
+ {
+ if (!upsample)
+ return;
+ if (max_cols / *cols > max_rows / *rows)
+ {
+ *cols = *cols * max_rows / *rows;
+ *cols = MIN(*cols, max_cols);
+ *rows = max_rows;
+ }
+ else
+ {
+ *rows = *rows * max_cols / *cols;
+ *rows = MIN(*rows, max_rows);
+ *cols = max_cols;
+ }
+ }
+ else if (*cols <= max_cols)
+ goto down_cols;
+ else if (*rows <= max_rows || max_rows * *cols > max_cols * *rows)
+ goto down_rows;
+down_cols:
+ *cols = *cols * max_rows / *rows;
+ *cols = MAX(*cols, 1);
+ *rows = max_rows;
+ return;
+down_rows:
+ *rows = *rows * max_cols / *cols;
+ *rows = MAX(*rows, 1);
+ *cols = max_cols;
+ return;
+}