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"
19 #include <sys/types.h>
22 #include <magick/api.h>
25 #define MAX_FILE_SIZE (1 << 30)
26 #define QUANTUM_SCALE (QuantumDepth - 8)
27 #define QUANTUM_TO_BYTE(x) ((uns)(x) >> QUANTUM_SCALE)
28 #define BYTE_TO_QUANTUM(x) ((uns)(x) << QUANTUM_SCALE)
29 #define ALPHA_TO_BYTE(x) (255 - QUANTUM_TO_BYTE(x))
30 #define BYTE_TO_ALPHA(x) (BYTE_TO_QUANTUM(255 - (x)))
32 static pthread_mutex_t libmagick_mutex = PTHREAD_MUTEX_INITIALIZER;
33 static uns libmagick_counter;
35 struct magick_read_data {
36 ExceptionInfo exception;
42 libmagick_init(struct image_io *io UNUSED)
44 pthread_mutex_lock(&libmagick_mutex);
45 if (!libmagick_counter++)
46 InitializeMagick(NULL);
47 pthread_mutex_unlock(&libmagick_mutex);
52 libmagick_cleanup(struct image_io *io UNUSED)
54 pthread_mutex_lock(&libmagick_mutex);
55 if (!--libmagick_counter)
57 pthread_mutex_unlock(&libmagick_mutex);
61 libmagick_destroy_read_data(struct magick_read_data *rd)
64 DestroyImage(rd->image);
65 DestroyImageInfo(rd->info);
66 DestroyExceptionInfo(&rd->exception);
70 libmagick_read_cancel(struct image_io *io)
72 DBG("libmagick_read_cancel()");
74 struct magick_read_data *rd = io->read_data;
75 libmagick_destroy_read_data(rd);
79 libmagick_read_header(struct image_io *io)
81 DBG("libmagick_read_header()");
83 /* Read entire stream */
84 sh_off_t file_size = bfilesize(io->fastbuf) - btell(io->fastbuf);
85 if (unlikely(file_size > MAX_FILE_SIZE))
87 image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Too long stream.");
90 uns buf_size = file_size;
91 byte *buf = xmalloc(buf_size);
92 breadb(io->fastbuf, buf, buf_size);
94 /* Allocate read structure */
95 struct magick_read_data *rd = io->read_data = mp_alloc_zero(io->internal_pool, sizeof(*rd));
97 /* Initialize GraphicsMagick */
98 GetExceptionInfo(&rd->exception);
99 rd->info = CloneImageInfo(NULL);
100 rd->info->subrange = 1;
103 rd->image = BlobToImage(rd->info, buf, buf_size, &rd->exception);
105 if (unlikely(!rd->image))
107 image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "GraphicsMagick failed to read the image.");
110 if (unlikely(rd->image->columns > IMAGE_MAX_SIZE || rd->image->rows > IMAGE_MAX_SIZE))
112 image_thread_err(io->thread, IMAGE_ERR_INVALID_DIMENSIONS, "Image too large.");
116 /* Fill image parameters */
117 io->cols = rd->image->columns;
118 io->rows = rd->image->rows;
119 switch (rd->image->colorspace)
122 io->flags = COLOR_SPACE_GRAYSCALE;
125 io->flags = COLOR_SPACE_RGB;
128 if (rd->image->matte)
129 io->flags |= IMAGE_ALPHA;
130 io->number_of_colors = rd->image->colors;
131 if (rd->image->storage_class == PseudoClass && rd->image->compression != JPEGCompression)
132 io->flags |= IMAGE_IO_HAS_PALETTE;
134 io->read_cancel = libmagick_read_cancel;
138 libmagick_destroy_read_data(rd);
143 libmagick_pixel_to_gray(PixelPacket *pixel)
145 return rgb_to_gray_func(pixel->red, pixel->green, pixel->blue) >> QUANTUM_SCALE;
149 libmagick_read_data(struct image_io *io)
151 DBG("libmagick_read_data()");
153 struct magick_read_data *rd = io->read_data;
156 switch (rd->image->colorspace)
162 QuantizeInfo quantize;
163 GetQuantizeInfo(&quantize);
164 quantize.colorspace = RGBColorspace;
165 QuantizeImage(&quantize, rd->image);
169 /* Prepare the image */
170 struct image_io_read_data_internals rdi;
171 uns read_flags = io->flags;
172 if ((read_flags & IMAGE_IO_USE_BACKGROUND) && !(read_flags & IMAGE_ALPHA))
173 read_flags = (read_flags | IMAGE_ALPHA) & IMAGE_CHANNELS_FORMAT;
174 if (unlikely(!image_io_read_data_prepare(&rdi, io, rd->image->columns, rd->image->rows, read_flags)))
176 libmagick_destroy_read_data(rd);
181 PixelPacket *src = (PixelPacket *)AcquireImagePixels(rd->image, 0, 0, rd->image->columns, rd->image->rows, &rd->exception);
184 image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Cannot acquire image pixels.");
185 libmagick_destroy_read_data(rd);
186 image_io_read_data_break(&rdi, io);
191 switch (rdi.image->pixel_size)
194 # define IMAGE_WALK_PREFIX(x) walk_##x
195 # define IMAGE_WALK_INLINE
196 # define IMAGE_WALK_IMAGE (rdi.image)
197 # define IMAGE_WALK_UNROLL 4
198 # define IMAGE_WALK_COL_STEP 1
199 # define IMAGE_WALK_DO_STEP do{ \
200 walk_pos[0] = libmagick_pixel_to_gray(src); \
202 # include "images/image-walk.h"
206 # define IMAGE_WALK_PREFIX(x) walk_##x
207 # define IMAGE_WALK_INLINE
208 # define IMAGE_WALK_IMAGE (rdi.image)
209 # define IMAGE_WALK_UNROLL 4
210 # define IMAGE_WALK_COL_STEP 2
211 # define IMAGE_WALK_DO_STEP do{ \
212 walk_pos[0] = libmagick_pixel_to_gray(src); \
213 walk_pos[1] = ALPHA_TO_BYTE(src->opacity); \
215 # include "images/image-walk.h"
219 # define IMAGE_WALK_PREFIX(x) walk_##x
220 # define IMAGE_WALK_INLINE
221 # define IMAGE_WALK_IMAGE (rdi.image)
222 # define IMAGE_WALK_UNROLL 4
223 # define IMAGE_WALK_COL_STEP 3
224 # define IMAGE_WALK_DO_STEP do{ \
225 walk_pos[0] = QUANTUM_TO_BYTE(src->red); \
226 walk_pos[1] = QUANTUM_TO_BYTE(src->green); \
227 walk_pos[2] = QUANTUM_TO_BYTE(src->blue); \
229 # include "images/image-walk.h"
233 # define IMAGE_WALK_PREFIX(x) walk_##x
234 # define IMAGE_WALK_INLINE
235 # define IMAGE_WALK_IMAGE (rdi.image)
236 # define IMAGE_WALK_UNROLL 4
237 # define IMAGE_WALK_COL_STEP 4
238 # define IMAGE_WALK_DO_STEP do{ \
239 walk_pos[0] = QUANTUM_TO_BYTE(src->red); \
240 walk_pos[1] = QUANTUM_TO_BYTE(src->green); \
241 walk_pos[2] = QUANTUM_TO_BYTE(src->blue); \
242 walk_pos[3] = ALPHA_TO_BYTE(src->opacity); \
244 # include "images/image-walk.h"
251 /* Free GraphicsMagick structures */
252 libmagick_destroy_read_data(rd);
254 /* Finish the image */
255 return image_io_read_data_finish(&rdi, io);
259 libmagick_write(struct image_io *io)
261 DBG("libmagick_write()");
263 /* Initialize GraphicsMagick */
265 ExceptionInfo exception;
267 GetExceptionInfo(&exception);
268 info = CloneImageInfo(NULL);
270 /* Setup image parameters and allocate the image*/
271 struct image *img = io->image;
272 switch (img->flags & IMAGE_COLOR_SPACE)
274 case COLOR_SPACE_GRAYSCALE:
275 info->colorspace = GRAYColorspace;
277 case COLOR_SPACE_RGB:
278 info->colorspace = RGBColorspace;
285 case IMAGE_FORMAT_JPEG:
286 strcpy(info->magick, "JPEG");
287 if (io->jpeg_quality)
288 info->quality = MIN(io->jpeg_quality, 100);
290 case IMAGE_FORMAT_PNG:
291 strcpy(info->magick, "PNG");
293 case IMAGE_FORMAT_GIF:
294 strcpy(info->magick, "GIF");
299 Image *image = AllocateImage(info);
300 if (unlikely(!image))
302 image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "GraphicsMagick failed to allocate the image.");
305 image->columns = img->cols;
306 image->rows = img->rows;
309 PixelPacket *pixels = SetImagePixels(image, 0, 0, img->cols, img->rows), *dest = pixels;
310 if (unlikely(!pixels))
312 image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "Cannot get GraphicsMagick pixels.");
317 switch (img->pixel_size)
320 # define IMAGE_WALK_PREFIX(x) walk_##x
321 # define IMAGE_WALK_INLINE
322 # define IMAGE_WALK_IMAGE img
323 # define IMAGE_WALK_UNROLL 4
324 # define IMAGE_WALK_COL_STEP 1
325 # define IMAGE_WALK_DO_STEP do{ \
326 dest->red = BYTE_TO_QUANTUM(walk_pos[0]); \
327 dest->green = BYTE_TO_QUANTUM(walk_pos[0]); \
328 dest->blue = BYTE_TO_QUANTUM(walk_pos[0]); \
331 # include "images/image-walk.h"
335 # define IMAGE_WALK_PREFIX(x) walk_##x
336 # define IMAGE_WALK_INLINE
337 # define IMAGE_WALK_IMAGE img
338 # define IMAGE_WALK_UNROLL 4
339 # define IMAGE_WALK_COL_STEP 2
340 # define IMAGE_WALK_DO_STEP do{ \
341 dest->red = BYTE_TO_QUANTUM(walk_pos[0]); \
342 dest->green = BYTE_TO_QUANTUM(walk_pos[0]); \
343 dest->blue = BYTE_TO_QUANTUM(walk_pos[0]); \
344 dest->opacity = BYTE_TO_ALPHA(walk_pos[1]); \
346 # include "images/image-walk.h"
350 # define IMAGE_WALK_PREFIX(x) walk_##x
351 # define IMAGE_WALK_INLINE
352 # define IMAGE_WALK_IMAGE img
353 # define IMAGE_WALK_UNROLL 4
354 # define IMAGE_WALK_COL_STEP 3
355 # define IMAGE_WALK_DO_STEP do{ \
356 dest->red = BYTE_TO_QUANTUM(walk_pos[0]); \
357 dest->green = BYTE_TO_QUANTUM(walk_pos[1]); \
358 dest->blue = BYTE_TO_QUANTUM(walk_pos[2]); \
361 # include "images/image-walk.h"
365 # define IMAGE_WALK_PREFIX(x) walk_##x
366 # define IMAGE_WALK_INLINE
367 # define IMAGE_WALK_IMAGE img
368 # define IMAGE_WALK_UNROLL 4
369 # define IMAGE_WALK_COL_STEP 4
370 # define IMAGE_WALK_DO_STEP do{ \
371 dest->red = BYTE_TO_QUANTUM(walk_pos[0]); \
372 dest->green = BYTE_TO_QUANTUM(walk_pos[1]); \
373 dest->blue = BYTE_TO_QUANTUM(walk_pos[2]); \
374 dest->opacity = BYTE_TO_ALPHA(walk_pos[3]); \
376 # include "images/image-walk.h"
384 if (unlikely(!SyncImagePixels(image)))
386 image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "Cannot sync GraphicsMagick pixels.");
392 void *buf = ImageToBlob(info, image, &buf_len, &exception);
395 image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "GraphicsMagick failed to compress the image.");
398 if (unlikely(buf_len > MAX_FILE_SIZE))
400 image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "Image too large.");
404 /* Write to stream */
405 bwrite(io->fastbuf, buf, buf_len);
413 DestroyImageInfo(info);
414 DestroyExceptionInfo(&exception);