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