1 : |
jhr |
1884 |
/*! \file load-png.c
|
2 : |
|
|
*
|
3 : |
|
|
* \brief This file implements a simple texture loading API on top of libpng.
|
4 : |
|
|
*
|
5 : |
|
|
* \author John Reppy
|
6 : |
|
|
*/
|
7 : |
|
|
|
8 : |
|
|
/*
|
9 : |
|
|
* COPYRIGHT (c) 2012 The Diderot Project (http://diderot-language.cs.uchicago.edu)
|
10 : |
|
|
* All rights reserved.
|
11 : |
|
|
*/
|
12 : |
|
|
|
13 : |
|
|
#include <png.h>
|
14 : |
|
|
#include "util.h" |
15 : |
|
|
#include "load-png.h" |
16 : |
|
|
|
17 : |
|
|
/* LoadImage:
|
18 : |
|
|
*/
|
19 : |
|
|
Image2D_t *LoadImage (const char *file, bool flip, ImageFormat_t fmt)
|
20 : |
|
|
{
|
21 : |
|
|
// open image file
|
22 : |
|
|
FILE *inS = fopen(file, "rb");
|
23 : |
|
|
if (inS == NULL) {
|
24 : |
|
|
fprintf (stderr, "unable to open \"%s\"\n", file);
|
25 : |
|
|
return 0;
|
26 : |
|
|
}
|
27 : |
|
|
|
28 : |
|
|
/* check PNG signature */
|
29 : |
|
|
unsigned char sig[8];
|
30 : |
|
|
if ((fread(sig, 1, 8, inS) != 8)
|
31 : |
|
|
|| (! png_check_sig(sig, 8))) {
|
32 : |
|
|
fprintf (stderr, "error reading \"%s\": bad signature\n", file);
|
33 : |
|
|
fclose(inS);
|
34 : |
|
|
return 0;
|
35 : |
|
|
}
|
36 : |
|
|
|
37 : |
|
|
/* setup read structures */
|
38 : |
|
|
png_structp pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
|
39 : |
|
|
if (pngPtr == 0) {
|
40 : |
|
|
fprintf (stderr, "error reading \"%s\"\n", file);
|
41 : |
|
|
fclose(inS);
|
42 : |
|
|
return 0;
|
43 : |
|
|
}
|
44 : |
|
|
png_infop infoPtr = png_create_info_struct(pngPtr);
|
45 : |
|
|
if (infoPtr == 0) {
|
46 : |
|
|
fprintf (stderr, "error reading \"%s\"\n", file);
|
47 : |
|
|
png_destroy_read_struct(&pngPtr, (png_infopp)0, (png_infopp)0);
|
48 : |
|
|
fclose(inS);
|
49 : |
|
|
return 0;
|
50 : |
|
|
}
|
51 : |
|
|
png_infop endPtr = png_create_info_struct(pngPtr);
|
52 : |
|
|
if (!endPtr) {
|
53 : |
|
|
fprintf (stderr, "error reading \"%s\"\n", file);
|
54 : |
|
|
png_destroy_read_struct (&pngPtr, &infoPtr, (png_infopp)0);
|
55 : |
|
|
fclose (inS);
|
56 : |
|
|
return 0;
|
57 : |
|
|
}
|
58 : |
|
|
|
59 : |
|
|
/* error handler */
|
60 : |
|
|
if (setjmp (png_jmpbuf(pngPtr))) {
|
61 : |
|
|
fprintf (stderr, "error reading \"%s\"\n", file);
|
62 : |
|
|
png_destroy_read_struct (&pngPtr, &infoPtr, &endPtr);
|
63 : |
|
|
fclose (inS);
|
64 : |
|
|
return 0;
|
65 : |
|
|
}
|
66 : |
|
|
|
67 : |
|
|
/* set the input stream */
|
68 : |
|
|
png_init_io (pngPtr, inS);
|
69 : |
|
|
|
70 : |
|
|
/* let the PNG library know that we already checked the signature */
|
71 : |
|
|
png_set_sig_bytes (pngPtr, 8);
|
72 : |
|
|
|
73 : |
|
|
/* get file info */
|
74 : |
|
|
png_uint_32 width, height;
|
75 : |
|
|
int bitDepth, colorType;
|
76 : |
|
|
png_read_info (pngPtr, infoPtr);
|
77 : |
|
|
png_get_IHDR (pngPtr, infoPtr, &width, &height,
|
78 : |
|
|
&bitDepth, &colorType, 0 /* interlace type */,
|
79 : |
|
|
0 /* compression type */, 0 /* filter method */);
|
80 : |
|
|
|
81 : |
|
|
// check file format against expected format
|
82 : |
|
|
switch (colorType) {
|
83 : |
|
|
case PNG_COLOR_TYPE_GRAY:
|
84 : |
|
|
if (bitDepth < 8) {
|
85 : |
|
|
png_set_expand_gray_1_2_4_to_8(pngPtr);
|
86 : |
|
|
}
|
87 : |
|
|
if (fmt != GRAY_IMAGE) {
|
88 : |
|
|
fprintf(stderr, "unexpected GRAY image format\n");
|
89 : |
|
|
png_destroy_read_struct (&pngPtr, &infoPtr, (png_infopp)0);
|
90 : |
|
|
fclose (inS);
|
91 : |
|
|
return 0;
|
92 : |
|
|
}
|
93 : |
|
|
break;
|
94 : |
|
|
case PNG_COLOR_TYPE_GRAY_ALPHA:
|
95 : |
|
|
if (fmt != GRAY_ALPHA_IMAGE) {
|
96 : |
|
|
fprintf(stderr, "unexpected GRAY_ALPHA image format\n");
|
97 : |
|
|
png_destroy_read_struct (&pngPtr, &infoPtr, (png_infopp)0);
|
98 : |
|
|
fclose (inS);
|
99 : |
|
|
return 0;
|
100 : |
|
|
}
|
101 : |
|
|
break;
|
102 : |
|
|
case PNG_COLOR_TYPE_PALETTE:
|
103 : |
|
|
png_set_palette_to_rgb (pngPtr);
|
104 : |
|
|
if (fmt != RGB_IMAGE) {
|
105 : |
|
|
fprintf(stderr, "unexpected PALETTE image format\n");
|
106 : |
|
|
png_destroy_read_struct (&pngPtr, &infoPtr, (png_infopp)0);
|
107 : |
|
|
fclose (inS);
|
108 : |
|
|
return 0;
|
109 : |
|
|
}
|
110 : |
|
|
break;
|
111 : |
|
|
case PNG_COLOR_TYPE_RGB:
|
112 : |
|
|
if (fmt != RGB_IMAGE) {
|
113 : |
|
|
fprintf(stderr, "unexpected RGB image format\n");
|
114 : |
|
|
png_destroy_read_struct (&pngPtr, &infoPtr, (png_infopp)0);
|
115 : |
|
|
fclose (inS);
|
116 : |
|
|
return 0;
|
117 : |
|
|
}
|
118 : |
|
|
break;
|
119 : |
|
|
case PNG_COLOR_TYPE_RGB_ALPHA:
|
120 : |
|
|
if (fmt != RGBA_IMAGE) {
|
121 : |
|
|
fprintf(stderr, "unexpected RGBA image format\n");
|
122 : |
|
|
png_destroy_read_struct (&pngPtr, &infoPtr, (png_infopp)0);
|
123 : |
|
|
fclose (inS);
|
124 : |
|
|
return 0;
|
125 : |
|
|
}
|
126 : |
|
|
break;
|
127 : |
|
|
default:
|
128 : |
|
|
fprintf(stderr, "unknown color type %d\n", colorType);
|
129 : |
|
|
png_destroy_read_struct (&pngPtr, &infoPtr, (png_infopp)0);
|
130 : |
|
|
fclose (inS);
|
131 : |
|
|
return 0;
|
132 : |
|
|
}
|
133 : |
|
|
|
134 : |
|
|
// figure out the OpenGL image type
|
135 : |
|
|
GLenum type;
|
136 : |
|
|
if (bitDepth <= 8) type = GL_UNSIGNED_BYTE;
|
137 : |
jhr |
1920 |
else if (bitDepth == 16) type = GL_UNSIGNED_SHORT;
|
138 : |
jhr |
1884 |
else {
|
139 : |
|
|
fprintf(stderr, "unsupported bit depth\n");
|
140 : |
|
|
png_destroy_read_struct (&pngPtr, &infoPtr, (png_infopp)0);
|
141 : |
|
|
fclose (inS);
|
142 : |
|
|
return 0;
|
143 : |
|
|
}
|
144 : |
|
|
|
145 : |
|
|
/* allocate image data */
|
146 : |
|
|
unsigned char *imgData = 0;
|
147 : |
|
|
png_bytepp rowPtrs = 0;
|
148 : |
|
|
int bytesPerRow = png_get_rowbytes(pngPtr, infoPtr);
|
149 : |
|
|
if ((imgData = NEWVEC(unsigned char, height * bytesPerRow)) == 0) {
|
150 : |
|
|
png_destroy_read_struct (&pngPtr, &infoPtr, &endPtr);
|
151 : |
|
|
free (rowPtrs);
|
152 : |
|
|
fclose (inS);
|
153 : |
|
|
return 0;
|
154 : |
|
|
}
|
155 : |
|
|
|
156 : |
|
|
rowPtrs = (png_bytepp) malloc (height * sizeof(png_bytep));
|
157 : |
|
|
if (rowPtrs == 0) {
|
158 : |
|
|
png_destroy_read_struct (&pngPtr, &infoPtr, &endPtr);
|
159 : |
|
|
free (imgData);
|
160 : |
|
|
fclose (inS);
|
161 : |
|
|
return 0;
|
162 : |
|
|
}
|
163 : |
|
|
|
164 : |
|
|
if (flip) {
|
165 : |
|
|
/* setup row pointers */
|
166 : |
|
|
for (int i = 0; i < height; i++)
|
167 : |
|
|
rowPtrs[i] = imgData + i*bytesPerRow;
|
168 : |
|
|
}
|
169 : |
|
|
else {
|
170 : |
|
|
/* setup row pointers so that the texture has OpenGL orientation */
|
171 : |
|
|
for (int i = 1; i <= height; i++)
|
172 : |
|
|
rowPtrs[height - i] = imgData + (i-1)*bytesPerRow;
|
173 : |
|
|
}
|
174 : |
|
|
|
175 : |
|
|
/* read the image */
|
176 : |
|
|
png_read_image(pngPtr, rowPtrs);
|
177 : |
|
|
|
178 : |
|
|
/* Clean up. */
|
179 : |
|
|
png_destroy_read_struct (&pngPtr, &infoPtr, &endPtr);
|
180 : |
|
|
free (rowPtrs);
|
181 : |
|
|
fclose (inS);
|
182 : |
|
|
|
183 : |
|
|
/* allocate and initialize the image data structure */
|
184 : |
|
|
Image2D_t *img = NEW(Image2D_t);
|
185 : |
|
|
img->wid = width;
|
186 : |
|
|
img->ht = height;
|
187 : |
|
|
img->fmt = fmt;
|
188 : |
|
|
img->type = type;
|
189 : |
|
|
img->data = imgData;
|
190 : |
|
|
|
191 : |
|
|
return img;
|
192 : |
|
|
|
193 : |
|
|
} /* end of LoadImage */
|
194 : |
|
|
|
195 : |
|
|
/* FreeImage:
|
196 : |
|
|
*/
|
197 : |
|
|
void FreeImage (Image2D_t *img)
|
198 : |
|
|
{
|
199 : |
|
|
if (img == 0) return;
|
200 : |
|
|
|
201 : |
|
|
free (img->data);
|
202 : |
|
|
free (img);
|
203 : |
|
|
|
204 : |
|
|
} /* end of FreeImage */
|
205 : |
|
|
|
206 : |
|
|
/* TexImage:
|
207 : |
|
|
*/
|
208 : |
|
|
int TexImage (Image2D_t *img)
|
209 : |
|
|
{
|
210 : |
|
|
assert (img != 0);
|
211 : |
|
|
|
212 : |
|
|
glTexImage2D (GL_TEXTURE_2D, 0, img->fmt, img->wid, img->ht, 0, img->fmt, img->type, img->data);
|
213 : |
|
|
|
214 : |
|
|
return (glGetError());
|
215 : |
|
|
|
216 : |
|
|
} /* end of TexImage */
|