2 * Image Library -- GraphicsMagick (slow fallback library)
4 * (c) 2006 Pavel Charvat <pchar@ucw.cz>
6 * This software may be freely distributed and used according to the terms
7 * of the GNU Lesser General Public License.
13 #include "lib/mempool.h"
14 #include "lib/fastbuf.h"
15 #include "images/images.h"
16 #include "images/color.h"
17 #include "images/io-main.h"
18 #include <sys/types.h>
21 #include <magick/api.h>
23 #define MAX_FILE_SIZE (1 << 30)
24 #define QUANTUM_SCALE (QuantumDepth - 8)
25 #define QUANTUM_TO_BYTE(x) ((uns)(x) >> QUANTUM_SCALE)
26 #define BYTE_TO_QUANTUM(x) ((uns)(x) << QUANTUM_SCALE)
27 #define ALPHA_TO_BYTE(x) (255 - QUANTUM_TO_BYTE(x))
28 #define BYTE_TO_ALPHA(x) (BYTE_TO_QUANTUM(255 - (x)))
30 static uns libmagick_counter;
32 struct magick_read_data {
33 ExceptionInfo exception;
39 libmagick_init(struct image_io *io UNUSED)
42 if (!libmagick_counter++)
43 InitializeMagick(NULL);
48 libmagick_cleanup(struct image_io *io UNUSED)
51 if (!--libmagick_counter)
56 libmagick_destroy_read_data(struct magick_read_data *rd)
59 DestroyImage(rd->image);
60 DestroyImageInfo(rd->info);
61 DestroyExceptionInfo(&rd->exception);
65 libmagick_read_cancel(struct image_io *io)
67 DBG("libmagick_read_cancel()");
69 struct magick_read_data *rd = io->read_data;
70 libmagick_destroy_read_data(rd);
74 libmagick_read_header(struct image_io *io)
76 DBG("libmagick_read_header()");
78 /* Read entire stream */
79 sh_off_t file_size = bfilesize(io->fastbuf) - btell(io->fastbuf);
80 if (unlikely(file_size > MAX_FILE_SIZE))
82 image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Too long stream.");
85 uns buf_size = file_size;
86 byte *buf = xmalloc(buf_size);
87 breadb(io->fastbuf, buf, buf_size);
89 /* Allocate read structure */
90 struct magick_read_data *rd = io->read_data = mp_alloc_zero(io->internal_pool, sizeof(*rd));
92 /* Initialize GraphicsMagick */
93 GetExceptionInfo(&rd->exception);
94 rd->info = CloneImageInfo(NULL);
95 rd->info->subrange = 1;
98 rd->image = BlobToImage(rd->info, buf, buf_size, &rd->exception);
100 if (unlikely(!rd->image))
102 image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "GraphicsMagick failed to read the image.");
105 if (unlikely(rd->image->columns > IMAGE_MAX_SIZE || rd->image->rows > IMAGE_MAX_SIZE))
107 image_thread_err(io->thread, IMAGE_ERR_INVALID_DIMENSIONS, "Image too large.");
111 /* Fill image parameters */
112 io->cols = rd->image->columns;
113 io->rows = rd->image->rows;
114 switch (rd->image->colorspace)
117 io->flags = COLOR_SPACE_GRAYSCALE;
120 io->flags = COLOR_SPACE_RGB;
123 if (rd->image->matte)
124 io->flags |= IMAGE_ALPHA;
125 io->number_of_colors = rd->image->colors;
126 if (rd->image->storage_class == PseudoClass && rd->image->compression != JPEGCompression)
127 io->flags |= IMAGE_IO_HAS_PALETTE;
129 io->read_cancel = libmagick_read_cancel;
133 libmagick_destroy_read_data(rd);
138 libmagick_pixel_to_gray(PixelPacket *pixel)
140 return rgb_to_gray_func(pixel->red, pixel->green, pixel->blue) >> QUANTUM_SCALE;
144 libmagick_read_data(struct image_io *io)
146 DBG("libmagick_read_data()");
148 struct magick_read_data *rd = io->read_data;
151 switch (rd->image->colorspace)
157 QuantizeInfo quantize;
158 GetQuantizeInfo(&quantize);
159 quantize.colorspace = RGBColorspace;
160 QuantizeImage(&quantize, rd->image);
164 /* Prepare the image */
165 struct image_io_read_data_internals rdi;
166 uns read_flags = io->flags;
167 if ((read_flags & IMAGE_IO_USE_BACKGROUND) && !(read_flags & IMAGE_ALPHA))
168 read_flags = (read_flags | IMAGE_ALPHA) & IMAGE_CHANNELS_FORMAT;
169 if (unlikely(!image_io_read_data_prepare(&rdi, io, rd->image->columns, rd->image->rows, read_flags)))
171 libmagick_destroy_read_data(rd);
176 PixelPacket *src = (PixelPacket *)AcquireImagePixels(rd->image, 0, 0, rd->image->columns, rd->image->rows, &rd->exception);
179 image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Cannot acquire image pixels.");
180 libmagick_destroy_read_data(rd);
181 image_io_read_data_break(&rdi, io);
186 switch (rdi.image->pixel_size)
189 # define IMAGE_WALK_PREFIX(x) walk_##x
190 # define IMAGE_WALK_INLINE
191 # define IMAGE_WALK_IMAGE (rdi.image)
192 # define IMAGE_WALK_UNROLL 4
193 # define IMAGE_WALK_COL_STEP 1
194 # define IMAGE_WALK_DO_STEP do{ \
195 walk_pos[0] = libmagick_pixel_to_gray(src); \
197 # include "images/image-walk.h"
201 # define IMAGE_WALK_PREFIX(x) walk_##x
202 # define IMAGE_WALK_INLINE
203 # define IMAGE_WALK_IMAGE (rdi.image)
204 # define IMAGE_WALK_UNROLL 4
205 # define IMAGE_WALK_COL_STEP 2
206 # define IMAGE_WALK_DO_STEP do{ \
207 walk_pos[0] = libmagick_pixel_to_gray(src); \
208 walk_pos[1] = ALPHA_TO_BYTE(src->opacity); \
210 # include "images/image-walk.h"
214 # define IMAGE_WALK_PREFIX(x) walk_##x
215 # define IMAGE_WALK_INLINE
216 # define IMAGE_WALK_IMAGE (rdi.image)
217 # define IMAGE_WALK_UNROLL 4
218 # define IMAGE_WALK_COL_STEP 3
219 # define IMAGE_WALK_DO_STEP do{ \
220 walk_pos[0] = QUANTUM_TO_BYTE(src->red); \
221 walk_pos[1] = QUANTUM_TO_BYTE(src->green); \
222 walk_pos[2] = QUANTUM_TO_BYTE(src->blue); \
224 # include "images/image-walk.h"
228 # define IMAGE_WALK_PREFIX(x) walk_##x
229 # define IMAGE_WALK_INLINE
230 # define IMAGE_WALK_IMAGE (rdi.image)
231 # define IMAGE_WALK_UNROLL 4
232 # define IMAGE_WALK_COL_STEP 4
233 # define IMAGE_WALK_DO_STEP do{ \
234 walk_pos[0] = QUANTUM_TO_BYTE(src->red); \
235 walk_pos[1] = QUANTUM_TO_BYTE(src->green); \
236 walk_pos[2] = QUANTUM_TO_BYTE(src->blue); \
237 walk_pos[3] = ALPHA_TO_BYTE(src->opacity); \
239 # include "images/image-walk.h"
246 /* Free GraphicsMagick structures */
247 libmagick_destroy_read_data(rd);
249 /* Finish the image */
250 return image_io_read_data_finish(&rdi, io);
254 libmagick_write(struct image_io *io)
256 DBG("libmagick_write()");
258 /* Initialize GraphicsMagick */
260 ExceptionInfo exception;
262 GetExceptionInfo(&exception);
263 info = CloneImageInfo(NULL);
265 /* Setup image parameters and allocate the image*/
266 struct image *img = io->image;
267 switch (img->flags & IMAGE_COLOR_SPACE)
269 case COLOR_SPACE_GRAYSCALE:
270 info->colorspace = GRAYColorspace;
272 case COLOR_SPACE_RGB:
273 info->colorspace = RGBColorspace;
280 case IMAGE_FORMAT_JPEG:
281 strcpy(info->magick, "JPEG");
282 if (io->jpeg_quality)
283 info->quality = MIN(io->jpeg_quality, 100);
285 case IMAGE_FORMAT_PNG:
286 strcpy(info->magick, "PNG");
288 case IMAGE_FORMAT_GIF:
289 strcpy(info->magick, "GIF");
294 Image *image = AllocateImage(info);
295 if (unlikely(!image))
297 image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "GraphicsMagick failed to allocate the image.");
300 image->columns = img->cols;
301 image->rows = img->rows;
304 PixelPacket *pixels = SetImagePixels(image, 0, 0, img->cols, img->rows), *dest = pixels;
305 if (unlikely(!pixels))
307 image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "Cannot get GraphicsMagick pixels.");
312 switch (img->pixel_size)
315 # define IMAGE_WALK_PREFIX(x) walk_##x
316 # define IMAGE_WALK_INLINE
317 # define IMAGE_WALK_IMAGE img
318 # define IMAGE_WALK_UNROLL 4
319 # define IMAGE_WALK_COL_STEP 1
320 # define IMAGE_WALK_DO_STEP do{ \
321 dest->red = BYTE_TO_QUANTUM(walk_pos[0]); \
322 dest->green = BYTE_TO_QUANTUM(walk_pos[0]); \
323 dest->blue = BYTE_TO_QUANTUM(walk_pos[0]); \
326 # include "images/image-walk.h"
330 # define IMAGE_WALK_PREFIX(x) walk_##x
331 # define IMAGE_WALK_INLINE
332 # define IMAGE_WALK_IMAGE img
333 # define IMAGE_WALK_UNROLL 4
334 # define IMAGE_WALK_COL_STEP 2
335 # define IMAGE_WALK_DO_STEP do{ \
336 dest->red = BYTE_TO_QUANTUM(walk_pos[0]); \
337 dest->green = BYTE_TO_QUANTUM(walk_pos[0]); \
338 dest->blue = BYTE_TO_QUANTUM(walk_pos[0]); \
339 dest->opacity = BYTE_TO_ALPHA(walk_pos[1]); \
341 # include "images/image-walk.h"
345 # define IMAGE_WALK_PREFIX(x) walk_##x
346 # define IMAGE_WALK_INLINE
347 # define IMAGE_WALK_IMAGE img
348 # define IMAGE_WALK_UNROLL 4
349 # define IMAGE_WALK_COL_STEP 3
350 # define IMAGE_WALK_DO_STEP do{ \
351 dest->red = BYTE_TO_QUANTUM(walk_pos[0]); \
352 dest->green = BYTE_TO_QUANTUM(walk_pos[1]); \
353 dest->blue = BYTE_TO_QUANTUM(walk_pos[2]); \
356 # include "images/image-walk.h"
360 # define IMAGE_WALK_PREFIX(x) walk_##x
361 # define IMAGE_WALK_INLINE
362 # define IMAGE_WALK_IMAGE img
363 # define IMAGE_WALK_UNROLL 4
364 # define IMAGE_WALK_COL_STEP 4
365 # define IMAGE_WALK_DO_STEP do{ \
366 dest->red = BYTE_TO_QUANTUM(walk_pos[0]); \
367 dest->green = BYTE_TO_QUANTUM(walk_pos[1]); \
368 dest->blue = BYTE_TO_QUANTUM(walk_pos[2]); \
369 dest->opacity = BYTE_TO_ALPHA(walk_pos[3]); \
371 # include "images/image-walk.h"
379 if (unlikely(!SyncImagePixels(image)))
381 image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "Cannot sync GraphicsMagick pixels.");
387 void *buf = ImageToBlob(info, image, &buf_len, &exception);
390 image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "GraphicsMagick failed to compress the image.");
393 if (unlikely(buf_len > MAX_FILE_SIZE))
395 image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "Image too large.");
399 /* Write to stream */
400 bwrite(io->fastbuf, buf, buf_len);
408 DestroyImageInfo(info);
409 DestroyExceptionInfo(&exception);