]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/q3map2/image.c
transfer from internal tree r5311 branches/1.4-gpl
[xonotic/netradiant.git] / tools / quake3 / q3map2 / image.c
1 /*\r
2 Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.\r
4 \r
5 This file is part of GtkRadiant.\r
6 \r
7 GtkRadiant is free software; you can redistribute it and/or modify\r
8 it under the terms of the GNU General Public License as published by\r
9 the Free Software Foundation; either version 2 of the License, or\r
10 (at your option) any later version.\r
11 \r
12 GtkRadiant is distributed in the hope that it will be useful,\r
13 but WITHOUT ANY WARRANTY; without even the implied warranty of\r
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
15 GNU General Public License for more details.\r
16 \r
17 You should have received a copy of the GNU General Public License\r
18 along with GtkRadiant; if not, write to the Free Software\r
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
20 \r
21 ----------------------------------------------------------------------------------\r
22 \r
23 This code has been altered significantly from its original form, to support\r
24 several games based on the Quake III Arena engine, in the form of "Q3Map2."\r
25 \r
26 ------------------------------------------------------------------------------- */\r
27 \r
28 \r
29 \r
30 /* marker */\r
31 #define IMAGE_C\r
32 \r
33 \r
34 \r
35 /* dependencies */\r
36 #include "q3map2.h"\r
37 \r
38 \r
39 \r
40 /* -------------------------------------------------------------------------------\r
41 \r
42 this file contains image pool management with reference counting. note: it isn't\r
43 reentrant, so only call it from init/shutdown code or wrap calls in a mutex\r
44 \r
45 ------------------------------------------------------------------------------- */\r
46 \r
47 /*\r
48 LoadDDSBuffer()\r
49 loads a dxtc (1, 3, 5) dds buffer into a valid rgba image\r
50 */\r
51 \r
52 static void LoadDDSBuffer( byte *buffer, int size, byte **pixels, int *width, int *height )\r
53 {\r
54         int             w, h;\r
55         ddsPF_t pf;\r
56         \r
57         \r
58         /* dummy check */\r
59         if( buffer == NULL || size <= 0 || pixels == NULL || width == NULL || height == NULL )\r
60                 return;\r
61         \r
62         /* null out */\r
63         *pixels = 0;\r
64         *width = 0;\r
65         *height = 0;\r
66         \r
67         /* get dds info */\r
68         if( DDSGetInfo( (ddsBuffer_t*) buffer, &w, &h, &pf ) )\r
69         {\r
70                 Sys_Printf( "WARNING: Invalid DDS texture\n" );\r
71                 return;\r
72         }\r
73         \r
74         /* only certain types of dds textures are supported */\r
75         if( pf != DDS_PF_ARGB8888 && pf != DDS_PF_DXT1 && pf != DDS_PF_DXT3 && pf != DDS_PF_DXT5 )\r
76         {\r
77                 Sys_Printf( "WARNING: Only DDS texture formats ARGB8888, DXT1, DXT3, and DXT5 are supported (%d)\n", pf );\r
78                 return;\r
79         }\r
80         \r
81         /* create image pixel buffer */\r
82         *width = w;\r
83         *height = h;\r
84         *pixels = safe_malloc( w * h * 4 );\r
85         \r
86         /* decompress the dds texture */\r
87         DDSDecompress( (ddsBuffer_t*) buffer, *pixels );\r
88 }\r
89 \r
90 \r
91 \r
92 /*\r
93 PNGReadData()\r
94 callback function for libpng to read from a memory buffer\r
95 note: this function is a total hack, as it reads/writes the png struct directly!\r
96 */\r
97 \r
98 typedef struct pngBuffer_s\r
99 {\r
100         byte    *buffer;\r
101         int             size, offset;\r
102 }\r
103 pngBuffer_t;\r
104 \r
105 void PNGReadData( png_struct *png, png_byte *buffer, png_size_t size )\r
106 {\r
107         pngBuffer_t             *pb = (pngBuffer_t*) png_get_io_ptr( png );\r
108         \r
109         \r
110         if( (pb->offset + size) > pb->size )\r
111                 size = (pb->size - pb->offset);\r
112         memcpy( buffer, &pb->buffer[ pb->offset ], size );\r
113         pb->offset += size;\r
114         //%     Sys_Printf( "Copying %d bytes from 0x%08X to 0x%08X (offset: %d of %d)\n", size, &pb->buffer[ pb->offset ], buffer, pb->offset, pb->size );\r
115 }\r
116 \r
117 \r
118 \r
119 /*\r
120 LoadPNGBuffer()\r
121 loads a png file buffer into a valid rgba image\r
122 */\r
123 \r
124 static void LoadPNGBuffer( byte *buffer, int size, byte **pixels, int *width, int *height )\r
125 {\r
126         png_struct      *png;\r
127         png_info        *info, *end;\r
128         pngBuffer_t     pb;\r
129         int                     i, bitDepth, colorType, channels;\r
130         png_uint_32     w, h;\r
131         byte            **rowPointers;\r
132         \r
133         \r
134         /* dummy check */\r
135         if( buffer == NULL || size <= 0 || pixels == NULL || width == NULL || height == NULL )\r
136                 return;\r
137         \r
138         /* null out */\r
139         *pixels = 0;\r
140         *width = 0;\r
141         *height = 0;\r
142         \r
143         /* determine if this is a png file */\r
144         if( png_sig_cmp( buffer, 0, 8 ) != 0 )\r
145         {\r
146                 Sys_Printf( "WARNING: Invalid PNG file\n" );\r
147                 return;\r
148         }\r
149         \r
150         /* create png structs */\r
151         png = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL );\r
152         if( png == NULL )\r
153         {\r
154                 Sys_Printf( "WARNING: Unable to create PNG read struct\n" );\r
155                 return;\r
156         }\r
157         \r
158         info = png_create_info_struct( png );\r
159         if( info == NULL )\r
160         {\r
161                 Sys_Printf( "WARNING: Unable to create PNG info struct\n" );\r
162                 png_destroy_read_struct( &png, NULL, NULL );\r
163                 return;\r
164         }\r
165         \r
166         end = png_create_info_struct( png );\r
167         if( end == NULL )\r
168         {\r
169                 Sys_Printf( "WARNING: Unable to create PNG end info struct\n" );\r
170                 png_destroy_read_struct( &png, &info, NULL );\r
171                 return;\r
172         }\r
173         \r
174         /* set read callback */\r
175         pb.buffer = buffer;\r
176         pb.size = size;\r
177         pb.offset = 0;\r
178         png_set_read_fn( png, &pb, PNGReadData );\r
179         png->io_ptr = &pb; /* hack! */\r
180         \r
181         /* set error longjmp */\r
182         if( setjmp( png->jmpbuf ) )\r
183         {\r
184                 Sys_Printf( "WARNING: An error occurred reading PNG image\n" );\r
185                 png_destroy_read_struct( &png, &info, &end );\r
186                 return;\r
187         }\r
188         \r
189         /* fixme: add proper i/o stuff here */\r
190 \r
191         /* read png info */\r
192         png_read_info( png, info );\r
193         \r
194         /* read image header chunk */\r
195         png_get_IHDR( png, info,\r
196                 &w, &h, &bitDepth, &colorType, NULL, NULL, NULL );\r
197         \r
198         /* read number of channels */\r
199         channels = png_get_channels( png, info );\r
200         \r
201         /* the following will probably bork on certain types of png images, but hey... */\r
202 \r
203         /* force indexed/gray/trans chunk to rgb */\r
204         if( (colorType == PNG_COLOR_TYPE_PALETTE && bitDepth <= 8) ||\r
205                 (colorType == PNG_COLOR_TYPE_GRAY && bitDepth <= 8) ||\r
206                 png_get_valid( png, info, PNG_INFO_tRNS ) )\r
207                 png_set_expand( png );\r
208         \r
209         /* strip 16bpc -> 8bpc */\r
210         if( bitDepth == 16 )\r
211                 png_set_strip_16( png );\r
212         \r
213         /* pad rgb to rgba */\r
214         if( bitDepth == 8 && colorType == PNG_COLOR_TYPE_RGB)\r
215                 png_set_filler( png, 255, PNG_FILLER_AFTER );\r
216         \r
217         /* create image pixel buffer */\r
218         *width = w;\r
219         *height = h;\r
220         *pixels = safe_malloc( w * h * 4 );\r
221         \r
222         /* create row pointers */\r
223         rowPointers = safe_malloc( h * sizeof( byte* ) );\r
224         for( i = 0; i < h; i++ )\r
225                 rowPointers[ i ] = *pixels + (i * w * 4);\r
226         \r
227         /* read the png */\r
228         png_read_image( png, rowPointers );\r
229         \r
230         /* clean up */\r
231         free( rowPointers );\r
232         png_destroy_read_struct( &png, &info, &end );\r
233         \r
234 }\r
235 \r
236 \r
237 \r
238 /*\r
239 ImageInit()\r
240 implicitly called by every function to set up image list\r
241 */\r
242 \r
243 static void ImageInit( void )\r
244 {\r
245         int             i;\r
246         \r
247         \r
248         if( numImages <= 0 )\r
249         {\r
250                 /* clear images (fixme: this could theoretically leak) */\r
251                 memset( images, 0, sizeof( images ) );\r
252                 \r
253                 /* generate *bogus image */\r
254                 images[ 0 ].name = safe_malloc( strlen( DEFAULT_IMAGE ) + 1 );\r
255                 strcpy( images[ 0 ].name, DEFAULT_IMAGE );\r
256                 images[ 0 ].filename = safe_malloc( strlen( DEFAULT_IMAGE ) + 1 );\r
257                 strcpy( images[ 0 ].filename, DEFAULT_IMAGE );\r
258                 images[ 0 ].width = 64;\r
259                 images[ 0 ].height = 64;\r
260                 images[ 0 ].refCount = 1;\r
261                 images[ 0 ].pixels = safe_malloc( 64 * 64 * 4 );\r
262                 for( i = 0; i < (64 * 64 * 4); i++ )\r
263                         images[ 0 ].pixels[ i ] = 255;\r
264         }\r
265 }\r
266 \r
267 \r
268 \r
269 /*\r
270 ImageFree()\r
271 frees an rgba image\r
272 */\r
273 \r
274 void ImageFree( image_t *image )\r
275 {\r
276         /* dummy check */\r
277         if( image == NULL )\r
278                 return;\r
279         \r
280         /* decrement refcount */\r
281         image->refCount--;\r
282         \r
283         /* free? */\r
284         if( image->refCount <= 0 )\r
285         {\r
286                 if( image->name != NULL )\r
287                         free( image->name );\r
288                 image->name = NULL;\r
289                 if( image->filename != NULL )\r
290                         free( image->filename );\r
291                 image->filename = NULL;\r
292                 free( image->pixels );\r
293                 image->width = 0;\r
294                 image->height = 0;\r
295                 numImages--;\r
296         }\r
297 }\r
298 \r
299 \r
300 \r
301 /*\r
302 ImageFind()\r
303 finds an existing rgba image and returns a pointer to the image_t struct or NULL if not found\r
304 */\r
305 \r
306 image_t *ImageFind( const char *filename )\r
307 {\r
308         int                     i;\r
309         char            name[ 1024 ];\r
310         \r
311         \r
312         /* init */\r
313         ImageInit();\r
314         \r
315         /* dummy check */\r
316         if( filename == NULL || filename[ 0 ] == '\0' )\r
317                 return NULL;\r
318         \r
319         /* strip file extension off name */\r
320         strcpy( name, filename );\r
321         StripExtension( name );\r
322         \r
323         /* search list */\r
324         for( i = 0; i < MAX_IMAGES; i++ )\r
325         {\r
326                 if( images[ i ].name != NULL && !strcmp( name, images[ i ].name ) )\r
327                         return &images[ i ];\r
328         }\r
329         \r
330         /* no matching image found */\r
331         return NULL;\r
332 }\r
333 \r
334 \r
335 \r
336 /*\r
337 ImageLoad()\r
338 loads an rgba image and returns a pointer to the image_t struct or NULL if not found\r
339 */\r
340 \r
341 image_t *ImageLoad( const char *filename )\r
342 {\r
343         int                     i;\r
344         image_t         *image;\r
345         char            name[ 1024 ];\r
346         int                     size;\r
347         byte            *buffer = NULL;\r
348 \r
349         \r
350         /* init */\r
351         ImageInit();\r
352         \r
353         /* dummy check */\r
354         if( filename == NULL || filename[ 0 ] == '\0' )\r
355                 return NULL;\r
356         \r
357         /* strip file extension off name */\r
358         strcpy( name, filename );\r
359         StripExtension( name );\r
360         \r
361         /* try to find existing image */\r
362         image = ImageFind( name );\r
363         if( image != NULL )\r
364         {\r
365                 image->refCount++;\r
366                 return image;\r
367         }\r
368         \r
369         /* none found, so find first non-null image */\r
370         image = NULL;\r
371         for( i = 0; i < MAX_IMAGES; i++ )\r
372         {\r
373                 if( images[ i ].name == NULL )\r
374                 {\r
375                         image = &images[ i ];\r
376                         break;\r
377                 }\r
378         }\r
379         \r
380         /* too many images? */\r
381         if( image == NULL )\r
382                 Error( "MAX_IMAGES (%d) exceeded, there are too many image files referenced by the map.", MAX_IMAGES );\r
383         \r
384         /* set it up */\r
385         image->name = safe_malloc( strlen( name ) + 1 );\r
386         strcpy( image->name, name );\r
387         \r
388         /* attempt to load tga */\r
389         StripExtension( name );\r
390         strcat( name, ".tga" );\r
391         size = vfsLoadFile( (const char*) name, (void**) &buffer, 0 );\r
392         if( size > 0 )\r
393                 LoadTGABuffer( buffer, &image->pixels, &image->width, &image->height );\r
394         else\r
395         {\r
396                 /* attempt to load png */\r
397                 StripExtension( name );\r
398                 strcat( name, ".png" );\r
399                 size = vfsLoadFile( (const char*) name, (void**) &buffer, 0 );\r
400                 if( size > 0 )\r
401                         LoadPNGBuffer( buffer, size, &image->pixels, &image->width, &image->height );\r
402                 else\r
403                 {\r
404                         /* attempt to load jpg */\r
405                         StripExtension( name );\r
406                         strcat( name, ".jpg" );\r
407                         size = vfsLoadFile( (const char*) name, (void**) &buffer, 0 );\r
408                         if( size > 0 )\r
409                         {\r
410                                 if( LoadJPGBuff( buffer, size, &image->pixels, &image->width, &image->height ) == -1 && image->pixels != NULL )\r
411                                         Sys_Printf( "WARNING: LoadJPGBuff: %s\n", (unsigned char*) image->pixels );\r
412                         }\r
413                         else\r
414                         {\r
415                                 /* attempt to load dds */\r
416                                 StripExtension( name );\r
417                                 strcat( name, ".dds" );\r
418                                 size = vfsLoadFile( (const char*) name, (void**) &buffer, 0 );\r
419                                 if( size > 0 )\r
420                                 {\r
421                                         LoadDDSBuffer( buffer, size, &image->pixels, &image->width, &image->height );\r
422                                         \r
423                                         /* debug code */\r
424                                         #if 1\r
425                                         {\r
426                                                 ddsPF_t pf;\r
427                                                 DDSGetInfo( (ddsBuffer_t*) buffer, NULL, NULL, &pf );\r
428                                                 Sys_Printf( "pf = %d\n", pf );\r
429                                                 if( image->width > 0 )\r
430                                                 {\r
431                                                         StripExtension( name );\r
432                                                         strcat( name, "_converted.tga" );\r
433                                                         WriteTGA( "C:\\games\\quake3\\baseq3\\textures\\rad\\dds_converted.tga", image->pixels, image->width, image->height );\r
434                                                 }\r
435                                         }\r
436                                         #endif\r
437                                 }\r
438                         }\r
439                 }\r
440         }\r
441         \r
442         /* free file buffer */\r
443         free( buffer );\r
444         \r
445         /* make sure everything's kosher */\r
446         if( size <= 0 || image->width <= 0 || image->height <= 0 || image->pixels == NULL )\r
447         {\r
448                 //%     Sys_Printf( "size = %d  width = %d  height = %d  pixels = 0x%08x (%s)\n",\r
449                 //%             size, image->width, image->height, image->pixels, name );\r
450                 free( image->name );\r
451                 image->name = NULL;\r
452                 return NULL;\r
453         }\r
454         \r
455         /* set filename */\r
456         image->filename = safe_malloc( strlen( name ) + 1 );\r
457         strcpy( image->filename, name );\r
458         \r
459         /* set count */\r
460         image->refCount = 1;\r
461         numImages++;\r
462         \r
463         /* return the image */\r
464         return image;\r
465 }\r
466 \r
467 \r