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