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