]> mj.ucw.cz Git - libucw.git/blob - images/io-libmagick.c
7ec379309e7c1f3bde917f0b8d9f217e77382136
[libucw.git] / images / io-libmagick.c
1 /*
2  *      Image Library -- GraphicsMagick
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 #define 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
25 struct magick_read_data {
26   ExceptionInfo exception;
27   ImageInfo *info;
28   Image *image;
29 };
30
31 static inline void
32 libmagick_destroy_read_data(struct magick_read_data *rd)
33 {
34   if (rd->image)
35     DestroyImage(rd->image);
36   DestroyImageInfo(rd->info);
37   DestroyExceptionInfo(&rd->exception);
38   DestroyMagick();
39 }
40
41 static void
42 libmagick_read_cancel(struct image_io *io)
43 {
44   DBG("libmagick_read_cancel()");
45
46   struct magick_read_data *rd = io->read_data;
47
48   DestroyImage(rd->image);
49   libmagick_destroy_read_data(rd);
50 }
51
52 int
53 libmagick_read_header(struct image_io *io)
54 {
55   DBG("libmagick_read_header()");
56
57   /* Read entire stream */
58   sh_off_t file_size = bfilesize(io->fastbuf) - btell(io->fastbuf);
59   if (file_size > MAX_FILE_SIZE)
60     {
61       image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Too long stream.");
62       return 0;
63     }
64   uns buf_size = file_size;
65   byte *buf = xmalloc(buf_size);
66   bread(io->fastbuf, buf, buf_size);
67
68   /* Allocate read structure */
69   struct magick_read_data *rd = io->read_data = mp_alloc(io->internal_pool, sizeof(*rd));
70
71   /* Initialize GraphicsMagick */
72   InitializeMagick(NULL);
73   GetExceptionInfo(&rd->exception);
74   rd->info = CloneImageInfo(NULL);
75   rd->info->subrange = 1;
76
77   /* Read the image */
78   rd->image = BlobToImage(rd->info, buf, buf_size, &rd->exception);
79   xfree(buf);
80   if (!rd->image)
81     {
82       image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "GraphicsMagick failed to read the image.");
83       goto err;
84     }
85   if (rd->image->columns > IMAGE_MAX_SIZE || rd->image->rows > IMAGE_MAX_SIZE)
86     {
87       image_thread_err(io->thread, IMAGE_ERR_INVALID_DIMENSIONS, "Image too large.");
88       goto err;
89     }
90
91   /* Fill image parameters */
92   if (!io->cols)
93     io->cols = rd->image->columns;
94   if (!io->rows)
95     io->rows = rd->image->rows;
96   if (!(io->flags & IMAGE_CHANNELS_FORMAT))
97     {
98       switch (rd->image->colorspace)
99         {
100           case GRAYColorspace:
101             io->flags |= COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA;
102             break;
103           default:
104             io->flags |= COLOR_SPACE_RGB | IMAGE_ALPHA;
105             break;
106         }
107     }
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 (!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 (!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 (!img2)
222         goto err2;
223       int result = image_scale(io->thread, img2, img);
224       image_destroy(io->thread, img);
225       img = img2;
226       need_destroy = !io->pool;
227       if (!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(io->thread, img);
242   return 0;
243 }
244
245 int
246 libmagick_write(struct image_io *io)
247 {
248   image_thread_err(io->thread, IMAGE_ERR_NOT_IMPLEMENTED, "GraphicsMagick write not implemented.");
249   return 0;
250 }