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