]> mj.ucw.cz Git - libucw.git/blob - images/io-libmagick.c
tableprinter&xtypes: assert changed to die in tests, fix of timestamp parsing
[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 <ucw/lib.h>
13 #include <ucw/mempool.h>
14 #include <ucw/fastbuf.h>
15 #include <images/images.h>
16 #include <images/error.h>
17 #include <images/color.h>
18 #include <images/io-main.h>
19
20 #include <sys/types.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <magick/api.h>
24 #include <pthread.h>
25
26 #define MAX_FILE_SIZE (1 << 30)
27 #define QUANTUM_SCALE (QuantumDepth - 8)
28 #define QUANTUM_TO_BYTE(x) ((uint)(x) >> QUANTUM_SCALE)
29 #define BYTE_TO_QUANTUM(x) ((uint)(x) << QUANTUM_SCALE)
30 #define ALPHA_TO_BYTE(x) (255 - QUANTUM_TO_BYTE(x))
31 #define BYTE_TO_ALPHA(x) (BYTE_TO_QUANTUM(255 - (x)))
32
33 static pthread_mutex_t libmagick_mutex = PTHREAD_MUTEX_INITIALIZER;
34 static uint libmagick_counter;
35
36 struct magick_read_data {
37   ExceptionInfo exception;
38   ImageInfo *info;
39   Image *image;
40 };
41
42 int
43 libmagick_init(struct image_io *io UNUSED)
44 {
45   pthread_mutex_lock(&libmagick_mutex);
46   if (!libmagick_counter++)
47     InitializeMagick(NULL);
48   pthread_mutex_unlock(&libmagick_mutex);
49   return 1;
50 }
51
52 void
53 libmagick_cleanup(struct image_io *io UNUSED)
54 {
55   pthread_mutex_lock(&libmagick_mutex);
56   if (!--libmagick_counter)
57     DestroyMagick();
58   pthread_mutex_unlock(&libmagick_mutex);
59 }
60
61 static void
62 libmagick_destroy_read_data(struct magick_read_data *rd)
63 {
64   if (rd->image)
65     DestroyImage(rd->image);
66   DestroyImageInfo(rd->info);
67   DestroyExceptionInfo(&rd->exception);
68 }
69
70 static void
71 libmagick_read_cancel(struct image_io *io)
72 {
73   DBG("libmagick_read_cancel()");
74
75   struct magick_read_data *rd = io->read_data;
76   libmagick_destroy_read_data(rd);
77 }
78
79 int
80 libmagick_read_header(struct image_io *io)
81 {
82   DBG("libmagick_read_header()");
83
84   /* Read entire stream */
85   ucw_off_t file_size = bfilesize(io->fastbuf) - btell(io->fastbuf);
86   if (unlikely(file_size > MAX_FILE_SIZE))
87     {
88       IMAGE_ERROR(io->context, IMAGE_ERROR_READ_FAILED, "Too long stream.");
89       return 0;
90     }
91   uint buf_size = file_size;
92   byte *buf = xmalloc(buf_size);
93   breadb(io->fastbuf, buf, buf_size);
94
95   /* Allocate read structure */
96   struct magick_read_data *rd = io->read_data = mp_alloc_zero(io->internal_pool, sizeof(*rd));
97
98   /* Initialize GraphicsMagick */
99   GetExceptionInfo(&rd->exception);
100   rd->info = CloneImageInfo(NULL);
101   rd->info->subrange = 1;
102
103   /* Read the image */
104   rd->image = BlobToImage(rd->info, buf, buf_size, &rd->exception);
105   xfree(buf);
106   if (unlikely(!rd->image))
107     {
108       IMAGE_ERROR(io->context, IMAGE_ERROR_READ_FAILED, "GraphicsMagick failed to read the image.");
109       goto err;
110     }
111   if (unlikely(rd->image->columns > image_max_dim || rd->image->rows > image_max_dim))
112     {
113       IMAGE_ERROR(io->context, IMAGE_ERROR_INVALID_DIMENSIONS, "Image too large.");
114       goto err;
115     }
116
117   /* Fill image parameters */
118   io->cols = rd->image->columns;
119   io->rows = rd->image->rows;
120   switch (rd->image->colorspace)
121     {
122       case GRAYColorspace:
123         io->flags = COLOR_SPACE_GRAYSCALE;
124         break;
125       default:
126         io->flags = COLOR_SPACE_RGB;
127         break;
128     }
129   if (rd->image->matte)
130     io->flags |= IMAGE_ALPHA;
131   io->number_of_colors = rd->image->colors;
132   if (rd->image->storage_class == PseudoClass && rd->image->compression != JPEGCompression)
133     io->flags |= IMAGE_IO_HAS_PALETTE;
134
135   io->read_cancel = libmagick_read_cancel;
136   return 1;
137
138 err:
139   libmagick_destroy_read_data(rd);
140   return 0;
141 }
142
143 static inline byte
144 libmagick_pixel_to_gray(PixelPacket *pixel)
145 {
146   return rgb_to_gray_func(pixel->red, pixel->green, pixel->blue) >> QUANTUM_SCALE;
147 }
148
149 int
150 libmagick_read_data(struct image_io *io)
151 {
152   DBG("libmagick_read_data()");
153
154   struct magick_read_data *rd = io->read_data;
155
156   /* Quantize image */
157   switch (rd->image->colorspace)
158     {
159       case RGBColorspace:
160       case GRAYColorspace:
161         break;
162       default: ;
163         QuantizeInfo quantize;
164         GetQuantizeInfo(&quantize);
165         quantize.colorspace = RGBColorspace;
166         QuantizeImage(&quantize, rd->image);
167         break;
168     }
169
170   /* Prepare the image */
171   struct image_io_read_data_internals rdi;
172   uint read_flags = io->flags;
173   uint cs = read_flags & IMAGE_COLOR_SPACE;
174   if (cs != COLOR_SPACE_GRAYSCALE && cs != COLOR_SPACE_RGB)
175     read_flags = (read_flags & ~IMAGE_COLOR_SPACE & IMAGE_PIXEL_FORMAT) | COLOR_SPACE_RGB;
176   if ((read_flags & IMAGE_IO_USE_BACKGROUND) && !(read_flags & IMAGE_ALPHA))
177     read_flags = (read_flags & IMAGE_CHANNELS_FORMAT) | IMAGE_ALPHA;
178   if (unlikely(!image_io_read_data_prepare(&rdi, io, rd->image->columns, rd->image->rows, read_flags)))
179     {
180       libmagick_destroy_read_data(rd);
181       return 0;
182     }
183
184   /* Acquire pixels */
185   PixelPacket *src = (PixelPacket *)AcquireImagePixels(rd->image, 0, 0, rd->image->columns, rd->image->rows, &rd->exception);
186   if (unlikely(!src))
187     {
188       IMAGE_ERROR(io->context, IMAGE_ERROR_READ_FAILED, "Cannot acquire image pixels.");
189       libmagick_destroy_read_data(rd);
190       image_io_read_data_break(&rdi, io);
191       return 0;
192     }
193
194   /* Convert pixels */
195   switch (rdi.image->pixel_size)
196     {
197       case 1:
198 #       define IMAGE_WALK_PREFIX(x) walk_##x
199 #       define IMAGE_WALK_INLINE
200 #       define IMAGE_WALK_IMAGE (rdi.image)
201 #       define IMAGE_WALK_UNROLL 4
202 #       define IMAGE_WALK_COL_STEP 1
203 #       define IMAGE_WALK_DO_STEP do{ \
204           walk_pos[0] = libmagick_pixel_to_gray(src); \
205           src++; }while(0)
206 #       include <images/image-walk.h>
207         break;
208
209       case 2:
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 2
215 #       define IMAGE_WALK_DO_STEP do{ \
216           walk_pos[0] = libmagick_pixel_to_gray(src); \
217           walk_pos[1] = ALPHA_TO_BYTE(src->opacity); \
218           src++; }while(0)
219 #       include <images/image-walk.h>
220         break;
221
222       case 3:
223 #       define IMAGE_WALK_PREFIX(x) walk_##x
224 #       define IMAGE_WALK_INLINE
225 #       define IMAGE_WALK_IMAGE (rdi.image)
226 #       define IMAGE_WALK_UNROLL 4
227 #       define IMAGE_WALK_COL_STEP 3
228 #       define IMAGE_WALK_DO_STEP do{ \
229           walk_pos[0] = QUANTUM_TO_BYTE(src->red); \
230           walk_pos[1] = QUANTUM_TO_BYTE(src->green); \
231           walk_pos[2] = QUANTUM_TO_BYTE(src->blue); \
232           src++; }while(0)
233 #       include <images/image-walk.h>
234         break;
235
236       case 4:
237 #       define IMAGE_WALK_PREFIX(x) walk_##x
238 #       define IMAGE_WALK_INLINE
239 #       define IMAGE_WALK_IMAGE (rdi.image)
240 #       define IMAGE_WALK_UNROLL 4
241 #       define IMAGE_WALK_COL_STEP 4
242 #       define IMAGE_WALK_DO_STEP do{ \
243           walk_pos[0] = QUANTUM_TO_BYTE(src->red); \
244           walk_pos[1] = QUANTUM_TO_BYTE(src->green); \
245           walk_pos[2] = QUANTUM_TO_BYTE(src->blue); \
246           walk_pos[3] = ALPHA_TO_BYTE(src->opacity); \
247           src++; }while(0)
248 #       include <images/image-walk.h>
249         break;
250
251       default:
252         ASSERT(0);
253     }
254
255   /* Free GraphicsMagick structures */
256   libmagick_destroy_read_data(rd);
257
258   /* Finish the image */
259   return image_io_read_data_finish(&rdi, io);
260 }
261
262 int
263 libmagick_write(struct image_io *io)
264 {
265   DBG("libmagick_write()");
266
267   /* Initialize GraphicsMagick */
268   int result = 0;
269   ExceptionInfo exception;
270   ImageInfo *info;
271   GetExceptionInfo(&exception);
272   info = CloneImageInfo(NULL);
273
274   /* Setup image parameters and allocate the image*/
275   struct image *img = io->image;
276   switch (img->flags & IMAGE_COLOR_SPACE)
277     {
278       case COLOR_SPACE_GRAYSCALE:
279         info->colorspace = GRAYColorspace;
280         break;
281       case COLOR_SPACE_RGB:
282         info->colorspace = RGBColorspace;
283         break;
284       default:
285         IMAGE_ERROR(io->context, IMAGE_ERROR_WRITE_FAILED, "Unsupported color space.");
286         goto err;
287     }
288   switch (io->format)
289     {
290       case IMAGE_FORMAT_JPEG:
291         strcpy(info->magick, "JPEG");
292         if (io->jpeg_quality)
293           info->quality = MIN(io->jpeg_quality, 100);
294         break;
295       case IMAGE_FORMAT_PNG:
296         strcpy(info->magick, "PNG");
297         break;
298       case IMAGE_FORMAT_GIF:
299         strcpy(info->magick, "GIF");
300         break;
301       default:
302         ASSERT(0);
303     }
304   Image *image = AllocateImage(info);
305   if (unlikely(!image))
306     {
307       IMAGE_ERROR(io->context, IMAGE_ERROR_WRITE_FAILED, "GraphicsMagick failed to allocate the image.");
308       goto err;
309     }
310   image->columns = img->cols;
311   image->rows = img->rows;
312
313   /* Get pixels */
314   PixelPacket *pixels = SetImagePixels(image, 0, 0, img->cols, img->rows), *dest = pixels;
315   if (unlikely(!pixels))
316     {
317       IMAGE_ERROR(io->context, IMAGE_ERROR_WRITE_FAILED, "Cannot get GraphicsMagick pixels.");
318       goto err2;
319     }
320
321   /* Convert pixels */
322   switch (img->pixel_size)
323     {
324       case 1:
325 #       define IMAGE_WALK_PREFIX(x) walk_##x
326 #       define IMAGE_WALK_INLINE
327 #       define IMAGE_WALK_IMAGE img
328 #       define IMAGE_WALK_UNROLL 4
329 #       define IMAGE_WALK_COL_STEP 1
330 #       define IMAGE_WALK_DO_STEP do{ \
331           dest->red = BYTE_TO_QUANTUM(walk_pos[0]); \
332           dest->green = BYTE_TO_QUANTUM(walk_pos[0]); \
333           dest->blue = BYTE_TO_QUANTUM(walk_pos[0]); \
334           dest->opacity = 0; \
335           dest++; }while(0)
336 #       include <images/image-walk.h>
337         break;
338
339       case 2:
340 #       define IMAGE_WALK_PREFIX(x) walk_##x
341 #       define IMAGE_WALK_INLINE
342 #       define IMAGE_WALK_IMAGE img
343 #       define IMAGE_WALK_UNROLL 4
344 #       define IMAGE_WALK_COL_STEP 2
345 #       define IMAGE_WALK_DO_STEP do{ \
346           dest->red = BYTE_TO_QUANTUM(walk_pos[0]); \
347           dest->green = BYTE_TO_QUANTUM(walk_pos[0]); \
348           dest->blue = BYTE_TO_QUANTUM(walk_pos[0]); \
349           dest->opacity = BYTE_TO_ALPHA(walk_pos[1]); \
350           dest++; }while(0)
351 #       include <images/image-walk.h>
352         break;
353
354       case 3:
355 #       define IMAGE_WALK_PREFIX(x) walk_##x
356 #       define IMAGE_WALK_INLINE
357 #       define IMAGE_WALK_IMAGE img
358 #       define IMAGE_WALK_UNROLL 4
359 #       define IMAGE_WALK_COL_STEP 3
360 #       define IMAGE_WALK_DO_STEP do{ \
361           dest->red = BYTE_TO_QUANTUM(walk_pos[0]); \
362           dest->green = BYTE_TO_QUANTUM(walk_pos[1]); \
363           dest->blue = BYTE_TO_QUANTUM(walk_pos[2]); \
364           dest->opacity = 0; \
365           dest++; }while(0)
366 #       include <images/image-walk.h>
367         break;
368
369       case 4:
370 #       define IMAGE_WALK_PREFIX(x) walk_##x
371 #       define IMAGE_WALK_INLINE
372 #       define IMAGE_WALK_IMAGE img
373 #       define IMAGE_WALK_UNROLL 4
374 #       define IMAGE_WALK_COL_STEP 4
375 #       define IMAGE_WALK_DO_STEP do{ \
376           dest->red = BYTE_TO_QUANTUM(walk_pos[0]); \
377           dest->green = BYTE_TO_QUANTUM(walk_pos[1]); \
378           dest->blue = BYTE_TO_QUANTUM(walk_pos[2]); \
379           dest->opacity = BYTE_TO_ALPHA(walk_pos[3]); \
380           dest++; }while(0)
381 #       include <images/image-walk.h>
382         break;
383
384       default:
385         ASSERT(0);
386     }
387
388   /* Store pixels */
389   if (unlikely(!SyncImagePixels(image)))
390     {
391       IMAGE_ERROR(io->context, IMAGE_ERROR_WRITE_FAILED, "Cannot sync GraphicsMagick pixels.");
392       goto err2;
393     }
394
395   /* Write image */
396   size_t buf_len = 0;
397   void *buf = ImageToBlob(info, image, &buf_len, &exception);
398   if (unlikely(!buf))
399     {
400       IMAGE_ERROR(io->context, IMAGE_ERROR_WRITE_FAILED, "GraphicsMagick failed to compress the image.");
401       goto err2;
402     }
403   if (unlikely(buf_len > MAX_FILE_SIZE))
404     {
405       IMAGE_ERROR(io->context, IMAGE_ERROR_WRITE_FAILED, "Image too large.");
406       goto err2;
407     }
408
409   /* Write to stream */
410   bwrite(io->fastbuf, buf, buf_len);
411
412   /* Success */
413   result = 1;
414
415 err2:
416   DestroyImage(image);
417 err:
418   DestroyImageInfo(info);
419   DestroyExceptionInfo(&exception);
420   return result;
421 }