]> mj.ucw.cz Git - libucw.git/blob - images/io-libpng.c
Merge with git+ssh://cvs.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 <png.h>
17 #include <setjmp.h>
18
19 struct libpng_internals {
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_error(png_structp png_ptr, png_const_charp msg)
44 {
45   DBG("libpng_error()");
46   image_thread_err(png_get_error_ptr(png_ptr), IMAGE_ERR_READ_FAILED, (byte *)msg);
47   longjmp(png_jmpbuf(png_ptr), 1);
48 }
49
50 static void
51 libpng_warning(png_structp png_ptr UNUSED, png_const_charp msg UNUSED)
52 {
53   DBG("libpng_warning(): %s", (byte *)msg);
54 }
55
56 static void
57 libpng_read(png_structp png_ptr, png_bytep data, png_size_t length)
58 {
59   DBG("libpng_read(): len=%d", (uns)length);
60   if (unlikely(bread(png_get_io_ptr(png_ptr), data, length) < length))
61     png_error(png_ptr, "Incomplete data");
62 }
63
64 static void
65 libpng_read_cancel(struct image_io *io)
66 {
67   DBG("libpng_read_cancel()");
68   struct libpng_internals *i = io->read_data;
69   png_destroy_read_struct(&i->png_ptr, &i->info_ptr, &i->end_ptr);
70 }
71
72 int
73 libpng_read_header(struct image_io *io)
74 {
75   DBG("libpng_read_header()");
76   struct libpng_internals *i = io->read_data = mp_alloc(io->internal_pool, sizeof(*i));
77   i->png_ptr = png_create_read_struct_2(PNG_LIBPNG_VER_STRING,
78       io->thread, libpng_error, libpng_warning,
79       io->internal_pool, libpng_malloc, libpng_free);
80   if (unlikely(!i->png_ptr))
81     goto err_create;
82   i->info_ptr = png_create_info_struct(i->png_ptr);
83   if (unlikely(!i->info_ptr))
84     {
85       png_destroy_read_struct(&i->png_ptr, NULL, NULL);
86       goto err_create;
87     }
88   i->end_ptr = png_create_info_struct(i->png_ptr);
89   if (unlikely(!i->end_ptr))
90     {
91       png_destroy_read_struct(&i->png_ptr, &i->info_ptr, NULL);
92       goto err_create;
93     }
94   if (setjmp(png_jmpbuf(i->png_ptr)))
95     {
96       DBG("Libpng failed to read the image, longjump saved us");
97       png_destroy_read_struct(&i->png_ptr, &i->info_ptr, &i->end_ptr);
98       return 0;
99     }
100   png_set_read_fn(i->png_ptr, io->fastbuf, libpng_read);
101   png_set_user_limits(i->png_ptr, 0xffff, 0xffff);
102
103   DBG("Reading image info");
104   png_read_info(i->png_ptr, i->info_ptr);
105   png_get_IHDR(i->png_ptr, i->info_ptr, &i->cols, &i->rows, &i->bit_depth, &i->color_type, NULL, NULL, NULL);
106
107   if (!io->cols)
108     io->cols = i->cols;
109   if (!io->rows)
110     io->rows = i->rows;
111   if (!(io->flags & IMAGE_CHANNELS_FORMAT))
112     switch (i->color_type)
113       {
114         case PNG_COLOR_TYPE_GRAY:
115           io->flags |= COLOR_SPACE_GRAYSCALE;
116           break;
117         case PNG_COLOR_TYPE_GRAY_ALPHA:
118           io->flags |= COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA;
119           break;
120         case PNG_COLOR_TYPE_RGB:
121           io->flags |= COLOR_SPACE_RGB;
122           break;
123         case PNG_COLOR_TYPE_RGB_ALPHA:
124         case PNG_COLOR_TYPE_PALETTE:
125           io->flags |= COLOR_SPACE_RGB | IMAGE_ALPHA;
126           break;
127         default:
128           png_destroy_read_struct(&i->png_ptr, &i->info_ptr, &i->end_ptr);
129           image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Unknown color type");
130           break;
131       }
132
133   io->read_cancel = libpng_read_cancel;
134   return 1;
135
136 err_create:
137   image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Cannot create libpng read structure.");
138   return 0;
139 }
140
141 int
142 libpng_read_data(struct image_io *io)
143 {
144   DBG("libpng_read_data()");
145
146   struct libpng_internals *i = io->read_data;
147
148   /* Test supported pixel formats */
149   switch (io->flags & IMAGE_COLOR_SPACE)
150     {
151       case COLOR_SPACE_GRAYSCALE:
152       case COLOR_SPACE_RGB:
153         break;
154       default:
155         png_destroy_read_struct(&i->png_ptr, &i->info_ptr, &i->end_ptr);
156         image_thread_err(io->thread, IMAGE_ERR_INVALID_PIXEL_FORMAT, "Unsupported color space.");
157         return 0;
158     }
159
160   volatile int need_scale = io->cols != i->cols || io->rows != i->rows;
161   struct image * volatile img = need_scale ? 
162     image_new(io->thread, i->cols, i->rows, io->flags & IMAGE_PIXEL_FORMAT, NULL) :
163     image_new(io->thread, i->cols, i->rows, io->flags, io->pool);
164   if (!img)
165     {
166       png_destroy_read_struct(&i->png_ptr, &i->info_ptr, &i->end_ptr);
167       return 0;
168     }
169
170   if (setjmp(png_jmpbuf(i->png_ptr)))
171     {
172       DBG("Libpng failed to read the image, longjump saved us");
173       png_destroy_read_struct(&i->png_ptr, &i->info_ptr, &i->end_ptr);
174       if (need_scale || !io->pool)
175         image_destroy(io->thread, img);
176       return 0;
177     }
178
179   /* Apply transformations */
180   if (i->bit_depth == 16)
181     png_set_strip_16(i->png_ptr);
182   switch (i->color_type)
183     {
184       case PNG_COLOR_TYPE_PALETTE:
185         if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE)
186           {
187             png_set_palette_to_rgb(i->png_ptr);
188             png_set_rgb_to_gray_fixed(i->png_ptr, 1, 21267, 71514);
189           }
190         else
191           png_set_palette_to_rgb(i->png_ptr);
192         if ((io->flags & IMAGE_ALPHA) || (io->flags & IMAGE_PIXEL_FORMAT) == (COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED))
193           png_set_add_alpha(i->png_ptr, 255, PNG_FILLER_AFTER);
194         else
195           png_set_strip_alpha(i->png_ptr);
196         break;
197       case PNG_COLOR_TYPE_GRAY:
198         if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_RGB)
199           png_set_gray_to_rgb(i->png_ptr);
200         if (io->flags & IMAGE_ALPHA)
201           png_set_add_alpha(i->png_ptr, 255, PNG_FILLER_AFTER);
202         break;
203       case PNG_COLOR_TYPE_GRAY_ALPHA:
204         if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_RGB)
205           png_set_gray_to_rgb(i->png_ptr);
206         if (!(io->flags & IMAGE_ALPHA))
207           png_set_strip_alpha(i->png_ptr);
208         break;
209       case PNG_COLOR_TYPE_RGB:
210         if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE)
211           png_set_rgb_to_gray_fixed(i->png_ptr, 1, 21267, 71514);
212         if ((io->flags & IMAGE_ALPHA) || (io->flags & IMAGE_PIXEL_FORMAT) == (COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED))
213           png_set_add_alpha(i->png_ptr, 255, PNG_FILLER_AFTER);
214         break;
215       case PNG_COLOR_TYPE_RGB_ALPHA:
216         if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE)
217           png_set_rgb_to_gray_fixed(i->png_ptr, 1, 21267, 71514);
218         if (!(io->flags & IMAGE_ALPHA) && (io->flags & IMAGE_PIXEL_FORMAT) != (COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED))
219           png_set_strip_alpha(i->png_ptr);
220         break;
221       default:
222         ASSERT(0);
223     }
224   png_read_update_info(i->png_ptr, i->info_ptr);
225
226   /* Read image data */
227   DBG("Reading image data");
228   byte *pixels = img->pixels;
229   png_bytep rows[img->rows];
230   for (uns r = 0; r < img->rows; r++, pixels += img->row_size)
231     rows[r] = (png_bytep)pixels;
232   png_read_image(i->png_ptr, rows);
233   png_read_end(i->png_ptr, i->end_ptr);
234
235   /* Destroy libpng read structure */
236   png_destroy_read_struct(&i->png_ptr, &i->info_ptr, &i->end_ptr);
237
238   /* Scale and store the resulting image */
239   if (need_scale)
240     {
241       struct image *dest = image_new(io->thread, io->cols, io->rows, io->flags, io->pool);
242       if (!dest)
243         {
244           image_destroy(io->thread, img);
245           return 0;
246         }
247       if (!image_scale(io->thread, dest, img))
248         {
249           image_destroy(io->thread, img);
250           if (!io->pool)
251             image_destroy(io->thread, dest);
252           return 0;
253         }
254       io->image = dest;
255     }
256   else
257     io->image = img;
258   io->image_destroy = !io->pool;
259   
260   return 1;
261 }
262
263 int
264 libpng_write(struct image_io *io)
265 {
266   image_thread_err(io->thread, IMAGE_ERR_NOT_IMPLEMENTED, "Libpng writing not implemented.");
267   return 0;
268 }