aboutsummaryrefslogtreecommitdiffstats
path: root/src/png.c
blob: 9dbe31ea923524ad90523963c35e054aaed9b4c8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include <stdlib.h>
#include <png.h>

#include "meh.h"


struct png_t{
	struct image img;
	FILE *f;
	png_structp png_ptr;
	png_infop info_ptr;
	png_infop end_info;
	int numpasses;
};

static int ispng(FILE *f){
	unsigned char buf[8];
	if(fread(buf, 1, 8, f) != 8)
		return 0;
	return !png_sig_cmp(buf, 1, 8);
}

struct image *png_open(FILE *f){
	struct png_t *p;

	rewind(f);
	if(!ispng(f))
		return NULL;

	p = malloc(sizeof(struct png_t));
	if((p->png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)) == NULL){
		free(p);
		return NULL;
	}
	if((p->info_ptr = png_create_info_struct(p->png_ptr)) == NULL){
		png_destroy_read_struct(&p->png_ptr, (png_infopp)NULL, (png_infopp)NULL);
		free(p);
		return NULL;
	}
	if((p->end_info = png_create_info_struct(p->png_ptr)) == NULL){
		png_destroy_read_struct(&p->png_ptr, &p->info_ptr, (png_infopp)NULL);
		free(p);
		return NULL;
	}
	if(setjmp(png_jmpbuf(p->png_ptr))){
		png_destroy_read_struct(&p->png_ptr, &p->info_ptr, &p->end_info);
		free(p);
		return NULL;
	}

	p->f = f;
	rewind(f);
	png_init_io(p->png_ptr, f);

	png_read_info(p->png_ptr, p->info_ptr);

	p->img.bufwidth = png_get_image_width(p->png_ptr, p->info_ptr);
	p->img.bufheight = png_get_image_height(p->png_ptr, p->info_ptr);

	p->img.fmt = &libpng;

	return (struct image *)p;
}

int png_read(struct image *img){
	unsigned int y;
	png_bytepp row_pointers;
	struct png_t *p = (struct png_t *)img;

	if(setjmp(png_jmpbuf(p->png_ptr))){
		png_destroy_read_struct(&p->png_ptr, &p->info_ptr, &p->end_info);
		return 1;
	}

	{
		int color_type = png_get_color_type(p->png_ptr, p->info_ptr);
		int bit_depth = png_get_bit_depth(p->png_ptr, p->info_ptr);
		if (color_type == PNG_COLOR_TYPE_PALETTE)
			png_set_expand(p->png_ptr);
		if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
			png_set_expand(p->png_ptr);
		if (png_get_valid(p->png_ptr, p->info_ptr, PNG_INFO_tRNS))
			png_set_expand(p->png_ptr);
		if (bit_depth == 16)
			png_set_strip_16(p->png_ptr);
		if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
			png_set_gray_to_rgb(p->png_ptr);
		//png_set_strip_alpha(p->png_ptr);

		png_color_16 my_background = {.red = 0xff, .green = 0xff, .blue = 0xff};
		png_color_16p image_background;

		if(png_get_bKGD(p->png_ptr, p->info_ptr, &image_background))
			png_set_background(p->png_ptr, image_background, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
		else
			png_set_background(p->png_ptr, &my_background, PNG_BACKGROUND_GAMMA_SCREEN, 2, 1.0);

		if(png_get_interlace_type(p->png_ptr, p->info_ptr) == PNG_INTERLACE_ADAM7)
			p->numpasses = png_set_interlace_handling(p->png_ptr);
		else
			p->numpasses = 1;
		png_read_update_info(p->png_ptr, p->info_ptr);
	}

	row_pointers = (png_bytepp)malloc(img->bufheight * sizeof(png_bytep));
	for(y = 0; y < img->bufheight; y++)
		row_pointers[y] = img->buf + y * img->bufwidth * 3;

	png_read_image(p->png_ptr, row_pointers);
	free(row_pointers);

	img->state |= LOADED | SLOWLOADED;

	return 0;
}

void png_close(struct image *img){
	struct png_t *p = (struct png_t *)img;
	png_destroy_read_struct(&p->png_ptr, &p->info_ptr, &p->end_info);
	fclose(p->f);
}

struct imageformat libpng = {
	png_open,
	NULL,
	png_read,
	png_close
};