]> mj.ucw.cz Git - libucw.git/blob - images/io-libpng.c
Exampled moved to a top-level directory
[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 <ucw/lib.h>
13 #include <ucw/mempool.h>
14 #include <ucw/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   struct image_io_read_data_internals rdi;
192   rdi.image = NULL;
193
194   if (setjmp(png_jmpbuf(rd->png_ptr)))
195     {
196       DBG("Libpng failed to read the image, longjump saved us");
197       png_destroy_read_struct(&rd->png_ptr, &rd->info_ptr, &rd->end_ptr);
198       if (rdi.image)
199         image_io_read_data_break(&rdi, io);
200       return 0;
201     }
202
203   uns read_flags = io->flags;
204
205   /* Apply transformations */
206   if (rd->bit_depth == 16)
207     png_set_strip_16(rd->png_ptr);
208   switch (rd->color_type)
209     {
210       case PNG_COLOR_TYPE_PALETTE:
211         if ((read_flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE)
212           {
213             png_set_palette_to_rgb(rd->png_ptr);
214             png_set_rgb_to_gray_fixed(rd->png_ptr, 1, 21267, 71514);
215           }
216         else
217           {
218             png_set_palette_to_rgb(rd->png_ptr);
219             read_flags = (read_flags & ~IMAGE_COLOR_SPACE & IMAGE_CHANNELS_FORMAT) | COLOR_SPACE_RGB;
220           }
221         if (!(read_flags & IMAGE_ALPHA))
222           {
223             if (io->flags & IMAGE_IO_USE_BACKGROUND)
224               {
225                 png_set_add_alpha(rd->png_ptr, 255, PNG_FILLER_AFTER);
226                 read_flags = (read_flags & IMAGE_CHANNELS_FORMAT) | IMAGE_ALPHA;
227               }
228             else if ((read_flags & IMAGE_PIXEL_FORMAT) == (COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED))
229               png_set_add_alpha(rd->png_ptr, 255, PNG_FILLER_AFTER);
230             else
231               png_set_strip_alpha(rd->png_ptr);
232           }
233         else
234           png_set_add_alpha(rd->png_ptr, 255, PNG_FILLER_AFTER);
235         break;
236       case PNG_COLOR_TYPE_GRAY:
237         if ((read_flags & IMAGE_COLOR_SPACE) != COLOR_SPACE_GRAYSCALE)
238           {
239             png_set_gray_to_rgb(rd->png_ptr);
240             read_flags = (read_flags & ~IMAGE_COLOR_SPACE & IMAGE_CHANNELS_FORMAT) | COLOR_SPACE_RGB;
241           }
242         if (read_flags & IMAGE_ALPHA)
243           png_set_add_alpha(rd->png_ptr, 255, PNG_FILLER_AFTER);
244         break;
245       case PNG_COLOR_TYPE_GRAY_ALPHA:
246         if ((read_flags & IMAGE_COLOR_SPACE) != COLOR_SPACE_GRAYSCALE)
247           {
248             png_set_gray_to_rgb(rd->png_ptr);
249             read_flags = (read_flags & ~IMAGE_COLOR_SPACE & IMAGE_CHANNELS_FORMAT) | COLOR_SPACE_RGB;
250           }
251         if (!(read_flags & IMAGE_ALPHA))
252           {
253             if (io->flags & IMAGE_IO_USE_BACKGROUND)
254               read_flags = (read_flags & IMAGE_CHANNELS_FORMAT) | IMAGE_ALPHA;
255             else
256               png_set_strip_alpha(rd->png_ptr);
257           }
258         break;
259       case PNG_COLOR_TYPE_RGB:
260         if ((read_flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE)
261           png_set_rgb_to_gray_fixed(rd->png_ptr, 1, 21267, 71514);
262         else
263           read_flags = (read_flags & ~IMAGE_COLOR_SPACE & IMAGE_CHANNELS_FORMAT) | COLOR_SPACE_RGB;
264         if ((read_flags & IMAGE_ALPHA) || (read_flags & IMAGE_PIXEL_FORMAT) == (COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED))
265           png_set_add_alpha(rd->png_ptr, 255, PNG_FILLER_AFTER);
266         break;
267       case PNG_COLOR_TYPE_RGB_ALPHA:
268         if ((read_flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE)
269           png_set_rgb_to_gray_fixed(rd->png_ptr, 1, 21267, 71514);
270         else
271           read_flags = (read_flags & ~IMAGE_COLOR_SPACE & IMAGE_CHANNELS_FORMAT) | COLOR_SPACE_RGB;
272         if (!(read_flags & IMAGE_ALPHA))
273           if (io->flags & IMAGE_IO_USE_BACKGROUND)
274             read_flags = (read_flags & IMAGE_CHANNELS_FORMAT) | IMAGE_ALPHA;
275           else if ((read_flags & IMAGE_PIXEL_FORMAT) != (COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED))
276             png_set_strip_alpha(rd->png_ptr);
277         break;
278       default:
279         ASSERT(0);
280     }
281   png_read_update_info(rd->png_ptr, rd->info_ptr);
282
283   /* Prepare the image */
284   if (unlikely(!image_io_read_data_prepare(&rdi, io, rd->cols, rd->rows, read_flags)))
285     {
286       png_destroy_read_struct(&rd->png_ptr, &rd->info_ptr, &rd->end_ptr);
287       return 0;
288     }
289   /* Read image data */
290   DBG("Reading image data");
291   struct image *img = rdi.image;
292   byte *pixels = img->pixels;
293   png_bytep rows[img->rows];
294   for (uns r = 0; r < img->rows; r++, pixels += img->row_size)
295     rows[r] = (png_bytep)pixels;
296   png_read_image(rd->png_ptr, rows);
297   png_read_end(rd->png_ptr, rd->end_ptr);
298
299   /* Destroy libpng read structure */
300   png_destroy_read_struct(&rd->png_ptr, &rd->info_ptr, &rd->end_ptr);
301
302   /* Finish the image  */
303   return image_io_read_data_finish(&rdi, io);
304 }
305
306 int
307 libpng_write(struct image_io *io)
308 {
309   DBG("libpng_write()");
310
311   /* Create libpng structures */
312   png_structp png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING,
313       io->context, libpng_write_error, libpng_warning,
314       io->internal_pool, libpng_malloc, libpng_free);
315   if (unlikely(!png_ptr))
316     {
317       IMAGE_ERROR(io->context, IMAGE_ERROR_WRITE_FAILED, "Cannot create libpng write structure.");
318       return 0;
319     }
320   png_infop info_ptr = png_create_info_struct(png_ptr);
321   if (unlikely(!info_ptr))
322     {
323       IMAGE_ERROR(io->context, IMAGE_ERROR_WRITE_FAILED, "Cannot create libpng info structure.");
324       png_destroy_write_struct(&png_ptr, NULL);
325       return 0;
326     }
327
328   /* Setup libpng longjump */
329   if (unlikely(setjmp(png_jmpbuf(png_ptr))))
330     {
331       DBG("Libpng failed to write the image, longjump saved us.");
332       png_destroy_write_struct(&png_ptr, &info_ptr);
333       return 0;
334     }
335
336   /* Setup libpng IO */
337   png_set_write_fn(png_ptr, io->fastbuf, libpng_write_fn, libpng_flush_fn);
338
339   /* Setup PNG parameters */
340   struct image *img = io->image;
341   switch (img->flags & IMAGE_PIXEL_FORMAT)
342     {
343       case COLOR_SPACE_GRAYSCALE | IMAGE_PIXELS_ALIGNED:
344         png_set_IHDR(png_ptr, info_ptr, img->cols, img->rows, 8, PNG_COLOR_TYPE_GRAY,
345           PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
346         break;
347       case COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA | IMAGE_PIXELS_ALIGNED:
348         png_set_IHDR(png_ptr, info_ptr, img->cols, img->rows, 8, PNG_COLOR_TYPE_GRAY_ALPHA,
349           PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
350         break;
351       case COLOR_SPACE_RGB:
352         png_set_IHDR(png_ptr, info_ptr, img->cols, img->rows, 8, PNG_COLOR_TYPE_RGB,
353           PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
354         break;
355       case COLOR_SPACE_RGB | IMAGE_ALPHA | IMAGE_PIXELS_ALIGNED:
356         png_set_IHDR(png_ptr, info_ptr, img->cols, img->rows, 8, PNG_COLOR_TYPE_RGB_ALPHA,
357           PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
358         break;
359       case COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED:
360         png_set_IHDR(png_ptr, info_ptr, img->cols, img->rows, 8, PNG_COLOR_TYPE_RGB,
361           PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
362         png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
363         break;
364       default:
365         IMAGE_ERROR(io->context, IMAGE_ERROR_WRITE_FAILED, "Libpng does not support this pixel format (0x%x)", img->flags & IMAGE_PIXEL_FORMAT);
366         png_destroy_write_struct(&png_ptr, &info_ptr);
367         return 0;
368     }
369   png_write_info(png_ptr, info_ptr);
370
371   /* Write pixels */
372   byte *pixels = img->pixels;
373   png_bytep rows[img->rows];
374   for (uns r = 0; r < img->rows; r++, pixels += img->row_size)
375     rows[r] = (png_bytep)pixels;
376   png_write_image(png_ptr, rows);
377   png_write_end(png_ptr, info_ptr);
378
379   /* Free libpng structure */
380   png_destroy_write_struct(&png_ptr, &info_ptr);
381   return 1;
382 }