]> mj.ucw.cz Git - libucw.git/blob - images/io-libmagick.c
added image compression via libpng
[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   bread(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   if (!io->cols)
95     io->cols = rd->image->columns;
96   if (!io->rows)
97     io->rows = rd->image->rows;
98   if (!(io->flags & IMAGE_CHANNELS_FORMAT))
99     {
100       switch (rd->image->colorspace)
101         {
102           case GRAYColorspace:
103             io->flags |= COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA;
104             break;
105           default:
106             io->flags |= COLOR_SPACE_RGB | IMAGE_ALPHA;
107             break;
108         }
109     }
110
111   io->read_cancel = libmagick_read_cancel;
112   return 1;
113
114 err:
115   libmagick_destroy_read_data(rd);
116   return 0;
117 }
118
119 static inline byte
120 libmagick_pixel_to_gray(PixelPacket *pixel)
121 {
122   return ((uns)pixel->red * 19660 + (uns)pixel->green * 38666 + (uns)pixel->blue * 7210) >> (16 + QUANTUM_SCALE);
123 }
124
125 int
126 libmagick_read_data(struct image_io *io)
127 {
128   DBG("libmagick_read_data()");
129
130   struct magick_read_data *rd = io->read_data;
131
132   /* Quantize image */
133   switch (rd->image->colorspace)
134     {
135       case RGBColorspace:
136       case GRAYColorspace:
137         break;
138       default: ;
139         QuantizeInfo quantize;
140         GetQuantizeInfo(&quantize);
141         quantize.colorspace = RGBColorspace;
142         QuantizeImage(&quantize, rd->image);
143         break;
144     }
145
146   /* Allocate image for conversion */
147   int need_scale = io->cols != rd->image->columns || io->rows != rd->image->rows;
148   int need_destroy = need_scale || !io->pool;
149   struct image *img = need_scale ?
150     image_new(io->thread, rd->image->columns, rd->image->rows, io->flags & IMAGE_CHANNELS_FORMAT, NULL) :
151     image_new(io->thread, io->cols, io->rows, io->flags, io->pool);
152   if (unlikely(!img))
153     goto err;
154
155   /* Acquire pixels */
156   PixelPacket *src = (PixelPacket *)AcquireImagePixels(rd->image, 0, 0, rd->image->columns, rd->image->rows, &rd->exception);
157   if (unlikely(!src))
158     {
159       image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Cannot acquire image pixels.");
160       goto err;
161     }
162
163   /* Convert pixels */
164   switch (img->pixel_size)
165     {
166       case 1:
167 #       define IMAGE_WALK_INLINE
168 #       define IMAGE_WALK_UNROLL 4
169 #       define IMAGE_WALK_COL_STEP 1
170 #       define IMAGE_WALK_DO_STEP do{ \
171           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_INLINE
178 #       define IMAGE_WALK_UNROLL 4
179 #       define IMAGE_WALK_COL_STEP 2
180 #       define IMAGE_WALK_DO_STEP do{ \
181           pos[0] = libmagick_pixel_to_gray(src); \
182           pos[1] = QUANTUM_TO_BYTE(src->opacity); \
183           src++; }while(0)
184 #       include "images/image-walk.h"
185         break;
186
187       case 3:
188 #       define IMAGE_WALK_INLINE
189 #       define IMAGE_WALK_UNROLL 4
190 #       define IMAGE_WALK_COL_STEP 3
191 #       define IMAGE_WALK_DO_STEP do{ \
192           pos[0] = QUANTUM_TO_BYTE(src->red); \
193           pos[1] = QUANTUM_TO_BYTE(src->green); \
194           pos[2] = QUANTUM_TO_BYTE(src->blue); \
195           src++; }while(0)
196 #       include "images/image-walk.h"
197         break;
198
199       case 4:
200 #       define IMAGE_WALK_INLINE
201 #       define IMAGE_WALK_UNROLL 4
202 #       define IMAGE_WALK_COL_STEP 4
203 #       define IMAGE_WALK_DO_STEP do{ \
204           pos[0] = QUANTUM_TO_BYTE(src->red); \
205           pos[1] = QUANTUM_TO_BYTE(src->green); \
206           pos[2] = QUANTUM_TO_BYTE(src->blue); \
207           pos[3] = QUANTUM_TO_BYTE(src->opacity); \
208           src++; }while(0)
209 #       include "images/image-walk.h"
210         break;
211
212       default:
213         ASSERT(0);
214     }
215
216   /* Free GraphicsMagick structures */
217   libmagick_destroy_read_data(rd);
218
219   /* Scale image */
220   if (need_scale)
221     {
222       struct image *img2 = image_new(io->thread, io->cols, io->rows, io->flags, io->pool);
223       if (unlikely(!img2))
224         goto err2;
225       int result = image_scale(io->thread, img2, img);
226       image_destroy(io->thread, img);
227       img = img2;
228       need_destroy = !io->pool;
229       if (unlikely(!result))
230         goto err2;
231     }
232
233   /* Success */
234   io->image = img;
235   io->image_destroy = need_destroy;
236   return 1;
237
238   /* Free structures */
239 err:
240   libmagick_destroy_read_data(rd);
241 err2:
242   if (need_destroy)
243     image_destroy(io->thread, img);
244   return 0;
245 }
246
247 int
248 libmagick_write(struct image_io *io)
249 {
250   DBG("libmagick_write()");
251
252   /* Initialize GraphicsMagick */
253   int result = 0;
254   ExceptionInfo exception;
255   ImageInfo *info;
256   InitializeMagick(NULL);
257   GetExceptionInfo(&exception);
258   info = CloneImageInfo(NULL);
259
260   /* Setup image parameters and allocate the image*/
261   struct image *img = io->image;
262   switch (img->flags & IMAGE_COLOR_SPACE)
263     {
264       case COLOR_SPACE_GRAYSCALE:
265         info->colorspace = GRAYColorspace;
266         break;
267       case COLOR_SPACE_RGB:
268         info->colorspace = RGBColorspace;
269         break;
270       default:
271         ASSERT(0);
272     }
273   switch (io->format)
274     {
275       case IMAGE_FORMAT_JPEG:
276         strcpy(info->magick, "JPEG");
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 }