2 * Image Library -- libpng
4 * (c) 2006 Pavel Charvat <pchar@ucw.cz>
6 * This software may be freely distributed and used according to the terms
7 * of the GNU Lesser General Public License.
13 #include "lib/mempool.h"
14 #include "lib/fastbuf.h"
15 #include "images/images.h"
16 #include "images/io-main.h"
20 struct libpng_read_data {
31 libpng_malloc(png_structp png_ptr, png_size_t size)
33 DBG("libpng_malloc(size=%u)", (uns)size);
34 return mp_alloc(png_get_mem_ptr(png_ptr), size);
38 libpng_free(png_structp png_ptr UNUSED, png_voidp ptr UNUSED)
44 libpng_read_error(png_structp png_ptr, png_const_charp msg)
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);
52 libpng_write_error(png_structp png_ptr, png_const_charp msg)
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);
60 libpng_warning(png_structp png_ptr UNUSED, png_const_charp msg UNUSED)
62 DBG("libpng_warning(): %s", (byte *)msg);
66 libpng_read_fn(png_structp png_ptr, png_bytep data, png_size_t length)
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");
74 libpng_write_fn(png_structp png_ptr, png_bytep data, png_size_t length)
76 DBG("libpng_write_fn(len=%u)", (uns)length);
77 bwrite((struct fastbuf *)png_get_io_ptr(png_ptr), (byte *)data, length);
81 libpng_flush_fn(png_structp png_ptr UNUSED)
83 DBG("libpng_flush_fn()");
87 libpng_read_cancel(struct image_io *io)
89 DBG("libpng_read_cancel()");
91 struct libpng_read_data *rd = io->read_data;
92 png_destroy_read_struct(&rd->png_ptr, &rd->info_ptr, &rd->end_ptr);
96 libpng_read_header(struct image_io *io)
98 DBG("libpng_read_header()");
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))
107 image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Cannot create libpng read structure.");
110 rd->info_ptr = png_create_info_struct(rd->png_ptr);
111 if (unlikely(!rd->info_ptr))
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);
117 rd->end_ptr = png_create_info_struct(rd->png_ptr);
118 if (unlikely(!rd->end_ptr))
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);
125 /* Setup libpng longjump */
126 if (unlikely(setjmp(png_jmpbuf(rd->png_ptr))))
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);
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);
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);
141 /* Fill image_io values */
144 switch (rd->color_type)
146 case PNG_COLOR_TYPE_GRAY:
147 io->flags = COLOR_SPACE_GRAYSCALE;
148 io->number_of_colors = 1 << 8;
150 case PNG_COLOR_TYPE_GRAY_ALPHA:
151 io->flags = COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA;
152 io->number_of_colors = 1 << 8;
154 case PNG_COLOR_TYPE_RGB:
155 io->flags = COLOR_SPACE_RGB;
156 io->number_of_colors = 1 << 24;
158 case PNG_COLOR_TYPE_RGB_ALPHA:
159 io->number_of_colors = 1 << 24;
160 io->flags = COLOR_SPACE_RGB | IMAGE_ALPHA;
162 case PNG_COLOR_TYPE_PALETTE:
163 io->flags = COLOR_SPACE_RGB | IMAGE_ALPHA | IMAGE_IO_HAS_PALETTE;
165 if (png_get_PLTE(rd->png_ptr, rd->info_ptr, NULL, &num_palette))
166 io->number_of_colors = num_palette;
168 io->number_of_colors = 1 << rd->bit_depth;
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");
177 io->read_cancel = libpng_read_cancel;
182 libpng_read_data(struct image_io *io)
184 DBG("libpng_read_data()");
186 struct libpng_read_data *rd = io->read_data;
188 /* Test supported pixel formats */
189 switch (io->flags & IMAGE_COLOR_SPACE)
191 case COLOR_SPACE_GRAYSCALE:
192 case COLOR_SPACE_RGB:
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.");
200 struct image_io_read_data_internals rdi;
203 if (setjmp(png_jmpbuf(rd->png_ptr)))
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);
208 image_io_read_data_break(&rdi, io);
212 uns read_flags = io->flags;
214 /* Apply transformations */
215 if (rd->bit_depth == 16)
216 png_set_strip_16(rd->png_ptr);
217 switch (rd->color_type)
219 case PNG_COLOR_TYPE_PALETTE:
220 if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE)
222 png_set_palette_to_rgb(rd->png_ptr);
223 png_set_rgb_to_gray_fixed(rd->png_ptr, 1, 21267, 71514);
226 png_set_palette_to_rgb(rd->png_ptr);
227 if (!(io->flags & IMAGE_ALPHA))
229 if (io->flags & IMAGE_IO_USE_BACKGROUND)
231 png_set_add_alpha(rd->png_ptr, 255, PNG_FILLER_AFTER);
232 read_flags = (read_flags | IMAGE_ALPHA) & IMAGE_CHANNELS_FORMAT;
234 else if ((io->flags & IMAGE_PIXEL_FORMAT) == (COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED))
235 png_set_add_alpha(rd->png_ptr, 255, PNG_FILLER_AFTER);
237 png_set_strip_alpha(rd->png_ptr);
240 png_set_add_alpha(rd->png_ptr, 255, PNG_FILLER_AFTER);
242 case PNG_COLOR_TYPE_GRAY:
243 if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_RGB)
244 png_set_gray_to_rgb(rd->png_ptr);
245 if (io->flags & IMAGE_ALPHA)
246 png_set_add_alpha(rd->png_ptr, 255, PNG_FILLER_AFTER);
248 case PNG_COLOR_TYPE_GRAY_ALPHA:
249 if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_RGB)
250 png_set_gray_to_rgb(rd->png_ptr);
251 if (!(io->flags & IMAGE_ALPHA))
253 if (io->flags & IMAGE_IO_USE_BACKGROUND)
254 read_flags = (read_flags | IMAGE_ALPHA) & IMAGE_CHANNELS_FORMAT;
256 png_set_strip_alpha(rd->png_ptr);
259 case PNG_COLOR_TYPE_RGB:
260 if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE)
261 png_set_rgb_to_gray_fixed(rd->png_ptr, 1, 21267, 71514);
262 if ((io->flags & IMAGE_ALPHA) || (io->flags & IMAGE_PIXEL_FORMAT) == (COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED))
263 png_set_add_alpha(rd->png_ptr, 255, PNG_FILLER_AFTER);
265 case PNG_COLOR_TYPE_RGB_ALPHA:
266 if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE)
267 png_set_rgb_to_gray_fixed(rd->png_ptr, 1, 21267, 71514);
268 if (!(io->flags & IMAGE_ALPHA))
269 if (io->flags & IMAGE_IO_USE_BACKGROUND)
270 read_flags = (read_flags | IMAGE_ALPHA) & IMAGE_CHANNELS_FORMAT;
271 else if ((io->flags & IMAGE_PIXEL_FORMAT) != (COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED))
272 png_set_strip_alpha(rd->png_ptr);
277 png_read_update_info(rd->png_ptr, rd->info_ptr);
279 /* Prepare the image */
280 if (unlikely(!image_io_read_data_prepare(&rdi, io, rd->cols, rd->rows, read_flags)))
282 png_destroy_read_struct(&rd->png_ptr, &rd->info_ptr, &rd->end_ptr);
285 /* Read image data */
286 DBG("Reading image data");
287 struct image *img = rdi.image;
288 byte *pixels = img->pixels;
289 png_bytep rows[img->rows];
290 for (uns r = 0; r < img->rows; r++, pixels += img->row_size)
291 rows[r] = (png_bytep)pixels;
292 png_read_image(rd->png_ptr, rows);
293 png_read_end(rd->png_ptr, rd->end_ptr);
295 /* Destroy libpng read structure */
296 png_destroy_read_struct(&rd->png_ptr, &rd->info_ptr, &rd->end_ptr);
298 /* Finish the image */
299 return image_io_read_data_finish(&rdi, io);
303 libpng_write(struct image_io *io)
305 DBG("libpng_write()");
307 /* Create libpng structures */
308 png_structp png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING,
309 io->thread, libpng_write_error, libpng_warning,
310 io->internal_pool, libpng_malloc, libpng_free);
311 if (unlikely(!png_ptr))
313 image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "Cannot create libpng write structure.");
316 png_infop info_ptr = png_create_info_struct(png_ptr);
317 if (unlikely(!info_ptr))
319 image_thread_err(io->thread, IMAGE_ERR_WRITE_FAILED, "Cannot create libpng info structure.");
320 png_destroy_write_struct(&png_ptr, NULL);
324 /* Setup libpng longjump */
325 if (unlikely(setjmp(png_jmpbuf(png_ptr))))
327 DBG("Libpng failed to write the image, longjump saved us.");
328 png_destroy_write_struct(&png_ptr, &info_ptr);
332 /* Setup libpng IO */
333 png_set_write_fn(png_ptr, io->fastbuf, libpng_write_fn, libpng_flush_fn);
335 /* Setup PNG parameters */
336 struct image *img = io->image;
337 switch (img->flags & IMAGE_PIXEL_FORMAT)
339 case COLOR_SPACE_GRAYSCALE | IMAGE_PIXELS_ALIGNED:
340 png_set_IHDR(png_ptr, info_ptr, img->cols, img->rows, 8, PNG_COLOR_TYPE_GRAY,
341 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
343 case COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA | IMAGE_PIXELS_ALIGNED:
344 png_set_IHDR(png_ptr, info_ptr, img->cols, img->rows, 8, PNG_COLOR_TYPE_GRAY_ALPHA,
345 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
347 case COLOR_SPACE_RGB:
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);
351 case COLOR_SPACE_RGB | IMAGE_ALPHA | IMAGE_PIXELS_ALIGNED:
352 png_set_IHDR(png_ptr, info_ptr, img->cols, img->rows, 8, PNG_COLOR_TYPE_RGB_ALPHA,
353 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
355 case COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED:
356 png_set_IHDR(png_ptr, info_ptr, img->cols, img->rows, 8, PNG_COLOR_TYPE_RGB,
357 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
358 png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
363 png_write_info(png_ptr, info_ptr);
366 byte *pixels = img->pixels;
367 png_bytep rows[img->rows];
368 for (uns r = 0; r < img->rows; r++, pixels += img->row_size)
369 rows[r] = (png_bytep)pixels;
370 png_write_image(png_ptr, rows);
371 png_write_end(png_ptr, info_ptr);
373 /* Free libpng structure */
374 png_destroy_write_struct(&png_ptr, &info_ptr);