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