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