From 966305189fb3c543170117da57f110a8e9851ac2 Mon Sep 17 00:00:00 2001 From: Pavel Charvat Date: Mon, 24 Jul 2006 10:42:30 +0200 Subject: [PATCH] backup of incomplete sources --- images/Makefile | 67 +++++-- images/dup-cmp.h | 4 +- images/hilbert-test.c | 1 - images/image-idx.c | 30 ++- images/image-obj.h | 2 +- images/image-sig.h | 2 +- images/image-test.c | 6 +- images/image-tool.c | 162 +++++++++++++++ images/image-walk.h | 157 +++++++++++++++ images/image.c | 211 ++++++++++++++++++++ images/images.h | 154 ++++++++++++-- images/io-libjpeg.c | 454 ++++++++++++++++++++++++++++++++++++++++++ images/io-libmagick.c | 114 +++++++++++ images/io-libpng.c | 268 +++++++++++++++++++++++++ images/io-libungif.c | 26 +++ images/io-main.c | 228 +++++++++++++++++++++ images/scale-gen.h | 186 +++++++++++++++++ images/scale.c | 104 ++++++++++ 18 files changed, 2130 insertions(+), 46 deletions(-) create mode 100644 images/image-tool.c create mode 100644 images/image-walk.h create mode 100644 images/image.c create mode 100644 images/io-libjpeg.c create mode 100644 images/io-libmagick.c create mode 100644 images/io-libpng.c create mode 100644 images/io-libungif.c create mode 100644 images/io-main.c create mode 100644 images/scale-gen.h create mode 100644 images/scale.c diff --git a/images/Makefile b/images/Makefile index 868dfbae..412d959c 100644 --- a/images/Makefile +++ b/images/Makefile @@ -2,28 +2,63 @@ 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 diff --git a/images/dup-cmp.h b/images/dup-cmp.h index f3a1ee68..03d477d3 100644 --- a/images/dup-cmp.h +++ b/images/dup-cmp.h @@ -4,7 +4,7 @@ struct image_data; struct image_dup { - struct image_data *image; + struct image *image; byte *buf; uns buf_size; uns flags; @@ -20,7 +20,7 @@ struct image_dup { #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); diff --git a/images/hilbert-test.c b/images/hilbert-test.c index b07efa8c..9824f092 100644 --- a/images/hilbert-test.c +++ b/images/hilbert-test.c @@ -5,7 +5,6 @@ #include "lib/lib.h" #include "lib/mempool.h" #include "lib/math.h" - #include #include diff --git a/images/image-idx.c b/images/image-idx.c index c53eb8b8..9e5ece0f 100644 --- a/images/image-idx.c +++ b/images/image-idx.c @@ -451,6 +451,8 @@ build_kd_tree(void) /*********************************************************************************/ +#if 0 + struct pass1_hilbert { u32 index; struct image_vector vec; @@ -463,7 +465,7 @@ struct pass1_node { byte *buf; oid_t oid; byte *url; - struct image_data image; + struct image image; struct image_dup dup; }; @@ -839,7 +841,7 @@ pass2_estimate_sizes(void) 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); @@ -864,6 +866,7 @@ pass2(void) random_clusters_cleanup(); vectors_cleanup(); } +#endif /*********************************************************************************/ @@ -909,10 +912,33 @@ main(int argc UNUSED, char **argv) 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; } diff --git a/images/image-obj.h b/images/image-obj.h index 6506466d..95976bc8 100644 --- a/images/image-obj.h +++ b/images/image-obj.h @@ -35,7 +35,7 @@ struct image_obj { uns thumb_format; byte *thumb_data; uns thumb_size; - struct image_data thumb; + struct image thumb; }; static inline void diff --git a/images/image-sig.h b/images/image-sig.h index fab4cf5f..3532262a 100644 --- a/images/image-sig.h +++ b/images/image-sig.h @@ -59,7 +59,7 @@ struct image_leaf { 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 diff --git a/images/image-test.c b/images/image-test.c index 9ff9df9e..b909ed65 100644 --- a/images/image-test.c +++ b/images/image-test.c @@ -3,8 +3,8 @@ #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" @@ -14,7 +14,7 @@ #include "sherlock/sherlock.h" #include "lib/fastbuf.h" -#include "images/images.h" +//#include "images/images.h" #include "sherlock/index.h" #include diff --git a/images/image-tool.c b/images/image-tool.c new file mode 100644 index 00000000..94fdd44d --- /dev/null +++ b/images/image-tool.c @@ -0,0 +1,162 @@ +/* + * Simple image manupulation utility + * + * (c) 2006 Pavel Charvat + * + * 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 +#include + +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; +} diff --git a/images/image-walk.h b/images/image-walk.h new file mode 100644 index 00000000..a276b08e --- /dev/null +++ b/images/image-walk.h @@ -0,0 +1,157 @@ +/* + * Image Library -- Pixels iteration + * + * (c) 2006 Pavel Charvat + * + * 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 diff --git a/images/image.c b/images/image.c new file mode 100644 index 00000000..4fb349de --- /dev/null +++ b/images/image.c @@ -0,0 +1,211 @@ +/* + * Image Library -- Basic image manipulation + * + * (c) 2006 Pavel Charvat + * + * 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 + +#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; +} diff --git a/images/images.h b/images/images.h index f57c78e2..ee68a52b 100644 --- a/images/images.h +++ b/images/images.h @@ -1,33 +1,118 @@ #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; @@ -38,14 +123,43 @@ struct image_io { }; }; -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 diff --git a/images/io-libjpeg.c b/images/io-libjpeg.c new file mode 100644 index 00000000..dfe44ddf --- /dev/null +++ b/images/io-libjpeg.c @@ -0,0 +1,454 @@ +/* + * Image Library -- libjpeg + * + * (c) 2006 Pavel Charvat + * + * 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 +#include +#include +#include + +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; +} diff --git a/images/io-libmagick.c b/images/io-libmagick.c new file mode 100644 index 00000000..d48553a4 --- /dev/null +++ b/images/io-libmagick.c @@ -0,0 +1,114 @@ +/* + * Image Library -- GrapgicsMagick + * + * (c) 2006 Pavel Charvat + * + * 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 +#include +#include +#include + +int +libmagick_read_header(struct image_io *io) +{ + image_thread_err(io->thread, IMAGE_ERR_NOT_IMPLEMENTED, "GraphicsMagick read not implemented."); + return 0; +} + +int +libmagick_read_data(struct image_io *io UNUSED) +{ + ASSERT(0); +} + +int +libmagick_write(struct image_io *io) +{ + image_thread_err(io->thread, IMAGE_ERR_NOT_IMPLEMENTED, "GraphicsMagick write not implemented."); + return 0; +} + +#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 diff --git a/images/io-libpng.c b/images/io-libpng.c new file mode 100644 index 00000000..6ed0516e --- /dev/null +++ b/images/io-libpng.c @@ -0,0 +1,268 @@ +/* + * Image Library -- libpng + * + * (c) 2006 Pavel Charvat + * + * 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 +#include + +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; +} diff --git a/images/io-libungif.c b/images/io-libungif.c new file mode 100644 index 00000000..0cc2622d --- /dev/null +++ b/images/io-libungif.c @@ -0,0 +1,26 @@ +/* + * Image Library -- libungif + * + * (c) 2006 Pavel Charvat + * + * 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); +} diff --git a/images/io-main.c b/images/io-main.c new file mode 100644 index 00000000..c45864fa --- /dev/null +++ b/images/io-main.c @@ -0,0 +1,228 @@ +/* + * Image Library -- Image compression/decompression interface + * + * (c) 2006 Pavel Charvat + * + * 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 + +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; +} diff --git a/images/scale-gen.h b/images/scale-gen.h new file mode 100644 index 00000000..ff51a513 --- /dev/null +++ b/images/scale-gen.h @@ -0,0 +1,186 @@ +/* + * Image Library -- Image scaling algorithms + * + * (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_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 diff --git a/images/scale.c b/images/scale.c new file mode 100644 index 00000000..f617b01e --- /dev/null +++ b/images/scale.c @@ -0,0 +1,104 @@ +/* + * Image Library -- Image scaling algorithms + * + * (c) 2006 Pavel Charvat + * + * 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 + +#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; +} -- 2.39.5