1 /***************************************************************************
2                           img.cpp -- Images manipulation
3                              -------------------
4     created              : Tue Aug 17 20:13:08 CEST 1999
5     copyright            : (C) 1999-2014 by Eric Espie, Bernhard Wymann
6     email                : torcs@free.fr
7     version              : $Id: img.cpp,v 1.5.2.4 2014/05/20 14:07:09 berniw Exp $
8  ***************************************************************************/
9 
10 /***************************************************************************
11  *                                                                         *
12  *   This program is free software; you can redistribute it and/or modify  *
13  *   it under the terms of the GNU General Public License as published by  *
14  *   the Free Software Foundation; either version 2 of the License, or     *
15  *   (at your option) any later version.                                   *
16  *                                                                         *
17  ***************************************************************************/
18 
19 /** @file
20     Image management API.
21 
22     Load and store png images.
23     @author Bernhard Wymann, Eric Espie
24     @version $Id: img.cpp,v 1.5.2.4 2014/05/20 14:07:09 berniw Exp $
25 */
26 
27 #ifdef WIN32
28 #include <windows.h>
29 #endif
30 
31 #include "png.h"
32 
33 #include <tgfclient.h>
34 #include <stdlib.h>
35 #include <stdio.h>
36 #ifdef WIN32
37 #include <direct.h>
38 #endif
39 
40 #include <portability.h>
41 
42 #define PNG_BYTES_TO_CHECK 4
43 
44 /** Load an image from disk to a buffer in RGBA mode.
45     @ingroup	img
46     @param	filename	name of the image to load
47     @param	widthp		width of the read image
48     @param	heightp		height of the read image
49     @param	screen_gamma	gamma correction value
50     @return	Pointer on the buffer containing the image
51 		<br>NULL Error
52  */
53 unsigned char *
GfImgReadPng(const char * filename,int * widthp,int * heightp,float screen_gamma)54 GfImgReadPng(const char *filename, int *widthp, int *heightp, float screen_gamma)
55 {
56 	unsigned char buf[PNG_BYTES_TO_CHECK];
57 	FILE *fp;
58 	png_structp	png_ptr;
59 	png_infop info_ptr;
60 	png_uint_32 width, height;
61 	int	bit_depth, color_type, interlace_type;
62 
63 	/*     png_color_16p	image_background; */
64 	double gamma;
65 	png_bytep *row_pointers;
66 	unsigned char *image_ptr, *cur_ptr;
67 	png_uint_32 rowbytes;
68 	png_uint_32 i;
69 
70 	if ((fp = fopen(filename, "rb")) == NULL) {
71 		GfTrace("Can't open file %s\n", filename);
72 		return (unsigned char *)NULL;
73 	}
74 
75 	if (fread(buf, 1, PNG_BYTES_TO_CHECK, fp) != PNG_BYTES_TO_CHECK) {
76 		GfTrace("Can't read file %s\n", filename);
77 		fclose(fp);
78 		return (unsigned char *)NULL;
79 	}
80 
81 	if (png_sig_cmp(buf, (png_size_t)0, PNG_BYTES_TO_CHECK) != 0) {
82 		GfTrace("File %s not in png format\n", filename);
83 		fclose(fp);
84 		return (unsigned char *)NULL;
85 	}
86 
87 	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, (png_error_ptr)NULL, (png_error_ptr)NULL);
88 	if (png_ptr == NULL) {
89 		GfTrace("Img Failed to create read_struct\n");
90 		fclose(fp);
91 		return (unsigned char *)NULL;
92 	}
93 
94 	info_ptr = png_create_info_struct(png_ptr);
95 	if (info_ptr == NULL) {
96 		fclose(fp);
97 		png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
98 		return (unsigned char *)NULL;
99 	}
100 
101 	if (setjmp(png_jmpbuf(png_ptr)))
102 	{
103 		/* Free all of the memory associated with the png_ptr and info_ptr */
104 		png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
105 		fclose(fp);
106 		/* If we get here, we had a problem reading the file */
107 		return (unsigned char *)NULL;
108 	}
109 
110 	png_init_io(png_ptr, fp);
111 	png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK);
112 	png_read_info(png_ptr, info_ptr);
113 	png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL);
114 	*widthp = (int)width;
115 	*heightp = (int)height;
116 
117 	if (bit_depth == 1 && color_type == PNG_COLOR_TYPE_GRAY) png_set_invert_mono(png_ptr);
118 	if (bit_depth == 16) {
119 		png_set_swap(png_ptr);
120 		png_set_strip_16(png_ptr);
121 	}
122 
123 	if (bit_depth < 8) {
124 		png_set_packing(png_ptr);
125 	}
126 
127 	if (color_type == PNG_COLOR_TYPE_PALETTE) {
128 		png_set_expand(png_ptr);
129 	}
130 
131 	if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
132 		png_set_expand(png_ptr);
133 	}
134 
135 	if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
136 		png_set_expand(png_ptr);
137 	}
138 
139 	if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
140 		png_set_gray_to_rgb(png_ptr);
141 	}
142 
143 	if (bit_depth == 8 && color_type == PNG_COLOR_TYPE_RGB) {
144 		png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
145 	}
146 
147 	if (png_get_gAMA(png_ptr, info_ptr, &gamma)) {
148 		png_set_gamma(png_ptr, screen_gamma, gamma);
149 	} else {
150 		png_set_gamma(png_ptr, screen_gamma, 0.50);
151 	}
152 
153 	png_read_update_info(png_ptr, info_ptr);
154 	rowbytes = png_get_rowbytes(png_ptr, info_ptr);
155 
156 	// RGBA expected.
157 	if (rowbytes != (4 * width)) {
158 		GfTrace("%s bad byte count... %lu instead of %lu\n", filename, (unsigned long) rowbytes, (unsigned long) (4 * width));
159 		fclose(fp);
160 		png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
161 		return (unsigned char *)NULL;
162 	}
163 
164 	row_pointers = (png_bytep*)malloc(height * sizeof(png_bytep));
165 	if (row_pointers == NULL) {
166 		fclose(fp);
167 		png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
168 		return (unsigned char *)NULL;
169 	}
170 
171 	image_ptr = (unsigned char *)malloc(height * rowbytes);
172 	if (image_ptr == NULL) {
173 		fclose(fp);
174 		png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
175 		return (unsigned char *)NULL;
176 	}
177 
178 	for (i = 0, cur_ptr = image_ptr + (height - 1) * rowbytes ; i < height; i++, cur_ptr -= rowbytes) {
179 		row_pointers[i] = cur_ptr;
180 	}
181 
182 	png_read_image(png_ptr, row_pointers);
183 	png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
184 	free(row_pointers);
185 
186 	fclose(fp);
187 	return image_ptr;
188 }
189 
190 
191 /** Write a buffer to a png image on disk.
192     @ingroup	img
193     @param	img		image data (RGB)
194     @param	filename	filename of the png file
195     @param	width		width of the image
196     @param	height		height of the image
197     @return	0 Ok
198 		<br>-1 Error
199  */
200 int
GfImgWritePng(unsigned char * img,const char * filename,int width,int height)201 GfImgWritePng(unsigned char *img, const char *filename, int width, int height)
202 {
203 	FILE *fp;
204 	png_structp	png_ptr;
205 	png_infop info_ptr;
206 	png_bytep *row_pointers;
207 	png_uint_32 rowbytes;
208 	int i;
209 	unsigned char *cur_ptr;
210 #if 0
211     void		*handle;
212 #endif
213 	float		screen_gamma;
214 
215 	fp = fopen(filename, "wb");
216 	if (fp == NULL) {
217 		GfTrace("Can't open file %s\n", filename);
218 		return -1;
219 	}
220 
221 	png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, (png_error_ptr)NULL, (png_error_ptr)NULL);
222 	if (png_ptr == NULL) {
223 		return -1;
224 	}
225 
226 	info_ptr = png_create_info_struct(png_ptr);
227 	if (info_ptr == NULL) {
228 		png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
229 		return -1;
230 	}
231 
232 	if (setjmp(png_jmpbuf(png_ptr))) {
233 		png_destroy_write_struct(&png_ptr, &info_ptr);
234 		fclose(fp);
235 		return -1;
236 	}
237 
238 	png_init_io(png_ptr, fp);
239 	png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB,
240 			PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
241 #if 0
242     handle = GfParmReadFile(GFSCR_CONF_FILE, GFPARM_RMODE_STD | GFPARM_RMODE_CREAT);
243     screen_gamma = (float)GfParmGetNum(handle, GFSCR_SECT_PROP, GFSCR_ATT_GAMMA, (char*)NULL, 2.0);
244     GfParmReleaseHandle(handle);
245 #else
246 	screen_gamma = 2.0;
247 #endif
248 	png_set_gAMA(png_ptr, info_ptr, screen_gamma);
249 	/* png_set_bgr(png_ptr);    TO INVERT THE COLORS !!!! */
250 	png_write_info(png_ptr, info_ptr);
251 	png_write_flush(png_ptr);
252 
253 	rowbytes = width * 3;
254 	row_pointers = (png_bytep*)malloc(height * sizeof(png_bytep));
255 
256 	if (row_pointers == NULL) {
257 		fclose(fp);
258 		png_destroy_write_struct(&png_ptr, &info_ptr);
259 		return -1;
260 	}
261 
262 	for (i = 0, cur_ptr = img + (height - 1) * rowbytes ; i < height; i++, cur_ptr -= rowbytes) {
263 		row_pointers[i] = cur_ptr;
264 	}
265 
266 	png_write_image(png_ptr, row_pointers);
267 	png_write_end(png_ptr, (png_infop)NULL);
268 	png_destroy_write_struct(&png_ptr, &info_ptr);
269 	fclose(fp);
270 	free(row_pointers);
271 	return 0;
272 }
273 
274 /** Free the texture
275     @ingroup	img
276     @param	tex	texture to free
277     @return	none
278 */
279 void
GfImgFreeTex(GLuint tex)280 GfImgFreeTex(GLuint tex)
281 {
282 	if (tex != 0) {
283 		glDeleteTextures(1, &tex);
284 	}
285 }
286 
287 /** Read a png image into a texture.
288     @ingroup	img
289     @param	filename	file name of the image
290     @return	None.
291  */
292 GLuint
GfImgReadTex(char * filename)293 GfImgReadTex(char *filename)
294 {
295 	void *handle;
296 	float screen_gamma;
297 	GLbyte *tex;
298 	int w, h;
299 	GLuint retTex;
300 	const int BUFSIZE = 1024;
301 	char buf[BUFSIZE];
302 
303 	snprintf(buf, BUFSIZE, "%s%s", GetLocalDir(), GFSCR_CONF_FILE);
304 	handle = GfParmReadFile(buf, GFPARM_RMODE_STD | GFPARM_RMODE_CREAT);
305 	screen_gamma = (float)GfParmGetNum(handle, GFSCR_SECT_PROP, GFSCR_ATT_GAMMA, (char*)NULL, 2.0);
306 	tex = (GLbyte*)GfImgReadPng(filename, &w, &h, screen_gamma);
307 
308 	if (!tex) {
309 		GfParmReleaseHandle(handle);
310 		return 0;
311 	}
312 
313 	glGenTextures(1, &retTex);
314 	glBindTexture(GL_TEXTURE_2D, retTex);
315 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
316 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
317 	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid *)(tex));
318 
319 	free(tex);
320 
321 	GfParmReleaseHandle(handle);
322 	return retTex;
323 }
324