]> mj.ucw.cz Git - libucw.git/blob - images/io-libpng.c
1e1704f8cef4547fe1b28766c4c9e5c716838117
[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) || (io->flags & IMAGE_PIXEL_FORMAT) == (COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED))
228           png_set_add_alpha(rd->png_ptr, 255, PNG_FILLER_AFTER);
229         else
230           png_set_strip_alpha(rd->png_ptr);
231         break;
232       case PNG_COLOR_TYPE_GRAY:
233         if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_RGB)
234           png_set_gray_to_rgb(rd->png_ptr);
235         if (io->flags & IMAGE_ALPHA)
236           png_set_add_alpha(rd->png_ptr, 255, PNG_FILLER_AFTER);
237         break;
238       case PNG_COLOR_TYPE_GRAY_ALPHA:
239         if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_RGB)
240           png_set_gray_to_rgb(rd->png_ptr);
241         if (!(io->flags & IMAGE_ALPHA))
242           {
243             if (io->flags & IMAGE_IO_USE_BACKGROUND)
244               read_flags |= IMAGE_ALPHA;
245             else
246               png_set_strip_alpha(rd->png_ptr);
247           }  
248         break;
249       case PNG_COLOR_TYPE_RGB:
250         if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE)
251           png_set_rgb_to_gray_fixed(rd->png_ptr, 1, 21267, 71514);
252         if ((io->flags & IMAGE_ALPHA) || (io->flags & IMAGE_PIXEL_FORMAT) == (COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED))
253           png_set_add_alpha(rd->png_ptr, 255, PNG_FILLER_AFTER);
254         break;
255       case PNG_COLOR_TYPE_RGB_ALPHA:
256         if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE)
257           png_set_rgb_to_gray_fixed(rd->png_ptr, 1, 21267, 71514);
258         if (!(io->flags & IMAGE_ALPHA) && (io->flags & IMAGE_PIXEL_FORMAT) != (COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED))
259           {
260             if (io->flags & IMAGE_IO_USE_BACKGROUND)
261               read_flags |= IMAGE_ALPHA;
262             else
263               png_set_strip_alpha(rd->png_ptr);
264           }
265         break;
266       default:
267         ASSERT(0);
268     }
269   png_read_update_info(rd->png_ptr, rd->info_ptr);
270
271   /* Prepare the image */
272   if (unlikely(!image_io_read_data_prepare(&rdi, io, rd->cols, rd->rows, read_flags)))
273     {
274       png_destroy_read_struct(&rd->png_ptr, &rd->info_ptr, &rd->end_ptr);
275       return 0;
276     }
277   /* Read image data */
278   DBG("Reading image data");
279   struct image *img = rdi.image;
280   byte *pixels = img->pixels;
281   png_bytep rows[img->rows];
282   for (uns r = 0; r < img->rows; r++, pixels += img->row_size)
283     rows[r] = (png_bytep)pixels;
284   png_read_image(rd->png_ptr, rows);
285   png_read_end(rd->png_ptr, rd->end_ptr);
286
287   /* Destroy libpng read structure */
288   png_destroy_read_struct(&rd->png_ptr, &rd->info_ptr, &rd->end_ptr);
289
290   /* Finish the image  */
291   return image_io_read_data_finish(&rdi, io);
292 }
293
294 int
295 libpng_write(struct image_io *io)
296 {
297   DBG("libpng_write()");
298
299   /* Create libpng structures */
300   png_structp png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING,
301       io->thread, libpng_write_error, libpng_warning,
302       io->internal_pool, libpng_malloc, libpng_free);
303   if (unlikely(!png_ptr))
304     {
305       image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "Cannot create libpng write structure.");
306       return 0;
307     }
308   png_infop info_ptr = png_create_info_struct(png_ptr);
309   if (unlikely(!info_ptr))
310     {
311       image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "Cannot create libpng info structure.");
312       png_destroy_write_struct(&png_ptr, NULL);
313       return 0;
314     }
315
316   /* Setup libpng longjump */
317   if (unlikely(setjmp(png_jmpbuf(png_ptr))))
318     {
319       DBG("Libpng failed to write the image, longjump saved us.");
320       png_destroy_write_struct(&png_ptr, &info_ptr);
321       return 0;
322     }
323
324   /* Setup libpng IO */
325   png_set_write_fn(png_ptr, io->fastbuf, libpng_write_fn, libpng_flush_fn);
326
327   /* Setup PNG parameters */
328   struct image *img = io->image;
329   switch (img->flags & IMAGE_PIXEL_FORMAT)
330     {
331       case COLOR_SPACE_GRAYSCALE | IMAGE_PIXELS_ALIGNED:
332         png_set_IHDR(png_ptr, info_ptr, img->cols, img->rows, 8, PNG_COLOR_TYPE_GRAY,
333           PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
334         break;
335       case COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA | IMAGE_PIXELS_ALIGNED:
336         png_set_IHDR(png_ptr, info_ptr, img->cols, img->rows, 8, PNG_COLOR_TYPE_GRAY_ALPHA,
337           PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
338         break;
339       case COLOR_SPACE_RGB:
340         png_set_IHDR(png_ptr, info_ptr, img->cols, img->rows, 8, PNG_COLOR_TYPE_RGB,
341           PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
342         break;
343       case COLOR_SPACE_RGB | IMAGE_ALPHA | IMAGE_PIXELS_ALIGNED:
344         png_set_IHDR(png_ptr, info_ptr, img->cols, img->rows, 8, PNG_COLOR_TYPE_RGB_ALPHA,
345           PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
346         break;
347       case COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED:
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         png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
351         break;
352       default:
353         ASSERT(0);
354     }
355   png_write_info(png_ptr, info_ptr);
356
357   /* Write pixels */
358   byte *pixels = img->pixels;
359   png_bytep rows[img->rows];
360   for (uns r = 0; r < img->rows; r++, pixels += img->row_size)
361     rows[r] = (png_bytep)pixels;
362   png_write_image(png_ptr, rows);
363   png_write_end(png_ptr, info_ptr);
364
365   /* Free libpng structure */
366   png_destroy_write_struct(&png_ptr, &info_ptr);
367   return 1;
368 }