aboutsummaryrefslogtreecommitdiffstats
path: root/rd/read-png.c
diff options
context:
space:
mode:
Diffstat (limited to 'rd/read-png.c')
-rw-r--r--rd/read-png.c164
1 files changed, 164 insertions, 0 deletions
diff --git a/rd/read-png.c b/rd/read-png.c
new file mode 100644
index 0000000..c4f82d2
--- /dev/null
+++ b/rd/read-png.c
@@ -0,0 +1,164 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <png.h>
+
+#include "readers.h"
+
+static const char *ct[] = {
+ "gray", "X1", "rgb", "palette",
+ "graya", "X5", "rgba", "X7",
+};
+
+struct png_state {
+ FILE *infile;
+ png_structp png;
+ png_infop info;
+ png_bytep image;
+ png_uint_32 w,h;
+ int color_type;
+};
+
+static void*
+png_init(FILE *fp, char *filename, unsigned int page,
+ struct ida_image_info *i, int thumbnail)
+{
+ struct png_state *h;
+ int bit_depth, interlace_type;
+ int pass, number_passes;
+ unsigned int y;
+ png_uint_32 resx, resy;
+ png_color_16 *file_bg, my_bg = {
+ .red = 192,
+ .green = 192,
+ .blue = 192,
+ .gray = 192,
+ };
+ int unit;
+
+ h = malloc(sizeof(*h));
+ memset(h,0,sizeof(*h));
+
+ h->infile = fp;
+
+ h->png = png_create_read_struct(PNG_LIBPNG_VER_STRING,
+ NULL, NULL, NULL);
+ if (NULL == h->png)
+ goto oops;
+ h->info = png_create_info_struct(h->png);
+ if (NULL == h->info)
+ goto oops;
+
+ png_init_io(h->png, h->infile);
+ png_read_info(h->png, h->info);
+ png_get_IHDR(h->png, h->info, &h->w, &h->h,
+ &bit_depth,&h->color_type,&interlace_type, NULL,NULL);
+ png_get_pHYs(h->png, h->info, &resx, &resy, &unit);
+ i->width = h->w;
+ i->height = h->h;
+ if (PNG_RESOLUTION_METER == unit)
+ i->dpi = res_m_to_inch(resx);
+ if (debug)
+ fprintf(stderr,"png: color_type=%s #1\n",ct[h->color_type]);
+ i->npages = 1;
+
+ png_set_packing(h->png);
+ if (bit_depth == 16)
+ png_set_strip_16(h->png);
+ if (h->color_type == PNG_COLOR_TYPE_PALETTE)
+ png_set_palette_to_rgb(h->png);
+ if (h->color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
+ png_set_gray_1_2_4_to_8(h->png);
+
+ if (png_get_bKGD(h->png, h->info, &file_bg)) {
+ png_set_background(h->png,file_bg,PNG_BACKGROUND_GAMMA_FILE,1,1.0);
+ } else {
+ png_set_background(h->png,&my_bg,PNG_BACKGROUND_GAMMA_SCREEN,0,1.0);
+ }
+
+ number_passes = png_set_interlace_handling(h->png);
+ png_read_update_info(h->png, h->info);
+
+ h->color_type = png_get_color_type(h->png, h->info);
+ if (debug)
+ fprintf(stderr,"png: color_type=%s #2\n",ct[h->color_type]);
+
+ h->image = malloc(i->width * i->height * 4);
+
+ for (pass = 0; pass < number_passes-1; pass++) {
+ if (debug)
+ fprintf(stderr,"png: pass #%d\n",pass);
+ for (y = 0; y < i->height; y++) {
+ png_bytep row = h->image + y * i->width * 4;
+ png_read_rows(h->png, &row, NULL, 1);
+ }
+ }
+
+ return h;
+
+ oops:
+ if (h->image)
+ free(h->image);
+ if (h->png)
+ png_destroy_read_struct(&h->png, NULL, NULL);
+ fclose(h->infile);
+ free(h);
+ return NULL;
+}
+
+static void
+png_read(unsigned char *dst, unsigned int line, void *data)
+{
+ struct png_state *h = data;
+
+ png_bytep row = h->image + line * h->w * 4;
+ switch (h->color_type) {
+ case PNG_COLOR_TYPE_GRAY:
+ png_read_rows(h->png, &row, NULL, 1);
+ load_gray(dst,row,h->w);
+ break;
+ case PNG_COLOR_TYPE_RGB:
+ png_read_rows(h->png, &row, NULL, 1);
+ memcpy(dst,row,3*h->w);
+ break;
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ png_read_rows(h->png, &row, NULL, 1);
+ load_rgba(dst,row,h->w);
+ break;
+ case PNG_COLOR_TYPE_GRAY_ALPHA:
+ png_read_rows(h->png, &row, NULL, 1);
+ load_graya(dst,row,h->w);
+ break;
+ default:
+ /* shouldn't happen */
+ fprintf(stderr,"Oops: %s:%d\n",__FILE__,__LINE__);
+ exit(1);
+ }
+}
+
+static void
+png_done(void *data)
+{
+ struct png_state *h = data;
+
+ free(h->image);
+ png_destroy_read_struct(&h->png, &h->info, NULL);
+ fclose(h->infile);
+ free(h);
+}
+
+static struct ida_loader png_loader = {
+ magic: "\x89PNG",
+ moff: 0,
+ mlen: 4,
+ name: "libpng",
+ init: png_init,
+ read: png_read,
+ done: png_done,
+};
+
+static void __init init_rd(void)
+{
+ load_register(&png_loader);
+}