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/io-main.h"
17 #include <sys/types.h>
20 #include <magick/api.h>
22 #define MAX_FILE_SIZE (1 << 30)
23 #define QUANTUM_SCALE (QuantumDepth - 8)
24 #define QUANTUM_TO_BYTE(x) ((uns)(x) >> QUANTUM_SCALE)
25 #define BYTE_TO_QUANTUM(x) ((uns)(x) << QUANTUM_SCALE)
26 #define OPACITY_MAX ((1 << QuantumDepth) - 1)
28 struct magick_read_data {
29 ExceptionInfo exception;
35 libmagick_destroy_read_data(struct magick_read_data *rd)
38 DestroyImage(rd->image);
39 DestroyImageInfo(rd->info);
40 DestroyExceptionInfo(&rd->exception);
45 libmagick_read_cancel(struct image_io *io)
47 DBG("libmagick_read_cancel()");
49 struct magick_read_data *rd = io->read_data;
51 DestroyImage(rd->image);
52 libmagick_destroy_read_data(rd);
56 libmagick_read_header(struct image_io *io)
58 DBG("libmagick_read_header()");
60 /* Read entire stream */
61 sh_off_t file_size = bfilesize(io->fastbuf) - btell(io->fastbuf);
62 if (unlikely(file_size > MAX_FILE_SIZE))
64 image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Too long stream.");
67 uns buf_size = file_size;
68 byte *buf = xmalloc(buf_size);
69 breadb(io->fastbuf, buf, buf_size);
71 /* Allocate read structure */
72 struct magick_read_data *rd = io->read_data = mp_alloc(io->internal_pool, sizeof(*rd));
74 /* Initialize GraphicsMagick */
75 InitializeMagick(NULL);
76 GetExceptionInfo(&rd->exception);
77 rd->info = CloneImageInfo(NULL);
78 rd->info->subrange = 1;
81 rd->image = BlobToImage(rd->info, buf, buf_size, &rd->exception);
83 if (unlikely(!rd->image))
85 image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "GraphicsMagick failed to read the image.");
88 if (unlikely(rd->image->columns > IMAGE_MAX_SIZE || rd->image->rows > IMAGE_MAX_SIZE))
90 image_thread_err(io->thread, IMAGE_ERR_INVALID_DIMENSIONS, "Image too large.");
94 /* Fill image parameters */
95 io->cols = rd->image->columns;
96 io->rows = rd->image->rows;
97 switch (rd->image->colorspace)
100 io->flags = COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA;
103 io->flags = COLOR_SPACE_RGB | IMAGE_ALPHA;
106 io->number_of_colors = rd->image->colors;
107 if (rd->image->storage_class == PseudoClass && rd->image->compression != JPEGCompression)
108 io->flags |= IMAGE_IO_HAS_PALETTE;
110 io->read_cancel = libmagick_read_cancel;
114 libmagick_destroy_read_data(rd);
119 libmagick_pixel_to_gray(PixelPacket *pixel)
121 return ((uns)pixel->red * 19660 + (uns)pixel->green * 38666 + (uns)pixel->blue * 7210) >> (16 + QUANTUM_SCALE);
125 libmagick_read_data(struct image_io *io)
127 DBG("libmagick_read_data()");
129 struct magick_read_data *rd = io->read_data;
132 switch (rd->image->colorspace)
138 QuantizeInfo quantize;
139 GetQuantizeInfo(&quantize);
140 quantize.colorspace = RGBColorspace;
141 QuantizeImage(&quantize, rd->image);
145 /* Prepare the image */
146 struct image_io_read_data_internals rdi;
147 if (unlikely(!image_io_read_data_prepare(&rdi, io, rd->image->columns, rd->image->rows)))
149 libmagick_destroy_read_data(rd);
154 PixelPacket *src = (PixelPacket *)AcquireImagePixels(rd->image, 0, 0, rd->image->columns, rd->image->rows, &rd->exception);
157 image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Cannot acquire image pixels.");
158 libmagick_destroy_read_data(rd);
159 image_io_read_data_break(&rdi, io);
164 switch (rdi.image->pixel_size)
167 # define IMAGE_WALK_PREFIX(x) walk_##x
168 # define IMAGE_WALK_INLINE
169 # define IMAGE_WALK_IMAGE (rdi.image)
170 # define IMAGE_WALK_UNROLL 4
171 # define IMAGE_WALK_COL_STEP 1
172 # define IMAGE_WALK_DO_STEP do{ \
173 walk_pos[0] = libmagick_pixel_to_gray(src); \
175 # include "images/image-walk.h"
179 # define IMAGE_WALK_PREFIX(x) walk_##x
180 # define IMAGE_WALK_INLINE
181 # define IMAGE_WALK_IMAGE (rdi.image)
182 # define IMAGE_WALK_UNROLL 4
183 # define IMAGE_WALK_COL_STEP 2
184 # define IMAGE_WALK_DO_STEP do{ \
185 walk_pos[0] = libmagick_pixel_to_gray(src); \
186 walk_pos[1] = QUANTUM_TO_BYTE(src->opacity); \
188 # include "images/image-walk.h"
192 # define IMAGE_WALK_PREFIX(x) walk_##x
193 # define IMAGE_WALK_INLINE
194 # define IMAGE_WALK_IMAGE (rdi.image)
195 # define IMAGE_WALK_UNROLL 4
196 # define IMAGE_WALK_COL_STEP 3
197 # define IMAGE_WALK_DO_STEP do{ \
198 walk_pos[0] = QUANTUM_TO_BYTE(src->red); \
199 walk_pos[1] = QUANTUM_TO_BYTE(src->green); \
200 walk_pos[2] = QUANTUM_TO_BYTE(src->blue); \
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 4
211 # define IMAGE_WALK_DO_STEP do{ \
212 walk_pos[0] = QUANTUM_TO_BYTE(src->red); \
213 walk_pos[1] = QUANTUM_TO_BYTE(src->green); \
214 walk_pos[2] = QUANTUM_TO_BYTE(src->blue); \
215 walk_pos[3] = QUANTUM_TO_BYTE(src->opacity); \
217 # include "images/image-walk.h"
224 /* Free GraphicsMagick structures */
225 libmagick_destroy_read_data(rd);
227 /* Finish the image */
228 return image_io_read_data_finish(&rdi, io);
232 libmagick_write(struct image_io *io)
234 DBG("libmagick_write()");
236 /* Initialize GraphicsMagick */
238 ExceptionInfo exception;
240 InitializeMagick(NULL);
241 GetExceptionInfo(&exception);
242 info = CloneImageInfo(NULL);
244 /* Setup image parameters and allocate the image*/
245 struct image *img = io->image;
246 switch (img->flags & IMAGE_COLOR_SPACE)
248 case COLOR_SPACE_GRAYSCALE:
249 info->colorspace = GRAYColorspace;
251 case COLOR_SPACE_RGB:
252 info->colorspace = RGBColorspace;
259 case IMAGE_FORMAT_JPEG:
260 strcpy(info->magick, "JPEG");
261 if (io->jpeg_quality)
262 info->quality = MIN(io->jpeg_quality, 100);
264 case IMAGE_FORMAT_PNG:
265 strcpy(info->magick, "PNG");
267 case IMAGE_FORMAT_GIF:
268 strcpy(info->magick, "GIF");
273 Image *image = AllocateImage(info);
274 if (unlikely(!image))
276 image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "GraphicsMagick failed to allocate the image.");
279 image->columns = img->cols;
280 image->rows = img->rows;
283 PixelPacket *pixels = SetImagePixels(image, 0, 0, img->cols, img->rows), *dest = pixels;
284 if (unlikely(!pixels))
286 image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "Cannot get GraphicsMagick pixels.");
291 switch (img->pixel_size)
294 # define IMAGE_WALK_PREFIX(x) walk_##x
295 # define IMAGE_WALK_INLINE
296 # define IMAGE_WALK_IMAGE img
297 # define IMAGE_WALK_UNROLL 4
298 # define IMAGE_WALK_COL_STEP 1
299 # define IMAGE_WALK_DO_STEP do{ \
300 dest->red = BYTE_TO_QUANTUM(walk_pos[0]); \
301 dest->green = BYTE_TO_QUANTUM(walk_pos[0]); \
302 dest->blue = BYTE_TO_QUANTUM(walk_pos[0]); \
303 dest->opacity = OPACITY_MAX; \
305 # include "images/image-walk.h"
309 # define IMAGE_WALK_PREFIX(x) walk_##x
310 # define IMAGE_WALK_INLINE
311 # define IMAGE_WALK_IMAGE img
312 # define IMAGE_WALK_UNROLL 4
313 # define IMAGE_WALK_COL_STEP 2
314 # define IMAGE_WALK_DO_STEP do{ \
315 dest->red = BYTE_TO_QUANTUM(walk_pos[0]); \
316 dest->green = BYTE_TO_QUANTUM(walk_pos[0]); \
317 dest->blue = BYTE_TO_QUANTUM(walk_pos[0]); \
318 dest->opacity = BYTE_TO_QUANTUM(walk_pos[1]); \
320 # include "images/image-walk.h"
324 # define IMAGE_WALK_PREFIX(x) walk_##x
325 # define IMAGE_WALK_INLINE
326 # define IMAGE_WALK_IMAGE img
327 # define IMAGE_WALK_UNROLL 4
328 # define IMAGE_WALK_COL_STEP 3
329 # define IMAGE_WALK_DO_STEP do{ \
330 dest->red = BYTE_TO_QUANTUM(walk_pos[0]); \
331 dest->green = BYTE_TO_QUANTUM(walk_pos[1]); \
332 dest->blue = BYTE_TO_QUANTUM(walk_pos[2]); \
333 dest->opacity = OPACITY_MAX; \
335 # include "images/image-walk.h"
339 # define IMAGE_WALK_PREFIX(x) walk_##x
340 # define IMAGE_WALK_INLINE
341 # define IMAGE_WALK_IMAGE img
342 # define IMAGE_WALK_UNROLL 4
343 # define IMAGE_WALK_COL_STEP 4
344 # define IMAGE_WALK_DO_STEP do{ \
345 dest->red = BYTE_TO_QUANTUM(walk_pos[0]); \
346 dest->green = BYTE_TO_QUANTUM(walk_pos[1]); \
347 dest->blue = BYTE_TO_QUANTUM(walk_pos[2]); \
348 dest->opacity = BYTE_TO_QUANTUM(walk_pos[3]); \
350 # include "images/image-walk.h"
358 if (unlikely(!SyncImagePixels(image)))
360 image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "Cannot sync GraphicsMagick pixels.");
366 void *buf = ImageToBlob(info, image, &buf_len, &exception);
369 image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "GraphicsMagick failed to compress the image.");
372 if (unlikely(buf_len > MAX_FILE_SIZE))
374 image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "Image too large.");
378 /* Write to stream */
379 bwrite(io->fastbuf, buf, buf_len);
387 DestroyImageInfo(info);
388 DestroyExceptionInfo(&exception);