]> mj.ucw.cz Git - libucw.git/commitdiff
backup of incomplete sources
authorPavel Charvat <pavel.charvat@netcentrum.cz>
Mon, 24 Jul 2006 08:42:30 +0000 (10:42 +0200)
committerPavel Charvat <pavel.charvat@netcentrum.cz>
Mon, 24 Jul 2006 08:42:30 +0000 (10:42 +0200)
18 files changed:
images/Makefile
images/dup-cmp.h
images/hilbert-test.c
images/image-idx.c
images/image-obj.h
images/image-sig.h
images/image-test.c
images/image-tool.c [new file with mode: 0644]
images/image-walk.h [new file with mode: 0644]
images/image.c [new file with mode: 0644]
images/images.h
images/io-libjpeg.c [new file with mode: 0644]
images/io-libmagick.c [new file with mode: 0644]
images/io-libpng.c [new file with mode: 0644]
images/io-libungif.c [new file with mode: 0644]
images/io-main.c [new file with mode: 0644]
images/scale-gen.h [new file with mode: 0644]
images/scale.c [new file with mode: 0644]

index 868dfbae1a51eafbbecc8962c89b95685f93ec6e..412d959cb0353b42eeb71ce4f2461b995311bac1 100644 (file)
@@ -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
 
index f3a1ee686853fea37ca30fda1e783b4b963e0f24..03d477d30dbd5b7e994dbd163e67ed56e055cfa4 100644 (file)
@@ -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);
 
index b07efa8c08e016d978f7b50bbe7ea9d419b0bfa1..9824f092600da500a8c7328773ed683c620f1311 100644 (file)
@@ -5,7 +5,6 @@
 #include "lib/lib.h"
 #include "lib/mempool.h"
 #include "lib/math.h"
-
 #include <stdlib.h>
 #include <stdio.h>
 
index c53eb8b88ecd7b8434c13fd79bce3c9968df2f0a..9e5ece0fd962b2b2aa1c28f01e709f3696fc7d12 100644 (file)
@@ -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;
 }
index 6506466d4b3044d12b6794d8837405b8dc29dad1..95976bc8ce85412b3f2644486b1f093e3cfff81f 100644 (file)
@@ -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
index fab4cf5f718fe5f00d4875aceb830aeca03f8997..3532262aa0df20d3daff871e26f416874feefed4 100644 (file)
@@ -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
 
index 9ff9df9e5b190af2fb646b0b479247685c14b223..b909ed65965b2df4cdc53f0f188e0551dbabac39 100644 (file)
@@ -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 <stdio.h>
diff --git a/images/image-tool.c b/images/image-tool.c
new file mode 100644 (file)
index 0000000..94fdd44
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ *     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;
+}
diff --git a/images/image-walk.h b/images/image-walk.h
new file mode 100644 (file)
index 0000000..a276b08
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ *     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
diff --git a/images/image.c b/images/image.c
new file mode 100644 (file)
index 0000000..4fb349d
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ *     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;
+}
index f57c78e2046618f36c0773361703fbe81759f964..ee68a52b35842c43cb8a6854eca0075154d4ff41 100644 (file)
 #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 (file)
index 0000000..dfe44dd
--- /dev/null
@@ -0,0 +1,454 @@
+/*
+ *     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;
+}
diff --git a/images/io-libmagick.c b/images/io-libmagick.c
new file mode 100644 (file)
index 0000000..d48553a
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ *     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
diff --git a/images/io-libpng.c b/images/io-libpng.c
new file mode 100644 (file)
index 0000000..6ed0516
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ *     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;
+}
diff --git a/images/io-libungif.c b/images/io-libungif.c
new file mode 100644 (file)
index 0000000..0cc2622
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ *     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);
+}
diff --git a/images/io-main.c b/images/io-main.c
new file mode 100644 (file)
index 0000000..c45864f
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ *     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;
+}
diff --git a/images/scale-gen.h b/images/scale-gen.h
new file mode 100644 (file)
index 0000000..ff51a51
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ *     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
diff --git a/images/scale.c b/images/scale.c
new file mode 100644 (file)
index 0000000..f617b01
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ *     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;
+}