]> mj.ucw.cz Git - libucw.git/blob - images/io-libmagick.c
Merge with git+ssh://cvs.ucw.cz/projects/sherlock/GIT/sherlock.git
[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
19 #include <sys/types.h>
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <magick/api.h>
23 #include <pthread.h>
24
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)))
31
32 static pthread_mutex_t libmagick_mutex = PTHREAD_MUTEX_INITIALIZER;
33 static uns libmagick_counter;
34
35 struct magick_read_data {
36   ExceptionInfo exception;
37   ImageInfo *info;
38   Image *image;
39 };
40
41 int
42 libmagick_init(struct image_io *io UNUSED)
43 {
44   pthread_mutex_lock(&libmagick_mutex);
45   if (!libmagick_counter++)
46     InitializeMagick(NULL);
47   pthread_mutex_unlock(&libmagick_mutex);
48   return 1;
49 }
50
51 void
52 libmagick_cleanup(struct image_io *io UNUSED)
53 {
54   pthread_mutex_lock(&libmagick_mutex);
55   if (!--libmagick_counter)
56     DestroyMagick();
57   pthread_mutex_unlock(&libmagick_mutex);
58 }
59
60 static void
61 libmagick_destroy_read_data(struct magick_read_data *rd)
62 {
63   if (rd->image)
64     DestroyImage(rd->image);
65   DestroyImageInfo(rd->info);
66   DestroyExceptionInfo(&rd->exception);
67 }
68
69 static void
70 libmagick_read_cancel(struct image_io *io)
71 {
72   DBG("libmagick_read_cancel()");
73
74   struct magick_read_data *rd = io->read_data;
75   libmagick_destroy_read_data(rd);
76 }
77
78 int
79 libmagick_read_header(struct image_io *io)
80 {
81   DBG("libmagick_read_header()");
82
83   /* Read entire stream */
84   sh_off_t file_size = bfilesize(io->fastbuf) - btell(io->fastbuf);
85   if (unlikely(file_size > MAX_FILE_SIZE))
86     {
87       image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Too long stream.");
88       return 0;
89     }
90   uns buf_size = file_size;
91   byte *buf = xmalloc(buf_size);
92   breadb(io->fastbuf, buf, buf_size);
93
94   /* Allocate read structure */
95   struct magick_read_data *rd = io->read_data = mp_alloc_zero(io->internal_pool, sizeof(*rd));
96
97   /* Initialize GraphicsMagick */
98   GetExceptionInfo(&rd->exception);
99   rd->info = CloneImageInfo(NULL);
100   rd->info->subrange = 1;
101
102   /* Read the image */
103   rd->image = BlobToImage(rd->info, buf, buf_size, &rd->exception);
104   xfree(buf);
105   if (unlikely(!rd->image))
106     {
107       image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "GraphicsMagick failed to read the image.");
108       goto err;
109     }
110   if (unlikely(rd->image->columns > IMAGE_MAX_SIZE || rd->image->rows > IMAGE_MAX_SIZE))
111     {
112       image_thread_err(io->thread, IMAGE_ERR_INVALID_DIMENSIONS, "Image too large.");
113       goto err;
114     }
115
116   /* Fill image parameters */
117   io->cols = rd->image->columns;
118   io->rows = rd->image->rows;
119   switch (rd->image->colorspace)
120     {
121       case GRAYColorspace:
122         io->flags = COLOR_SPACE_GRAYSCALE;
123         break;
124       default:
125         io->flags = COLOR_SPACE_RGB;
126         break;
127     }
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;
133
134   io->read_cancel = libmagick_read_cancel;
135   return 1;
136
137 err:
138   libmagick_destroy_read_data(rd);
139   return 0;
140 }
141
142 static inline byte
143 libmagick_pixel_to_gray(PixelPacket *pixel)
144 {
145   return rgb_to_gray_func(pixel->red, pixel->green, pixel->blue) >> QUANTUM_SCALE;
146 }
147
148 int
149 libmagick_read_data(struct image_io *io)
150 {
151   DBG("libmagick_read_data()");
152
153   struct magick_read_data *rd = io->read_data;
154
155   /* Quantize image */
156   switch (rd->image->colorspace)
157     {
158       case RGBColorspace:
159       case GRAYColorspace:
160         break;
161       default: ;
162         QuantizeInfo quantize;
163         GetQuantizeInfo(&quantize);
164         quantize.colorspace = RGBColorspace;
165         QuantizeImage(&quantize, rd->image);
166         break;
167     }
168
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)))
175     {
176       libmagick_destroy_read_data(rd);
177       return 0;
178     }
179
180   /* Acquire pixels */
181   PixelPacket *src = (PixelPacket *)AcquireImagePixels(rd->image, 0, 0, rd->image->columns, rd->image->rows, &rd->exception);
182   if (unlikely(!src))
183     {
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);
187       return 0;
188     }
189
190   /* Convert pixels */
191   switch (rdi.image->pixel_size)
192     {
193       case 1:
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); \
201           src++; }while(0)
202 #       include "images/image-walk.h"
203         break;
204
205       case 2:
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); \
214           src++; }while(0)
215 #       include "images/image-walk.h"
216         break;
217
218       case 3:
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); \
228           src++; }while(0)
229 #       include "images/image-walk.h"
230         break;
231
232       case 4:
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); \
243           src++; }while(0)
244 #       include "images/image-walk.h"
245         break;
246
247       default:
248         ASSERT(0);
249     }
250
251   /* Free GraphicsMagick structures */
252   libmagick_destroy_read_data(rd);
253
254   /* Finish the image */
255   return image_io_read_data_finish(&rdi, io);
256 }
257
258 int
259 libmagick_write(struct image_io *io)
260 {
261   DBG("libmagick_write()");
262
263   /* Initialize GraphicsMagick */
264   int result = 0;
265   ExceptionInfo exception;
266   ImageInfo *info;
267   GetExceptionInfo(&exception);
268   info = CloneImageInfo(NULL);
269
270   /* Setup image parameters and allocate the image*/
271   struct image *img = io->image;
272   switch (img->flags & IMAGE_COLOR_SPACE)
273     {
274       case COLOR_SPACE_GRAYSCALE:
275         info->colorspace = GRAYColorspace;
276         break;
277       case COLOR_SPACE_RGB:
278         info->colorspace = RGBColorspace;
279         break;
280       default:
281         ASSERT(0);
282     }
283   switch (io->format)
284     {
285       case IMAGE_FORMAT_JPEG:
286         strcpy(info->magick, "JPEG");
287         if (io->jpeg_quality)
288           info->quality = MIN(io->jpeg_quality, 100);
289         break;
290       case IMAGE_FORMAT_PNG:
291         strcpy(info->magick, "PNG");
292         break;
293       case IMAGE_FORMAT_GIF:
294         strcpy(info->magick, "GIF");
295         break;
296       default:
297         ASSERT(0);
298     }
299   Image *image = AllocateImage(info);
300   if (unlikely(!image))
301     {
302       image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "GraphicsMagick failed to allocate the image.");
303       goto err;
304     }
305   image->columns = img->cols;
306   image->rows = img->rows;
307
308   /* Get pixels */
309   PixelPacket *pixels = SetImagePixels(image, 0, 0, img->cols, img->rows), *dest = pixels;
310   if (unlikely(!pixels))
311     {
312       image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "Cannot get GraphicsMagick pixels.");
313       goto err2;
314     }
315
316   /* Convert pixels */
317   switch (img->pixel_size)
318     {
319       case 1:
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]); \
329           dest->opacity = 0; \
330           dest++; }while(0)
331 #       include "images/image-walk.h"
332         break;
333
334       case 2:
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]); \
345           dest++; }while(0)
346 #       include "images/image-walk.h"
347         break;
348
349       case 3:
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]); \
359           dest->opacity = 0; \
360           dest++; }while(0)
361 #       include "images/image-walk.h"
362         break;
363
364       case 4:
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]); \
375           dest++; }while(0)
376 #       include "images/image-walk.h"
377         break;
378
379       default:
380         ASSERT(0);
381     }
382
383   /* Store pixels */
384   if (unlikely(!SyncImagePixels(image)))
385     {
386       image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "Cannot sync GraphicsMagick pixels.");
387       goto err2;
388     }
389
390   /* Write image */
391   size_t buf_len = 0;
392   void *buf = ImageToBlob(info, image, &buf_len, &exception);
393   if (unlikely(!buf))
394     {
395       image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "GraphicsMagick failed to compress the image.");
396       goto err2;
397     }
398   if (unlikely(buf_len > MAX_FILE_SIZE))
399     {
400       image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "Image too large.");
401       goto err2;
402     }
403
404   /* Write to stream */
405   bwrite(io->fastbuf, buf, buf_len);
406
407   /* Success */
408   result = 1;
409
410 err2:
411   DestroyImage(image);
412 err:
413   DestroyImageInfo(info);
414   DestroyExceptionInfo(&exception);
415   return result;
416 }