]> mj.ucw.cz Git - libucw.git/blob - images/io-libmagick.c
bd503e792a72b0dbb1fbfe906547e474dd73a144
[libucw.git] / images / io-libmagick.c
1 /*
2  *      Image Library -- GraphicsMagick (slow fallback library)
3  *
4  *      (c) 2006 Pavel Charvat <pchar@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU Lesser General Public License.
8  */
9
10 #undef LOCAL_DEBUG
11
12 #include "lib/lib.h"
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>
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <magick/api.h>
22
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)
28
29 struct magick_read_data {
30   ExceptionInfo exception;
31   ImageInfo *info;
32   Image *image;
33 };
34
35 static inline void
36 libmagick_destroy_read_data(struct magick_read_data *rd)
37 {
38   if (rd->image)
39     DestroyImage(rd->image);
40   DestroyImageInfo(rd->info);
41   DestroyExceptionInfo(&rd->exception);
42   DestroyMagick();
43 }
44
45 static void
46 libmagick_read_cancel(struct image_io *io)
47 {
48   DBG("libmagick_read_cancel()");
49
50   struct magick_read_data *rd = io->read_data;
51
52   DestroyImage(rd->image);
53   libmagick_destroy_read_data(rd);
54 }
55
56 int
57 libmagick_read_header(struct image_io *io)
58 {
59   DBG("libmagick_read_header()");
60
61   /* Read entire stream */
62   sh_off_t file_size = bfilesize(io->fastbuf) - btell(io->fastbuf);
63   if (unlikely(file_size > MAX_FILE_SIZE))
64     {
65       image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Too long stream.");
66       return 0;
67     }
68   uns buf_size = file_size;
69   byte *buf = xmalloc(buf_size);
70   breadb(io->fastbuf, buf, buf_size);
71
72   /* Allocate read structure */
73   struct magick_read_data *rd = io->read_data = mp_alloc(io->internal_pool, sizeof(*rd));
74
75   /* Initialize GraphicsMagick */
76   InitializeMagick(NULL);
77   GetExceptionInfo(&rd->exception);
78   rd->info = CloneImageInfo(NULL);
79   rd->info->subrange = 1;
80
81   /* Read the image */
82   rd->image = BlobToImage(rd->info, buf, buf_size, &rd->exception);
83   xfree(buf);
84   if (unlikely(!rd->image))
85     {
86       image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "GraphicsMagick failed to read the image.");
87       goto err;
88     }
89   if (unlikely(rd->image->columns > IMAGE_MAX_SIZE || rd->image->rows > IMAGE_MAX_SIZE))
90     {
91       image_thread_err(io->thread, IMAGE_ERR_INVALID_DIMENSIONS, "Image too large.");
92       goto err;
93     }
94
95   /* Fill image parameters */
96   io->cols = rd->image->columns;
97   io->rows = rd->image->rows;
98   switch (rd->image->colorspace)
99     {
100       case GRAYColorspace:
101         io->flags = COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA;
102         break;
103       default:
104         io->flags = COLOR_SPACE_RGB | IMAGE_ALPHA;
105         break;
106     }
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;
110
111   io->read_cancel = libmagick_read_cancel;
112   return 1;
113
114 err:
115   libmagick_destroy_read_data(rd);
116   return 0;
117 }
118
119 static inline byte
120 libmagick_pixel_to_gray(PixelPacket *pixel)
121 {
122   return rgb_to_gray_func(pixel->red, pixel->green, pixel->blue) >> QUANTUM_SCALE;
123 }
124
125 int
126 libmagick_read_data(struct image_io *io)
127 {
128   DBG("libmagick_read_data()");
129
130   struct magick_read_data *rd = io->read_data;
131
132   /* Quantize image */
133   switch (rd->image->colorspace)
134     {
135       case RGBColorspace:
136       case GRAYColorspace:
137         break;
138       default: ;
139         QuantizeInfo quantize;
140         GetQuantizeInfo(&quantize);
141         quantize.colorspace = RGBColorspace;
142         QuantizeImage(&quantize, rd->image);
143         break;
144     }
145
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)))
152     {
153       libmagick_destroy_read_data(rd);
154       return 0;
155     }
156
157   /* Acquire pixels */
158   PixelPacket *src = (PixelPacket *)AcquireImagePixels(rd->image, 0, 0, rd->image->columns, rd->image->rows, &rd->exception);
159   if (unlikely(!src))
160     {
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);
164       return 0;
165     }
166
167   /* Convert pixels */
168   switch (rdi.image->pixel_size)
169     {
170       case 1:
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); \
178           src++; }while(0)
179 #       include "images/image-walk.h"
180         break;
181
182       case 2:
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); \
191           src++; }while(0)
192 #       include "images/image-walk.h"
193         break;
194
195       case 3:
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); \
205           src++; }while(0)
206 #       include "images/image-walk.h"
207         break;
208
209       case 4:
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); \
220           src++; }while(0)
221 #       include "images/image-walk.h"
222         break;
223
224       default:
225         ASSERT(0);
226     }
227
228   /* Free GraphicsMagick structures */
229   libmagick_destroy_read_data(rd);
230
231   /* Finish the image */
232   return image_io_read_data_finish(&rdi, io);
233 }
234
235 int
236 libmagick_write(struct image_io *io)
237 {
238   DBG("libmagick_write()");
239
240   /* Initialize GraphicsMagick */
241   int result = 0;
242   ExceptionInfo exception;
243   ImageInfo *info;
244   InitializeMagick(NULL);
245   GetExceptionInfo(&exception);
246   info = CloneImageInfo(NULL);
247
248   /* Setup image parameters and allocate the image*/
249   struct image *img = io->image;
250   switch (img->flags & IMAGE_COLOR_SPACE)
251     {
252       case COLOR_SPACE_GRAYSCALE:
253         info->colorspace = GRAYColorspace;
254         break;
255       case COLOR_SPACE_RGB:
256         info->colorspace = RGBColorspace;
257         break;
258       default:
259         ASSERT(0);
260     }
261   switch (io->format)
262     {
263       case IMAGE_FORMAT_JPEG:
264         strcpy(info->magick, "JPEG");
265         if (io->jpeg_quality)
266           info->quality = MIN(io->jpeg_quality, 100);
267         break;
268       case IMAGE_FORMAT_PNG:
269         strcpy(info->magick, "PNG");
270         break;
271       case IMAGE_FORMAT_GIF:
272         strcpy(info->magick, "GIF");
273         break;
274       default:
275         ASSERT(0);
276     }
277   Image *image = AllocateImage(info);
278   if (unlikely(!image))
279     {
280       image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "GraphicsMagick failed to allocate the image.");
281       goto err;
282     }
283   image->columns = img->cols;
284   image->rows = img->rows;
285
286   /* Get pixels */
287   PixelPacket *pixels = SetImagePixels(image, 0, 0, img->cols, img->rows), *dest = pixels;
288   if (unlikely(!pixels))
289     {
290       image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "Cannot get GraphicsMagick pixels.");
291       goto err2;
292     }
293
294   /* Convert pixels */
295   switch (img->pixel_size)
296     {
297       case 1:
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; \
308           dest++; }while(0)
309 #       include "images/image-walk.h"
310         break;
311
312       case 2:
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]); \
323           dest++; }while(0)
324 #       include "images/image-walk.h"
325         break;
326
327       case 3:
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; \
338           dest++; }while(0)
339 #       include "images/image-walk.h"
340         break;
341
342       case 4:
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]); \
353           dest++; }while(0)
354 #       include "images/image-walk.h"
355         break;
356
357       default:
358         ASSERT(0);
359     }
360
361   /* Store pixels */
362   if (unlikely(!SyncImagePixels(image)))
363     {
364       image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "Cannot sync GraphicsMagick pixels.");
365       goto err2;
366     }
367
368   /* Write image */
369   size_t buf_len = 0;
370   void *buf = ImageToBlob(info, image, &buf_len, &exception);
371   if (unlikely(!buf))
372     {
373       image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "GraphicsMagick failed to compress the image.");
374       goto err2;
375     }
376   if (unlikely(buf_len > MAX_FILE_SIZE))
377     {
378       image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "Image too large.");
379       goto err2;
380     }
381
382   /* Write to stream */
383   bwrite(io->fastbuf, buf, buf_len);
384
385   /* Success */
386   result = 1;
387
388 err2:
389   DestroyImage(image);
390 err:
391   DestroyImageInfo(info);
392   DestroyExceptionInfo(&exception);
393   DestroyMagick();
394   return result;
395 }