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