]> mj.ucw.cz Git - libucw.git/blob - images/io-libmagick.c
Partial merge with dev-img - added libimages.
[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/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) ((uns)(x) >> QUANTUM_SCALE)
29 #define BYTE_TO_QUANTUM(x) ((uns)(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 uns 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   sh_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   uns 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   uns read_flags = io->flags;
173   if ((read_flags & IMAGE_IO_USE_BACKGROUND) && !(read_flags & IMAGE_ALPHA))
174     read_flags = (read_flags | IMAGE_ALPHA) & IMAGE_CHANNELS_FORMAT;
175   if (unlikely(!image_io_read_data_prepare(&rdi, io, rd->image->columns, rd->image->rows, read_flags)))
176     {
177       libmagick_destroy_read_data(rd);
178       return 0;
179     }
180
181   /* Acquire pixels */
182   PixelPacket *src = (PixelPacket *)AcquireImagePixels(rd->image, 0, 0, rd->image->columns, rd->image->rows, &rd->exception);
183   if (unlikely(!src))
184     {
185       IMAGE_ERROR(io->context, IMAGE_ERROR_READ_FAILED, "Cannot acquire image pixels.");
186       libmagick_destroy_read_data(rd);
187       image_io_read_data_break(&rdi, io);
188       return 0;
189     }
190
191   /* Convert pixels */
192   switch (rdi.image->pixel_size)
193     {
194       case 1:
195 #       define IMAGE_WALK_PREFIX(x) walk_##x
196 #       define IMAGE_WALK_INLINE
197 #       define IMAGE_WALK_IMAGE (rdi.image)
198 #       define IMAGE_WALK_UNROLL 4
199 #       define IMAGE_WALK_COL_STEP 1
200 #       define IMAGE_WALK_DO_STEP do{ \
201           walk_pos[0] = libmagick_pixel_to_gray(src); \
202           src++; }while(0)
203 #       include "images/image-walk.h"
204         break;
205
206       case 2:
207 #       define IMAGE_WALK_PREFIX(x) walk_##x
208 #       define IMAGE_WALK_INLINE
209 #       define IMAGE_WALK_IMAGE (rdi.image)
210 #       define IMAGE_WALK_UNROLL 4
211 #       define IMAGE_WALK_COL_STEP 2
212 #       define IMAGE_WALK_DO_STEP do{ \
213           walk_pos[0] = libmagick_pixel_to_gray(src); \
214           walk_pos[1] = ALPHA_TO_BYTE(src->opacity); \
215           src++; }while(0)
216 #       include "images/image-walk.h"
217         break;
218
219       case 3:
220 #       define IMAGE_WALK_PREFIX(x) walk_##x
221 #       define IMAGE_WALK_INLINE
222 #       define IMAGE_WALK_IMAGE (rdi.image)
223 #       define IMAGE_WALK_UNROLL 4
224 #       define IMAGE_WALK_COL_STEP 3
225 #       define IMAGE_WALK_DO_STEP do{ \
226           walk_pos[0] = QUANTUM_TO_BYTE(src->red); \
227           walk_pos[1] = QUANTUM_TO_BYTE(src->green); \
228           walk_pos[2] = QUANTUM_TO_BYTE(src->blue); \
229           src++; }while(0)
230 #       include "images/image-walk.h"
231         break;
232
233       case 4:
234 #       define IMAGE_WALK_PREFIX(x) walk_##x
235 #       define IMAGE_WALK_INLINE
236 #       define IMAGE_WALK_IMAGE (rdi.image)
237 #       define IMAGE_WALK_UNROLL 4
238 #       define IMAGE_WALK_COL_STEP 4
239 #       define IMAGE_WALK_DO_STEP do{ \
240           walk_pos[0] = QUANTUM_TO_BYTE(src->red); \
241           walk_pos[1] = QUANTUM_TO_BYTE(src->green); \
242           walk_pos[2] = QUANTUM_TO_BYTE(src->blue); \
243           walk_pos[3] = ALPHA_TO_BYTE(src->opacity); \
244           src++; }while(0)
245 #       include "images/image-walk.h"
246         break;
247
248       default:
249         ASSERT(0);
250     }
251
252   /* Free GraphicsMagick structures */
253   libmagick_destroy_read_data(rd);
254
255   /* Finish the image */
256   return image_io_read_data_finish(&rdi, io);
257 }
258
259 int
260 libmagick_write(struct image_io *io)
261 {
262   DBG("libmagick_write()");
263
264   /* Initialize GraphicsMagick */
265   int result = 0;
266   ExceptionInfo exception;
267   ImageInfo *info;
268   GetExceptionInfo(&exception);
269   info = CloneImageInfo(NULL);
270
271   /* Setup image parameters and allocate the image*/
272   struct image *img = io->image;
273   switch (img->flags & IMAGE_COLOR_SPACE)
274     {
275       case COLOR_SPACE_GRAYSCALE:
276         info->colorspace = GRAYColorspace;
277         break;
278       case COLOR_SPACE_RGB:
279         info->colorspace = RGBColorspace;
280         break;
281       default:
282         ASSERT(0);
283     }
284   switch (io->format)
285     {
286       case IMAGE_FORMAT_JPEG:
287         strcpy(info->magick, "JPEG");
288         if (io->jpeg_quality)
289           info->quality = MIN(io->jpeg_quality, 100);
290         break;
291       case IMAGE_FORMAT_PNG:
292         strcpy(info->magick, "PNG");
293         break;
294       case IMAGE_FORMAT_GIF:
295         strcpy(info->magick, "GIF");
296         break;
297       default:
298         ASSERT(0);
299     }
300   Image *image = AllocateImage(info);
301   if (unlikely(!image))
302     {
303       IMAGE_ERROR(io->context, IMAGE_ERROR_WRITE_FAILED, "GraphicsMagick failed to allocate the image.");
304       goto err;
305     }
306   image->columns = img->cols;
307   image->rows = img->rows;
308
309   /* Get pixels */
310   PixelPacket *pixels = SetImagePixels(image, 0, 0, img->cols, img->rows), *dest = pixels;
311   if (unlikely(!pixels))
312     {
313       IMAGE_ERROR(io->context, IMAGE_ERROR_WRITE_FAILED, "Cannot get GraphicsMagick pixels.");
314       goto err2;
315     }
316
317   /* Convert pixels */
318   switch (img->pixel_size)
319     {
320       case 1:
321 #       define IMAGE_WALK_PREFIX(x) walk_##x
322 #       define IMAGE_WALK_INLINE
323 #       define IMAGE_WALK_IMAGE img
324 #       define IMAGE_WALK_UNROLL 4
325 #       define IMAGE_WALK_COL_STEP 1
326 #       define IMAGE_WALK_DO_STEP do{ \
327           dest->red = BYTE_TO_QUANTUM(walk_pos[0]); \
328           dest->green = BYTE_TO_QUANTUM(walk_pos[0]); \
329           dest->blue = BYTE_TO_QUANTUM(walk_pos[0]); \
330           dest->opacity = 0; \
331           dest++; }while(0)
332 #       include "images/image-walk.h"
333         break;
334
335       case 2:
336 #       define IMAGE_WALK_PREFIX(x) walk_##x
337 #       define IMAGE_WALK_INLINE
338 #       define IMAGE_WALK_IMAGE img
339 #       define IMAGE_WALK_UNROLL 4
340 #       define IMAGE_WALK_COL_STEP 2
341 #       define IMAGE_WALK_DO_STEP do{ \
342           dest->red = BYTE_TO_QUANTUM(walk_pos[0]); \
343           dest->green = BYTE_TO_QUANTUM(walk_pos[0]); \
344           dest->blue = BYTE_TO_QUANTUM(walk_pos[0]); \
345           dest->opacity = BYTE_TO_ALPHA(walk_pos[1]); \
346           dest++; }while(0)
347 #       include "images/image-walk.h"
348         break;
349
350       case 3:
351 #       define IMAGE_WALK_PREFIX(x) walk_##x
352 #       define IMAGE_WALK_INLINE
353 #       define IMAGE_WALK_IMAGE img
354 #       define IMAGE_WALK_UNROLL 4
355 #       define IMAGE_WALK_COL_STEP 3
356 #       define IMAGE_WALK_DO_STEP do{ \
357           dest->red = BYTE_TO_QUANTUM(walk_pos[0]); \
358           dest->green = BYTE_TO_QUANTUM(walk_pos[1]); \
359           dest->blue = BYTE_TO_QUANTUM(walk_pos[2]); \
360           dest->opacity = 0; \
361           dest++; }while(0)
362 #       include "images/image-walk.h"
363         break;
364
365       case 4:
366 #       define IMAGE_WALK_PREFIX(x) walk_##x
367 #       define IMAGE_WALK_INLINE
368 #       define IMAGE_WALK_IMAGE img
369 #       define IMAGE_WALK_UNROLL 4
370 #       define IMAGE_WALK_COL_STEP 4
371 #       define IMAGE_WALK_DO_STEP do{ \
372           dest->red = BYTE_TO_QUANTUM(walk_pos[0]); \
373           dest->green = BYTE_TO_QUANTUM(walk_pos[1]); \
374           dest->blue = BYTE_TO_QUANTUM(walk_pos[2]); \
375           dest->opacity = BYTE_TO_ALPHA(walk_pos[3]); \
376           dest++; }while(0)
377 #       include "images/image-walk.h"
378         break;
379
380       default:
381         ASSERT(0);
382     }
383
384   /* Store pixels */
385   if (unlikely(!SyncImagePixels(image)))
386     {
387       IMAGE_ERROR(io->context, IMAGE_ERROR_WRITE_FAILED, "Cannot sync GraphicsMagick pixels.");
388       goto err2;
389     }
390
391   /* Write image */
392   size_t buf_len = 0;
393   void *buf = ImageToBlob(info, image, &buf_len, &exception);
394   if (unlikely(!buf))
395     {
396       IMAGE_ERROR(io->context, IMAGE_ERROR_WRITE_FAILED, "GraphicsMagick failed to compress the image.");
397       goto err2;
398     }
399   if (unlikely(buf_len > MAX_FILE_SIZE))
400     {
401       IMAGE_ERROR(io->context, IMAGE_ERROR_WRITE_FAILED, "Image too large.");
402       goto err2;
403     }
404
405   /* Write to stream */
406   bwrite(io->fastbuf, buf, buf_len);
407
408   /* Success */
409   result = 1;
410
411 err2:
412   DestroyImage(image);
413 err:
414   DestroyImageInfo(info);
415   DestroyExceptionInfo(&exception);
416   return result;
417 }