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