PROGS+=$(addprefix $(o)/images/,image-tool)
-LIBIMAGES_MODS=image scale io-main
+LIBIMAGES_MODS=image scale color alpha io-main
LIBIMAGES=$(o)/images/libimages.$(LS)
LIBIMAGES_LIBS=
ifdef CONFIG_IMAGES_LIBUNGIF
LIBIMAGES_MODS+=io-libungif
LIBIMAGES_LIBS+=-lungif
+else
+ifdef CONFIG_IMAGES_LIBGIF
+LIBIMAGES_MODS+=io-libungif
+LIBIMAGES_LIBS+=-lgif
+endif
endif
ifdef CONFIG_IMAGES_LIBMAGICK
$(o)/images/hilbert-test: LIBS+=-lm $(LIBSH)
$(o)/images/hilbert-test.test: $(o)/images/hilbert-test
+TESTS+=$(o)/images/color.test
+$(o)/images/color-t: LIBS+=-lm
+$(o)/images/color.test: $(o)/images/color-t
+
#$(o)/images/image-test: $(o)/images/image-test.o $(LIBIMAGES) $(LIBUCW)
#$(o)/images/image-test: LIBS+=$(LIBIMAGES_LIBS)
#$(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
#PROGS+=$(addprefix $(o)/images/,decomp)
#
--- /dev/null
+/*
+ * Image Library -- Alpha channels
+ *
+ * (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 "images/color.h"
+
+static inline uns
+merge_func(uns value, uns alpha, uns acoef, uns bcoef)
+{
+ return ((uns)(acoef + (int)alpha * (int)(value - bcoef)) * (0xffffffffU / 255 / 255)) >> 24;
+}
+
+int
+image_apply_background(struct image_thread *thread UNUSED, struct image *dest, struct image *src, struct color *background)
+{
+ DBG("image_apply_background()");
+
+ /* Grayscale */
+ if (src->pixel_size == 2)
+ {
+ ASSERT(dest->pixel_size == 1);
+ byte bg;
+ color_put_grayscale(&bg, background);
+ uns a = 255 * bg, b = bg;
+# define IMAGE_WALK_PREFIX(x) walk_##x
+# define IMAGE_WALK_INLINE
+# define IMAGE_WALK_DOUBLE
+# define IMAGE_WALK_UNROLL 4
+# define IMAGE_WALK_IMAGE dest
+# define IMAGE_WALK_SEC_IMAGE src
+# define IMAGE_WALK_COL_STEP 1
+# define IMAGE_WALK_SEC_COL_STEP 2
+# define IMAGE_WALK_DO_STEP do{ walk_pos[0] = merge_func(walk_sec_pos[0], walk_sec_pos[1], a, b); }while(0)
+# include "images/image-walk.h"
+ }
+
+ /* RGBA to RGB or aligned RGB */
+ else if (src->pixel_size == 4)
+ {
+ ASSERT((src->flags & IMAGE_ALPHA) && dest->pixel_size >= 3 && !(dest->flags & IMAGE_ALPHA));
+ byte bg[3];
+ color_put_rgb(bg, background);
+ uns a0 = 255 * bg[0], b0 = bg[0];
+ uns a1 = 255 * bg[1], b1 = bg[1];
+ uns a2 = 255 * bg[2], b2 = bg[2];
+# define IMAGE_WALK_PREFIX(x) walk_##x
+# define IMAGE_WALK_INLINE
+# define IMAGE_WALK_DOUBLE
+# define IMAGE_WALK_UNROLL 2
+# define IMAGE_WALK_IMAGE dest
+# define IMAGE_WALK_SEC_IMAGE src
+# define IMAGE_WALK_SEC_COL_STEP 4
+# define IMAGE_WALK_DO_STEP do{ \
+ walk_pos[0] = merge_func(walk_sec_pos[0], walk_sec_pos[3], a0, b0); \
+ walk_pos[1] = merge_func(walk_sec_pos[1], walk_sec_pos[3], a1, b1); \
+ walk_pos[2] = merge_func(walk_sec_pos[2], walk_sec_pos[3], a2, b2); \
+ }while(0)
+# include "images/image-walk.h"
+ }
+ else
+ ASSERT(0);
+ return 1;
+}
#include "sherlock/sherlock.h"
#include "lib/math.h"
+#include "images/images.h"
#include "images/color.h"
+struct color color_black = { .color_space = COLOR_SPACE_GRAYSCALE };
+struct color color_white = { .c = { 255 }, .color_space = COLOR_SPACE_GRAYSCALE };
+
+inline void
+color_put_grayscale(byte *dest, struct color *color)
+{
+ switch (color->color_space)
+ {
+ case COLOR_SPACE_GRAYSCALE:
+ dest[0] = color->c[0];
+ break;
+ case COLOR_SPACE_RGB:
+ dest[0] = rgb_to_gray_func(color->c[0], color->c[1], color->c[2]);
+ break;
+ default:
+ ASSERT(0);
+ }
+}
+
+inline void
+color_put_rgb(byte *dest, struct color *color)
+{
+ switch (color->color_space)
+ {
+ case COLOR_SPACE_GRAYSCALE:
+ dest[0] = dest[1] = dest[2] = color->c[0];
+ break;
+ case COLOR_SPACE_RGB:
+ dest[0] = color->c[0];
+ dest[1] = color->c[1];
+ dest[2] = color->c[2];
+ break;
+ default:
+ ASSERT(0);
+ }
+}
+
+void
+color_put_color_space(byte *dest, struct color *color, enum color_space color_space)
+{
+ switch (color_space)
+ {
+ case COLOR_SPACE_GRAYSCALE:
+ color_put_grayscale(dest, color);
+ break;
+ case COLOR_SPACE_RGB:
+ color_put_rgb(dest, color);
+ break;
+ default:
+ ASSERT(0);
+ }
+}
/********************* EXACT CONVERSION ROUTINES **********************/
#ifndef _IMAGES_COLOR_H
#define _IMAGES_COLOR_H
+#include "images/images.h"
+
+static inline uns
+rgb_to_gray_func(uns r, uns g, uns b)
+{
+ return (r * 19660 + g * 38666 + b * 7210) >> 16;
+}
+
+extern struct color color_black, color_white;
+
+static inline void
+color_make_gray(struct color *color, uns gray)
+{
+ color->c[0] = gray;
+ color->color_space = COLOR_SPACE_GRAYSCALE;
+}
+
+static inline void
+color_make_rgb(struct color *color, uns r, uns g, uns b)
+{
+ color->c[0] = r;
+ color->c[1] = g;
+ color->c[2] = b;
+ color->color_space = COLOR_SPACE_RGB;
+}
+
+void color_put_color_space(byte *dest, struct color *color, enum color_space color_space);
+void color_put_grayscale(byte *dest, struct color *color);
+void color_put_rgb(byte *dest, struct color *color);
+
/* Exact slow conversion routines */
void srgb_to_xyz_slow(double dest[3], double src[3]);
void xyz_to_luv_slow(double dest[3], double src[3]);
image_destroy(struct image *img)
{
DBG("image_destroy(img=%p)", img);
- xfree((byte *)img);
+ xfree(img);
}
void
byte *image_channels_format_to_name(uns format);
uns image_name_to_channels_format(byte *name);
+struct color {
+ byte c[3];
+ byte color_space;
+} PACKED;
+
/* 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);
+/* alpha.c */
+
+int image_apply_background(struct image_thread *thread, struct image *dest, struct image *src, struct color *background);
+
/* image-io.c */
enum image_format {
u32 flags; /* [ HI ] - see enum image_io_flags */
u32 jpeg_quality; /* [ W] - JPEG compression quality (1..100) */
u32 number_of_colors; /* [ H ] - number of image colors */
+ struct color background_color; /* [ HI ] - background color, zero if undefined */
/* internals */
struct image_thread *thread;
};
enum image_io_flags {
- IMAGE_IO_IMAGE_FLAGS = 0xffff, /* [ HI ] - parameter to image new, read_header fills IMAGE_CHANNELS_FORMAT */
+ IMAGE_IO_IMAGE_FLAGS = 0xffff, /* [ HI ] - mask of parameters to image new, read_header fills IMAGE_CHANNELS_FORMAT */
IMAGE_IO_HAS_PALETTE = 0x10000, /* [ H ] - true for image with indexed colors */
+ IMAGE_IO_HAS_BACKGROUND = 0x20000, /* [ H ] - picture contains background info */
+ IMAGE_IO_USE_BACKGROUND = 0x40000, /* [ I ] - merge transparent pixels with background_color */
};
void image_io_init(struct image_thread *it, struct image_io *io);
/* Prepare the image ... FIXME: use libjpeg feature to speed up downscale */
struct image_io_read_data_internals rdi;
- if (unlikely(!image_io_read_data_prepare(&rdi, io, i->cinfo.image_width, i->cinfo.image_height)))
+ if (unlikely(!image_io_read_data_prepare(&rdi, io, i->cinfo.image_width, i->cinfo.image_height, io->flags)))
{
jpeg_destroy_decompress(&i->cinfo);
return 0;
}
}
break;
- /* garscale with alpha */
+ /* grayscale with alpha */
case 2:
{
byte buf[img->cols], *src;
#include "lib/mempool.h"
#include "lib/fastbuf.h"
#include "images/images.h"
+#include "images/color.h"
#include "images/io-main.h"
#include <sys/types.h>
#include <stdlib.h>
static inline byte
libmagick_pixel_to_gray(PixelPacket *pixel)
{
- return ((uns)pixel->red * 19660 + (uns)pixel->green * 38666 + (uns)pixel->blue * 7210) >> (16 + QUANTUM_SCALE);
+ return rgb_to_gray_func(pixel->red, pixel->green, pixel->blue) >> QUANTUM_SCALE;
}
int
/* Prepare the image */
struct image_io_read_data_internals rdi;
- if (unlikely(!image_io_read_data_prepare(&rdi, io, rd->image->columns, rd->image->rows)))
+ uns read_flags = io->flags;
+ if ((read_flags & IMAGE_IO_USE_BACKGROUND) && !(read_flags & IMAGE_ALPHA))
+ read_flags |= IMAGE_ALPHA;
+ if (unlikely(!image_io_read_data_prepare(&rdi, io, rd->image->columns, rd->image->rows, read_flags)))
{
libmagick_destroy_read_data(rd);
return 0;
return 0;
}
- /* Prepare the image */
struct image_io_read_data_internals rdi;
- if (unlikely(!image_io_read_data_prepare(&rdi, io, rd->cols, rd->rows)))
- {
- png_destroy_read_struct(&rd->png_ptr, &rd->info_ptr, &rd->end_ptr);
- return 0;
- }
+ rdi.image = NULL;
if (setjmp(png_jmpbuf(rd->png_ptr)))
{
DBG("Libpng failed to read the image, longjump saved us");
png_destroy_read_struct(&rd->png_ptr, &rd->info_ptr, &rd->end_ptr);
- image_io_read_data_break(&rdi, io);
+ if (rdi.image)
+ image_io_read_data_break(&rdi, io);
return 0;
}
+ uns read_flags = io->flags;
+
/* Apply transformations */
if (rd->bit_depth == 16)
png_set_strip_16(rd->png_ptr);
if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_RGB)
png_set_gray_to_rgb(rd->png_ptr);
if (!(io->flags & IMAGE_ALPHA))
- png_set_strip_alpha(rd->png_ptr);
+ {
+ if (io->flags & IMAGE_IO_USE_BACKGROUND)
+ read_flags |= IMAGE_ALPHA;
+ else
+ png_set_strip_alpha(rd->png_ptr);
+ }
break;
case PNG_COLOR_TYPE_RGB:
if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE)
if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE)
png_set_rgb_to_gray_fixed(rd->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(rd->png_ptr);
+ {
+ if (io->flags & IMAGE_IO_USE_BACKGROUND)
+ read_flags |= IMAGE_ALPHA;
+ else
+ png_set_strip_alpha(rd->png_ptr);
+ }
break;
default:
ASSERT(0);
}
png_read_update_info(rd->png_ptr, rd->info_ptr);
+ /* Prepare the image */
+ if (unlikely(!image_io_read_data_prepare(&rdi, io, rd->cols, rd->rows, read_flags)))
+ {
+ png_destroy_read_struct(&rd->png_ptr, &rd->info_ptr, &rd->end_ptr);
+ return 0;
+ }
/* Read image data */
DBG("Reading image data");
struct image *img = rdi.image;
#include "lib/mempool.h"
#include "lib/fastbuf.h"
#include "images/images.h"
+#include "images/color.h"
#include "images/io-main.h"
#include <gif_lib.h>
}
io->cols = image->ImageDesc.Width;
io->rows = image->ImageDesc.Height;
- io->number_of_colors = color_map->ColorCount;
+ if (unlikely((io->number_of_colors = color_map->ColorCount) > 256))
+ {
+ image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Too many gif colors.");
+ DGifCloseFile(gif);
+ return 0;
+ }
io->flags = COLOR_SPACE_RGB | IMAGE_IO_HAS_PALETTE;
if ((uns)gif->SBackGroundColor < (uns)color_map->ColorCount)
- io->flags |= IMAGE_ALPHA;
+ {
+ io->flags |= IMAGE_ALPHA | IMAGE_IO_HAS_BACKGROUND;
+ GifColorType *background = color_map->Colors + gif->SBackGroundColor;
+ color_make_rgb(&io->background_color, background->Red, background->Green, background->Blue);
+ }
/* Success */
io->read_cancel = libungif_read_cancel;
return 1;
}
-static inline byte
-libungif_pixel_to_gray(GifColorType *pixel)
-{
- return ((uns)pixel->Red * 19660 + (uns)pixel->Green * 38666 + (uns)pixel->Blue * 7210) >> 16;
-}
-
int
libungif_read_data(struct image_io *io)
{
/* Prepare image */
struct image_io_read_data_internals rdi;
- if (unlikely(!image_io_read_data_prepare(&rdi, io, image->ImageDesc.Width, image->ImageDesc.Height)))
+ if (unlikely(!image_io_read_data_prepare(&rdi, io, image->ImageDesc.Width, image->ImageDesc.Height, io->flags)))
{
DGifCloseFile(gif);
return 0;
{
byte pal[256], *pal_pos = pal, *pal_end = pal + 256;
for (uns i = 0; i < (uns)color_map->ColorCount; i++, pal_pos++, palette++)
- *pal_pos = libungif_pixel_to_gray(palette);
+ *pal_pos = rgb_to_gray_func(palette->Red, palette->Green, palette->Blue);
if (pal_pos != pal_end)
bzero(pal_pos, pal_end - pal_pos);
+ if (io->flags & IMAGE_IO_USE_BACKGROUND)
+ color_put_grayscale(pal + background, &io->background_color);
# define DO_ROW_END do{ \
walk_row_start += dein_step; \
if (walk_row_start > img_end) \
byte pal[256 * 2], *pal_pos = pal, *pal_end = pal + 256 * 2;
for (uns i = 0; i < (uns)color_map->ColorCount; i++, pal_pos += 2, palette++)
{
- pal_pos[0] = libungif_pixel_to_gray(palette);
+ pal_pos[0] = rgb_to_gray_func(palette->Red, palette->Green, palette->Blue);
pal_pos[1] = 255;
}
if (pal_pos != pal_end)
}
if (pal_pos != pal_end)
bzero(pal_pos, pal_end - pal_pos);
+ if (io->flags & IMAGE_IO_USE_BACKGROUND)
+ color_put_rgb(pal + background, &io->background_color);
# define IMAGE_WALK_PREFIX(x) walk_##x
# define IMAGE_WALK_INLINE
# define IMAGE_WALK_IMAGE (rdi.image)
* of the GNU Lesser General Public License.
*/
-#undef LOCAL_DEBUG
+#define LOCAL_DEBUG
#include "lib/lib.h"
#include "images/images.h"
break;
case IMAGE_FORMAT_GIF:
-#if defined(CONFIG_IMAGES_LIBUNGIF)
+#if defined(CONFIG_IMAGES_LIBUNGIF) || defined(CONFIG_IMAGES_LIBGIF)
return libungif_read_header(io);
#elif defined(CONFIG_IMAGES_LIBMAGICK)
return libmagick_read_header(io);
break;
case IMAGE_FORMAT_GIF:
-#if defined(CONFIG_IMAGES_LIBUNGIF)
+#if defined(CONFIG_IMAGES_LIBUNGIF) || defined(CONFIG_IMAGES_LIBGIF)
result = libungif_read_data(io);
#elif defined(CONFIG_IMAGES_LIBMAGICK)
result = libmagick_read_data(io);
byte *extension = strrchr(file_name, '.');
return extension ? image_extension_to_format(extension + 1) : IMAGE_FORMAT_UNDEFINED;
}
+
+struct image *
+image_io_read_data_prepare(struct image_io_read_data_internals *rdi, struct image_io *io, uns cols, uns rows, uns flags)
+{
+ DBG("image_io_read_data_prepare()");
+ if (rdi->need_transformations = io->cols != cols || io->rows != rows || io->flags != flags)
+ {
+ rdi->need_destroy = 1;
+ return rdi->image = image_new(io->thread, cols, rows, flags & IMAGE_IO_IMAGE_FLAGS, NULL);
+ }
+ else
+ {
+ rdi->need_destroy = !io->pool;
+ return rdi->image = image_new(io->thread, io->cols, io->rows, io->flags & IMAGE_IO_IMAGE_FLAGS, io->pool);
+ }
+}
+
+int
+image_io_read_data_finish(struct image_io_read_data_internals *rdi, struct image_io *io)
+{
+ DBG("image_io_read_data_finish()");
+ if (rdi->need_transformations)
+ {
+ /* Scale the image */
+ if (io->cols != rdi->image->cols || io->rows != rdi->image->rows)
+ {
+ DBG("Scaling image");
+ rdi->need_transformations = io->flags != rdi->image->flags;
+ rdi->need_destroy = rdi->need_transformations || !io->pool;
+ struct image *img = image_new(io->thread, io->cols, io->rows, rdi->image->flags, rdi->need_transformations ? NULL : io->pool);
+ if (unlikely(!img))
+ {
+ image_destroy(rdi->image);
+ return 0;
+ }
+ if (unlikely(!image_scale(io->thread, img, rdi->image)))
+ {
+ image_destroy(rdi->image);
+ if (rdi->need_destroy)
+ image_destroy(img);
+ return 0;
+ }
+ rdi->image = img;
+ }
+
+ /* Merge with background */
+ if ((io->flags ^ rdi->image->flags) & IMAGE_ALPHA)
+ {
+ DBG("Aplying background");
+ rdi->need_transformations = 0;
+ rdi->need_destroy = rdi->need_transformations || !io->pool;
+ struct image *img = image_new(io->thread, io->cols, io->rows, io->flags, rdi->need_transformations ? NULL : io->pool);
+ if (unlikely(!img))
+ {
+ image_destroy(rdi->image);
+ return 0;
+ }
+ if (unlikely(!image_apply_background(io->thread, img, rdi->image, &io->background_color)))
+ {
+ image_destroy(rdi->image);
+ if (rdi->need_destroy)
+ image_destroy(img);
+ return 0;
+ }
+ rdi->image = img;
+ }
+
+ ASSERT(!rdi->need_transformations);
+ }
+
+ /* Success */
+ io->image = rdi->image;
+ io->image_destroy = rdi->need_destroy;
+ return 1;
+}
+
+void
+image_io_read_data_break(struct image_io_read_data_internals *rdi, struct image_io *io UNUSED)
+{
+ DBG("image_io_read_data_break()");
+ if (rdi->need_destroy)
+ image_destroy(rdi->image);
+}
struct image_io_read_data_internals {
struct image *image;
- int need_scale;
+ int need_transformations;
int need_destroy;
};
-static inline struct image *
-image_io_read_data_prepare(struct image_io_read_data_internals *rdi, struct image_io *io, uns cols, uns rows)
-{
- rdi->need_scale = io->cols != cols | io->rows != rows;
- rdi->need_destroy = rdi->need_scale || !io->pool;
- return rdi->image = rdi->need_scale ?
- image_new(io->thread, cols, rows, io->flags & IMAGE_CHANNELS_FORMAT, NULL) :
- image_new(io->thread, io->cols, io->rows, io->flags & IMAGE_IO_IMAGE_FLAGS, io->pool);
-}
-
-static inline int
-image_io_read_data_finish(struct image_io_read_data_internals *rdi, struct image_io *io)
-{
- if (rdi->need_scale)
- {
- struct image *img = image_new(io->thread, io->cols, io->rows, io->flags & IMAGE_IO_IMAGE_FLAGS, io->pool);
- if (unlikely(!img))
- {
- if (rdi->need_destroy)
- image_destroy(rdi->image);
- return 0;
- }
- if (unlikely(!image_scale(io->thread, img, rdi->image)))
- {
- image_destroy(rdi->image);
- if (!io->pool)
- image_destroy(img);
- return 0;
- }
- io->image = img;
- io->image_destroy = !io->pool;
- }
- else
- {
- io->image = rdi->image;
- io->image_destroy = rdi->need_destroy;
- }
- return 1;
-}
-
-static inline void
-image_io_read_data_break(struct image_io_read_data_internals *rdi, struct image_io *io UNUSED)
-{
- if (rdi->need_destroy)
- image_destroy(rdi->image);
-}
+struct image *image_io_read_data_prepare(struct image_io_read_data_internals *rdi, struct image_io *io, uns cols, uns rows, uns flags);
+int image_io_read_data_finish(struct image_io_read_data_internals *rdi, struct image_io *io);
+void image_io_read_data_break(struct image_io_read_data_internals *rdi, struct image_io *io);
#endif
{
if (!upsample)
return;
- if (max_cols / *cols > max_rows / *rows)
+ if (max_cols * *rows > max_rows * *cols)
{
*cols = *cols * max_rows / *rows;
*cols = MIN(*cols, max_cols);
*rows = *rows * max_cols / *cols;
*rows = MAX(*rows, 1);
*cols = max_cols;
- return;
}