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