]> git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/common/imagelib.c
8955d35a3def7fde4642f4f43998b02bf7b487c8
[xonotic/netradiant.git] / tools / quake3 / common / imagelib.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 // imagelib.c\r
23 \r
24 #include "cmdlib.h"\r
25 #include "imagelib.h"\r
26 #include "vfs.h"\r
27 \r
28 int fgetLittleShort (FILE *f)\r
29 {\r
30         byte    b1, b2;\r
31 \r
32         b1 = fgetc(f);\r
33         b2 = fgetc(f);\r
34 \r
35         return (short)(b1 + b2*256);\r
36 }\r
37 \r
38 int fgetLittleLong (FILE *f)\r
39 {\r
40         byte    b1, b2, b3, b4;\r
41 \r
42         b1 = fgetc(f);\r
43         b2 = fgetc(f);\r
44         b3 = fgetc(f);\r
45         b4 = fgetc(f);\r
46 \r
47         return b1 + (b2<<8) + (b3<<16) + (b4<<24);\r
48 }\r
49 \r
50 int bufLittleShort (byte *buf, int len, int *pos)\r
51 {\r
52   byte  b1, b2;\r
53 \r
54   if ((len - *pos) < 2)\r
55     Error ("Unexpected buffer end");\r
56 \r
57   b1 = buf[*pos]; *pos += 1;\r
58   b2 = buf[*pos]; *pos += 1;\r
59 \r
60   return (short)(b1 + b2*256);\r
61 }\r
62 \r
63 int bufLittleLong (byte *buf, int len, int *pos)\r
64 {\r
65   byte b1, b2, b3, b4;\r
66 \r
67   if ((len - *pos) < 4)\r
68     Error ("Unexpected buffer end");\r
69 \r
70   b1 = buf[*pos]; *pos += 1;\r
71   b2 = buf[*pos]; *pos += 1;\r
72   b3 = buf[*pos]; *pos += 1;\r
73   b4 = buf[*pos]; *pos += 1;\r
74 \r
75   return b1 + (b2<<8) + (b3<<16) + (b4<<24);\r
76 }\r
77 \r
78 \r
79 /*\r
80 ============================================================================\r
81 \r
82                                                 LBM STUFF\r
83 \r
84 ============================================================================\r
85 */\r
86 \r
87 \r
88 typedef unsigned char   UBYTE;\r
89 //conflicts with windows typedef short                  WORD;\r
90 typedef unsigned short  UWORD;\r
91 typedef long                    LONG;\r
92 \r
93 typedef enum\r
94 {\r
95         ms_none,\r
96         ms_mask,\r
97         ms_transcolor,\r
98         ms_lasso\r
99 } mask_t;\r
100 \r
101 typedef enum\r
102 {\r
103         cm_none,\r
104         cm_rle1\r
105 } compress_t;\r
106 \r
107 typedef struct\r
108 {\r
109         UWORD           w,h;\r
110         short           x,y;\r
111         UBYTE           nPlanes;\r
112         UBYTE           masking;\r
113         UBYTE           compression;\r
114         UBYTE           pad1;\r
115         UWORD           transparentColor;\r
116         UBYTE           xAspect,yAspect;\r
117         short           pageWidth,pageHeight;\r
118 } bmhd_t;\r
119 \r
120 extern  bmhd_t  bmhd;                                           // will be in native byte order\r
121 \r
122 \r
123 \r
124 #define FORMID ('F'+('O'<<8)+((int)'R'<<16)+((int)'M'<<24))\r
125 #define ILBMID ('I'+('L'<<8)+((int)'B'<<16)+((int)'M'<<24))\r
126 #define PBMID  ('P'+('B'<<8)+((int)'M'<<16)+((int)' '<<24))\r
127 #define BMHDID ('B'+('M'<<8)+((int)'H'<<16)+((int)'D'<<24))\r
128 #define BODYID ('B'+('O'<<8)+((int)'D'<<16)+((int)'Y'<<24))\r
129 #define CMAPID ('C'+('M'<<8)+((int)'A'<<16)+((int)'P'<<24))\r
130 \r
131 \r
132 bmhd_t  bmhd;\r
133 \r
134 int    Align (int l)\r
135 {\r
136         if (l&1)\r
137                 return l+1;\r
138         return l;\r
139 }\r
140 \r
141 \r
142 \r
143 /*\r
144 ================\r
145 LBMRLEdecompress\r
146 \r
147 Source must be evenly aligned!\r
148 ================\r
149 */\r
150 byte  *LBMRLEDecompress (byte *source,byte *unpacked, int bpwidth)\r
151 {\r
152         int     count;\r
153         byte    b,rept;\r
154 \r
155         count = 0;\r
156 \r
157         do\r
158         {\r
159                 rept = *source++;\r
160 \r
161                 if (rept > 0x80)\r
162                 {\r
163                         rept = (rept^0xff)+2;\r
164                         b = *source++;\r
165                         memset(unpacked,b,rept);\r
166                         unpacked += rept;\r
167                 }\r
168                 else if (rept < 0x80)\r
169                 {\r
170                         rept++;\r
171                         memcpy(unpacked,source,rept);\r
172                         unpacked += rept;\r
173                         source += rept;\r
174                 }\r
175                 else\r
176                         rept = 0;               // rept of 0x80 is NOP\r
177 \r
178                 count += rept;\r
179 \r
180         } while (count<bpwidth);\r
181 \r
182         if (count>bpwidth)\r
183                 Error ("Decompression exceeded width!\n");\r
184 \r
185 \r
186         return source;\r
187 }\r
188 \r
189 \r
190 /*\r
191 =================\r
192 LoadLBM\r
193 =================\r
194 */\r
195 void LoadLBM (const char *filename, byte **picture, byte **palette)\r
196 {\r
197         byte    *LBMbuffer, *picbuffer, *cmapbuffer;\r
198         int             y;\r
199         byte    *LBM_P, *LBMEND_P;\r
200         byte    *pic_p;\r
201         byte    *body_p;\r
202 \r
203         int    formtype,formlength;\r
204         int    chunktype,chunklength;\r
205 \r
206 // qiet compiler warnings\r
207         picbuffer = NULL;\r
208         cmapbuffer = NULL;\r
209 \r
210 //\r
211 // load the LBM\r
212 //\r
213         LoadFile (filename, (void **)&LBMbuffer);\r
214 \r
215 //\r
216 // parse the LBM header\r
217 //\r
218         LBM_P = LBMbuffer;\r
219         if ( *(int *)LBMbuffer != LittleLong(FORMID) )\r
220            Error ("No FORM ID at start of file!\n");\r
221 \r
222         LBM_P += 4;\r
223         formlength = BigLong( *(int *)LBM_P );\r
224         LBM_P += 4;\r
225         LBMEND_P = LBM_P + Align(formlength);\r
226 \r
227         formtype = LittleLong(*(int *)LBM_P);\r
228 \r
229         if (formtype != ILBMID && formtype != PBMID)\r
230                 Error ("Unrecognized form type: %c%c%c%c\n", formtype&0xff\r
231                 ,(formtype>>8)&0xff,(formtype>>16)&0xff,(formtype>>24)&0xff);\r
232 \r
233         LBM_P += 4;\r
234 \r
235 //\r
236 // parse chunks\r
237 //\r
238 \r
239         while (LBM_P < LBMEND_P)\r
240         {\r
241                 chunktype = LBM_P[0] + (LBM_P[1]<<8) + (LBM_P[2]<<16) + (LBM_P[3]<<24);\r
242                 LBM_P += 4;\r
243                 chunklength = LBM_P[3] + (LBM_P[2]<<8) + (LBM_P[1]<<16) + (LBM_P[0]<<24);\r
244                 LBM_P += 4;\r
245 \r
246                 switch ( chunktype )\r
247                 {\r
248                 case BMHDID:\r
249                         memcpy (&bmhd,LBM_P,sizeof(bmhd));\r
250                         bmhd.w = BigShort(bmhd.w);\r
251                         bmhd.h = BigShort(bmhd.h);\r
252                         bmhd.x = BigShort(bmhd.x);\r
253                         bmhd.y = BigShort(bmhd.y);\r
254                         bmhd.pageWidth = BigShort(bmhd.pageWidth);\r
255                         bmhd.pageHeight = BigShort(bmhd.pageHeight);\r
256                         break;\r
257 \r
258                 case CMAPID:\r
259                         cmapbuffer = safe_malloc (768);\r
260                         memset (cmapbuffer, 0, 768);\r
261                         memcpy (cmapbuffer, LBM_P, chunklength);\r
262                         break;\r
263 \r
264                 case BODYID:\r
265                         body_p = LBM_P;\r
266 \r
267                         pic_p = picbuffer = safe_malloc (bmhd.w*bmhd.h);\r
268                         if (formtype == PBMID)\r
269                         {\r
270                         //\r
271                         // unpack PBM\r
272                         //\r
273                                 for (y=0 ; y<bmhd.h ; y++, pic_p += bmhd.w)\r
274                                 {\r
275                                         if (bmhd.compression == cm_rle1)\r
276                                                 body_p = LBMRLEDecompress ((byte *)body_p\r
277                                                 , pic_p , bmhd.w);\r
278                                         else if (bmhd.compression == cm_none)\r
279                                         {\r
280                                                 memcpy (pic_p,body_p,bmhd.w);\r
281                                                 body_p += Align(bmhd.w);\r
282                                         }\r
283                                 }\r
284 \r
285                         }\r
286                         else\r
287                         {\r
288                         //\r
289                         // unpack ILBM\r
290                         //\r
291                                 Error ("%s is an interlaced LBM, not packed", filename);\r
292                         }\r
293                         break;\r
294                 }\r
295 \r
296                 LBM_P += Align(chunklength);\r
297         }\r
298 \r
299         free (LBMbuffer);\r
300 \r
301         *picture = picbuffer;\r
302 \r
303         if (palette)\r
304                 *palette = cmapbuffer;\r
305 }\r
306 \r
307 \r
308 /*\r
309 ============================================================================\r
310 \r
311                                                         WRITE LBM\r
312 \r
313 ============================================================================\r
314 */\r
315 \r
316 /*\r
317 ==============\r
318 WriteLBMfile\r
319 ==============\r
320 */\r
321 void WriteLBMfile (const char *filename, byte *data,\r
322                                    int width, int height, byte *palette)\r
323 {\r
324         byte    *lbm, *lbmptr;\r
325         int    *formlength, *bmhdlength, *cmaplength, *bodylength;\r
326         int    length;\r
327         bmhd_t  basebmhd;\r
328 \r
329         lbm = lbmptr = safe_malloc (width*height+1000);\r
330 \r
331 //\r
332 // start FORM\r
333 //\r
334         *lbmptr++ = 'F';\r
335         *lbmptr++ = 'O';\r
336         *lbmptr++ = 'R';\r
337         *lbmptr++ = 'M';\r
338 \r
339         formlength = (int*)lbmptr;\r
340         lbmptr+=4;                      // leave space for length\r
341 \r
342         *lbmptr++ = 'P';\r
343         *lbmptr++ = 'B';\r
344         *lbmptr++ = 'M';\r
345         *lbmptr++ = ' ';\r
346 \r
347 //\r
348 // write BMHD\r
349 //\r
350         *lbmptr++ = 'B';\r
351         *lbmptr++ = 'M';\r
352         *lbmptr++ = 'H';\r
353         *lbmptr++ = 'D';\r
354 \r
355         bmhdlength = (int *)lbmptr;\r
356         lbmptr+=4;                      // leave space for length\r
357 \r
358         memset (&basebmhd,0,sizeof(basebmhd));\r
359         basebmhd.w = BigShort((short)width);\r
360         basebmhd.h = BigShort((short)height);\r
361         basebmhd.nPlanes = BigShort(8);\r
362         basebmhd.xAspect = BigShort(5);\r
363         basebmhd.yAspect = BigShort(6);\r
364         basebmhd.pageWidth = BigShort((short)width);\r
365         basebmhd.pageHeight = BigShort((short)height);\r
366 \r
367         memcpy (lbmptr,&basebmhd,sizeof(basebmhd));\r
368         lbmptr += sizeof(basebmhd);\r
369 \r
370         length = lbmptr-(byte *)bmhdlength-4;\r
371         *bmhdlength = BigLong(length);\r
372         if (length&1)\r
373                 *lbmptr++ = 0;          // pad chunk to even offset\r
374 \r
375 //\r
376 // write CMAP\r
377 //\r
378         *lbmptr++ = 'C';\r
379         *lbmptr++ = 'M';\r
380         *lbmptr++ = 'A';\r
381         *lbmptr++ = 'P';\r
382 \r
383         cmaplength = (int *)lbmptr;\r
384         lbmptr+=4;                      // leave space for length\r
385 \r
386         memcpy (lbmptr,palette,768);\r
387         lbmptr += 768;\r
388 \r
389         length = lbmptr-(byte *)cmaplength-4;\r
390         *cmaplength = BigLong(length);\r
391         if (length&1)\r
392                 *lbmptr++ = 0;          // pad chunk to even offset\r
393 \r
394 //\r
395 // write BODY\r
396 //\r
397         *lbmptr++ = 'B';\r
398         *lbmptr++ = 'O';\r
399         *lbmptr++ = 'D';\r
400         *lbmptr++ = 'Y';\r
401 \r
402         bodylength = (int *)lbmptr;\r
403         lbmptr+=4;                      // leave space for length\r
404 \r
405         memcpy (lbmptr,data,width*height);\r
406         lbmptr += width*height;\r
407 \r
408         length = lbmptr-(byte *)bodylength-4;\r
409         *bodylength = BigLong(length);\r
410         if (length&1)\r
411                 *lbmptr++ = 0;          // pad chunk to even offset\r
412 \r
413 //\r
414 // done\r
415 //\r
416         length = lbmptr-(byte *)formlength-4;\r
417         *formlength = BigLong(length);\r
418         if (length&1)\r
419                 *lbmptr++ = 0;          // pad chunk to even offset\r
420 \r
421 //\r
422 // write output file\r
423 //\r
424         SaveFile (filename, lbm, lbmptr-lbm);\r
425         free (lbm);\r
426 }\r
427 \r
428 \r
429 /*\r
430 ============================================================================\r
431 \r
432 LOAD PCX\r
433 \r
434 ============================================================================\r
435 */\r
436 \r
437 typedef struct\r
438 {\r
439     char        manufacturer;\r
440     char        version;\r
441     char        encoding;\r
442     char        bits_per_pixel;\r
443     unsigned short      xmin,ymin,xmax,ymax;\r
444     unsigned short      hres,vres;\r
445     unsigned char       palette[48];\r
446     char        reserved;\r
447     char        color_planes;\r
448     unsigned short      bytes_per_line;\r
449     unsigned short      palette_type;\r
450     char        filler[58];\r
451     unsigned char       data;                   // unbounded\r
452 } pcx_t;\r
453 \r
454 \r
455 /*\r
456 ==============\r
457 LoadPCX\r
458 ==============\r
459 */\r
460 \r
461 /* RR2DO2 */\r
462 #define DECODEPCX( b, d, r ) d=*b++;if((d&0xC0)==0xC0){r=d&0x3F;d=*b++;}else{r=1;}\r
463 \r
464 void LoadPCX( const char *filename, byte **pic, byte **palette, int *width, int *height )\r
465 {\r
466         byte    *raw;\r
467         pcx_t   *pcx;\r
468         int             x, y, lsize;\r
469         int             len;\r
470         int             dataByte, runLength;\r
471         byte    *out, *pix;\r
472         \r
473 \r
474         /* load the file */\r
475         len = vfsLoadFile (filename, (void **)&raw, 0);\r
476     if( len == -1 ) \r
477                 Error( "LoadPCX: Couldn't read %s", filename );\r
478 \r
479         \r
480         /* parse the PCX file */\r
481         pcx = (pcx_t *)raw;\r
482         raw = &pcx->data;\r
483 \r
484         pcx->xmin = LittleShort(pcx->xmin);\r
485         pcx->ymin = LittleShort(pcx->ymin);\r
486         pcx->xmax = LittleShort(pcx->xmax);\r
487         pcx->ymax = LittleShort(pcx->ymax);\r
488         pcx->hres = LittleShort(pcx->hres);\r
489         pcx->vres = LittleShort(pcx->vres);\r
490         pcx->bytes_per_line = LittleShort(pcx->bytes_per_line);\r
491         pcx->palette_type = LittleShort(pcx->palette_type);\r
492 \r
493         if (pcx->manufacturer != 0x0a\r
494                 || pcx->version != 5\r
495                 || pcx->encoding != 1\r
496                 || pcx->bits_per_pixel != 8\r
497                 || pcx->xmax >= 640\r
498                 || pcx->ymax >= 480)\r
499                 Error ("Bad pcx file %s", filename);\r
500         \r
501         if (palette)\r
502         {\r
503                 *palette = safe_malloc(768);\r
504                 memcpy (*palette, (byte *)pcx + len - 768, 768);\r
505         }\r
506 \r
507         if (width)\r
508                 *width = pcx->xmax+1;\r
509         if (height)\r
510                 *height = pcx->ymax+1;\r
511 \r
512         if (!pic)\r
513                 return;\r
514 \r
515         out = safe_malloc ( (pcx->ymax+1) * (pcx->xmax+1) );\r
516         if (!out)\r
517                 Error( "LoadPCX: couldn't allocate");\r
518 \r
519         *pic = out;\r
520         pix = out;\r
521         \r
522         /* RR2DO2: pcx fix  */\r
523         lsize = pcx->color_planes * pcx->bytes_per_line;\r
524         \r
525         /* go scanline by scanline */\r
526         for( y = 0; y <= pcx->ymax; y++, pix += pcx->xmax + 1 )\r
527         {\r
528                 /* do a scanline */\r
529                 for( x=0; x <= pcx->xmax; )\r
530                 {\r
531                         /* RR2DO2 */\r
532                         DECODEPCX( raw, dataByte, runLength );\r
533                         while( runLength-- > 0 )\r
534                                 pix[ x++ ] = dataByte;\r
535                 }\r
536 \r
537                 /* RR2DO2: discard any other data */\r
538                 while( x < lsize )\r
539                 {\r
540                         DECODEPCX( raw, dataByte, runLength );\r
541                         x++;\r
542                 }\r
543                 while( runLength-- > 0 )\r
544                         x++;\r
545         }\r
546         \r
547         /* validity check */\r
548         if( raw - (byte *) pcx > len)\r
549                 Error( "PCX file %s was malformed", filename );\r
550         free( pcx );\r
551 }\r
552 \r
553 \r
554 \r
555 /* \r
556 ============== \r
557 WritePCXfile \r
558 ============== \r
559 */ \r
560 void WritePCXfile (const char *filename, byte *data, \r
561                                    int width, int height, byte *palette) \r
562 {\r
563         int             i, j, length;\r
564         pcx_t   *pcx;\r
565         byte            *pack;\r
566           \r
567         pcx = safe_malloc (width*height*2+1000);\r
568         memset (pcx, 0, sizeof(*pcx));\r
569 \r
570         pcx->manufacturer = 0x0a;       // PCX id\r
571         pcx->version = 5;                       // 256 color\r
572         pcx->encoding = 1;              // uncompressed\r
573         pcx->bits_per_pixel = 8;                // 256 color\r
574         pcx->xmin = 0;\r
575         pcx->ymin = 0;\r
576         pcx->xmax = LittleShort((short)(width-1));\r
577         pcx->ymax = LittleShort((short)(height-1));\r
578         pcx->hres = LittleShort((short)width);\r
579         pcx->vres = LittleShort((short)height);\r
580         pcx->color_planes = 1;          // chunky image\r
581         pcx->bytes_per_line = LittleShort((short)width);\r
582         pcx->palette_type = LittleShort(1);             // not a grey scale\r
583 \r
584         // pack the image\r
585         pack = &pcx->data;\r
586         \r
587         for (i=0 ; i<height ; i++)\r
588         {\r
589                 for (j=0 ; j<width ; j++)\r
590                 {\r
591                         if ( (*data & 0xc0) != 0xc0)\r
592                                 *pack++ = *data++;\r
593                         else\r
594                         {\r
595                                 *pack++ = 0xc1;\r
596                                 *pack++ = *data++;\r
597                         }\r
598                 }\r
599         }\r
600                         \r
601         // write the palette\r
602         *pack++ = 0x0c; // palette ID byte\r
603         for (i=0 ; i<768 ; i++)\r
604                 *pack++ = *palette++;\r
605                 \r
606 // write output file \r
607         length = pack - (byte *)pcx;\r
608         SaveFile (filename, pcx, length);\r
609 \r
610         free (pcx);\r
611\r
612  \r
613 /*\r
614 ============================================================================\r
615 \r
616 LOAD BMP\r
617 \r
618 ============================================================================\r
619 */\r
620 \r
621 \r
622 /*\r
623 \r
624 // we can't just use these structures, because\r
625 // compiler structure alignment will not be portable\r
626 // on this unaligned stuff\r
627 \r
628 typedef struct tagBITMAPFILEHEADER { // bmfh \r
629         WORD    bfType;                         // BM\r
630         DWORD   bfSize; \r
631         WORD    bfReserved1; \r
632         WORD    bfReserved2; \r
633         DWORD   bfOffBits; \r
634 } BITMAPFILEHEADER; \r
635  \r
636 typedef struct tagBITMAPINFOHEADER{ // bmih \r
637    DWORD  biSize; \r
638    LONG   biWidth; \r
639    LONG   biHeight; \r
640    WORD   biPlanes; \r
641    WORD   biBitCount \r
642    DWORD  biCompression; \r
643    DWORD  biSizeImage; \r
644    LONG   biXPelsPerMeter; \r
645    LONG   biYPelsPerMeter; \r
646    DWORD  biClrUsed; \r
647    DWORD  biClrImportant; \r
648 } BITMAPINFOHEADER; \r
649  \r
650 typedef struct tagBITMAPINFO { // bmi \r
651    BITMAPINFOHEADER bmiHeader; \r
652    RGBQUAD          bmiColors[1]; \r
653 } BITMAPINFO; \r
654 \r
655 typedef struct tagBITMAPCOREHEADER { // bmch \r
656         DWORD   bcSize; \r
657         WORD    bcWidth; \r
658         WORD    bcHeight; \r
659         WORD    bcPlanes; \r
660         WORD    bcBitCount; \r
661 } BITMAPCOREHEADER; \r
662  \r
663 typedef struct _BITMAPCOREINFO {    // bmci \r
664         BITMAPCOREHEADER  bmciHeader; \r
665         RGBTRIPLE         bmciColors[1]; \r
666 } BITMAPCOREINFO; \r
667  \r
668 */\r
669 \r
670 /*\r
671 ==============\r
672 LoadBMP\r
673 ==============\r
674 */\r
675 void LoadBMP (const char *filename, byte **pic, byte **palette, int *width, int *height)\r
676 {\r
677   byte  *out;\r
678   int           i;\r
679   int           bfSize; \r
680   int           bfOffBits; \r
681   int           structSize;\r
682   int           bcWidth; \r
683   int     bcHeight; \r
684   int       bcPlanes; \r
685   int           bcBitCount; \r
686   byte  bcPalette[1024];\r
687   qboolean      flipped;\r
688   byte *in;\r
689   int len, pos = 0;\r
690 \r
691   len = vfsLoadFile (filename, (void **)&in, 0);\r
692   if (len == -1) \r
693   {\r
694     Error ("Couldn't read %s", filename);\r
695   }\r
696 \r
697   i = bufLittleShort (in, len, &pos);\r
698   if (i != 'B' + ('M'<<8) ) {\r
699     Error ("%s is not a bmp file", filename);\r
700   }\r
701 \r
702   bfSize = bufLittleLong (in, len, &pos);\r
703   bufLittleShort(in, len, &pos);\r
704   bufLittleShort(in, len, &pos);\r
705   bfOffBits = bufLittleLong (in, len, &pos);\r
706 \r
707   // the size will tell us if it is a\r
708   // bitmapinfo or a bitmapcore\r
709   structSize = bufLittleLong (in, len, &pos);\r
710   if (structSize == 40)\r
711   {\r
712     // bitmapinfo\r
713     bcWidth = bufLittleLong(in, len, &pos); \r
714     bcHeight= bufLittleLong(in, len, &pos); \r
715     bcPlanes = bufLittleShort(in, len, &pos); \r
716     bcBitCount = bufLittleShort(in, len, &pos); \r
717 \r
718     pos += 24;\r
719 \r
720     if (palette)\r
721     {\r
722       memcpy (bcPalette, in+pos, 1024);\r
723       pos += 1024;\r
724       *palette = safe_malloc(768);\r
725 \r
726       for (i = 0 ; i < 256 ; i++)\r
727       {\r
728         (*palette)[i * 3 + 0] = bcPalette[i * 4 + 2];\r
729         (*palette)[i * 3 + 1] = bcPalette[i * 4 + 1];\r
730         (*palette)[i * 3 + 2] = bcPalette[i * 4 + 0];\r
731       }\r
732     }\r
733   }\r
734   else if (structSize == 12)\r
735   {\r
736     // bitmapcore\r
737     bcWidth = bufLittleShort(in, len, &pos); \r
738     bcHeight= bufLittleShort(in, len, &pos); \r
739     bcPlanes = bufLittleShort(in, len, &pos); \r
740     bcBitCount = bufLittleShort(in, len, &pos); \r
741 \r
742     if (palette)\r
743     {\r
744       memcpy (bcPalette, in+pos, 768);\r
745       pos += 768;\r
746       *palette = safe_malloc(768);\r
747 \r
748       for (i = 0 ; i < 256 ; i++) {\r
749         (*palette)[i * 3 + 0] = bcPalette[i * 3 + 2];\r
750         (*palette)[i * 3 + 1] = bcPalette[i * 3 + 1];\r
751         (*palette)[i * 3 + 2] = bcPalette[i * 3 + 0];\r
752       }\r
753     }\r
754   } else {\r
755     Error ("%s had strange struct size", filename);\r
756   }\r
757 \r
758   if (bcPlanes != 1) {\r
759     Error ("%s was not a single plane image", filename);\r
760   }\r
761 \r
762   if (bcBitCount != 8) {\r
763     Error ("%s was not an 8 bit image", filename);\r
764   }\r
765 \r
766   if (bcHeight < 0) {\r
767     bcHeight = -bcHeight;\r
768     flipped = qtrue;\r
769   } else {\r
770     flipped = qfalse;\r
771   }\r
772 \r
773   if (width)\r
774     *width = bcWidth;\r
775   if (height)\r
776     *height = bcHeight;\r
777 \r
778   if (!pic) {\r
779     free (in);\r
780     return;\r
781   }\r
782 \r
783   out = safe_malloc ( bcWidth * bcHeight );\r
784   *pic = out;\r
785   pos = bfOffBits;\r
786 \r
787   if (flipped) {\r
788     for (i = 0 ; i < bcHeight ; i++) {\r
789       memcpy (out + bcWidth * (bcHeight - 1 - i), in+pos, bcWidth);\r
790       pos += bcWidth;\r
791     }\r
792   } else {\r
793     memcpy (out, in+pos, bcWidth*bcHeight);\r
794     pos += bcWidth*bcHeight;\r
795   }\r
796 \r
797   free (in);\r
798 }\r
799 \r
800 \r
801 /*\r
802 ============================================================================\r
803 \r
804 LOAD IMAGE\r
805 \r
806 ============================================================================\r
807 */\r
808 \r
809 /*\r
810 ==============\r
811 Load256Image\r
812 \r
813 Will load either an lbm or pcx, depending on extension.\r
814 Any of the return pointers can be NULL if you don't want them.\r
815 ==============\r
816 */\r
817 void Load256Image (const char *name, byte **pixels, byte **palette, int *width, int *height)\r
818 {\r
819   char ext[128];\r
820 \r
821   ExtractFileExtension (name, ext);\r
822   if (!Q_stricmp (ext, "lbm"))\r
823   {\r
824     LoadLBM (name, pixels, palette);\r
825     if (width)\r
826       *width = bmhd.w;\r
827     if (height)\r
828       *height = bmhd.h;\r
829   }\r
830   else if (!Q_stricmp (ext, "pcx"))\r
831   {\r
832     LoadPCX (name, pixels, palette, width, height);\r
833   }\r
834   else if (!Q_stricmp (ext, "bmp"))\r
835   {\r
836     LoadBMP (name, pixels, palette, width, height);\r
837   }\r
838   else\r
839     Error ("%s doesn't have a known image extension", name);\r
840 }\r
841 \r
842 \r
843 /*\r
844 ==============\r
845 Save256Image\r
846 \r
847 Will save either an lbm or pcx, depending on extension.\r
848 ==============\r
849 */\r
850 void Save256Image (const char *name, byte *pixels, byte *palette,\r
851                                    int width, int height)\r
852 {\r
853         char    ext[128];\r
854 \r
855         ExtractFileExtension (name, ext);\r
856         if (!Q_stricmp (ext, "lbm"))\r
857         {\r
858                 WriteLBMfile (name, pixels, width, height, palette);\r
859         }\r
860         else if (!Q_stricmp (ext, "pcx"))\r
861         {\r
862                 WritePCXfile (name, pixels, width, height, palette);\r
863         }\r
864         else\r
865                 Error ("%s doesn't have a known image extension", name);\r
866 }\r
867 \r
868 \r
869 \r
870 \r
871 /*\r
872 ============================================================================\r
873 \r
874 TARGA IMAGE\r
875 \r
876 ============================================================================\r
877 */\r
878 \r
879 typedef struct _TargaHeader {\r
880         unsigned char   id_length, colormap_type, image_type;\r
881         unsigned short  colormap_index, colormap_length;\r
882         unsigned char   colormap_size;\r
883         unsigned short  x_origin, y_origin, width, height;\r
884         unsigned char   pixel_size, attributes;\r
885 } TargaHeader;\r
886 \r
887 /*\r
888 =============\r
889 LoadTGABuffer\r
890 =============\r
891 */\r
892 void LoadTGABuffer ( byte *buffer, byte **pic, int *width, int *height)\r
893 {\r
894         int             columns, rows, numPixels;\r
895         byte    *pixbuf;\r
896         int             row, column;\r
897         byte    *buf_p;\r
898         TargaHeader     targa_header;\r
899         byte            *targa_rgba;\r
900 \r
901         *pic = NULL;\r
902 \r
903         buf_p = buffer;\r
904 \r
905         targa_header.id_length = *buf_p++;\r
906         targa_header.colormap_type = *buf_p++;\r
907         targa_header.image_type = *buf_p++;\r
908         \r
909         targa_header.colormap_index = LittleShort ( *(short *)buf_p );\r
910         buf_p += 2;\r
911         targa_header.colormap_length = LittleShort ( *(short *)buf_p );\r
912         buf_p += 2;\r
913         targa_header.colormap_size = *buf_p++;\r
914         targa_header.x_origin = LittleShort ( *(short *)buf_p );\r
915         buf_p += 2;\r
916         targa_header.y_origin = LittleShort ( *(short *)buf_p );\r
917         buf_p += 2;\r
918         targa_header.width = LittleShort ( *(short *)buf_p );\r
919         buf_p += 2;\r
920         targa_header.height = LittleShort ( *(short *)buf_p );\r
921         buf_p += 2;\r
922         targa_header.pixel_size = *buf_p++;\r
923         targa_header.attributes = *buf_p++;\r
924 \r
925         if (targa_header.image_type!=2 \r
926                 && targa_header.image_type!=10\r
927                 && targa_header.image_type != 3 ) \r
928         {\r
929                 Error("LoadTGA: Only type 2 (RGB), 3 (gray), and 10 (RGB) TGA images supported\n");\r
930         }\r
931 \r
932         if ( targa_header.colormap_type != 0 )\r
933         {\r
934                 Error("LoadTGA: colormaps not supported\n" );\r
935         }\r
936 \r
937         if ( ( targa_header.pixel_size != 32 && targa_header.pixel_size != 24 ) && targa_header.image_type != 3 )\r
938         {\r
939                 Error("LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n");\r
940         }\r
941 \r
942         columns = targa_header.width;\r
943         rows = targa_header.height;\r
944         numPixels = columns * rows;\r
945 \r
946         if (width)\r
947                 *width = columns;\r
948         if (height)\r
949                 *height = rows;\r
950 \r
951         targa_rgba = safe_malloc (numPixels*4);\r
952         *pic = targa_rgba;\r
953 \r
954         if (targa_header.id_length != 0)\r
955                 buf_p += targa_header.id_length;  // skip TARGA image comment\r
956         \r
957         if ( targa_header.image_type==2 || targa_header.image_type == 3 )\r
958         { \r
959                 // Uncompressed RGB or gray scale image\r
960                 for(row=rows-1; row>=0; row--) \r
961                 {\r
962                         pixbuf = targa_rgba + row*columns*4;\r
963                         for(column=0; column<columns; column++) \r
964                         {\r
965                                 unsigned char red,green,blue,alphabyte;\r
966                                 switch (targa_header.pixel_size) \r
967                                 {\r
968                                         \r
969                                 case 8:\r
970                                         blue = *buf_p++;\r
971                                         green = blue;\r
972                                         red = blue;\r
973                                         *pixbuf++ = red;\r
974                                         *pixbuf++ = green;\r
975                                         *pixbuf++ = blue;\r
976                                         *pixbuf++ = 255;\r
977                                         break;\r
978 \r
979                                 case 24:\r
980                                         blue = *buf_p++;\r
981                                         green = *buf_p++;\r
982                                         red = *buf_p++;\r
983                                         *pixbuf++ = red;\r
984                                         *pixbuf++ = green;\r
985                                         *pixbuf++ = blue;\r
986                                         *pixbuf++ = 255;\r
987                                         break;\r
988                                 case 32:\r
989                                         blue = *buf_p++;\r
990                                         green = *buf_p++;\r
991                                         red = *buf_p++;\r
992                                         alphabyte = *buf_p++;\r
993                                         *pixbuf++ = red;\r
994                                         *pixbuf++ = green;\r
995                                         *pixbuf++ = blue;\r
996                                         *pixbuf++ = alphabyte;\r
997                                         break;\r
998                                 default:\r
999                                         //Error("LoadTGA: illegal pixel_size '%d' in file '%s'\n", targa_header.pixel_size, name );\r
1000                                         break;\r
1001                                 }\r
1002                         }\r
1003                 }\r
1004         }\r
1005         else if (targa_header.image_type==10) {   // Runlength encoded RGB images\r
1006                 unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j;\r
1007 \r
1008                 red = 0;\r
1009                 green = 0;\r
1010                 blue = 0;\r
1011                 alphabyte = 0xff;\r
1012 \r
1013                 for(row=rows-1; row>=0; row--) {\r
1014                         pixbuf = targa_rgba + row*columns*4;\r
1015                         for(column=0; column<columns; ) {\r
1016                                 packetHeader= *buf_p++;\r
1017                                 packetSize = 1 + (packetHeader & 0x7f);\r
1018                                 if (packetHeader & 0x80) {        // run-length packet\r
1019                                         switch (targa_header.pixel_size) {\r
1020                                                 case 24:\r
1021                                                                 blue = *buf_p++;\r
1022                                                                 green = *buf_p++;\r
1023                                                                 red = *buf_p++;\r
1024                                                                 alphabyte = 255;\r
1025                                                                 break;\r
1026                                                 case 32:\r
1027                                                                 blue = *buf_p++;\r
1028                                                                 green = *buf_p++;\r
1029                                                                 red = *buf_p++;\r
1030                                                                 alphabyte = *buf_p++;\r
1031                                                                 break;\r
1032                                                 default:\r
1033                                                         //Error("LoadTGA: illegal pixel_size '%d' in file '%s'\n", targa_header.pixel_size, name );\r
1034                                                         break;\r
1035                                         }\r
1036         \r
1037                                         for(j=0;j<packetSize;j++) {\r
1038                                                 *pixbuf++=red;\r
1039                                                 *pixbuf++=green;\r
1040                                                 *pixbuf++=blue;\r
1041                                                 *pixbuf++=alphabyte;\r
1042                                                 column++;\r
1043                                                 if (column==columns) { // run spans across rows\r
1044                                                         column=0;\r
1045                                                         if (row>0)\r
1046                                                                 row--;\r
1047                                                         else\r
1048                                                                 goto breakOut;\r
1049                                                         pixbuf = targa_rgba + row*columns*4;\r
1050                                                 }\r
1051                                         }\r
1052                                 }\r
1053                                 else {                            // non run-length packet\r
1054                                         for(j=0;j<packetSize;j++) {\r
1055                                                 switch (targa_header.pixel_size) {\r
1056                                                         case 24:\r
1057                                                                         blue = *buf_p++;\r
1058                                                                         green = *buf_p++;\r
1059                                                                         red = *buf_p++;\r
1060                                                                         *pixbuf++ = red;\r
1061                                                                         *pixbuf++ = green;\r
1062                                                                         *pixbuf++ = blue;\r
1063                                                                         *pixbuf++ = 255;\r
1064                                                                         break;\r
1065                                                         case 32:\r
1066                                                                         blue = *buf_p++;\r
1067                                                                         green = *buf_p++;\r
1068                                                                         red = *buf_p++;\r
1069                                                                         alphabyte = *buf_p++;\r
1070                                                                         *pixbuf++ = red;\r
1071                                                                         *pixbuf++ = green;\r
1072                                                                         *pixbuf++ = blue;\r
1073                                                                         *pixbuf++ = alphabyte;\r
1074                                                                         break;\r
1075                                                         default:\r
1076                                                                 //Sysprintf("LoadTGA: illegal pixel_size '%d' in file '%s'\n", targa_header.pixel_size, name );\r
1077                                                                 break;\r
1078                                                 }\r
1079                                                 column++;\r
1080                                                 if (column==columns) { // pixel packet run spans across rows\r
1081                                                         column=0;\r
1082                                                         if (row>0)\r
1083                                                                 row--;\r
1084                                                         else\r
1085                                                                 goto breakOut;\r
1086                                                         pixbuf = targa_rgba + row*columns*4;\r
1087                                                 }                                               \r
1088                                         }\r
1089                                 }\r
1090                         }\r
1091                         breakOut:;\r
1092                 }\r
1093         }\r
1094 \r
1095         // vertically flipped\r
1096         if ( (targa_header.attributes & (1<<5)) ) {\r
1097                 int flip;\r
1098                 for (row = 0; row < .5f * rows; row++)\r
1099                 {\r
1100                         for (column = 0; column < columns; column++)\r
1101                         {\r
1102                                 flip = *( (int*)targa_rgba + row * columns + column);\r
1103                                 *( (int*)targa_rgba + row * columns + column) = *( (int*)targa_rgba + ( ( rows - 1 ) - row ) * columns + column );\r
1104                                 *( (int*)targa_rgba + ( ( rows - 1 ) - row ) * columns + column ) = flip;\r
1105                         }\r
1106                 }\r
1107         }\r
1108 \r
1109         //free(buffer);\r
1110 }\r
1111 \r
1112 \r
1113 \r
1114 /*\r
1115 =============\r
1116 LoadTGA\r
1117 =============\r
1118 */\r
1119 void LoadTGA (const char *name, byte **pixels, int *width, int *height)\r
1120 {\r
1121         byte                    *buffer;\r
1122   int nLen;\r
1123         //\r
1124         // load the file\r
1125         //\r
1126         nLen = vfsLoadFile ( ( char * ) name, (void **)&buffer, 0);\r
1127         if (nLen == -1) \r
1128   {\r
1129                 Error ("Couldn't read %s", name);\r
1130   }\r
1131 \r
1132   LoadTGABuffer(buffer, pixels, width, height);\r
1133 \r
1134 }\r
1135 \r
1136 \r
1137 /*\r
1138 ================\r
1139 WriteTGA\r
1140 ================\r
1141 */\r
1142 void WriteTGA (const char *filename, byte *data, int width, int height) {\r
1143         byte    *buffer;\r
1144         int             i;\r
1145         int             c;\r
1146         FILE    *f;\r
1147 \r
1148         buffer = safe_malloc(width*height*4 + 18);\r
1149         memset (buffer, 0, 18);\r
1150         buffer[2] = 2;          // uncompressed type\r
1151         buffer[12] = width&255;\r
1152         buffer[13] = width>>8;\r
1153         buffer[14] = height&255;\r
1154         buffer[15] = height>>8;\r
1155         buffer[16] = 32;        // pixel size\r
1156 \r
1157         // swap rgb to bgr\r
1158         c = 18 + width * height * 4;\r
1159         for (i=18 ; i<c ; i+=4)\r
1160         {\r
1161                 buffer[i] = data[i-18+2];               // blue\r
1162                 buffer[i+1] = data[i-18+1];             // green\r
1163                 buffer[i+2] = data[i-18+0];             // red\r
1164                 buffer[i+3] = data[i-18+3];             // alpha\r
1165         }\r
1166 \r
1167         f = fopen (filename, "wb");\r
1168         fwrite (buffer, 1, c, f);\r
1169         fclose (f);\r
1170 \r
1171         free (buffer);\r
1172 }\r
1173 \r
1174 /*\r
1175 ============================================================================\r
1176 \r
1177 LOAD32BITIMAGE\r
1178 \r
1179 ============================================================================\r
1180 */\r
1181 \r
1182 /*\r
1183 ==============\r
1184 Load32BitImage\r
1185 \r
1186 Any of the return pointers can be NULL if you don't want them.\r
1187 ==============\r
1188 */\r
1189 void Load32BitImage (const char *name, unsigned **pixels,  int *width, int *height)\r
1190 {\r
1191         char    ext[128];\r
1192         byte    *palette;\r
1193         byte    *pixels8;\r
1194         byte    *pixels32;\r
1195         int             size;\r
1196         int             i;\r
1197         int             v;\r
1198 \r
1199         ExtractFileExtension (name, ext);\r
1200         if (!Q_stricmp (ext, "tga")) {\r
1201                 LoadTGA (name, (byte **)pixels, width, height);\r
1202         } else {\r
1203                 Load256Image (name, &pixels8, &palette, width, height);\r
1204                 if (!pixels) {\r
1205                         return;\r
1206                 }\r
1207                 size = *width * *height;\r
1208                 pixels32 = safe_malloc(size * 4);\r
1209                 *pixels = (unsigned *)pixels32;\r
1210                 for (i = 0 ; i < size ; i++) {\r
1211                         v = pixels8[i];\r
1212                         pixels32[i*4 + 0] = palette[ v * 3 + 0 ];\r
1213                         pixels32[i*4 + 1] = palette[ v * 3 + 1 ];\r
1214                         pixels32[i*4 + 2] = palette[ v * 3 + 2 ];\r
1215                         pixels32[i*4 + 3] = 0xff;\r
1216                 }\r
1217         }\r
1218 }\r
1219 \r
1220 \r