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