]> mj.ucw.cz Git - libucw.git/blob - images/io-libpng.c
cleaning image flags manipulation
[libucw.git] / images / io-libpng.c
1 /*
2  *      Image Library -- libpng
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 <png.h>
18 #include <setjmp.h>
19
20 struct libpng_read_data {
21   png_structp png_ptr;
22   png_infop info_ptr;
23   png_infop end_ptr;
24   png_uint_32 cols;
25   png_uint_32 rows;
26   int bit_depth;
27   int color_type;
28 };
29
30 static png_voidp
31 libpng_malloc(png_structp png_ptr, png_size_t size)
32 {
33   DBG("libpng_malloc(size=%u)", (uns)size);
34   return mp_alloc(png_get_mem_ptr(png_ptr), size);
35 }
36
37 static void
38 libpng_free(png_structp png_ptr UNUSED, png_voidp ptr UNUSED)
39 {
40   DBG("libpng_free()");
41 }
42
43 static void NONRET
44 libpng_read_error(png_structp png_ptr, png_const_charp msg)
45 {
46   DBG("libpng_read_error()");
47   image_thread_err_dup(png_get_error_ptr(png_ptr), IMAGE_ERR_READ_FAILED, (byte *)msg);
48   longjmp(png_jmpbuf(png_ptr), 1);
49 }
50
51 static void NONRET
52 libpng_write_error(png_structp png_ptr, png_const_charp msg)
53 {
54   DBG("libpng_write_error()");
55   image_thread_err_dup(png_get_error_ptr(png_ptr), IMAGE_ERR_WRITE_FAILED, (byte *)msg);
56   longjmp(png_jmpbuf(png_ptr), 1);
57 }
58
59 static void
60 libpng_warning(png_structp png_ptr UNUSED, png_const_charp msg UNUSED)
61 {
62   DBG("libpng_warning(): %s", (byte *)msg);
63 }
64
65 static void
66 libpng_read_fn(png_structp png_ptr, png_bytep data, png_size_t length)
67 {
68   DBG("libpng_read_fn(len=%u)", (uns)length);
69   if (unlikely(bread((struct fastbuf *)png_get_io_ptr(png_ptr), (byte *)data, length) < length))
70     png_error(png_ptr, "Incomplete data");
71 }
72
73 static void
74 libpng_write_fn(png_structp png_ptr, png_bytep data, png_size_t length)
75 {
76   DBG("libpng_write_fn(len=%u)", (uns)length);
77   bwrite((struct fastbuf *)png_get_io_ptr(png_ptr), (byte *)data, length);
78 }
79
80 static void
81 libpng_flush_fn(png_structp png_ptr UNUSED)
82 {
83   DBG("libpng_flush_fn()");
84 }
85
86 static void
87 libpng_read_cancel(struct image_io *io)
88 {
89   DBG("libpng_read_cancel()");
90
91   struct libpng_read_data *rd = io->read_data;
92   png_destroy_read_struct(&rd->png_ptr, &rd->info_ptr, &rd->end_ptr);
93 }
94
95 int
96 libpng_read_header(struct image_io *io)
97 {
98   DBG("libpng_read_header()");
99
100   /* Create libpng structures */
101   struct libpng_read_data *rd = io->read_data = mp_alloc(io->internal_pool, sizeof(*rd));
102   rd->png_ptr = png_create_read_struct_2(PNG_LIBPNG_VER_STRING,
103       io->thread, libpng_read_error, libpng_warning,
104       io->internal_pool, libpng_malloc, libpng_free);
105   if (unlikely(!rd->png_ptr))
106     {
107       image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Cannot create libpng read structure.");
108       return 0;
109     }
110   rd->info_ptr = png_create_info_struct(rd->png_ptr);
111   if (unlikely(!rd->info_ptr))
112     {
113       image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Cannot create libpng info structure.");
114       png_destroy_read_struct(&rd->png_ptr, NULL, NULL);
115       return 0;
116     }
117   rd->end_ptr = png_create_info_struct(rd->png_ptr);
118   if (unlikely(!rd->end_ptr))
119     {
120       image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Cannot create libpng info structure.");
121       png_destroy_read_struct(&rd->png_ptr, &rd->info_ptr, NULL);
122       return 0;
123     }
124
125   /* Setup libpng longjump */
126   if (unlikely(setjmp(png_jmpbuf(rd->png_ptr))))
127     {
128       DBG("Libpng failed to read the image, longjump saved us");
129       png_destroy_read_struct(&rd->png_ptr, &rd->info_ptr, &rd->end_ptr);
130       return 0;
131     }
132
133   /* Setup libpng IO */
134   png_set_read_fn(rd->png_ptr, io->fastbuf, libpng_read_fn);
135   png_set_user_limits(rd->png_ptr, IMAGE_MAX_SIZE, IMAGE_MAX_SIZE);
136
137   /* Read header */
138   png_read_info(rd->png_ptr, rd->info_ptr);
139   png_get_IHDR(rd->png_ptr, rd->info_ptr, &rd->cols, &rd->rows, &rd->bit_depth, &rd->color_type, NULL, NULL, NULL);
140
141   /* Fill image_io values */
142   io->cols = rd->cols;
143   io->rows = rd->rows;
144   switch (rd->color_type)
145     {
146       case PNG_COLOR_TYPE_GRAY:
147         io->flags = COLOR_SPACE_GRAYSCALE;
148         io->number_of_colors = 1 << 8;
149         break;
150       case PNG_COLOR_TYPE_GRAY_ALPHA:
151         io->flags = COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA;
152         io->number_of_colors = 1 << 8;
153         break;
154       case PNG_COLOR_TYPE_RGB:
155         io->flags = COLOR_SPACE_RGB;
156         io->number_of_colors = 1 << 24;
157         break;
158       case PNG_COLOR_TYPE_RGB_ALPHA:
159         io->number_of_colors = 1 << 24;
160         io->flags = COLOR_SPACE_RGB | IMAGE_ALPHA;
161         break;
162       case PNG_COLOR_TYPE_PALETTE:
163         io->flags = COLOR_SPACE_RGB | IMAGE_ALPHA | IMAGE_IO_HAS_PALETTE;
164         int num_palette;
165         if (png_get_PLTE(rd->png_ptr, rd->info_ptr, NULL, &num_palette))
166           io->number_of_colors = num_palette;
167         else
168           io->number_of_colors = 1 << rd->bit_depth;
169         break;
170       default:
171         png_destroy_read_struct(&rd->png_ptr, &rd->info_ptr, &rd->end_ptr);
172         image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Unknown color type");
173         break;
174     }
175
176   /* Success */
177   io->read_cancel = libpng_read_cancel;
178   return 1;
179 }
180
181 int
182 libpng_read_data(struct image_io *io)
183 {
184   DBG("libpng_read_data()");
185
186   struct libpng_read_data *rd = io->read_data;
187
188   /* Test supported pixel formats */
189   switch (io->flags & IMAGE_COLOR_SPACE)
190     {
191       case COLOR_SPACE_GRAYSCALE:
192       case COLOR_SPACE_RGB:
193         break;
194       default:
195         png_destroy_read_struct(&rd->png_ptr, &rd->info_ptr, &rd->end_ptr);
196         image_thread_err(io->thread, IMAGE_ERR_INVALID_PIXEL_FORMAT, "Unsupported color space.");
197         return 0;
198     }
199
200   struct image_io_read_data_internals rdi;
201   rdi.image = NULL;
202
203   if (setjmp(png_jmpbuf(rd->png_ptr)))
204     {
205       DBG("Libpng failed to read the image, longjump saved us");
206       png_destroy_read_struct(&rd->png_ptr, &rd->info_ptr, &rd->end_ptr);
207       if (rdi.image)
208         image_io_read_data_break(&rdi, io);
209       return 0;
210     }
211
212   uns read_flags = io->flags;
213   
214   /* Apply transformations */
215   if (rd->bit_depth == 16)
216     png_set_strip_16(rd->png_ptr);
217   switch (rd->color_type)
218     {
219       case PNG_COLOR_TYPE_PALETTE:
220         if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE)
221           {
222             png_set_palette_to_rgb(rd->png_ptr);
223             png_set_rgb_to_gray_fixed(rd->png_ptr, 1, 21267, 71514);
224           }
225         else
226           png_set_palette_to_rgb(rd->png_ptr);
227         if (!(io->flags & IMAGE_ALPHA))
228           {
229             if (io->flags & IMAGE_IO_USE_BACKGROUND)
230               {
231                 png_set_add_alpha(rd->png_ptr, 255, PNG_FILLER_AFTER);
232                 read_flags = (read_flags | IMAGE_ALPHA) & IMAGE_CHANNELS_FORMAT;
233               }
234             else if ((io->flags & IMAGE_PIXEL_FORMAT) == (COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED))
235               png_set_add_alpha(rd->png_ptr, 255, PNG_FILLER_AFTER);
236             else
237               png_set_strip_alpha(rd->png_ptr);
238           }
239         else
240           png_set_add_alpha(rd->png_ptr, 255, PNG_FILLER_AFTER);
241         break;
242       case PNG_COLOR_TYPE_GRAY:
243         if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_RGB)
244           png_set_gray_to_rgb(rd->png_ptr);
245         if (io->flags & IMAGE_ALPHA)
246           png_set_add_alpha(rd->png_ptr, 255, PNG_FILLER_AFTER);
247         break;
248       case PNG_COLOR_TYPE_GRAY_ALPHA:
249         if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_RGB)
250           png_set_gray_to_rgb(rd->png_ptr);
251         if (!(io->flags & IMAGE_ALPHA))
252           {
253             if (io->flags & IMAGE_IO_USE_BACKGROUND)
254               read_flags = (read_flags | IMAGE_ALPHA) & IMAGE_CHANNELS_FORMAT;
255             else
256               png_set_strip_alpha(rd->png_ptr);
257           }  
258         break;
259       case PNG_COLOR_TYPE_RGB:
260         if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE)
261           png_set_rgb_to_gray_fixed(rd->png_ptr, 1, 21267, 71514);
262         if ((io->flags & IMAGE_ALPHA) || (io->flags & IMAGE_PIXEL_FORMAT) == (COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED))
263           png_set_add_alpha(rd->png_ptr, 255, PNG_FILLER_AFTER);
264         break;
265       case PNG_COLOR_TYPE_RGB_ALPHA:
266         if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE)
267           png_set_rgb_to_gray_fixed(rd->png_ptr, 1, 21267, 71514);
268         if (!(io->flags & IMAGE_ALPHA))
269           if (io->flags & IMAGE_IO_USE_BACKGROUND)
270             read_flags = (read_flags | IMAGE_ALPHA) & IMAGE_CHANNELS_FORMAT;
271           else if ((io->flags & IMAGE_PIXEL_FORMAT) != (COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED))
272             png_set_strip_alpha(rd->png_ptr);
273         break;
274       default:
275         ASSERT(0);
276     }
277   png_read_update_info(rd->png_ptr, rd->info_ptr);
278
279   /* Prepare the image */
280   if (unlikely(!image_io_read_data_prepare(&rdi, io, rd->cols, rd->rows, read_flags)))
281     {
282       png_destroy_read_struct(&rd->png_ptr, &rd->info_ptr, &rd->end_ptr);
283       return 0;
284     }
285   /* Read image data */
286   DBG("Reading image data");
287   struct image *img = rdi.image;
288   byte *pixels = img->pixels;
289   png_bytep rows[img->rows];
290   for (uns r = 0; r < img->rows; r++, pixels += img->row_size)
291     rows[r] = (png_bytep)pixels;
292   png_read_image(rd->png_ptr, rows);
293   png_read_end(rd->png_ptr, rd->end_ptr);
294
295   /* Destroy libpng read structure */
296   png_destroy_read_struct(&rd->png_ptr, &rd->info_ptr, &rd->end_ptr);
297
298   /* Finish the image  */
299   return image_io_read_data_finish(&rdi, io);
300 }
301
302 int
303 libpng_write(struct image_io *io)
304 {
305   DBG("libpng_write()");
306
307   /* Create libpng structures */
308   png_structp png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING,
309       io->thread, libpng_write_error, libpng_warning,
310       io->internal_pool, libpng_malloc, libpng_free);
311   if (unlikely(!png_ptr))
312     {
313       image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "Cannot create libpng write structure.");
314       return 0;
315     }
316   png_infop info_ptr = png_create_info_struct(png_ptr);
317   if (unlikely(!info_ptr))
318     {
319       image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "Cannot create libpng info structure.");
320       png_destroy_write_struct(&png_ptr, NULL);
321       return 0;
322     }
323
324   /* Setup libpng longjump */
325   if (unlikely(setjmp(png_jmpbuf(png_ptr))))
326     {
327       DBG("Libpng failed to write the image, longjump saved us.");
328       png_destroy_write_struct(&png_ptr, &info_ptr);
329       return 0;
330     }
331
332   /* Setup libpng IO */
333   png_set_write_fn(png_ptr, io->fastbuf, libpng_write_fn, libpng_flush_fn);
334
335   /* Setup PNG parameters */
336   struct image *img = io->image;
337   switch (img->flags & IMAGE_PIXEL_FORMAT)
338     {
339       case COLOR_SPACE_GRAYSCALE | IMAGE_PIXELS_ALIGNED:
340         png_set_IHDR(png_ptr, info_ptr, img->cols, img->rows, 8, PNG_COLOR_TYPE_GRAY,
341           PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
342         break;
343       case COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA | IMAGE_PIXELS_ALIGNED:
344         png_set_IHDR(png_ptr, info_ptr, img->cols, img->rows, 8, PNG_COLOR_TYPE_GRAY_ALPHA,
345           PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
346         break;
347       case COLOR_SPACE_RGB:
348         png_set_IHDR(png_ptr, info_ptr, img->cols, img->rows, 8, PNG_COLOR_TYPE_RGB,
349           PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
350         break;
351       case COLOR_SPACE_RGB | IMAGE_ALPHA | IMAGE_PIXELS_ALIGNED:
352         png_set_IHDR(png_ptr, info_ptr, img->cols, img->rows, 8, PNG_COLOR_TYPE_RGB_ALPHA,
353           PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
354         break;
355       case COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED:
356         png_set_IHDR(png_ptr, info_ptr, img->cols, img->rows, 8, PNG_COLOR_TYPE_RGB,
357           PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
358         png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
359         break;
360       default:
361         ASSERT(0);
362     }
363   png_write_info(png_ptr, info_ptr);
364
365   /* Write pixels */
366   byte *pixels = img->pixels;
367   png_bytep rows[img->rows];
368   for (uns r = 0; r < img->rows; r++, pixels += img->row_size)
369     rows[r] = (png_bytep)pixels;
370   png_write_image(png_ptr, rows);
371   png_write_end(png_ptr, info_ptr);
372
373   /* Free libpng structure */
374   png_destroy_write_struct(&png_ptr, &info_ptr);
375   return 1;
376 }