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