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