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