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 OPACITY_MAX ((1 << QuantumDepth) - 1)
29 struct magick_read_data {
30 ExceptionInfo exception;
36 libmagick_destroy_read_data(struct magick_read_data *rd)
39 DestroyImage(rd->image);
40 DestroyImageInfo(rd->info);
41 DestroyExceptionInfo(&rd->exception);
46 libmagick_read_cancel(struct image_io *io)
48 DBG("libmagick_read_cancel()");
50 struct magick_read_data *rd = io->read_data;
52 DestroyImage(rd->image);
53 libmagick_destroy_read_data(rd);
57 libmagick_read_header(struct image_io *io)
59 DBG("libmagick_read_header()");
61 /* Read entire stream */
62 sh_off_t file_size = bfilesize(io->fastbuf) - btell(io->fastbuf);
63 if (unlikely(file_size > MAX_FILE_SIZE))
65 image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Too long stream.");
68 uns buf_size = file_size;
69 byte *buf = xmalloc(buf_size);
70 breadb(io->fastbuf, buf, buf_size);
72 /* Allocate read structure */
73 struct magick_read_data *rd = io->read_data = mp_alloc(io->internal_pool, sizeof(*rd));
75 /* Initialize GraphicsMagick */
76 InitializeMagick(NULL);
77 GetExceptionInfo(&rd->exception);
78 rd->info = CloneImageInfo(NULL);
79 rd->info->subrange = 1;
82 rd->image = BlobToImage(rd->info, buf, buf_size, &rd->exception);
84 if (unlikely(!rd->image))
86 image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "GraphicsMagick failed to read the image.");
89 if (unlikely(rd->image->columns > IMAGE_MAX_SIZE || rd->image->rows > IMAGE_MAX_SIZE))
91 image_thread_err(io->thread, IMAGE_ERR_INVALID_DIMENSIONS, "Image too large.");
95 /* Fill image parameters */
96 io->cols = rd->image->columns;
97 io->rows = rd->image->rows;
98 switch (rd->image->colorspace)
101 io->flags = COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA;
104 io->flags = COLOR_SPACE_RGB | IMAGE_ALPHA;
107 io->number_of_colors = rd->image->colors;
108 if (rd->image->storage_class == PseudoClass && rd->image->compression != JPEGCompression)
109 io->flags |= IMAGE_IO_HAS_PALETTE;
111 io->read_cancel = libmagick_read_cancel;
115 libmagick_destroy_read_data(rd);
120 libmagick_pixel_to_gray(PixelPacket *pixel)
122 return rgb_to_gray_func(pixel->red, pixel->green, pixel->blue) >> QUANTUM_SCALE;
126 libmagick_read_data(struct image_io *io)
128 DBG("libmagick_read_data()");
130 struct magick_read_data *rd = io->read_data;
133 switch (rd->image->colorspace)
139 QuantizeInfo quantize;
140 GetQuantizeInfo(&quantize);
141 quantize.colorspace = RGBColorspace;
142 QuantizeImage(&quantize, rd->image);
146 /* Prepare the image */
147 struct image_io_read_data_internals rdi;
148 uns read_flags = io->flags;
149 if ((read_flags & IMAGE_IO_USE_BACKGROUND) && !(read_flags & IMAGE_ALPHA))
150 read_flags = (read_flags | IMAGE_ALPHA) & IMAGE_CHANNELS_FORMAT;
151 if (unlikely(!image_io_read_data_prepare(&rdi, io, rd->image->columns, rd->image->rows, read_flags)))
153 libmagick_destroy_read_data(rd);
158 PixelPacket *src = (PixelPacket *)AcquireImagePixels(rd->image, 0, 0, rd->image->columns, rd->image->rows, &rd->exception);
161 image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Cannot acquire image pixels.");
162 libmagick_destroy_read_data(rd);
163 image_io_read_data_break(&rdi, io);
168 switch (rdi.image->pixel_size)
171 # define IMAGE_WALK_PREFIX(x) walk_##x
172 # define IMAGE_WALK_INLINE
173 # define IMAGE_WALK_IMAGE (rdi.image)
174 # define IMAGE_WALK_UNROLL 4
175 # define IMAGE_WALK_COL_STEP 1
176 # define IMAGE_WALK_DO_STEP do{ \
177 walk_pos[0] = libmagick_pixel_to_gray(src); \
179 # include "images/image-walk.h"
183 # define IMAGE_WALK_PREFIX(x) walk_##x
184 # define IMAGE_WALK_INLINE
185 # define IMAGE_WALK_IMAGE (rdi.image)
186 # define IMAGE_WALK_UNROLL 4
187 # define IMAGE_WALK_COL_STEP 2
188 # define IMAGE_WALK_DO_STEP do{ \
189 walk_pos[0] = libmagick_pixel_to_gray(src); \
190 walk_pos[1] = QUANTUM_TO_BYTE(src->opacity); \
192 # include "images/image-walk.h"
196 # define IMAGE_WALK_PREFIX(x) walk_##x
197 # define IMAGE_WALK_INLINE
198 # define IMAGE_WALK_IMAGE (rdi.image)
199 # define IMAGE_WALK_UNROLL 4
200 # define IMAGE_WALK_COL_STEP 3
201 # define IMAGE_WALK_DO_STEP do{ \
202 walk_pos[0] = QUANTUM_TO_BYTE(src->red); \
203 walk_pos[1] = QUANTUM_TO_BYTE(src->green); \
204 walk_pos[2] = QUANTUM_TO_BYTE(src->blue); \
206 # include "images/image-walk.h"
210 # define IMAGE_WALK_PREFIX(x) walk_##x
211 # define IMAGE_WALK_INLINE
212 # define IMAGE_WALK_IMAGE (rdi.image)
213 # define IMAGE_WALK_UNROLL 4
214 # define IMAGE_WALK_COL_STEP 4
215 # define IMAGE_WALK_DO_STEP do{ \
216 walk_pos[0] = QUANTUM_TO_BYTE(src->red); \
217 walk_pos[1] = QUANTUM_TO_BYTE(src->green); \
218 walk_pos[2] = QUANTUM_TO_BYTE(src->blue); \
219 walk_pos[3] = QUANTUM_TO_BYTE(src->opacity); \
221 # include "images/image-walk.h"
228 /* Free GraphicsMagick structures */
229 libmagick_destroy_read_data(rd);
231 /* Finish the image */
232 return image_io_read_data_finish(&rdi, io);
236 libmagick_write(struct image_io *io)
238 DBG("libmagick_write()");
240 /* Initialize GraphicsMagick */
242 ExceptionInfo exception;
244 InitializeMagick(NULL);
245 GetExceptionInfo(&exception);
246 info = CloneImageInfo(NULL);
248 /* Setup image parameters and allocate the image*/
249 struct image *img = io->image;
250 switch (img->flags & IMAGE_COLOR_SPACE)
252 case COLOR_SPACE_GRAYSCALE:
253 info->colorspace = GRAYColorspace;
255 case COLOR_SPACE_RGB:
256 info->colorspace = RGBColorspace;
263 case IMAGE_FORMAT_JPEG:
264 strcpy(info->magick, "JPEG");
265 if (io->jpeg_quality)
266 info->quality = MIN(io->jpeg_quality, 100);
268 case IMAGE_FORMAT_PNG:
269 strcpy(info->magick, "PNG");
271 case IMAGE_FORMAT_GIF:
272 strcpy(info->magick, "GIF");
277 Image *image = AllocateImage(info);
278 if (unlikely(!image))
280 image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "GraphicsMagick failed to allocate the image.");
283 image->columns = img->cols;
284 image->rows = img->rows;
287 PixelPacket *pixels = SetImagePixels(image, 0, 0, img->cols, img->rows), *dest = pixels;
288 if (unlikely(!pixels))
290 image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "Cannot get GraphicsMagick pixels.");
295 switch (img->pixel_size)
298 # define IMAGE_WALK_PREFIX(x) walk_##x
299 # define IMAGE_WALK_INLINE
300 # define IMAGE_WALK_IMAGE img
301 # define IMAGE_WALK_UNROLL 4
302 # define IMAGE_WALK_COL_STEP 1
303 # define IMAGE_WALK_DO_STEP do{ \
304 dest->red = BYTE_TO_QUANTUM(walk_pos[0]); \
305 dest->green = BYTE_TO_QUANTUM(walk_pos[0]); \
306 dest->blue = BYTE_TO_QUANTUM(walk_pos[0]); \
307 dest->opacity = OPACITY_MAX; \
309 # include "images/image-walk.h"
313 # define IMAGE_WALK_PREFIX(x) walk_##x
314 # define IMAGE_WALK_INLINE
315 # define IMAGE_WALK_IMAGE img
316 # define IMAGE_WALK_UNROLL 4
317 # define IMAGE_WALK_COL_STEP 2
318 # define IMAGE_WALK_DO_STEP do{ \
319 dest->red = BYTE_TO_QUANTUM(walk_pos[0]); \
320 dest->green = BYTE_TO_QUANTUM(walk_pos[0]); \
321 dest->blue = BYTE_TO_QUANTUM(walk_pos[0]); \
322 dest->opacity = BYTE_TO_QUANTUM(walk_pos[1]); \
324 # include "images/image-walk.h"
328 # define IMAGE_WALK_PREFIX(x) walk_##x
329 # define IMAGE_WALK_INLINE
330 # define IMAGE_WALK_IMAGE img
331 # define IMAGE_WALK_UNROLL 4
332 # define IMAGE_WALK_COL_STEP 3
333 # define IMAGE_WALK_DO_STEP do{ \
334 dest->red = BYTE_TO_QUANTUM(walk_pos[0]); \
335 dest->green = BYTE_TO_QUANTUM(walk_pos[1]); \
336 dest->blue = BYTE_TO_QUANTUM(walk_pos[2]); \
337 dest->opacity = OPACITY_MAX; \
339 # include "images/image-walk.h"
343 # define IMAGE_WALK_PREFIX(x) walk_##x
344 # define IMAGE_WALK_INLINE
345 # define IMAGE_WALK_IMAGE img
346 # define IMAGE_WALK_UNROLL 4
347 # define IMAGE_WALK_COL_STEP 4
348 # define IMAGE_WALK_DO_STEP do{ \
349 dest->red = BYTE_TO_QUANTUM(walk_pos[0]); \
350 dest->green = BYTE_TO_QUANTUM(walk_pos[1]); \
351 dest->blue = BYTE_TO_QUANTUM(walk_pos[2]); \
352 dest->opacity = BYTE_TO_QUANTUM(walk_pos[3]); \
354 # include "images/image-walk.h"
362 if (unlikely(!SyncImagePixels(image)))
364 image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "Cannot sync GraphicsMagick pixels.");
370 void *buf = ImageToBlob(info, image, &buf_len, &exception);
373 image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "GraphicsMagick failed to compress the image.");
376 if (unlikely(buf_len > MAX_FILE_SIZE))
378 image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "Image too large.");
382 /* Write to stream */
383 bwrite(io->fastbuf, buf, buf_len);
391 DestroyImageInfo(info);
392 DestroyExceptionInfo(&exception);