cruft removal, general cleanup, fix for delta compression bugs, generic targa writer
[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 extern byte qgamma[];
46 void Image_CopyRGBAGamma(byte *in, byte *out, int pixels)
47 {
48         while (pixels--)
49         {
50                 out[0] = qgamma[in[0]];
51                 out[1] = qgamma[in[1]];
52                 out[2] = qgamma[in[2]];
53                 out[3] =        in[3] ;
54                 in += 4;
55                 out += 4;
56         }
57 }
58
59
60 /*
61 =================================================================
62
63   PCX Loading
64
65 =================================================================
66 */
67
68 typedef struct
69 {
70     char        manufacturer;
71     char        version;
72     char        encoding;
73     char        bits_per_pixel;
74     unsigned short      xmin,ymin,xmax,ymax;
75     unsigned short      hres,vres;
76     unsigned char       palette[48];
77     char        reserved;
78     char        color_planes;
79     unsigned short      bytes_per_line;
80     unsigned short      palette_type;
81     char        filler[58];
82     unsigned    data;                   // unbounded
83 } pcx_t;
84
85 /*
86 ============
87 LoadPCX
88 ============
89 */
90 byte* LoadPCX (FILE *f, int matchwidth, int matchheight)
91 {
92         pcx_t   *pcx, pcxbuf;
93         byte    palette[768];
94         byte    *pix, *image_rgba;
95         int             x, y;
96         int             dataByte, runLength;
97         int             count;
98
99 //
100 // parse the PCX file
101 //
102         fread (&pcxbuf, 1, sizeof(pcxbuf), f);
103
104         pcx = &pcxbuf;
105
106         if (pcx->manufacturer != 0x0a
107                 || pcx->version != 5
108                 || pcx->encoding != 1
109                 || pcx->bits_per_pixel != 8
110                 || pcx->xmax > 320
111                 || pcx->ymax > 256)
112         {
113                 Con_Printf ("Bad pcx file\n");
114                 return NULL;
115         }
116
117         if (matchwidth && (pcx->xmax+1) != matchwidth)
118                 return NULL;
119         if (matchheight && (pcx->ymax+1) != matchheight)
120                 return NULL;
121
122         // seek to palette
123         fseek (f, -768, SEEK_END);
124         fread (palette, 1, 768, f);
125
126         fseek (f, sizeof(pcxbuf) - 4, SEEK_SET);
127
128         count = (pcx->xmax+1) * (pcx->ymax+1);
129         image_rgba = malloc( count * 4);
130
131         for (y=0 ; y<=pcx->ymax ; y++)
132         {
133                 pix = image_rgba + 4*y*(pcx->xmax+1);
134                 for (x=0 ; x<=pcx->xmax ; )
135                 {
136                         dataByte = fgetc(f);
137
138                         if((dataByte & 0xC0) == 0xC0)
139                         {
140                                 runLength = dataByte & 0x3F;
141                                 dataByte = fgetc(f);
142                         }
143                         else
144                                 runLength = 1;
145
146                         while(runLength-- > 0)
147                         {
148                                 pix[0] = palette[dataByte*3];
149                                 pix[1] = palette[dataByte*3+1];
150                                 pix[2] = palette[dataByte*3+2];
151                                 pix[3] = 255;
152                                 pix += 4;
153                                 x++;
154                         }
155                 }
156         }
157         fclose(f);
158         image_width = pcx->xmax+1;
159         image_height = pcx->ymax+1;
160         return image_rgba;
161 }
162
163 /*
164 =========================================================
165
166 TARGA LOADING
167
168 =========================================================
169 */
170
171 typedef struct _TargaHeader {
172         unsigned char   id_length, colormap_type, image_type;
173         unsigned short  colormap_index, colormap_length;
174         unsigned char   colormap_size;
175         unsigned short  x_origin, y_origin, width, height;
176         unsigned char   pixel_size, attributes;
177 } TargaHeader;
178
179
180 TargaHeader             targa_header;
181
182 int fgetLittleShort (FILE *f)
183 {
184         byte    b1, b2;
185
186         b1 = fgetc(f);
187         b2 = fgetc(f);
188
189         return (short)(b1 + b2*256);
190 }
191
192 int fgetLittleLong (FILE *f)
193 {
194         byte    b1, b2, b3, b4;
195
196         b1 = fgetc(f);
197         b2 = fgetc(f);
198         b3 = fgetc(f);
199         b4 = fgetc(f);
200
201         return b1 + (b2<<8) + (b3<<16) + (b4<<24);
202 }
203
204
205 /*
206 =============
207 LoadTGA
208 =============
209 */
210 byte* LoadTGA (FILE *fin, int matchwidth, int matchheight)
211 {
212         int                             columns, rows, numPixels;
213         byte                    *pixbuf;
214         int                             row, column;
215         byte                    *image_rgba;
216
217         targa_header.id_length = fgetc(fin);
218         targa_header.colormap_type = fgetc(fin);
219         targa_header.image_type = fgetc(fin);
220         
221         targa_header.colormap_index = fgetLittleShort(fin);
222         targa_header.colormap_length = fgetLittleShort(fin);
223         targa_header.colormap_size = fgetc(fin);
224         targa_header.x_origin = fgetLittleShort(fin);
225         targa_header.y_origin = fgetLittleShort(fin);
226         targa_header.width = fgetLittleShort(fin);
227         targa_header.height = fgetLittleShort(fin);
228         if (matchwidth && targa_header.width != matchwidth)
229                 return NULL;
230         if (matchheight && targa_header.height != matchheight)
231                 return NULL;
232         targa_header.pixel_size = fgetc(fin);
233         targa_header.attributes = fgetc(fin);
234
235         if (targa_header.image_type!=2 
236                 && targa_header.image_type!=10) 
237                 Host_Error ("LoadTGA: Only type 2 and 10 targa RGB images supported\n");
238
239         if (targa_header.colormap_type !=0 
240                 || (targa_header.pixel_size!=32 && targa_header.pixel_size!=24))
241                 Host_Error ("LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n");
242
243         columns = targa_header.width;
244         rows = targa_header.height;
245         numPixels = columns * rows;
246
247         image_rgba = malloc (numPixels*4);
248         
249         if (targa_header.id_length != 0)
250                 fseek(fin, targa_header.id_length, SEEK_CUR);  // skip TARGA image comment
251         
252         if (targa_header.image_type==2) {  // Uncompressed, RGB images
253                 for(row=rows-1; row>=0; row--) {
254                         pixbuf = image_rgba + row*columns*4;
255                         for(column=0; column<columns; column++) {
256                                 unsigned char red = 0,green = 0,blue = 0,alphabyte = 0;
257                                 switch (targa_header.pixel_size) {
258                                         case 24:
259                                                         
260                                                         blue = getc(fin);
261                                                         green = getc(fin);
262                                                         red = getc(fin);
263                                                         *pixbuf++ = red;
264                                                         *pixbuf++ = green;
265                                                         *pixbuf++ = blue;
266                                                         *pixbuf++ = 255;
267                                                         break;
268                                         case 32:
269                                                         blue = getc(fin);
270                                                         green = getc(fin);
271                                                         red = getc(fin);
272                                                         alphabyte = getc(fin);
273                                                         *pixbuf++ = red;
274                                                         *pixbuf++ = green;
275                                                         *pixbuf++ = blue;
276                                                         *pixbuf++ = alphabyte;
277                                                         break;
278                                 }
279                         }
280                 }
281         }
282         else if (targa_header.image_type==10) {   // Runlength encoded RGB images
283                 unsigned char red = 0,green = 0,blue = 0,alphabyte = 0,packetHeader,packetSize,j;
284                 for(row=rows-1; row>=0; row--) {
285                         pixbuf = image_rgba + row*columns*4;
286                         for(column=0; column<columns; ) {
287                                 packetHeader=getc(fin);
288                                 packetSize = 1 + (packetHeader & 0x7f);
289                                 if (packetHeader & 0x80) {        // run-length packet
290                                         switch (targa_header.pixel_size) {
291                                                 case 24:
292                                                                 blue = getc(fin);
293                                                                 green = getc(fin);
294                                                                 red = getc(fin);
295                                                                 alphabyte = 255;
296                                                                 break;
297                                                 case 32:
298                                                                 blue = getc(fin);
299                                                                 green = getc(fin);
300                                                                 red = getc(fin);
301                                                                 alphabyte = getc(fin);
302                                                                 break;
303                                         }
304         
305                                         for(j=0;j<packetSize;j++) {
306                                                 *pixbuf++=red;
307                                                 *pixbuf++=green;
308                                                 *pixbuf++=blue;
309                                                 *pixbuf++=alphabyte;
310                                                 column++;
311                                                 if (column==columns) { // run spans across rows
312                                                         column=0;
313                                                         if (row>0)
314                                                                 row--;
315                                                         else
316                                                                 goto breakOut;
317                                                         pixbuf = image_rgba + row*columns*4;
318                                                 }
319                                         }
320                                 }
321                                 else {                            // non run-length packet
322                                         for(j=0;j<packetSize;j++) {
323                                                 switch (targa_header.pixel_size) {
324                                                         case 24:
325                                                                         blue = getc(fin);
326                                                                         green = getc(fin);
327                                                                         red = getc(fin);
328                                                                         *pixbuf++ = red;
329                                                                         *pixbuf++ = green;
330                                                                         *pixbuf++ = blue;
331                                                                         *pixbuf++ = 255;
332                                                                         break;
333                                                         case 32:
334                                                                         blue = getc(fin);
335                                                                         green = getc(fin);
336                                                                         red = getc(fin);
337                                                                         alphabyte = getc(fin);
338                                                                         *pixbuf++ = red;
339                                                                         *pixbuf++ = green;
340                                                                         *pixbuf++ = blue;
341                                                                         *pixbuf++ = alphabyte;
342                                                                         break;
343                                                 }
344                                                 column++;
345                                                 if (column==columns) { // pixel packet run spans across rows
346                                                         column=0;
347                                                         if (row>0)
348                                                                 row--;
349                                                         else
350                                                                 goto breakOut;
351                                                         pixbuf = image_rgba + row*columns*4;
352                                                 }                                               
353                                         }
354                                 }
355                         }
356                         breakOut:;
357                 }
358         }
359         
360         fclose(fin);
361         image_width = columns;
362         image_height = rows;
363         return image_rgba;
364 }
365
366 /*
367 ============
368 LoadLMP
369 ============
370 */
371 byte* LoadLMP (FILE *f, int matchwidth, int matchheight)
372 {
373         byte    *image_rgba;
374         int             width, height;
375
376         // parse the very complicated header *chuckle*
377         width = fgetLittleLong(f);
378         height = fgetLittleLong(f);
379         if ((unsigned) width > 4096 || (unsigned) height > 4096)
380                 Host_Error("LoadLMP: invalid size\n");
381         if (matchwidth && width != matchwidth)
382                 return NULL;
383         if (matchheight && height != matchheight)
384                 return NULL;
385
386         image_rgba = malloc(width*height*4);
387         fread(image_rgba + width*height*3, 1, width*height, f);
388         fclose(f);
389
390         Image_Copy8bitRGBA(image_rgba + width*height*3, image_rgba, width*height, d_8to24table);
391         image_width = width;
392         image_height = height;
393         return image_rgba;
394 }
395
396 byte* loadimagepixels (char* filename, qboolean complain, int matchwidth, int matchheight)
397 {
398         FILE    *f;
399         char    basename[128], name[128];
400         byte    *image_rgba, *c;
401         COM_StripExtension(filename, basename); // strip the extension to allow TGA skins on Q2 models despite the .pcx in the skin name
402         // replace *'s with +, so commandline utils don't get confused when dealing with the external files
403         c = basename;
404         while (*c)
405         {
406                 if (*c == '*')
407                         *c = '+';
408                 c++;
409         }
410         sprintf (name, "textures/%s.tga", basename);
411         COM_FOpenFile (name, &f, true);
412         if (f)
413                 return LoadTGA (f, matchwidth, matchheight);
414         sprintf (name, "textures/%s.pcx", basename);
415         COM_FOpenFile (name, &f, true);
416         if (f)
417                 return LoadPCX (f, matchwidth, matchheight);
418         sprintf (name, "%s.tga", basename);
419         COM_FOpenFile (name, &f, true);
420         if (f)
421                 return LoadTGA (f, matchwidth, matchheight);
422         sprintf (name, "%s.pcx", basename);
423         COM_FOpenFile (name, &f, true);
424         if (f)
425                 return LoadPCX (f, matchwidth, matchheight);
426         sprintf (name, "%s.lmp", basename);
427         COM_FOpenFile (name, &f, true);
428         if (f)
429                 return LoadLMP (f, matchwidth, matchheight);
430         if ((image_rgba = W_GetTexture(basename, matchwidth, matchheight)))
431                 return image_rgba;
432         COM_StripExtension(filename, basename); // do it again with a * this time
433         if ((image_rgba = W_GetTexture(basename, matchwidth, matchheight)))
434                 return image_rgba;
435         if (complain)
436                 Con_Printf ("Couldn't load %s.tga or .pcx\n", filename);
437         return NULL;
438 }
439
440 int image_makemask (byte *in, byte *out, int size)
441 {
442         int             i, count;
443         count = 0;
444         for (i = 0;i < size;i++)
445         {
446                 out[0] = out[1] = out[2] = 255;
447                 out[3] = in[3];
448                 if (in[3] != 255)
449                         count++;
450                 in += 4;
451                 out += 4;
452         }
453         return count;
454 }
455
456 byte* loadimagepixelsmask (char* filename, qboolean complain, int matchwidth, int matchheight)
457 {
458         byte    *in, *data;
459         in = data = loadimagepixels(filename, complain, matchwidth, matchheight);
460         if (!data)
461                 return NULL;
462         if (image_makemask(data, data, image_width * image_height))
463                 return data; // some transparency
464         else
465         {
466                 free(data);
467                 return NULL; // all opaque
468         }
469 }
470
471 int loadtextureimage (char* filename, int matchwidth, int matchheight, qboolean complain, qboolean mipmap)
472 {
473         int texnum;
474         byte *data;
475         if (!(data = loadimagepixels (filename, complain, matchwidth, matchheight)))
476                 return 0;
477         texnum = GL_LoadTexture (filename, image_width, image_height, data, mipmap, true, 4);
478         free(data);
479         return texnum;
480 }
481
482 int loadtextureimagemask (char* filename, int matchwidth, int matchheight, qboolean complain, qboolean mipmap)
483 {
484         int texnum;
485         byte *data;
486         if (!(data = loadimagepixelsmask (filename, complain, matchwidth, matchheight)))
487                 return 0;
488         texnum = GL_LoadTexture (filename, image_width, image_height, data, mipmap, true, 4);
489         free(data);
490         return texnum;
491 }
492
493 int image_masktexnum;
494 int loadtextureimagewithmask (char* filename, int matchwidth, int matchheight, qboolean complain, qboolean mipmap)
495 {
496         int texnum, count;
497         byte *data;
498         char *filename2;
499         image_masktexnum = 0;
500         if (!(data = loadimagepixels (filename, complain, matchwidth, matchheight)))
501                 return 0;
502         texnum = GL_LoadTexture (filename, image_width, image_height, data, mipmap, true, 4);
503         count = image_makemask(data, data, image_width * image_height);
504         if (count)
505         {
506                 filename2 = malloc(strlen(filename) + 6);
507                 sprintf(filename2, "%s_mask", filename);
508                 image_masktexnum = GL_LoadTexture (filename2, image_width, image_height, data, mipmap, true, 4);
509                 free(filename2);
510         }
511         free(data);
512         return texnum;
513 }
514
515 void Image_WriteTGARGB (char *filename, int width, int height, byte *data)
516 {
517         byte *buffer, *in, *out, *end;
518
519         buffer = malloc(width*height*3 + 18);
520
521         memset (buffer, 0, 18);
522         buffer[2] = 2;          // uncompressed type
523         buffer[12] = (width >> 0) & 0xFF;
524         buffer[13] = (width >> 8) & 0xFF;
525         buffer[14] = (height >> 0) & 0xFF;
526         buffer[15] = (height >> 8) & 0xFF;
527         buffer[16] = 24;        // pixel size
528
529         // swap rgb to bgr
530         in = data;
531         end = in + width*height*3;
532         out = buffer + 18;
533         for (;in < end;in += 3)
534         {
535                 *out++ = in[2];
536                 *out++ = in[1];
537                 *out++ = in[0];
538         }
539         COM_WriteFile (filename, buffer, glwidth*glheight*3 + 18 );
540
541         free(buffer);
542 }