added newmap function to render modules (so explosions and other things are reset...
[xonotic/darkplaces.git] / image.c
1
2 #include "quakedef.h"
3
4 int             image_width;
5 int             image_height;
6
7 // note: pal must be 32bit color
8 void Image_Copy8bitRGBA(byte *in, byte *out, int pixels, int *pal)
9 {
10         int *iout = (void *)out;
11         while (pixels >= 8)
12         {
13                 iout[0] = pal[in[0]];
14                 iout[1] = pal[in[1]];
15                 iout[2] = pal[in[2]];
16                 iout[3] = pal[in[3]];
17                 iout[4] = pal[in[4]];
18                 iout[5] = pal[in[5]];
19                 iout[6] = pal[in[6]];
20                 iout[7] = pal[in[7]];
21                 in += 8;
22                 iout += 8;
23                 pixels -= 8;
24         }
25         if (pixels & 4)
26         {
27                 iout[0] = pal[in[0]];
28                 iout[1] = pal[in[1]];
29                 iout[2] = pal[in[2]];
30                 iout[3] = pal[in[3]];
31                 in += 4;
32                 iout += 4;
33         }
34         if (pixels & 2)
35         {
36                 iout[0] = pal[in[0]];
37                 iout[1] = pal[in[1]];
38                 in += 2;
39                 iout += 2;
40         }
41         if (pixels & 1)
42                 iout[0] = pal[in[0]];
43 }
44
45 void Image_CopyRGBAGamma(byte *in, byte *out, int pixels)
46 {
47         while (pixels--)
48         {
49                 out[0] = qgamma[in[0]];
50                 out[1] = qgamma[in[1]];
51                 out[2] = qgamma[in[2]];
52                 out[3] =        in[3] ;
53                 in += 4;
54                 out += 4;
55         }
56 }
57
58
59 /*
60 =================================================================
61
62   PCX Loading
63
64 =================================================================
65 */
66
67 typedef struct
68 {
69     char        manufacturer;
70     char        version;
71     char        encoding;
72     char        bits_per_pixel;
73     unsigned short      xmin,ymin,xmax,ymax;
74     unsigned short      hres,vres;
75     unsigned char       palette[48];
76     char        reserved;
77     char        color_planes;
78     unsigned short      bytes_per_line;
79     unsigned short      palette_type;
80     char        filler[58];
81     unsigned    data;                   // unbounded
82 } pcx_t;
83
84 /*
85 ============
86 LoadPCX
87 ============
88 */
89 byte* LoadPCX (FILE *f, int matchwidth, int matchheight)
90 {
91         pcx_t   *pcx, pcxbuf;
92         byte    palette[768];
93         byte    *pix, *image_rgba;
94         int             x, y;
95         int             dataByte, runLength;
96         int             count;
97
98 //
99 // parse the PCX file
100 //
101         fread (&pcxbuf, 1, sizeof(pcxbuf), f);
102
103         pcx = &pcxbuf;
104
105         // LordHavoc: big-endian support ported from QF newtree
106         pcx->xmax = LittleShort (pcx->xmax);
107         pcx->xmin = LittleShort (pcx->xmin);
108         pcx->ymax = LittleShort (pcx->ymax);
109         pcx->ymin = LittleShort (pcx->ymin);
110         pcx->hres = LittleShort (pcx->hres);
111         pcx->vres = LittleShort (pcx->vres);
112         pcx->bytes_per_line = LittleShort (pcx->bytes_per_line);
113         pcx->palette_type = LittleShort (pcx->palette_type);
114
115         if (pcx->manufacturer != 0x0a || pcx->version != 5 || pcx->encoding != 1 || pcx->bits_per_pixel != 8 || pcx->xmax > 320 || pcx->ymax > 256)
116         {
117                 Con_Printf ("Bad pcx file\n");
118                 return NULL;
119         }
120
121         if (matchwidth && (pcx->xmax+1) != matchwidth)
122                 return NULL;
123         if (matchheight && (pcx->ymax+1) != matchheight)
124                 return NULL;
125
126         // seek to palette
127         fseek (f, -768, SEEK_END);
128         fread (palette, 1, 768, f);
129
130         fseek (f, sizeof(pcxbuf) - 4, SEEK_SET);
131
132         count = (pcx->xmax+1) * (pcx->ymax+1);
133         image_rgba = qmalloc( count * 4);
134
135         for (y=0 ; y<=pcx->ymax ; y++)
136         {
137                 pix = image_rgba + 4*y*(pcx->xmax+1);
138                 for (x=0 ; x<=pcx->xmax ; )
139                 {
140                         dataByte = fgetc(f);
141
142                         if((dataByte & 0xC0) == 0xC0)
143                         {
144                                 runLength = dataByte & 0x3F;
145                                 dataByte = fgetc(f);
146                                 if (runLength)
147                                 {
148                                         x += runLength;
149                                         while(runLength--)
150                                         {
151                                                 pix[0] = palette[dataByte*3];
152                                                 pix[1] = palette[dataByte*3+1];
153                                                 pix[2] = palette[dataByte*3+2];
154                                                 pix[3] = 255;
155                                                 pix += 4;
156                                         }
157                                 }
158                         }
159                         else
160                         {
161                                 x++;
162                                 pix[0] = palette[dataByte*3];
163                                 pix[1] = palette[dataByte*3+1];
164                                 pix[2] = palette[dataByte*3+2];
165                                 pix[3] = 255;
166                                 pix += 4;
167                         }
168
169                 }
170         }
171         fclose(f);
172         image_width = pcx->xmax+1;
173         image_height = pcx->ymax+1;
174         return image_rgba;
175 }
176
177 /*
178 =========================================================
179
180 TARGA LOADING
181
182 =========================================================
183 */
184
185 typedef struct _TargaHeader {
186         unsigned char   id_length, colormap_type, image_type;
187         unsigned short  colormap_index, colormap_length;
188         unsigned char   colormap_size;
189         unsigned short  x_origin, y_origin, width, height;
190         unsigned char   pixel_size, attributes;
191 } TargaHeader;
192
193
194 TargaHeader             targa_header;
195
196 int fgetLittleShort (FILE *f)
197 {
198         byte    b1, b2;
199
200         b1 = fgetc(f);
201         b2 = fgetc(f);
202
203         return (short)(b1 + b2*256);
204 }
205
206 int fgetLittleLong (FILE *f)
207 {
208         byte    b1, b2, b3, b4;
209
210         b1 = fgetc(f);
211         b2 = fgetc(f);
212         b3 = fgetc(f);
213         b4 = fgetc(f);
214
215         return b1 + (b2<<8) + (b3<<16) + (b4<<24);
216 }
217
218
219 /*
220 =============
221 LoadTGA
222 =============
223 */
224 byte* LoadTGA (FILE *fin, int matchwidth, int matchheight)
225 {
226         int                             columns, rows, numPixels;
227         byte                    *pixbuf;
228         int                             row, column;
229         byte                    *image_rgba;
230
231         targa_header.id_length = fgetc(fin);
232         targa_header.colormap_type = fgetc(fin);
233         targa_header.image_type = fgetc(fin);
234         
235         targa_header.colormap_index = fgetLittleShort(fin);
236         targa_header.colormap_length = fgetLittleShort(fin);
237         targa_header.colormap_size = fgetc(fin);
238         targa_header.x_origin = fgetLittleShort(fin);
239         targa_header.y_origin = fgetLittleShort(fin);
240         targa_header.width = fgetLittleShort(fin);
241         targa_header.height = fgetLittleShort(fin);
242         if (matchwidth && targa_header.width != matchwidth)
243                 return NULL;
244         if (matchheight && targa_header.height != matchheight)
245                 return NULL;
246         targa_header.pixel_size = fgetc(fin);
247         targa_header.attributes = fgetc(fin);
248
249         if (targa_header.image_type!=2 
250                 && targa_header.image_type!=10) 
251                 Host_Error ("LoadTGA: Only type 2 and 10 targa RGB images supported\n");
252
253         if (targa_header.colormap_type !=0 
254                 || (targa_header.pixel_size!=32 && targa_header.pixel_size!=24))
255                 Host_Error ("LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n");
256
257         columns = targa_header.width;
258         rows = targa_header.height;
259         numPixels = columns * rows;
260
261         image_rgba = qmalloc(numPixels*4);
262         
263         if (targa_header.id_length != 0)
264                 fseek(fin, targa_header.id_length, SEEK_CUR);  // skip TARGA image comment
265         
266         if (targa_header.image_type==2) {  // Uncompressed, RGB images
267                 for(row=rows-1; row>=0; row--) {
268                         pixbuf = image_rgba + row*columns*4;
269                         for(column=0; column<columns; column++) {
270                                 unsigned char red = 0,green = 0,blue = 0,alphabyte = 0;
271                                 switch (targa_header.pixel_size) {
272                                         case 24:
273                                                         
274                                                         blue = getc(fin);
275                                                         green = getc(fin);
276                                                         red = getc(fin);
277                                                         *pixbuf++ = red;
278                                                         *pixbuf++ = green;
279                                                         *pixbuf++ = blue;
280                                                         *pixbuf++ = 255;
281                                                         break;
282                                         case 32:
283                                                         blue = getc(fin);
284                                                         green = getc(fin);
285                                                         red = getc(fin);
286                                                         alphabyte = getc(fin);
287                                                         *pixbuf++ = red;
288                                                         *pixbuf++ = green;
289                                                         *pixbuf++ = blue;
290                                                         *pixbuf++ = alphabyte;
291                                                         break;
292                                 }
293                         }
294                 }
295         }
296         else if (targa_header.image_type==10) {   // Runlength encoded RGB images
297                 unsigned char red = 0,green = 0,blue = 0,alphabyte = 0,packetHeader,packetSize,j;
298                 for(row=rows-1; row>=0; row--) {
299                         pixbuf = image_rgba + row*columns*4;
300                         for(column=0; column<columns; ) {
301                                 packetHeader=getc(fin);
302                                 packetSize = 1 + (packetHeader & 0x7f);
303                                 if (packetHeader & 0x80) {        // run-length packet
304                                         switch (targa_header.pixel_size) {
305                                                 case 24:
306                                                                 blue = getc(fin);
307                                                                 green = getc(fin);
308                                                                 red = getc(fin);
309                                                                 alphabyte = 255;
310                                                                 break;
311                                                 case 32:
312                                                                 blue = getc(fin);
313                                                                 green = getc(fin);
314                                                                 red = getc(fin);
315                                                                 alphabyte = getc(fin);
316                                                                 break;
317                                         }
318         
319                                         for(j=0;j<packetSize;j++) {
320                                                 *pixbuf++=red;
321                                                 *pixbuf++=green;
322                                                 *pixbuf++=blue;
323                                                 *pixbuf++=alphabyte;
324                                                 column++;
325                                                 if (column==columns) { // run spans across rows
326                                                         column=0;
327                                                         if (row>0)
328                                                                 row--;
329                                                         else
330                                                                 goto breakOut;
331                                                         pixbuf = image_rgba + row*columns*4;
332                                                 }
333                                         }
334                                 }
335                                 else {                            // non run-length packet
336                                         for(j=0;j<packetSize;j++) {
337                                                 switch (targa_header.pixel_size) {
338                                                         case 24:
339                                                                         blue = getc(fin);
340                                                                         green = getc(fin);
341                                                                         red = getc(fin);
342                                                                         *pixbuf++ = red;
343                                                                         *pixbuf++ = green;
344                                                                         *pixbuf++ = blue;
345                                                                         *pixbuf++ = 255;
346                                                                         break;
347                                                         case 32:
348                                                                         blue = getc(fin);
349                                                                         green = getc(fin);
350                                                                         red = getc(fin);
351                                                                         alphabyte = getc(fin);
352                                                                         *pixbuf++ = red;
353                                                                         *pixbuf++ = green;
354                                                                         *pixbuf++ = blue;
355                                                                         *pixbuf++ = alphabyte;
356                                                                         break;
357                                                 }
358                                                 column++;
359                                                 if (column==columns) { // pixel packet run spans across rows
360                                                         column=0;
361                                                         if (row>0)
362                                                                 row--;
363                                                         else
364                                                                 goto breakOut;
365                                                         pixbuf = image_rgba + row*columns*4;
366                                                 }                                               
367                                         }
368                                 }
369                         }
370                         breakOut:;
371                 }
372         }
373         
374         fclose(fin);
375         image_width = columns;
376         image_height = rows;
377         return image_rgba;
378 }
379
380 /*
381 ============
382 LoadLMP
383 ============
384 */
385 byte* LoadLMP (FILE *f, int matchwidth, int matchheight)
386 {
387         byte    *image_rgba;
388         int             width, height;
389
390         // parse the very complicated header *chuckle*
391         width = fgetLittleLong(f);
392         height = fgetLittleLong(f);
393         if ((unsigned) width > 4096 || (unsigned) height > 4096)
394                 Host_Error("LoadLMP: invalid size\n");
395         if (matchwidth && width != matchwidth)
396                 return NULL;
397         if (matchheight && height != matchheight)
398                 return NULL;
399
400         image_rgba = qmalloc(width*height*4);
401         fread(image_rgba + width*height*3, 1, width*height, f);
402         fclose(f);
403
404         Image_Copy8bitRGBA(image_rgba + width*height*3, image_rgba, width*height, d_8to24table);
405         image_width = width;
406         image_height = height;
407         return image_rgba;
408 }
409
410 void Image_StripImageExtension (char *in, char *out)
411 {
412         char *end, *temp;
413         end = in + strlen(in);
414         if ((end - in) >= 4)
415         {
416                 temp = end - 4;
417                 if (strcmp(temp, ".tga") == 0 || strcmp(temp, ".pcx") == 0 || strcmp(temp, ".lmp") == 0)
418                         end = temp;
419                 while (in < end)
420                         *out++ = *in++;
421                 *out++ = 0;
422         }
423         else
424                 strcpy(out, in);
425 }
426
427 byte* loadimagepixels (char* filename, qboolean complain, int matchwidth, int matchheight)
428 {
429         FILE    *f;
430         char    basename[256], name[256];
431         byte    *c;
432         Image_StripImageExtension(filename, basename); // strip .tga, .pcx and .lmp extensions to allow replacement by other types
433         // replace *'s with #, so commandline utils don't get confused when dealing with the external files
434         for (c = basename;*c;c++)
435                 if (*c == '*')
436                         *c = '#';
437         sprintf (name, "textures/%s.tga", basename);
438         COM_FOpenFile (name, &f, true);
439         if (f)
440                 return LoadTGA (f, matchwidth, matchheight);
441         sprintf (name, "textures/%s.pcx", basename);
442         COM_FOpenFile (name, &f, true);
443         if (f)
444                 return LoadPCX (f, matchwidth, matchheight);
445         sprintf (name, "%s.tga", basename);
446         COM_FOpenFile (name, &f, true);
447         if (f)
448                 return LoadTGA (f, matchwidth, matchheight);
449         sprintf (name, "%s.pcx", basename);
450         COM_FOpenFile (name, &f, true);
451         if (f)
452                 return LoadPCX (f, matchwidth, matchheight);
453         sprintf (name, "%s.lmp", basename);
454         COM_FOpenFile (name, &f, true);
455         if (f)
456                 return LoadLMP (f, matchwidth, matchheight);
457         if (complain)
458                 Con_Printf ("Couldn't load %s.tga or .pcx\n", filename);
459         return NULL;
460 }
461
462 int image_makemask (byte *in, byte *out, int size)
463 {
464         int             i, count;
465         count = 0;
466         for (i = 0;i < size;i++)
467         {
468                 out[0] = out[1] = out[2] = 255;
469                 out[3] = in[3];
470                 if (in[3] != 255)
471                         count++;
472                 in += 4;
473                 out += 4;
474         }
475         return count;
476 }
477
478 byte* loadimagepixelsmask (char* filename, qboolean complain, int matchwidth, int matchheight)
479 {
480         byte    *in, *data;
481         in = data = loadimagepixels(filename, complain, matchwidth, matchheight);
482         if (!data)
483                 return NULL;
484         if (image_makemask(data, data, image_width * image_height))
485                 return data; // some transparency
486         else
487         {
488                 qfree(data);
489                 return NULL; // all opaque
490         }
491 }
492
493 rtexture_t *loadtextureimage (char* filename, int matchwidth, int matchheight, qboolean complain, qboolean mipmap, qboolean precache)
494 {
495         byte *data;
496         rtexture_t *rt;
497         if (!(data = loadimagepixels (filename, complain, matchwidth, matchheight)))
498                 return 0;
499         rt = R_LoadTexture (filename, image_width, image_height, data, TEXF_ALPHA | TEXF_RGBA | (mipmap ? TEXF_MIPMAP : 0) | (mipmap ? TEXF_PRECACHE : 0));
500         qfree(data);
501         return rt;
502 }
503
504 rtexture_t *loadtextureimagemask (char* filename, int matchwidth, int matchheight, qboolean complain, qboolean mipmap, qboolean precache)
505 {
506         byte *data;
507         rtexture_t *rt;
508         if (!(data = loadimagepixelsmask (filename, complain, matchwidth, matchheight)))
509                 return 0;
510         rt = R_LoadTexture (filename, image_width, image_height, data, TEXF_ALPHA | TEXF_RGBA | (mipmap ? TEXF_MIPMAP : 0) | (mipmap ? TEXF_PRECACHE : 0));
511         qfree(data);
512         return rt;
513 }
514
515 rtexture_t *image_masktex;
516 rtexture_t *loadtextureimagewithmask (char* filename, int matchwidth, int matchheight, qboolean complain, qboolean mipmap, qboolean precache)
517 {
518         int count;
519         byte *data;
520         char *filename2;
521         rtexture_t *rt;
522         image_masktex = NULL;
523         if (!(data = loadimagepixels (filename, complain, matchwidth, matchheight)))
524                 return 0;
525         rt = R_LoadTexture (filename, image_width, image_height, data, TEXF_ALPHA | TEXF_RGBA | (mipmap ? TEXF_MIPMAP : 0) | (mipmap ? TEXF_PRECACHE : 0));
526         count = image_makemask(data, data, image_width * image_height);
527         if (count)
528         {
529                 filename2 = qmalloc(strlen(filename) + 6);
530                 sprintf(filename2, "%s_mask", filename);
531                 image_masktex = R_LoadTexture (filename2, image_width, image_height, data, TEXF_ALPHA | TEXF_RGBA | (mipmap ? TEXF_MIPMAP : 0) | (mipmap ? TEXF_PRECACHE : 0));
532                 qfree(filename2);
533         }
534         qfree(data);
535         return rt;
536 }
537
538 void Image_WriteTGARGB_preflipped (char *filename, int width, int height, byte *data)
539 {
540         byte *buffer, *in, *out, *end;
541
542         buffer = qmalloc(width*height*3 + 18);
543
544         memset (buffer, 0, 18);
545         buffer[2] = 2;          // uncompressed type
546         buffer[12] = (width >> 0) & 0xFF;
547         buffer[13] = (width >> 8) & 0xFF;
548         buffer[14] = (height >> 0) & 0xFF;
549         buffer[15] = (height >> 8) & 0xFF;
550         buffer[16] = 24;        // pixel size
551
552         // swap rgb to bgr
553         in = data;
554         out = buffer + 18;
555         end = in + width*height*3;
556         for (;in < end;in += 3)
557         {
558                 *out++ = in[2];
559                 *out++ = in[1];
560                 *out++ = in[0];
561         }
562         COM_WriteFile (filename, buffer, width*height*3 + 18 );
563
564         qfree(buffer);
565 }
566
567 void Image_WriteTGARGB (char *filename, int width, int height, byte *data)
568 {
569         int y;
570         byte *buffer, *in, *out, *end;
571
572         buffer = qmalloc(width*height*3 + 18);
573
574         memset (buffer, 0, 18);
575         buffer[2] = 2;          // uncompressed type
576         buffer[12] = (width >> 0) & 0xFF;
577         buffer[13] = (width >> 8) & 0xFF;
578         buffer[14] = (height >> 0) & 0xFF;
579         buffer[15] = (height >> 8) & 0xFF;
580         buffer[16] = 24;        // pixel size
581
582         // swap rgb to bgr and flip upside down
583         out = buffer + 18;
584         for (y = height - 1;y >= 0;y--)
585         {
586                 in = data + y * width * 3;
587                 end = in + width * 3;
588                 for (;in < end;in += 3)
589                 {
590                         *out++ = in[2];
591                         *out++ = in[1];
592                         *out++ = in[0];
593                 }
594         }
595         COM_WriteFile (filename, buffer, width*height*3 + 18 );
596
597         qfree(buffer);
598 }
599
600 void Image_WriteTGARGBA (char *filename, int width, int height, byte *data)
601 {
602         int y;
603         byte *buffer, *in, *out, *end;
604
605         buffer = qmalloc(width*height*4 + 18);
606
607         memset (buffer, 0, 18);
608         buffer[2] = 2;          // uncompressed type
609         buffer[12] = (width >> 0) & 0xFF;
610         buffer[13] = (width >> 8) & 0xFF;
611         buffer[14] = (height >> 0) & 0xFF;
612         buffer[15] = (height >> 8) & 0xFF;
613         buffer[16] = 32;        // pixel size
614
615         // swap rgba to bgra and flip upside down
616         out = buffer + 18;
617         for (y = height - 1;y >= 0;y--)
618         {
619                 in = data + y * width * 4;
620                 end = in + width * 4;
621                 for (;in < end;in += 4)
622                 {
623                         *out++ = in[2];
624                         *out++ = in[1];
625                         *out++ = in[0];
626                         *out++ = in[3];
627                 }
628         }
629         COM_WriteFile (filename, buffer, width*height*4 + 18 );
630
631         qfree(buffer);
632 }
633
634 qboolean Image_CheckAlpha(byte *data, int size, qboolean rgba)
635 {
636         byte *end;
637         if (rgba)
638         {
639                 // check alpha bytes
640                 for (end = data + size * 4, data += 3;data < end;data += 4)
641                         if (*data < 255)
642                                 return 1;
643         }
644         else
645         {
646                 // color 255 is transparent
647                 for (end = data + size;data < end;data++)
648                         if (*data == 255)
649                                 return 1;
650         }
651         return 0;
652 }