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