1 /*
2 ===========================================================================
3 Copyright (C) 1999 - 2005, Id Software, Inc.
4 Copyright (C) 2000 - 2013, Raven Software, Inc.
5 Copyright (C) 2001 - 2013, Activision, Inc.
6 Copyright (C) 2005 - 2015, ioquake3 contributors
7 Copyright (C) 2013 - 2015, OpenJK contributors
8 
9 This file is part of the OpenJK source code.
10 
11 OpenJK is free software; you can redistribute it and/or modify it
12 under the terms of the GNU General Public License version 2 as
13 published by the Free Software Foundation.
14 
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, see <http://www.gnu.org/licenses/>.
22 ===========================================================================
23 */
24 
25 #include "tr_common.h"
26 #include <png.h>
27 
user_write_data(png_structp png_ptr,png_bytep data,png_size_t length)28 void user_write_data( png_structp png_ptr, png_bytep data, png_size_t length ) {
29 	fileHandle_t fp = *(fileHandle_t*)png_get_io_ptr( png_ptr );
30 	ri.FS_Write( data, length, fp );
31 }
user_flush_data(png_structp png_ptr)32 void user_flush_data( png_structp png_ptr ) {
33 	//TODO: ri.FS_Flush?
34 }
35 
RE_SavePNG(const char * filename,byte * buf,size_t width,size_t height,int byteDepth)36 int RE_SavePNG( const char *filename, byte *buf, size_t width, size_t height, int byteDepth ) {
37 	fileHandle_t fp;
38 	png_structp png_ptr = NULL;
39 	png_infop info_ptr = NULL;
40 	unsigned int x, y;
41 	png_byte ** row_pointers = NULL;
42 	/* "status" contains the return value of this function. At first
43 	it is set to a value which means 'failure'. When the routine
44 	has finished its work, it is set to a value which means
45 	'success'. */
46 	int status = -1;
47 	/* The following number is set by trial and error only. I cannot
48 	see where it it is documented in the libpng manual.
49 	*/
50 	int depth = 8;
51 
52 	fp = ri.FS_FOpenFileWrite( filename, qtrue );
53 	if ( !fp ) {
54 		goto fopen_failed;
55 	}
56 
57 	png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
58 	if (png_ptr == NULL) {
59 		goto png_create_write_struct_failed;
60 	}
61 
62 	info_ptr = png_create_info_struct (png_ptr);
63 	if (info_ptr == NULL) {
64 		goto png_create_info_struct_failed;
65 	}
66 
67 	/* Set up error handling. */
68 
69 	if (setjmp (png_jmpbuf (png_ptr))) {
70 		goto png_failure;
71 	}
72 
73 	/* Set image attributes. */
74 
75 	png_set_IHDR (png_ptr,
76 		info_ptr,
77 		width,
78 		height,
79 		depth,
80 		PNG_COLOR_TYPE_RGB,
81 		PNG_INTERLACE_NONE,
82 		PNG_COMPRESSION_TYPE_DEFAULT,
83 		PNG_FILTER_TYPE_DEFAULT);
84 
85 	/* Initialize rows of PNG. */
86 
87 	row_pointers = (png_byte **)png_malloc (png_ptr, height * sizeof (png_byte *));
88 	for ( y=0; y<height; ++y ) {
89 		png_byte *row = (png_byte *)png_malloc (png_ptr, sizeof (uint8_t) * width * byteDepth);
90 		row_pointers[height-y-1] = row;
91 		for (x = 0; x < width; ++x) {
92 			byte *px = buf + (width * y + x)*3;
93 			*row++ = px[0];
94 			*row++ = px[1];
95 			*row++ = px[2];
96 		}
97 	}
98 
99 	/* Write the image data to "fp". */
100 
101 //	png_init_io (png_ptr, fp);
102 	png_set_write_fn( png_ptr, (png_voidp)&fp, user_write_data, user_flush_data );
103 	png_set_rows (png_ptr, info_ptr, row_pointers);
104 	png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
105 
106 	/* The routine has successfully written the file, so we set
107 	"status" to a value which indicates success. */
108 
109 	status = 0;
110 
111 	for (y = 0; y < height; y++) {
112 		png_free (png_ptr, row_pointers[y]);
113 	}
114 	png_free (png_ptr, row_pointers);
115 
116 png_failure:
117 png_create_info_struct_failed:
118 	png_destroy_write_struct (&png_ptr, &info_ptr);
119 png_create_write_struct_failed:
120 	ri.FS_FCloseFile( fp );
121 fopen_failed:
122 	return status;
123 }
124 
125 void user_read_data( png_structp png_ptr, png_bytep data, png_size_t length );
png_print_error(png_structp png_ptr,png_const_charp err)126 void png_print_error ( png_structp png_ptr, png_const_charp err )
127 {
128 	ri.Printf (PRINT_ERROR, "%s\n", err);
129 }
130 
png_print_warning(png_structp png_ptr,png_const_charp warning)131 void png_print_warning ( png_structp png_ptr, png_const_charp warning )
132 {
133 	ri.Printf (PRINT_WARNING, "%s\n", warning);
134 }
135 
IsPowerOfTwo(int i)136 bool IsPowerOfTwo ( int i ) { return (i & (i - 1)) == 0; }
137 
138 struct PNGFileReader
139 {
PNGFileReaderPNGFileReader140 	PNGFileReader ( char *buf ) : buf(buf), offset(0), png_ptr(NULL), info_ptr(NULL) {}
~PNGFileReaderPNGFileReader141 	~PNGFileReader()
142 	{
143 		ri.FS_FreeFile (buf);
144 		png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
145 	}
146 
ReadPNGFileReader147 	int Read ( byte **data, int *width, int *height )
148 	{
149 		// Setup the pointers
150 		*data = NULL;
151 		*width = 0;
152 		*height = 0;
153 
154 		// Make sure we're actually reading PNG data.
155 		const int SIGNATURE_LEN = 8;
156 
157 		byte ident[SIGNATURE_LEN];
158 		memcpy (ident, buf, SIGNATURE_LEN);
159 
160 		if ( !png_check_sig (ident, SIGNATURE_LEN) )
161 		{
162 			ri.Printf (PRINT_ERROR, "PNG signature not found in given image.");
163 			return 0;
164 		}
165 
166 		png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, png_print_error, png_print_warning);
167 		if ( png_ptr == NULL )
168 		{
169 			ri.Printf (PRINT_ERROR, "Could not allocate enough memory to load the image.");
170 			return 0;
171 		}
172 
173 		info_ptr = png_create_info_struct (png_ptr);
174 		if ( setjmp (png_jmpbuf (png_ptr)) )
175 		{
176 			return 0;
177 		}
178 
179 		// We've read the signature
180 		offset += SIGNATURE_LEN;
181 
182 		// Setup reading information, and read header
183 		png_set_read_fn (png_ptr, (png_voidp)this, &user_read_data);
184 #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
185 		// This generic "ignore all, except required chunks" requires 1.6.0 or newer"
186 		png_set_keep_unknown_chunks (png_ptr, PNG_HANDLE_CHUNK_NEVER, NULL, -1);
187 #endif
188 		png_set_sig_bytes (png_ptr, SIGNATURE_LEN);
189 		png_read_info (png_ptr, info_ptr);
190 
191 		png_uint_32 width_;
192 		png_uint_32 height_;
193 		int depth;
194 		int colortype;
195 
196 		png_get_IHDR (png_ptr, info_ptr, &width_, &height_, &depth, &colortype, NULL, NULL, NULL);
197 
198 		// While modern OpenGL can handle non-PoT textures, it's faster to handle only PoT
199 		// so that the graphics driver doesn't have to fiddle about with the texture when uploading.
200 		if ( !IsPowerOfTwo (width_) || !IsPowerOfTwo (height_) )
201 		{
202 			ri.Printf (PRINT_ERROR, "Width or height is not a power-of-two.\n");
203 			return 0;
204 		}
205 
206 		// This function is equivalent to using what used to be LoadPNG32. LoadPNG8 also existed,
207 		// but this only seemed to be used by the RMG system which does not work in JKA. If this
208 		// does need to be re-implemented, then colortype should be PNG_COLOR_TYPE_PALETTE or
209 		// PNG_COLOR_TYPE_GRAY.
210 		if ( colortype != PNG_COLOR_TYPE_RGB && colortype != PNG_COLOR_TYPE_RGBA )
211 		{
212 			ri.Printf (PRINT_ERROR, "Image is not 24-bit or 32-bit.");
213 			return 0;
214 		}
215 
216 		// Read the png data
217 		if ( colortype == PNG_COLOR_TYPE_RGB )
218 		{
219 			// Expand RGB -> RGBA
220 			png_set_add_alpha (png_ptr, 0xff, PNG_FILLER_AFTER);
221 		}
222 
223 		png_read_update_info (png_ptr, info_ptr);
224 
225 		// We always assume there are 4 channels. RGB channels are expanded to RGBA when read.
226 		byte *tempData = (byte *)ri.Z_Malloc (width_ * height_ * 4, TAG_TEMP_PNG, qfalse, 4);
227 		if ( !tempData )
228 		{
229 			ri.Printf (PRINT_ERROR, "Could not allocate enough memory to load the image.");
230 			return 0;
231 		}
232 
233 		// Dynamic array of row pointers, with 'height' elements, initialized to NULL.
234 		byte **row_pointers = (byte **)ri.Hunk_AllocateTempMemory (sizeof (byte *) * height_);
235 		if ( !row_pointers )
236 		{
237 			ri.Printf (PRINT_ERROR, "Could not allocate enough memory to load the image.");
238 
239 			ri.Z_Free (tempData);
240 
241 			return 0;
242 		}
243 
244 		// Re-set the jmp so that these new memory allocations can be reclaimed
245 		if ( setjmp (png_jmpbuf (png_ptr)) )
246 		{
247 			ri.Hunk_FreeTempMemory (row_pointers);
248 			ri.Z_Free (tempData);
249 			return 0;
250 		}
251 
252 		for ( unsigned int i = 0, j = 0; i < height_; i++, j += 4 )
253 		{
254 			row_pointers[i] = tempData + j * width_;
255 		}
256 
257 		png_read_image (png_ptr, row_pointers);
258 
259 		// Finish reading
260 		png_read_end (png_ptr, NULL);
261 
262 		ri.Hunk_FreeTempMemory (row_pointers);
263 
264 		// Finally assign all the parameters
265 		*data = tempData;
266 		*width = width_;
267 		*height = height_;
268 
269 		return 1;
270 	}
271 
ReadBytesPNGFileReader272 	void ReadBytes ( void *dest, size_t len )
273 	{
274 		memcpy (dest, buf + offset, len);
275 		offset += len;
276 	}
277 
278 private:
279 	char *buf;
280 	size_t offset;
281 	png_structp png_ptr;
282 	png_infop info_ptr;
283 };
284 
user_read_data(png_structp png_ptr,png_bytep data,png_size_t length)285 void user_read_data( png_structp png_ptr, png_bytep data, png_size_t length ) {
286 	png_voidp r = png_get_io_ptr (png_ptr);
287 	PNGFileReader *reader = (PNGFileReader *)r;
288 	reader->ReadBytes (data, length);
289 }
290 
291 // Loads a PNG image from file.
LoadPNG(const char * filename,byte ** data,int * width,int * height)292 void LoadPNG ( const char *filename, byte **data, int *width, int *height )
293 {
294 	char *buf = NULL;
295 	int len = ri.FS_ReadFile (filename, (void **)&buf);
296 	if ( len < 0 || buf == NULL )
297 	{
298 		return;
299 	}
300 
301 	PNGFileReader reader (buf);
302 	reader.Read (data, width, height);
303 }
304 
305