-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\
+-b --fit-to-box scale to fit the box (100x200)\n\
+-c --colorspace force output colorspace (Gray, GrayAlpha, RGB, RGBAlpha)\n\
+-Q --jpeg-quality JPEG quality (1..100)\n\
", stderr);
exit(1);
}
-static char *shortopts = "qf:F:s:b:c:" CF_SHORT_OPTS;
+static char *shortopts = "qf:F:s:b:c:Q:" CF_SHORT_OPTS;
static struct option longopts[] =
{
CF_LONG_OPTS
{ "input-format", 0, 0, 'f' },
{ "output-format", 0, 0, 'F' },
{ "size", 0, 0, 's' },
- { "fit-box", 0, 0, 'b' },
+ { "fit-to-box", 0, 0, 'b' },
{ "colorspace", 0, 0, 'c' },
+ { "jpeg-quality", 0, 0, 'Q' },
{ NULL, 0, 0, 0 }
};
static enum image_format output_format;
static uns cols;
static uns rows;
-static uns fit_box;
+static uns fit_to_box;
static uns channels_format;
+static uns jpeg_quality;
#define MSG(x...) do{ if (verbose) log(L_INFO, ##x); }while(0)
*r++ = 0;
if (!(cols = atoi(optarg)) || !(rows = atoi(r)))
usage();
- fit_box = 0;
+ fit_to_box = 0;
break;
}
case 'b':
*r++ = 0;
if (!(cols = atoi(optarg)) || !(rows = atoi(r)))
usage();
- fit_box = 1;
+ fit_to_box = 1;
break;
}
case 'c':
if (!(channels_format = image_name_to_channels_format(optarg)))
usage();
break;
+ case 'Q':
+ if (!(jpeg_quality = atoi(optarg)))
+ usage();
+ break;
default:
usage();
}
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));
+ printf("Colorspace: %s\n", io.has_palette ? (byte *)"Palette" : image_channels_format_to_name(io.flags & IMAGE_CHANNELS_FORMAT));
+ printf("NumColors: %d\n", io.number_of_colors);
}
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));
+ io.has_palette ? (byte *)"Palette" : image_channels_format_to_name(io.flags & IMAGE_CHANNELS_FORMAT));
if (cols)
- if (fit_box)
+ if (fit_to_box)
{
image_dimensions_fit_to_box(&io.cols, &io.rows, MIN(cols, 0xffff), MIN(rows, 0xffff), 0);
}
}
if (channels_format)
io.flags = io.flags & ~IMAGE_PIXEL_FORMAT | channels_format;
+ if (jpeg_quality)
+ io.jpeg_quality = jpeg_quality;
TRY(image_io_read_data(&io, 0));
bclose(io.fastbuf);
MSG("Writing %s", output_file_name);
}
void
-image_destroy(struct image_thread *it UNUSED, struct image *img)
+image_destroy(struct image *img)
{
DBG("image_destroy(img=%p)", img);
xfree((byte *)img);
thread->err_msg = (byte *)msg;
}
+static inline void
+image_thread_err_dup(struct image_thread *thread, uns code, char *msg)
+{
+ thread->err_code = code;
+ thread->err_msg = mp_strdup(thread->pool, msg);
+}
+
void image_thread_err_format(struct image_thread *thread, uns code, char *msg, ...);
/* basic image manupulation */
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_destroy(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);
};
struct image_io {
- struct image *image;
- struct fastbuf *fastbuf;
- enum image_format format;
- struct mempool *pool;
- u32 cols;
- u32 rows;
- u32 flags;
+ /* R - read_header input */
+ /* H - read_header output */
+ /* I - read_data input */
+ /* O - read_data output */
+ /* W - write input */
+
+ struct image *image; /* [ OW] - image data */
+ enum image_format format; /* [R W] - file format (IMAGE_FORMAT_x) */
+ struct fastbuf *fastbuf; /* [R W] - source/destination stream */
+ struct mempool *pool; /* [ I ] - parameter to image_new */
+ u32 cols; /* [ HI ] - number of columns, parameter to image_new */
+ u32 rows; /* [ HI ] - number of rows, parameter to image_new */
+ u32 flags; /* [ HI ] - parameter to image new, read_header fills IMAGE_CHANNELS_FORMAT */
+ u32 jpeg_quality; /* [ W] - JPEG compression quality (1..100) */
+ u32 number_of_colors; /* [ H ] - number of image colors */
+ u32 has_palette; /* [ H ] - true for image with indexed colors */
+
/* 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;
- struct {
- } png;
- struct {
- } gif;
- };
};
void image_io_init(struct image_thread *it, struct image_io *io);
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);
+ image_thread_err_dup(e->io->thread, IMAGE_ERR_READ_FAILED, buf);
longjmp(e->setjmp_buf, 1);
}
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);
+ image_thread_err_dup(e->io->thread, IMAGE_ERR_WRITE_FAILED, buf);
longjmp(e->setjmp_buf, 1);
}
/* 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;
+ switch (i->cinfo.jpeg_color_space)
+ {
+ case JCS_GRAYSCALE:
+ io->flags = COLOR_SPACE_GRAYSCALE;
+ io->number_of_colors = 1 << 8;
+ break;
+ default:
+ io->flags = COLOR_SPACE_RGB;
+ io->number_of_colors = 1 << 24;
+ break;
+ }
+ io->cols = i->cinfo.image_width;
+ io->rows = i->cinfo.image_height;
io->read_cancel = libjpeg_read_cancel;
return 1;
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);
+ image_destroy(img);
return 0;
}
struct image *dest = image_new(io->thread, io->cols, io->rows, io->flags, io->pool);
if (!dest)
{
- image_destroy(io->thread, img);
+ image_destroy(img);
return 0;
}
if (!(image_scale(io->thread, dest, img)))
{
- image_destroy(io->thread, img);
+ image_destroy(img);
if (!io->pool)
- image_destroy(io->thread, dest);
+ image_destroy(dest);
return 0;
}
- image_destroy(io->thread, img);
+ image_destroy(img);
io->image = dest;
}
else
return 0;
}
jpeg_set_defaults(&i.cinfo);
+ if (io->jpeg_quality)
+ jpeg_set_quality(&i.cinfo, MIN(io->jpeg_quality, 100), 1);
/* Compress the image */
jpeg_start_compress(&i.cinfo, TRUE);
}
/* Fill image parameters */
- if (!io->cols)
- io->cols = rd->image->columns;
- if (!io->rows)
- io->rows = rd->image->rows;
- if (!(io->flags & IMAGE_CHANNELS_FORMAT))
+ io->cols = rd->image->columns;
+ io->rows = rd->image->rows;
+ switch (rd->image->colorspace)
{
- switch (rd->image->colorspace)
- {
- case GRAYColorspace:
- io->flags |= COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA;
- break;
- default:
- io->flags |= COLOR_SPACE_RGB | IMAGE_ALPHA;
- break;
- }
+ case GRAYColorspace:
+ io->flags |= COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA;
+ break;
+ default:
+ io->flags |= COLOR_SPACE_RGB | IMAGE_ALPHA;
+ break;
}
+ io->number_of_colors = rd->image->colors;
+ if (rd->image->storage_class == PseudoClass && rd->image->compression != JPEGCompression)
+ io->has_palette = 1;
io->read_cancel = libmagick_read_cancel;
return 1;
if (unlikely(!img2))
goto err2;
int result = image_scale(io->thread, img2, img);
- image_destroy(io->thread, img);
+ image_destroy(img);
img = img2;
need_destroy = !io->pool;
if (unlikely(!result))
libmagick_destroy_read_data(rd);
err2:
if (need_destroy)
- image_destroy(io->thread, img);
+ image_destroy(img);
return 0;
}
{
case IMAGE_FORMAT_JPEG:
strcpy(info->magick, "JPEG");
+ if (io->jpeg_quality)
+ info->quality = MIN(io->jpeg_quality, 100);
break;
case IMAGE_FORMAT_PNG:
strcpy(info->magick, "PNG");
libpng_read_error(png_structp png_ptr, png_const_charp msg)
{
DBG("libpng_read_error()");
- image_thread_err(png_get_error_ptr(png_ptr), IMAGE_ERR_READ_FAILED, (byte *)msg);
+ image_thread_err_dup(png_get_error_ptr(png_ptr), IMAGE_ERR_READ_FAILED, (byte *)msg);
longjmp(png_jmpbuf(png_ptr), 1);
}
libpng_write_error(png_structp png_ptr, png_const_charp msg)
{
DBG("libpng_write_error()");
- image_thread_err(png_get_error_ptr(png_ptr), IMAGE_ERR_WRITE_FAILED, (byte *)msg);
+ image_thread_err_dup(png_get_error_ptr(png_ptr), IMAGE_ERR_WRITE_FAILED, (byte *)msg);
longjmp(png_jmpbuf(png_ptr), 1);
}
png_get_IHDR(rd->png_ptr, rd->info_ptr, &rd->cols, &rd->rows, &rd->bit_depth, &rd->color_type, NULL, NULL, NULL);
/* Fill image_io values */
- if (!io->cols)
- io->cols = rd->cols;
- if (!io->rows)
- io->rows = rd->rows;
- if (!(io->flags & IMAGE_CHANNELS_FORMAT))
- switch (rd->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(&rd->png_ptr, &rd->info_ptr, &rd->end_ptr);
- image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Unknown color type");
- break;
- }
+ io->cols = rd->cols;
+ io->rows = rd->rows;
+ switch (rd->color_type)
+ {
+ case PNG_COLOR_TYPE_GRAY:
+ io->flags |= COLOR_SPACE_GRAYSCALE;
+ io->number_of_colors = 1 << 8;
+ break;
+ case PNG_COLOR_TYPE_GRAY_ALPHA:
+ io->flags |= COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA;
+ io->number_of_colors = 1 << 8;
+ break;
+ case PNG_COLOR_TYPE_RGB:
+ io->flags |= COLOR_SPACE_RGB;
+ io->number_of_colors = 1 << 24;
+ break;
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ io->number_of_colors = 1 << 24;
+ io->flags |= COLOR_SPACE_RGB | IMAGE_ALPHA;
+ break;
+ case PNG_COLOR_TYPE_PALETTE:
+ io->flags |= COLOR_SPACE_RGB | IMAGE_ALPHA;
+ int num_palette;
+ if (png_get_PLTE(rd->png_ptr, rd->info_ptr, NULL, &num_palette))
+ io->number_of_colors = num_palette;
+ else
+ io->number_of_colors = 1 << rd->bit_depth;
+ io->has_palette = 1;
+ break;
+ default:
+ png_destroy_read_struct(&rd->png_ptr, &rd->info_ptr, &rd->end_ptr);
+ image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Unknown color type");
+ break;
+ }
/* Success */
io->read_cancel = libpng_read_cancel;
DBG("Libpng failed to read the image, longjump saved us");
png_destroy_read_struct(&rd->png_ptr, &rd->info_ptr, &rd->end_ptr);
if (need_scale || !io->pool)
- image_destroy(io->thread, img);
+ image_destroy(img);
return 0;
}
struct image *dest = image_new(io->thread, io->cols, io->rows, io->flags, io->pool);
if (!dest)
{
- image_destroy(io->thread, img);
+ image_destroy(img);
return 0;
}
if (!image_scale(io->thread, dest, img))
{
- image_destroy(io->thread, img);
+ image_destroy(img);
if (!io->pool)
- image_destroy(io->thread, dest);
+ image_destroy(dest);
return 0;
}
io->image = dest;
{
if (io->image_destroy)
{
- image_destroy(io->thread, io->image);
+ image_destroy(io->image);
io->image_destroy = 0;
io->image = NULL;
}