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