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