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