]> git.xonotic.org Git - xonotic/darkplaces.git/blob - image.c
optimizing R_DrawSurfaces and WorldNode functions
[xonotic/darkplaces.git] / image.c
1
2 #include "quakedef.h"
3
4 int             image_width;
5 int             image_height;
6
7 void Image_GammaRemapRGB(qbyte *in, qbyte *out, int pixels, qbyte *gammar, qbyte *gammag, qbyte *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(qbyte *in, qbyte *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 /*
58 =================================================================
59
60   PCX Loading
61
62 =================================================================
63 */
64
65 typedef struct
66 {
67     char        manufacturer;
68     char        version;
69     char        encoding;
70     char        bits_per_pixel;
71     unsigned short      xmin,ymin,xmax,ymax;
72     unsigned short      hres,vres;
73     unsigned char       palette[48];
74     char        reserved;
75     char        color_planes;
76     unsigned short      bytes_per_line;
77     unsigned short      palette_type;
78     char        filler[58];
79 } pcx_t;
80
81 /*
82 ============
83 LoadPCX
84 ============
85 */
86 qbyte* LoadPCX (qbyte *f, int matchwidth, int matchheight)
87 {
88         pcx_t pcx;
89         qbyte *palette, *a, *b, *image_rgba, *fin, *pbuf, *enddata;
90         int x, y, x2, dataByte;
91
92         if (loadsize < sizeof(pcx) + 768)
93         {
94                 Con_Printf ("Bad pcx file\n");
95                 return NULL;
96         }
97
98         fin = f;
99
100         memcpy(&pcx, fin, sizeof(pcx));
101         fin += sizeof(pcx);
102
103         // LordHavoc: big-endian support ported from QF newtree
104         pcx.xmax = LittleShort (pcx.xmax);
105         pcx.xmin = LittleShort (pcx.xmin);
106         pcx.ymax = LittleShort (pcx.ymax);
107         pcx.ymin = LittleShort (pcx.ymin);
108         pcx.hres = LittleShort (pcx.hres);
109         pcx.vres = LittleShort (pcx.vres);
110         pcx.bytes_per_line = LittleShort (pcx.bytes_per_line);
111         pcx.palette_type = LittleShort (pcx.palette_type);
112
113         if (pcx.manufacturer != 0x0a || pcx.version != 5 || pcx.encoding != 1 || pcx.bits_per_pixel != 8 || pcx.xmax > 320 || pcx.ymax > 256)
114         {
115                 Con_Printf ("Bad pcx file\n");
116                 return NULL;
117         }
118
119         if (matchwidth && (pcx.xmax+1) != matchwidth)
120         {
121                 return NULL;
122         }
123         if (matchheight && (pcx.ymax+1) != matchheight)
124         {
125                 return NULL;
126         }
127
128         image_width = pcx.xmax+1;
129         image_height = pcx.ymax+1;
130
131         palette = f + loadsize - 768;
132
133         image_rgba = Mem_Alloc(tempmempool, image_width*image_height*4);
134         if (!image_rgba)
135         {
136                 Con_Printf("LoadPCX: not enough memory for %i by %i image\n", image_width, image_height);
137                 return NULL;
138         }
139         pbuf = image_rgba + image_width*image_height*3;
140         enddata = palette;
141
142         for (y = 0;y < image_height && fin < enddata;y++)
143         {
144                 a = pbuf + y * image_width;
145                 for (x = 0;x < image_width && fin < enddata;)
146                 {
147                         dataByte = *fin++;
148                         if(dataByte >= 0xC0)
149                         {
150                                 if (fin >= enddata)
151                                         break;
152                                 x2 = x + (dataByte & 0x3F);
153                                 dataByte = *fin++;
154                                 if (x2 > image_width)
155                                         x2 = image_width; // technically an error
156                                 while(x < x2)
157                                         a[x++] = dataByte;
158                         }
159                         else
160                                 a[x++] = dataByte;
161                 }
162                 fin += pcx.bytes_per_line - image_width; // the number of bytes per line is always forced to an even number
163                 while(x < image_width)
164                         a[x++] = 0;
165         }
166
167         a = image_rgba;
168         b = pbuf;
169
170         for(x = 0;x < image_width*image_height;x++)
171         {
172                 y = *b++ * 3;
173                 *a++ = palette[y];
174                 *a++ = palette[y+1];
175                 *a++ = palette[y+2];
176                 *a++ = 255;
177         }
178
179         return image_rgba;
180 }
181
182 /*
183 =========================================================
184
185 TARGA LOADING
186
187 =========================================================
188 */
189
190 typedef struct _TargaHeader
191 {
192         unsigned char   id_length, colormap_type, image_type;
193         unsigned short  colormap_index, colormap_length;
194         unsigned char   colormap_size;
195         unsigned short  x_origin, y_origin, width, height;
196         unsigned char   pixel_size, attributes;
197 }
198 TargaHeader;
199
200 TargaHeader             targa_header;
201
202
203 /*
204 =============
205 LoadTGA
206 =============
207 */
208 qbyte *LoadTGA (qbyte *f, int matchwidth, int matchheight)
209 {
210         int x, y, row_inc;
211         unsigned char red, green, blue, alpha, run, runlen;
212         qbyte *pixbuf, *image_rgba, *fin, *enddata;
213
214         if (loadsize < 18+3)
215                 return NULL;
216         targa_header.id_length = f[0];
217         targa_header.colormap_type = f[1];
218         targa_header.image_type = f[2];
219
220         targa_header.colormap_index = f[3] + f[4] * 256;
221         targa_header.colormap_length = f[5] + f[6] * 256;
222         targa_header.colormap_size = f[7];
223         targa_header.x_origin = f[8] + f[9] * 256;
224         targa_header.y_origin = f[10] + f[11] * 256;
225         targa_header.width = f[12] + f[13] * 256;
226         targa_header.height = f[14] + f[15] * 256;
227         if (matchwidth && targa_header.width != matchwidth)
228                 return NULL;
229         if (matchheight && targa_header.height != matchheight)
230                 return NULL;
231         targa_header.pixel_size = f[16];
232         targa_header.attributes = f[17];
233
234         if (targa_header.image_type != 2 && targa_header.image_type != 10)
235         {
236                 Con_Printf ("LoadTGA: Only type 2 and 10 targa RGB images supported\n");
237                 return NULL;
238         }
239
240         if (targa_header.colormap_type != 0     || (targa_header.pixel_size != 32 && targa_header.pixel_size != 24))
241         {
242                 Con_Printf ("LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n");
243                 return NULL;
244         }
245
246         enddata = f + loadsize;
247
248         image_width = targa_header.width;
249         image_height = targa_header.height;
250
251         image_rgba = Mem_Alloc(tempmempool, image_width * image_height * 4);
252         if (!image_rgba)
253         {
254                 Con_Printf ("LoadTGA: not enough memory for %i by %i image\n", image_width, image_height);
255                 return NULL;
256         }
257
258         fin = f + 18;
259         if (targa_header.id_length != 0)
260                 fin += targa_header.id_length;  // skip TARGA image comment
261
262         // If bit 5 of attributes isn't set, the image has been stored from bottom to top
263         if ((targa_header.attributes & 0x20) == 0)
264         {
265                 pixbuf = image_rgba + (image_height - 1)*image_width*4;
266                 row_inc = -image_width*4*2;
267         }
268         else
269         {
270                 pixbuf = image_rgba;
271                 row_inc = 0;
272         }
273
274         if (targa_header.image_type == 2)
275         {
276                 // Uncompressed, RGB images
277                 if (targa_header.pixel_size == 24)
278                 {
279                         if (fin + image_width * image_height * 3 <= enddata)
280                         {
281                                 for(y = 0;y < image_height;y++)
282                                 {
283                                         for(x = 0;x < image_width;x++)
284                                         {
285                                                 *pixbuf++ = fin[2];
286                                                 *pixbuf++ = fin[1];
287                                                 *pixbuf++ = fin[0];
288                                                 *pixbuf++ = 255;
289                                                 fin += 3;
290                                         }
291                                         pixbuf += row_inc;
292                                 }
293                         }
294                 }
295                 else
296                 {
297                         if (fin + image_width * image_height * 4 <= enddata)
298                         {
299                                 for(y = 0;y < image_height;y++)
300                                 {
301                                         for(x = 0;x < image_width;x++)
302                                         {
303                                                 *pixbuf++ = fin[2];
304                                                 *pixbuf++ = fin[1];
305                                                 *pixbuf++ = fin[0];
306                                                 *pixbuf++ = fin[3];
307                                                 fin += 4;
308                                         }
309                                         pixbuf += row_inc;
310                                 }
311                         }
312                 }
313         }
314         else if (targa_header.image_type==10)
315         {
316                 // Runlength encoded RGB images
317                 x = 0;
318                 y = 0;
319                 while (y < image_height && fin < enddata)
320                 {
321                         runlen = *fin++;
322                         if (runlen & 0x80)
323                         {
324                                 // RLE compressed run
325                                 runlen = 1 + (runlen & 0x7f);
326                                 if (targa_header.pixel_size == 24)
327                                 {
328                                         if (fin + 3 > enddata)
329                                                 break;
330                                         blue = *fin++;
331                                         green = *fin++;
332                                         red = *fin++;
333                                         alpha = 255;
334                                 }
335                                 else
336                                 {
337                                         if (fin + 4 > enddata)
338                                                 break;
339                                         blue = *fin++;
340                                         green = *fin++;
341                                         red = *fin++;
342                                         alpha = *fin++;
343                                 }
344
345                                 while (runlen && y < image_height)
346                                 {
347                                         run = runlen;
348                                         if (run > image_width - x)
349                                                 run = image_width - x;
350                                         x += run;
351                                         runlen -= run;
352                                         while(run--)
353                                         {
354                                                 *pixbuf++ = red;
355                                                 *pixbuf++ = green;
356                                                 *pixbuf++ = blue;
357                                                 *pixbuf++ = alpha;
358                                         }
359                                         if (x == image_width)
360                                         {
361                                                 // end of line, advance to next
362                                                 x = 0;
363                                                 y++;
364                                                 pixbuf += row_inc;
365                                         }
366                                 }
367                         }
368                         else
369                         {
370                                 // RLE uncompressed run
371                                 runlen = 1 + (runlen & 0x7f);
372                                 while (runlen && y < image_height)
373                                 {
374                                         run = runlen;
375                                         if (run > image_width - x)
376                                                 run = image_width - x;
377                                         x += run;
378                                         runlen -= run;
379                                         if (targa_header.pixel_size == 24)
380                                         {
381                                                 if (fin + run * 3 > enddata)
382                                                         break;
383                                                 while(run--)
384                                                 {
385                                                         *pixbuf++ = fin[2];
386                                                         *pixbuf++ = fin[1];
387                                                         *pixbuf++ = fin[0];
388                                                         *pixbuf++ = 255;
389                                                         fin += 3;
390                                                 }
391                                         }
392                                         else
393                                         {
394                                                 if (fin + run * 4 > enddata)
395                                                         break;
396                                                 while(run--)
397                                                 {
398                                                         *pixbuf++ = fin[2];
399                                                         *pixbuf++ = fin[1];
400                                                         *pixbuf++ = fin[0];
401                                                         *pixbuf++ = fin[3];
402                                                         fin += 4;
403                                                 }
404                                         }
405                                         if (x == image_width)
406                                         {
407                                                 // end of line, advance to next
408                                                 x = 0;
409                                                 y++;
410                                                 pixbuf += row_inc;
411                                         }
412                                 }
413                         }
414                 }
415         }
416         return image_rgba;
417 }
418
419 /*
420 ============
421 LoadLMP
422 ============
423 */
424 qbyte *LoadLMP (qbyte *f, int matchwidth, int matchheight)
425 {
426         qbyte *image_rgba;
427         int width, height;
428
429         if (loadsize < 9)
430         {
431                 Con_Printf("LoadLMP: invalid LMP file\n");
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                 return NULL;
442         }
443         if ((matchwidth && width != matchwidth) || (matchheight && height != matchheight))
444                 return NULL;
445
446         if (loadsize < 8 + width * height)
447         {
448                 Con_Printf("LoadLMP: invalid LMP file\n");
449                 return NULL;
450         }
451
452         image_width = width;
453         image_height = height;
454
455         image_rgba = Mem_Alloc(tempmempool, image_width * image_height * 4);
456         if (!image_rgba)
457         {
458                 Con_Printf("LoadLMP: not enough memory for %i by %i image\n", image_width, image_height);
459                 return NULL;
460         }
461         Image_Copy8bitRGBA(f + 8, image_rgba, image_width * image_height, d_8to24table);
462         return image_rgba;
463 }
464
465 /*
466 ============
467 LoadLMP
468 ============
469 */
470 qbyte *LoadLMPAs8Bit (qbyte *f, int matchwidth, int matchheight)
471 {
472         qbyte *image_8bit;
473         int width, height;
474
475         if (loadsize < 9)
476         {
477                 Con_Printf("LoadLMPAs8Bit: invalid LMP file\n");
478                 return NULL;
479         }
480
481         // parse the very complicated header *chuckle*
482         width = f[0] + f[1] * 256 + f[2] * 65536 + f[3] * 16777216;
483         height = f[4] + f[5] * 256 + f[6] * 65536 + f[7] * 16777216;
484         if ((unsigned) width > 4096 || (unsigned) height > 4096)
485         {
486                 Con_Printf("LoadLMPAs8Bit: invalid size\n");
487                 return NULL;
488         }
489         if ((matchwidth && width != matchwidth) || (matchheight && height != matchheight))
490                 return NULL;
491
492         if (loadsize < 8 + width * height)
493         {
494                 Con_Printf("LoadLMPAs8Bit: invalid LMP file\n");
495                 return NULL;
496         }
497
498         image_width = width;
499         image_height = height;
500
501         image_8bit = Mem_Alloc(tempmempool, image_width * image_height);
502         if (!image_8bit)
503         {
504                 Con_Printf("LoadLMPAs8Bit: not enough memory for %i by %i image\n", image_width, image_height);
505                 return NULL;
506         }
507         memcpy(image_8bit, f + 8, image_width * image_height);
508         return image_8bit;
509 }
510
511 void Image_StripImageExtension (char *in, char *out)
512 {
513         char *end, *temp;
514         end = in + strlen(in);
515         if ((end - in) >= 4)
516         {
517                 temp = end - 4;
518                 if (strcmp(temp, ".tga") == 0 || strcmp(temp, ".pcx") == 0 || strcmp(temp, ".lmp") == 0)
519                         end = temp;
520                 while (in < end)
521                         *out++ = *in++;
522                 *out++ = 0;
523         }
524         else
525                 strcpy(out, in);
526 }
527
528 qbyte *loadimagepixels (char *filename, qboolean complain, int matchwidth, int matchheight)
529 {
530         qbyte *f, *data;
531         char basename[256], name[256], *c;
532         Image_StripImageExtension(filename, basename); // strip .tga, .pcx and .lmp extensions to allow replacement by other types
533         // replace *'s with #, so commandline utils don't get confused when dealing with the external files
534         for (c = basename;*c;c++)
535                 if (*c == '*')
536                         *c = '#';
537         sprintf (name, "textures/%s.tga", basename);
538         f = COM_LoadFile(name, true);
539         if (f)
540         {
541                 data = LoadTGA (f, matchwidth, matchheight);
542                 Mem_Free(f);
543                 return data;
544         }
545         sprintf (name, "textures/%s.pcx", basename);
546         f = COM_LoadFile(name, true);
547         if (f)
548         {
549                 data = LoadPCX (f, matchwidth, matchheight);
550                 Mem_Free(f);
551                 return data;
552         }
553         sprintf (name, "%s.tga", basename);
554         f = COM_LoadFile(name, true);
555         if (f)
556         {
557                 data = LoadTGA (f, matchwidth, matchheight);
558                 Mem_Free(f);
559                 return data;
560         }
561         sprintf (name, "%s.pcx", basename);
562         f = COM_LoadFile(name, true);
563         if (f)
564         {
565                 data = LoadPCX (f, matchwidth, matchheight);
566                 Mem_Free(f);
567                 return data;
568         }
569         sprintf (name, "%s.lmp", basename);
570         f = COM_LoadFile(name, true);
571         if (f)
572         {
573                 data = LoadLMP (f, matchwidth, matchheight);
574                 Mem_Free(f);
575                 return data;
576         }
577         if (complain)
578                 Con_Printf ("Couldn't load %s.tga, .pcx, .lmp\n", filename);
579         return NULL;
580 }
581
582 int image_makemask (qbyte *in, qbyte *out, int size)
583 {
584         int             i, count;
585         count = 0;
586         for (i = 0;i < size;i++)
587         {
588                 out[0] = out[1] = out[2] = 255;
589                 out[3] = in[3];
590                 if (in[3] != 255)
591                         count++;
592                 in += 4;
593                 out += 4;
594         }
595         return count;
596 }
597
598 qbyte* loadimagepixelsmask (char* filename, qboolean complain, int matchwidth, int matchheight)
599 {
600         qbyte *in, *data;
601         in = data = loadimagepixels(filename, complain, matchwidth, matchheight);
602         if (!data)
603                 return NULL;
604         if (image_makemask(data, data, image_width * image_height))
605                 return data; // some transparency
606         else
607         {
608                 Mem_Free(data);
609                 return NULL; // all opaque
610         }
611 }
612
613 rtexture_t *loadtextureimage (rtexturepool_t *pool, char* filename, int matchwidth, int matchheight, qboolean complain, qboolean mipmap, qboolean precache)
614 {
615         qbyte *data;
616         rtexture_t *rt;
617         if (!(data = loadimagepixels (filename, complain, matchwidth, matchheight)))
618                 return 0;
619         rt = R_LoadTexture (pool, filename, image_width, image_height, data, TEXTYPE_RGBA, TEXF_ALPHA | (mipmap ? TEXF_MIPMAP : 0) | (precache ? TEXF_PRECACHE : 0));
620         Mem_Free(data);
621         return rt;
622 }
623
624 rtexture_t *loadtextureimagemask (rtexturepool_t *pool, char* filename, int matchwidth, int matchheight, qboolean complain, qboolean mipmap, qboolean precache)
625 {
626         qbyte *data;
627         rtexture_t *rt;
628         if (!(data = loadimagepixelsmask (filename, complain, matchwidth, matchheight)))
629                 return 0;
630         rt = R_LoadTexture (pool, filename, image_width, image_height, data, TEXTYPE_RGBA, TEXF_ALPHA | (mipmap ? TEXF_MIPMAP : 0) | (precache ? TEXF_PRECACHE : 0));
631         Mem_Free(data);
632         return rt;
633 }
634
635 rtexture_t *image_masktex;
636 rtexture_t *loadtextureimagewithmask (rtexturepool_t *pool, char* filename, int matchwidth, int matchheight, qboolean complain, qboolean mipmap, qboolean precache)
637 {
638         int count;
639         qbyte *data;
640         char *filename2;
641         rtexture_t *rt;
642         image_masktex = NULL;
643         if (!(data = loadimagepixels (filename, complain, matchwidth, matchheight)))
644                 return 0;
645         rt = R_LoadTexture (pool, filename, image_width, image_height, data, TEXTYPE_RGBA, TEXF_ALPHA | (mipmap ? TEXF_MIPMAP : 0) | (precache ? TEXF_PRECACHE : 0));
646         count = image_makemask(data, data, image_width * image_height);
647         if (count)
648         {
649                 filename2 = Mem_Alloc(tempmempool, strlen(filename) + 6);
650                 sprintf(filename2, "%s_mask", filename);
651                 image_masktex = R_LoadTexture (pool, filename2, image_width, image_height, data, TEXTYPE_RGBA, TEXF_ALPHA | (mipmap ? TEXF_MIPMAP : 0) | (precache ? TEXF_PRECACHE : 0));
652                 Mem_Free(filename2);
653         }
654         Mem_Free(data);
655         return rt;
656 }
657
658 qboolean Image_WriteTGARGB_preflipped (char *filename, int width, int height, qbyte *data)
659 {
660         qboolean ret;
661         qbyte *buffer, *in, *out, *end;
662
663         buffer = Mem_Alloc(tempmempool, width*height*3 + 18);
664
665         memset (buffer, 0, 18);
666         buffer[2] = 2;          // uncompressed type
667         buffer[12] = (width >> 0) & 0xFF;
668         buffer[13] = (width >> 8) & 0xFF;
669         buffer[14] = (height >> 0) & 0xFF;
670         buffer[15] = (height >> 8) & 0xFF;
671         buffer[16] = 24;        // pixel size
672
673         // swap rgb to bgr
674         in = data;
675         out = buffer + 18;
676         end = in + width*height*3;
677         for (;in < end;in += 3)
678         {
679                 *out++ = in[2];
680                 *out++ = in[1];
681                 *out++ = in[0];
682         }
683         ret = COM_WriteFile (filename, buffer, width*height*3 + 18 );
684
685         Mem_Free(buffer);
686         return ret;
687 }
688
689 void Image_WriteTGARGB (char *filename, int width, int height, qbyte *data)
690 {
691         int y;
692         qbyte *buffer, *in, *out, *end;
693
694         buffer = Mem_Alloc(tempmempool, width*height*3 + 18);
695
696         memset (buffer, 0, 18);
697         buffer[2] = 2;          // uncompressed type
698         buffer[12] = (width >> 0) & 0xFF;
699         buffer[13] = (width >> 8) & 0xFF;
700         buffer[14] = (height >> 0) & 0xFF;
701         buffer[15] = (height >> 8) & 0xFF;
702         buffer[16] = 24;        // pixel size
703
704         // swap rgb to bgr and flip upside down
705         out = buffer + 18;
706         for (y = height - 1;y >= 0;y--)
707         {
708                 in = data + y * width * 3;
709                 end = in + width * 3;
710                 for (;in < end;in += 3)
711                 {
712                         *out++ = in[2];
713                         *out++ = in[1];
714                         *out++ = in[0];
715                 }
716         }
717         COM_WriteFile (filename, buffer, width*height*3 + 18 );
718
719         Mem_Free(buffer);
720 }
721
722 void Image_WriteTGARGBA (char *filename, int width, int height, qbyte *data)
723 {
724         int y;
725         qbyte *buffer, *in, *out, *end;
726
727         buffer = Mem_Alloc(tempmempool, width*height*4 + 18);
728
729         memset (buffer, 0, 18);
730         buffer[2] = 2;          // uncompressed type
731         buffer[12] = (width >> 0) & 0xFF;
732         buffer[13] = (width >> 8) & 0xFF;
733         buffer[14] = (height >> 0) & 0xFF;
734         buffer[15] = (height >> 8) & 0xFF;
735         buffer[16] = 32;        // pixel size
736
737         // swap rgba to bgra and flip upside down
738         out = buffer + 18;
739         for (y = height - 1;y >= 0;y--)
740         {
741                 in = data + y * width * 4;
742                 end = in + width * 4;
743                 for (;in < end;in += 4)
744                 {
745                         *out++ = in[2];
746                         *out++ = in[1];
747                         *out++ = in[0];
748                         *out++ = in[3];
749                 }
750         }
751         COM_WriteFile (filename, buffer, width*height*4 + 18 );
752
753         Mem_Free(buffer);
754 }
755
756 qboolean Image_CheckAlpha(qbyte *data, int size, qboolean rgba)
757 {
758         qbyte *end;
759         if (rgba)
760         {
761                 // check alpha bytes
762                 for (end = data + size * 4, data += 3;data < end;data += 4)
763                         if (*data < 255)
764                                 return 1;
765         }
766         else
767         {
768                 // color 255 is transparent
769                 for (end = data + size;data < end;data++)
770                         if (*data == 255)
771                                 return 1;
772         }
773         return 0;
774 }
775
776 static void Image_Resample32LerpLine (qbyte *in, qbyte *out, int inwidth, int outwidth)
777 {
778         int             j, xi, oldx = 0, f, fstep, endx, lerp;
779         fstep = (int) (inwidth*65536.0f/outwidth);
780         endx = (inwidth-1);
781         for (j = 0,f = 0;j < outwidth;j++, f += fstep)
782         {
783                 xi = f >> 16;
784                 if (xi != oldx)
785                 {
786                         in += (xi - oldx) * 4;
787                         oldx = xi;
788                 }
789                 if (xi < endx)
790                 {
791                         lerp = f & 0xFFFF;
792                         *out++ = (qbyte) ((((in[4] - in[0]) * lerp) >> 16) + in[0]);
793                         *out++ = (qbyte) ((((in[5] - in[1]) * lerp) >> 16) + in[1]);
794                         *out++ = (qbyte) ((((in[6] - in[2]) * lerp) >> 16) + in[2]);
795                         *out++ = (qbyte) ((((in[7] - in[3]) * lerp) >> 16) + in[3]);
796                 }
797                 else // last pixel of the line has no pixel to lerp to
798                 {
799                         *out++ = in[0];
800                         *out++ = in[1];
801                         *out++ = in[2];
802                         *out++ = in[3];
803                 }
804         }
805 }
806
807 static void Image_Resample24LerpLine (qbyte *in, qbyte *out, int inwidth, int outwidth)
808 {
809         int             j, xi, oldx = 0, f, fstep, endx, lerp;
810         fstep = (int) (inwidth*65536.0f/outwidth);
811         endx = (inwidth-1);
812         for (j = 0,f = 0;j < outwidth;j++, f += fstep)
813         {
814                 xi = f >> 16;
815                 if (xi != oldx)
816                 {
817                         in += (xi - oldx) * 3;
818                         oldx = xi;
819                 }
820                 if (xi < endx)
821                 {
822                         lerp = f & 0xFFFF;
823                         *out++ = (qbyte) ((((in[3] - in[0]) * lerp) >> 16) + in[0]);
824                         *out++ = (qbyte) ((((in[4] - in[1]) * lerp) >> 16) + in[1]);
825                         *out++ = (qbyte) ((((in[5] - in[2]) * lerp) >> 16) + in[2]);
826                 }
827                 else // last pixel of the line has no pixel to lerp to
828                 {
829                         *out++ = in[0];
830                         *out++ = in[1];
831                         *out++ = in[2];
832                 }
833         }
834 }
835
836 int resamplerowsize = 0;
837 qbyte *resamplerow1 = NULL;
838 qbyte *resamplerow2 = NULL;
839 mempool_t *resamplemempool = NULL;
840
841 #define LERPBYTE(i) r = resamplerow1[i];out[i] = (qbyte) ((((resamplerow2[i] - r) * lerp) >> 16) + r)
842 void Image_Resample32Lerp(void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight)
843 {
844         int i, j, r, yi, oldy, f, fstep, lerp, endy = (inheight-1), inwidth4 = inwidth*4, outwidth4 = outwidth*4;
845         qbyte *inrow, *out;
846         out = outdata;
847         fstep = (int) (inheight*65536.0f/outheight);
848
849         inrow = indata;
850         oldy = 0;
851         Image_Resample32LerpLine (inrow, resamplerow1, inwidth, outwidth);
852         Image_Resample32LerpLine (inrow + inwidth4, resamplerow2, inwidth, outwidth);
853         for (i = 0, f = 0;i < outheight;i++,f += fstep)
854         {
855                 yi = f >> 16;
856                 if (yi < endy)
857                 {
858                         lerp = f & 0xFFFF;
859                         if (yi != oldy)
860                         {
861                                 inrow = (qbyte *)indata + inwidth4*yi;
862                                 if (yi == oldy+1)
863                                         memcpy(resamplerow1, resamplerow2, outwidth4);
864                                 else
865                                         Image_Resample32LerpLine (inrow, resamplerow1, inwidth, outwidth);
866                                 Image_Resample32LerpLine (inrow + inwidth4, resamplerow2, inwidth, outwidth);
867                                 oldy = yi;
868                         }
869                         j = outwidth - 4;
870                         while(j >= 0)
871                         {
872                                 LERPBYTE( 0);
873                                 LERPBYTE( 1);
874                                 LERPBYTE( 2);
875                                 LERPBYTE( 3);
876                                 LERPBYTE( 4);
877                                 LERPBYTE( 5);
878                                 LERPBYTE( 6);
879                                 LERPBYTE( 7);
880                                 LERPBYTE( 8);
881                                 LERPBYTE( 9);
882                                 LERPBYTE(10);
883                                 LERPBYTE(11);
884                                 LERPBYTE(12);
885                                 LERPBYTE(13);
886                                 LERPBYTE(14);
887                                 LERPBYTE(15);
888                                 out += 16;
889                                 resamplerow1 += 16;
890                                 resamplerow2 += 16;
891                                 j -= 4;
892                         }
893                         if (j & 2)
894                         {
895                                 LERPBYTE( 0);
896                                 LERPBYTE( 1);
897                                 LERPBYTE( 2);
898                                 LERPBYTE( 3);
899                                 LERPBYTE( 4);
900                                 LERPBYTE( 5);
901                                 LERPBYTE( 6);
902                                 LERPBYTE( 7);
903                                 out += 8;
904                                 resamplerow1 += 8;
905                                 resamplerow2 += 8;
906                         }
907                         if (j & 1)
908                         {
909                                 LERPBYTE( 0);
910                                 LERPBYTE( 1);
911                                 LERPBYTE( 2);
912                                 LERPBYTE( 3);
913                                 out += 4;
914                                 resamplerow1 += 4;
915                                 resamplerow2 += 4;
916                         }
917                         resamplerow1 -= outwidth4;
918                         resamplerow2 -= outwidth4;
919                 }
920                 else
921                 {
922                         if (yi != oldy)
923                         {
924                                 inrow = (qbyte *)indata + inwidth4*yi;
925                                 if (yi == oldy+1)
926                                         memcpy(resamplerow1, resamplerow2, outwidth4);
927                                 else
928                                         Image_Resample32LerpLine (inrow, resamplerow1, inwidth, outwidth);
929                                 oldy = yi;
930                         }
931                         memcpy(out, resamplerow1, outwidth4);
932                 }
933         }
934 }
935
936 void Image_Resample32Nearest(void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight)
937 {
938         int i, j;
939         unsigned frac, fracstep;
940         // relies on int being 4 bytes
941         int *inrow, *out;
942         out = outdata;
943
944         fracstep = inwidth*0x10000/outwidth;
945         for (i = 0;i < outheight;i++)
946         {
947                 inrow = (int *)indata + inwidth*(i*inheight/outheight);
948                 frac = fracstep >> 1;
949                 j = outwidth - 4;
950                 while (j >= 0)
951                 {
952                         out[0] = inrow[frac >> 16];frac += fracstep;
953                         out[1] = inrow[frac >> 16];frac += fracstep;
954                         out[2] = inrow[frac >> 16];frac += fracstep;
955                         out[3] = inrow[frac >> 16];frac += fracstep;
956                         out += 4;
957                         j -= 4;
958                 }
959                 if (j & 2)
960                 {
961                         out[0] = inrow[frac >> 16];frac += fracstep;
962                         out[1] = inrow[frac >> 16];frac += fracstep;
963                         out += 2;
964                 }
965                 if (j & 1)
966                 {
967                         out[0] = inrow[frac >> 16];frac += fracstep;
968                         out += 1;
969                 }
970         }
971 }
972
973 void Image_Resample24Lerp(void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight)
974 {
975         int i, j, r, yi, oldy, f, fstep, lerp, endy = (inheight-1), inwidth3 = inwidth * 3, outwidth3 = outwidth * 3;
976         qbyte *inrow, *out;
977         out = outdata;
978         fstep = (int) (inheight*65536.0f/outheight);
979
980         inrow = indata;
981         oldy = 0;
982         Image_Resample24LerpLine (inrow, resamplerow1, inwidth, outwidth);
983         Image_Resample24LerpLine (inrow + inwidth3, resamplerow2, inwidth, outwidth);
984         for (i = 0, f = 0;i < outheight;i++,f += fstep)
985         {
986                 yi = f >> 16;
987                 if (yi < endy)
988                 {
989                         lerp = f & 0xFFFF;
990                         if (yi != oldy)
991                         {
992                                 inrow = (qbyte *)indata + inwidth3*yi;
993                                 if (yi == oldy+1)
994                                         memcpy(resamplerow1, resamplerow2, outwidth3);
995                                 else
996                                         Image_Resample24LerpLine (inrow, resamplerow1, inwidth, outwidth);
997                                 Image_Resample24LerpLine (inrow + inwidth3, resamplerow2, inwidth, outwidth);
998                                 oldy = yi;
999                         }
1000                         j = outwidth - 4;
1001                         while(j >= 0)
1002                         {
1003                                 LERPBYTE( 0);
1004                                 LERPBYTE( 1);
1005                                 LERPBYTE( 2);
1006                                 LERPBYTE( 3);
1007                                 LERPBYTE( 4);
1008                                 LERPBYTE( 5);
1009                                 LERPBYTE( 6);
1010                                 LERPBYTE( 7);
1011                                 LERPBYTE( 8);
1012                                 LERPBYTE( 9);
1013                                 LERPBYTE(10);
1014                                 LERPBYTE(11);
1015                                 out += 12;
1016                                 resamplerow1 += 12;
1017                                 resamplerow2 += 12;
1018                                 j -= 4;
1019                         }
1020                         if (j & 2)
1021                         {
1022                                 LERPBYTE( 0);
1023                                 LERPBYTE( 1);
1024                                 LERPBYTE( 2);
1025                                 LERPBYTE( 3);
1026                                 LERPBYTE( 4);
1027                                 LERPBYTE( 5);
1028                                 out += 6;
1029                                 resamplerow1 += 6;
1030                                 resamplerow2 += 6;
1031                         }
1032                         if (j & 1)
1033                         {
1034                                 LERPBYTE( 0);
1035                                 LERPBYTE( 1);
1036                                 LERPBYTE( 2);
1037                                 out += 3;
1038                                 resamplerow1 += 3;
1039                                 resamplerow2 += 3;
1040                         }
1041                         resamplerow1 -= outwidth3;
1042                         resamplerow2 -= outwidth3;
1043                 }
1044                 else
1045                 {
1046                         if (yi != oldy)
1047                         {
1048                                 inrow = (qbyte *)indata + inwidth3*yi;
1049                                 if (yi == oldy+1)
1050                                         memcpy(resamplerow1, resamplerow2, outwidth3);
1051                                 else
1052                                         Image_Resample24LerpLine (inrow, resamplerow1, inwidth, outwidth);
1053                                 oldy = yi;
1054                         }
1055                         memcpy(out, resamplerow1, outwidth3);
1056                 }
1057         }
1058 }
1059
1060 void Image_Resample24Nolerp(void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight)
1061 {
1062         int i, j, f, inwidth3 = inwidth * 3;
1063         unsigned frac, fracstep;
1064         qbyte *inrow, *out;
1065         out = outdata;
1066
1067         fracstep = inwidth*0x10000/outwidth;
1068         for (i = 0;i < outheight;i++)
1069         {
1070                 inrow = (qbyte *)indata + inwidth3*(i*inheight/outheight);
1071                 frac = fracstep >> 1;
1072                 j = outwidth - 4;
1073                 while (j >= 0)
1074                 {
1075                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1076                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1077                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1078                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1079                         j -= 4;
1080                 }
1081                 if (j & 2)
1082                 {
1083                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1084                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1085                         out += 2;
1086                 }
1087                 if (j & 1)
1088                 {
1089                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1090                         out += 1;
1091                 }
1092         }
1093 }
1094
1095 /*
1096 ================
1097 Image_Resample
1098 ================
1099 */
1100 void Image_Resample (void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight, int bytesperpixel, int quality)
1101 {
1102         if (resamplerowsize < outwidth*4)
1103         {
1104                 if (resamplerow1)
1105                         Mem_Free(resamplerow1);
1106                 resamplerowsize = outwidth*4;
1107                 if (!resamplemempool)
1108                         resamplemempool = Mem_AllocPool("Image Scaling Buffer");
1109                 resamplerow1 = Mem_Alloc(resamplemempool, resamplerowsize*2);
1110                 resamplerow2 = resamplerow1 + resamplerowsize;
1111         }
1112         if (bytesperpixel == 4)
1113         {
1114                 if (quality)
1115                         Image_Resample32Lerp(indata, inwidth, inheight, outdata, outwidth, outheight);
1116                 else
1117                         Image_Resample32Nearest(indata, inwidth, inheight, outdata, outwidth, outheight);
1118         }
1119         else if (bytesperpixel == 3)
1120         {
1121                 if (quality)
1122                         Image_Resample24Lerp(indata, inwidth, inheight, outdata, outwidth, outheight);
1123                 else
1124                         Image_Resample24Nolerp(indata, inwidth, inheight, outdata, outwidth, outheight);
1125         }
1126         else
1127                 Sys_Error("Image_Resample: unsupported bytesperpixel %i\n", bytesperpixel);
1128 }
1129
1130 // in can be the same as out
1131 void Image_MipReduce(qbyte *in, qbyte *out, int *width, int *height, int destwidth, int destheight, int bytesperpixel)
1132 {
1133         int x, y, nextrow;
1134         nextrow = *width * bytesperpixel;
1135         if (*width > destwidth)
1136         {
1137                 *width >>= 1;
1138                 if (*height > destheight)
1139                 {
1140                         // reduce both
1141                         *height >>= 1;
1142                         if (bytesperpixel == 4)
1143                         {
1144                                 for (y = 0;y < *height;y++)
1145                                 {
1146                                         for (x = 0;x < *width;x++)
1147                                         {
1148                                                 out[0] = (qbyte) ((in[0] + in[4] + in[nextrow  ] + in[nextrow+4]) >> 2);
1149                                                 out[1] = (qbyte) ((in[1] + in[5] + in[nextrow+1] + in[nextrow+5]) >> 2);
1150                                                 out[2] = (qbyte) ((in[2] + in[6] + in[nextrow+2] + in[nextrow+6]) >> 2);
1151                                                 out[3] = (qbyte) ((in[3] + in[7] + in[nextrow+3] + in[nextrow+7]) >> 2);
1152                                                 out += 4;
1153                                                 in += 8;
1154                                         }
1155                                         in += nextrow; // skip a line
1156                                 }
1157                         }
1158                         else if (bytesperpixel == 3)
1159                         {
1160                                 for (y = 0;y < *height;y++)
1161                                 {
1162                                         for (x = 0;x < *width;x++)
1163                                         {
1164                                                 out[0] = (qbyte) ((in[0] + in[3] + in[nextrow  ] + in[nextrow+3]) >> 2);
1165                                                 out[1] = (qbyte) ((in[1] + in[4] + in[nextrow+1] + in[nextrow+4]) >> 2);
1166                                                 out[2] = (qbyte) ((in[2] + in[5] + in[nextrow+2] + in[nextrow+5]) >> 2);
1167                                                 out += 3;
1168                                                 in += 6;
1169                                         }
1170                                         in += nextrow; // skip a line
1171                                 }
1172                         }
1173                         else
1174                                 Sys_Error("Image_MipReduce: unsupported bytesperpixel %i\n", bytesperpixel);
1175                 }
1176                 else
1177                 {
1178                         // reduce width
1179                         if (bytesperpixel == 4)
1180                         {
1181                                 for (y = 0;y < *height;y++)
1182                                 {
1183                                         for (x = 0;x < *width;x++)
1184                                         {
1185                                                 out[0] = (qbyte) ((in[0] + in[4]) >> 1);
1186                                                 out[1] = (qbyte) ((in[1] + in[5]) >> 1);
1187                                                 out[2] = (qbyte) ((in[2] + in[6]) >> 1);
1188                                                 out[3] = (qbyte) ((in[3] + in[7]) >> 1);
1189                                                 out += 4;
1190                                                 in += 8;
1191                                         }
1192                                 }
1193                         }
1194                         else if (bytesperpixel == 3)
1195                         {
1196                                 for (y = 0;y < *height;y++)
1197                                 {
1198                                         for (x = 0;x < *width;x++)
1199                                         {
1200                                                 out[0] = (qbyte) ((in[0] + in[3]) >> 1);
1201                                                 out[1] = (qbyte) ((in[1] + in[4]) >> 1);
1202                                                 out[2] = (qbyte) ((in[2] + in[5]) >> 1);
1203                                                 out += 3;
1204                                                 in += 6;
1205                                         }
1206                                 }
1207                         }
1208                         else
1209                                 Sys_Error("Image_MipReduce: unsupported bytesperpixel %i\n", bytesperpixel);
1210                 }
1211         }
1212         else
1213         {
1214                 if (*height > destheight)
1215                 {
1216                         // reduce height
1217                         *height >>= 1;
1218                         if (bytesperpixel == 4)
1219                         {
1220                                 for (y = 0;y < *height;y++)
1221                                 {
1222                                         for (x = 0;x < *width;x++)
1223                                         {
1224                                                 out[0] = (qbyte) ((in[0] + in[nextrow  ]) >> 1);
1225                                                 out[1] = (qbyte) ((in[1] + in[nextrow+1]) >> 1);
1226                                                 out[2] = (qbyte) ((in[2] + in[nextrow+2]) >> 1);
1227                                                 out[3] = (qbyte) ((in[3] + in[nextrow+3]) >> 1);
1228                                                 out += 4;
1229                                                 in += 4;
1230                                         }
1231                                         in += nextrow; // skip a line
1232                                 }
1233                         }
1234                         else if (bytesperpixel == 3)
1235                         {
1236                                 for (y = 0;y < *height;y++)
1237                                 {
1238                                         for (x = 0;x < *width;x++)
1239                                         {
1240                                                 out[0] = (qbyte) ((in[0] + in[nextrow  ]) >> 1);
1241                                                 out[1] = (qbyte) ((in[1] + in[nextrow+1]) >> 1);
1242                                                 out[2] = (qbyte) ((in[2] + in[nextrow+2]) >> 1);
1243                                                 out += 3;
1244                                                 in += 3;
1245                                         }
1246                                         in += nextrow; // skip a line
1247                                 }
1248                         }
1249                         else
1250                                 Sys_Error("Image_MipReduce: unsupported bytesperpixel %i\n", bytesperpixel);
1251                 }
1252                 else
1253                         Sys_Error("Image_MipReduce: desired size already achieved\n");
1254         }
1255 }
1256