]> icculus.org git repositories - divverent/darkplaces.git/blob - image.c
reverse engineered md3 format and wrote struct declarations (this is *NOT* based...
[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 qboolean Image_WriteTGARGB_preflipped (const char *filename, int width, int height, const qbyte *data)
727 {
728         qboolean ret;
729         qbyte *buffer, *out;
730         const qbyte *in, *end;
731
732         buffer = Mem_Alloc(tempmempool, width*height*3 + 18);
733
734         memset (buffer, 0, 18);
735         buffer[2] = 2;          // uncompressed type
736         buffer[12] = (width >> 0) & 0xFF;
737         buffer[13] = (width >> 8) & 0xFF;
738         buffer[14] = (height >> 0) & 0xFF;
739         buffer[15] = (height >> 8) & 0xFF;
740         buffer[16] = 24;        // pixel size
741
742         // swap rgb to bgr
743         in = data;
744         out = buffer + 18;
745         end = in + width*height*3;
746         for (;in < end;in += 3)
747         {
748                 *out++ = in[2];
749                 *out++ = in[1];
750                 *out++ = in[0];
751         }
752         ret = COM_WriteFile (filename, buffer, width*height*3 + 18 );
753
754         Mem_Free(buffer);
755         return ret;
756 }
757
758 void Image_WriteTGARGB (const char *filename, int width, int height, const qbyte *data)
759 {
760         int y;
761         qbyte *buffer, *out;
762         const qbyte *in, *end;
763
764         buffer = Mem_Alloc(tempmempool, width*height*3 + 18);
765
766         memset (buffer, 0, 18);
767         buffer[2] = 2;          // uncompressed type
768         buffer[12] = (width >> 0) & 0xFF;
769         buffer[13] = (width >> 8) & 0xFF;
770         buffer[14] = (height >> 0) & 0xFF;
771         buffer[15] = (height >> 8) & 0xFF;
772         buffer[16] = 24;        // pixel size
773
774         // swap rgb to bgr and flip upside down
775         out = buffer + 18;
776         for (y = height - 1;y >= 0;y--)
777         {
778                 in = data + y * width * 3;
779                 end = in + width * 3;
780                 for (;in < end;in += 3)
781                 {
782                         *out++ = in[2];
783                         *out++ = in[1];
784                         *out++ = in[0];
785                 }
786         }
787         COM_WriteFile (filename, buffer, width*height*3 + 18 );
788
789         Mem_Free(buffer);
790 }
791
792 void Image_WriteTGARGBA (const char *filename, int width, int height, const qbyte *data)
793 {
794         int y;
795         qbyte *buffer, *out;
796         const qbyte *in, *end;
797
798         buffer = Mem_Alloc(tempmempool, width*height*4 + 18);
799
800         memset (buffer, 0, 18);
801         buffer[2] = 2;          // uncompressed type
802         buffer[12] = (width >> 0) & 0xFF;
803         buffer[13] = (width >> 8) & 0xFF;
804         buffer[14] = (height >> 0) & 0xFF;
805         buffer[15] = (height >> 8) & 0xFF;
806         buffer[16] = 32;        // pixel size
807
808         // swap rgba to bgra and flip upside down
809         out = buffer + 18;
810         for (y = height - 1;y >= 0;y--)
811         {
812                 in = data + y * width * 4;
813                 end = in + width * 4;
814                 for (;in < end;in += 4)
815                 {
816                         *out++ = in[2];
817                         *out++ = in[1];
818                         *out++ = in[0];
819                         *out++ = in[3];
820                 }
821         }
822         COM_WriteFile (filename, buffer, width*height*4 + 18 );
823
824         Mem_Free(buffer);
825 }
826
827 qboolean Image_CheckAlpha(const qbyte *data, int size, qboolean rgba)
828 {
829         const qbyte *end;
830         if (rgba)
831         {
832                 // check alpha bytes
833                 for (end = data + size * 4, data += 3;data < end;data += 4)
834                         if (*data < 255)
835                                 return 1;
836         }
837         else
838         {
839                 // color 255 is transparent
840                 for (end = data + size;data < end;data++)
841                         if (*data == 255)
842                                 return 1;
843         }
844         return 0;
845 }
846
847 static void Image_Resample32LerpLine (const qbyte *in, qbyte *out, int inwidth, int outwidth)
848 {
849         int             j, xi, oldx = 0, f, fstep, endx, lerp;
850         fstep = (int) (inwidth*65536.0f/outwidth);
851         endx = (inwidth-1);
852         for (j = 0,f = 0;j < outwidth;j++, f += fstep)
853         {
854                 xi = f >> 16;
855                 if (xi != oldx)
856                 {
857                         in += (xi - oldx) * 4;
858                         oldx = xi;
859                 }
860                 if (xi < endx)
861                 {
862                         lerp = f & 0xFFFF;
863                         *out++ = (qbyte) ((((in[4] - in[0]) * lerp) >> 16) + in[0]);
864                         *out++ = (qbyte) ((((in[5] - in[1]) * lerp) >> 16) + in[1]);
865                         *out++ = (qbyte) ((((in[6] - in[2]) * lerp) >> 16) + in[2]);
866                         *out++ = (qbyte) ((((in[7] - in[3]) * lerp) >> 16) + in[3]);
867                 }
868                 else // last pixel of the line has no pixel to lerp to
869                 {
870                         *out++ = in[0];
871                         *out++ = in[1];
872                         *out++ = in[2];
873                         *out++ = in[3];
874                 }
875         }
876 }
877
878 static void Image_Resample24LerpLine (const qbyte *in, qbyte *out, int inwidth, int outwidth)
879 {
880         int             j, xi, oldx = 0, f, fstep, endx, lerp;
881         fstep = (int) (inwidth*65536.0f/outwidth);
882         endx = (inwidth-1);
883         for (j = 0,f = 0;j < outwidth;j++, f += fstep)
884         {
885                 xi = f >> 16;
886                 if (xi != oldx)
887                 {
888                         in += (xi - oldx) * 3;
889                         oldx = xi;
890                 }
891                 if (xi < endx)
892                 {
893                         lerp = f & 0xFFFF;
894                         *out++ = (qbyte) ((((in[3] - in[0]) * lerp) >> 16) + in[0]);
895                         *out++ = (qbyte) ((((in[4] - in[1]) * lerp) >> 16) + in[1]);
896                         *out++ = (qbyte) ((((in[5] - in[2]) * lerp) >> 16) + in[2]);
897                 }
898                 else // last pixel of the line has no pixel to lerp to
899                 {
900                         *out++ = in[0];
901                         *out++ = in[1];
902                         *out++ = in[2];
903                 }
904         }
905 }
906
907 int resamplerowsize = 0;
908 qbyte *resamplerow1 = NULL;
909 qbyte *resamplerow2 = NULL;
910 mempool_t *resamplemempool = NULL;
911
912 #define LERPBYTE(i) r = resamplerow1[i];out[i] = (qbyte) ((((resamplerow2[i] - r) * lerp) >> 16) + r)
913 void Image_Resample32Lerp(const void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight)
914 {
915         int i, j, r, yi, oldy, f, fstep, lerp, endy = (inheight-1), inwidth4 = inwidth*4, outwidth4 = outwidth*4;
916         qbyte *out;
917         const qbyte *inrow;
918         out = outdata;
919         fstep = (int) (inheight*65536.0f/outheight);
920
921         inrow = indata;
922         oldy = 0;
923         Image_Resample32LerpLine (inrow, resamplerow1, inwidth, outwidth);
924         Image_Resample32LerpLine (inrow + inwidth4, resamplerow2, inwidth, outwidth);
925         for (i = 0, f = 0;i < outheight;i++,f += fstep)
926         {
927                 yi = f >> 16;
928                 if (yi < endy)
929                 {
930                         lerp = f & 0xFFFF;
931                         if (yi != oldy)
932                         {
933                                 inrow = (qbyte *)indata + inwidth4*yi;
934                                 if (yi == oldy+1)
935                                         memcpy(resamplerow1, resamplerow2, outwidth4);
936                                 else
937                                         Image_Resample32LerpLine (inrow, resamplerow1, inwidth, outwidth);
938                                 Image_Resample32LerpLine (inrow + inwidth4, resamplerow2, inwidth, outwidth);
939                                 oldy = yi;
940                         }
941                         j = outwidth - 4;
942                         while(j >= 0)
943                         {
944                                 LERPBYTE( 0);
945                                 LERPBYTE( 1);
946                                 LERPBYTE( 2);
947                                 LERPBYTE( 3);
948                                 LERPBYTE( 4);
949                                 LERPBYTE( 5);
950                                 LERPBYTE( 6);
951                                 LERPBYTE( 7);
952                                 LERPBYTE( 8);
953                                 LERPBYTE( 9);
954                                 LERPBYTE(10);
955                                 LERPBYTE(11);
956                                 LERPBYTE(12);
957                                 LERPBYTE(13);
958                                 LERPBYTE(14);
959                                 LERPBYTE(15);
960                                 out += 16;
961                                 resamplerow1 += 16;
962                                 resamplerow2 += 16;
963                                 j -= 4;
964                         }
965                         if (j & 2)
966                         {
967                                 LERPBYTE( 0);
968                                 LERPBYTE( 1);
969                                 LERPBYTE( 2);
970                                 LERPBYTE( 3);
971                                 LERPBYTE( 4);
972                                 LERPBYTE( 5);
973                                 LERPBYTE( 6);
974                                 LERPBYTE( 7);
975                                 out += 8;
976                                 resamplerow1 += 8;
977                                 resamplerow2 += 8;
978                         }
979                         if (j & 1)
980                         {
981                                 LERPBYTE( 0);
982                                 LERPBYTE( 1);
983                                 LERPBYTE( 2);
984                                 LERPBYTE( 3);
985                                 out += 4;
986                                 resamplerow1 += 4;
987                                 resamplerow2 += 4;
988                         }
989                         resamplerow1 -= outwidth4;
990                         resamplerow2 -= outwidth4;
991                 }
992                 else
993                 {
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                                 oldy = yi;
1002                         }
1003                         memcpy(out, resamplerow1, outwidth4);
1004                 }
1005         }
1006 }
1007
1008 void Image_Resample32Nearest(const void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight)
1009 {
1010         int i, j;
1011         unsigned frac, fracstep;
1012         // relies on int being 4 bytes
1013         int *inrow, *out;
1014         out = outdata;
1015
1016         fracstep = inwidth*0x10000/outwidth;
1017         for (i = 0;i < outheight;i++)
1018         {
1019                 inrow = (int *)indata + inwidth*(i*inheight/outheight);
1020                 frac = fracstep >> 1;
1021                 j = outwidth - 4;
1022                 while (j >= 0)
1023                 {
1024                         out[0] = inrow[frac >> 16];frac += fracstep;
1025                         out[1] = inrow[frac >> 16];frac += fracstep;
1026                         out[2] = inrow[frac >> 16];frac += fracstep;
1027                         out[3] = inrow[frac >> 16];frac += fracstep;
1028                         out += 4;
1029                         j -= 4;
1030                 }
1031                 if (j & 2)
1032                 {
1033                         out[0] = inrow[frac >> 16];frac += fracstep;
1034                         out[1] = inrow[frac >> 16];frac += fracstep;
1035                         out += 2;
1036                 }
1037                 if (j & 1)
1038                 {
1039                         out[0] = inrow[frac >> 16];frac += fracstep;
1040                         out += 1;
1041                 }
1042         }
1043 }
1044
1045 void Image_Resample24Lerp(const void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight)
1046 {
1047         int i, j, r, yi, oldy, f, fstep, lerp, endy = (inheight-1), inwidth3 = inwidth * 3, outwidth3 = outwidth * 3;
1048         qbyte *out;
1049         const qbyte *inrow;
1050         out = outdata;
1051         fstep = (int) (inheight*65536.0f/outheight);
1052
1053         inrow = indata;
1054         oldy = 0;
1055         Image_Resample24LerpLine (inrow, resamplerow1, inwidth, outwidth);
1056         Image_Resample24LerpLine (inrow + inwidth3, resamplerow2, inwidth, outwidth);
1057         for (i = 0, f = 0;i < outheight;i++,f += fstep)
1058         {
1059                 yi = f >> 16;
1060                 if (yi < endy)
1061                 {
1062                         lerp = f & 0xFFFF;
1063                         if (yi != oldy)
1064                         {
1065                                 inrow = (qbyte *)indata + inwidth3*yi;
1066                                 if (yi == oldy+1)
1067                                         memcpy(resamplerow1, resamplerow2, outwidth3);
1068                                 else
1069                                         Image_Resample24LerpLine (inrow, resamplerow1, inwidth, outwidth);
1070                                 Image_Resample24LerpLine (inrow + inwidth3, resamplerow2, inwidth, outwidth);
1071                                 oldy = yi;
1072                         }
1073                         j = outwidth - 4;
1074                         while(j >= 0)
1075                         {
1076                                 LERPBYTE( 0);
1077                                 LERPBYTE( 1);
1078                                 LERPBYTE( 2);
1079                                 LERPBYTE( 3);
1080                                 LERPBYTE( 4);
1081                                 LERPBYTE( 5);
1082                                 LERPBYTE( 6);
1083                                 LERPBYTE( 7);
1084                                 LERPBYTE( 8);
1085                                 LERPBYTE( 9);
1086                                 LERPBYTE(10);
1087                                 LERPBYTE(11);
1088                                 out += 12;
1089                                 resamplerow1 += 12;
1090                                 resamplerow2 += 12;
1091                                 j -= 4;
1092                         }
1093                         if (j & 2)
1094                         {
1095                                 LERPBYTE( 0);
1096                                 LERPBYTE( 1);
1097                                 LERPBYTE( 2);
1098                                 LERPBYTE( 3);
1099                                 LERPBYTE( 4);
1100                                 LERPBYTE( 5);
1101                                 out += 6;
1102                                 resamplerow1 += 6;
1103                                 resamplerow2 += 6;
1104                         }
1105                         if (j & 1)
1106                         {
1107                                 LERPBYTE( 0);
1108                                 LERPBYTE( 1);
1109                                 LERPBYTE( 2);
1110                                 out += 3;
1111                                 resamplerow1 += 3;
1112                                 resamplerow2 += 3;
1113                         }
1114                         resamplerow1 -= outwidth3;
1115                         resamplerow2 -= outwidth3;
1116                 }
1117                 else
1118                 {
1119                         if (yi != oldy)
1120                         {
1121                                 inrow = (qbyte *)indata + inwidth3*yi;
1122                                 if (yi == oldy+1)
1123                                         memcpy(resamplerow1, resamplerow2, outwidth3);
1124                                 else
1125                                         Image_Resample24LerpLine (inrow, resamplerow1, inwidth, outwidth);
1126                                 oldy = yi;
1127                         }
1128                         memcpy(out, resamplerow1, outwidth3);
1129                 }
1130         }
1131 }
1132
1133 void Image_Resample24Nolerp(const void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight)
1134 {
1135         int i, j, f, inwidth3 = inwidth * 3;
1136         unsigned frac, fracstep;
1137         qbyte *inrow, *out;
1138         out = outdata;
1139
1140         fracstep = inwidth*0x10000/outwidth;
1141         for (i = 0;i < outheight;i++)
1142         {
1143                 inrow = (qbyte *)indata + inwidth3*(i*inheight/outheight);
1144                 frac = fracstep >> 1;
1145                 j = outwidth - 4;
1146                 while (j >= 0)
1147                 {
1148                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1149                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1150                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1151                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1152                         j -= 4;
1153                 }
1154                 if (j & 2)
1155                 {
1156                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1157                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1158                         out += 2;
1159                 }
1160                 if (j & 1)
1161                 {
1162                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1163                         out += 1;
1164                 }
1165         }
1166 }
1167
1168 /*
1169 ================
1170 Image_Resample
1171 ================
1172 */
1173 void Image_Resample (const void *indata, int inwidth, int inheight, int indepth, void *outdata, int outwidth, int outheight, int outdepth, int bytesperpixel, int quality)
1174 {
1175         if (indepth != 1 || outdepth != 1)
1176                 Sys_Error("Image_Resample: 3D resampling not supported\n");
1177         if (resamplerowsize < outwidth*4)
1178         {
1179                 if (resamplerow1)
1180                         Mem_Free(resamplerow1);
1181                 resamplerowsize = outwidth*4;
1182                 if (!resamplemempool)
1183                         resamplemempool = Mem_AllocPool("Image Scaling Buffer");
1184                 resamplerow1 = Mem_Alloc(resamplemempool, resamplerowsize*2);
1185                 resamplerow2 = resamplerow1 + resamplerowsize;
1186         }
1187         if (bytesperpixel == 4)
1188         {
1189                 if (quality)
1190                         Image_Resample32Lerp(indata, inwidth, inheight, outdata, outwidth, outheight);
1191                 else
1192                         Image_Resample32Nearest(indata, inwidth, inheight, outdata, outwidth, outheight);
1193         }
1194         else if (bytesperpixel == 3)
1195         {
1196                 if (quality)
1197                         Image_Resample24Lerp(indata, inwidth, inheight, outdata, outwidth, outheight);
1198                 else
1199                         Image_Resample24Nolerp(indata, inwidth, inheight, outdata, outwidth, outheight);
1200         }
1201         else
1202                 Sys_Error("Image_Resample: unsupported bytesperpixel %i\n", bytesperpixel);
1203 }
1204
1205 // in can be the same as out
1206 void Image_MipReduce(const qbyte *in, qbyte *out, int *width, int *height, int *depth, int destwidth, int destheight, int destdepth, int bytesperpixel)
1207 {
1208         int x, y, nextrow;
1209         if (*depth != 1 || destdepth != 1)
1210                 Sys_Error("Image_Resample: 3D resampling not supported\n");
1211         nextrow = *width * bytesperpixel;
1212         if (*width > destwidth)
1213         {
1214                 *width >>= 1;
1215                 if (*height > destheight)
1216                 {
1217                         // reduce both
1218                         *height >>= 1;
1219                         if (bytesperpixel == 4)
1220                         {
1221                                 for (y = 0;y < *height;y++)
1222                                 {
1223                                         for (x = 0;x < *width;x++)
1224                                         {
1225                                                 out[0] = (qbyte) ((in[0] + in[4] + in[nextrow  ] + in[nextrow+4]) >> 2);
1226                                                 out[1] = (qbyte) ((in[1] + in[5] + in[nextrow+1] + in[nextrow+5]) >> 2);
1227                                                 out[2] = (qbyte) ((in[2] + in[6] + in[nextrow+2] + in[nextrow+6]) >> 2);
1228                                                 out[3] = (qbyte) ((in[3] + in[7] + in[nextrow+3] + in[nextrow+7]) >> 2);
1229                                                 out += 4;
1230                                                 in += 8;
1231                                         }
1232                                         in += nextrow; // skip a line
1233                                 }
1234                         }
1235                         else if (bytesperpixel == 3)
1236                         {
1237                                 for (y = 0;y < *height;y++)
1238                                 {
1239                                         for (x = 0;x < *width;x++)
1240                                         {
1241                                                 out[0] = (qbyte) ((in[0] + in[3] + in[nextrow  ] + in[nextrow+3]) >> 2);
1242                                                 out[1] = (qbyte) ((in[1] + in[4] + in[nextrow+1] + in[nextrow+4]) >> 2);
1243                                                 out[2] = (qbyte) ((in[2] + in[5] + in[nextrow+2] + in[nextrow+5]) >> 2);
1244                                                 out += 3;
1245                                                 in += 6;
1246                                         }
1247                                         in += nextrow; // skip a line
1248                                 }
1249                         }
1250                         else
1251                                 Sys_Error("Image_MipReduce: unsupported bytesperpixel %i\n", bytesperpixel);
1252                 }
1253                 else
1254                 {
1255                         // reduce width
1256                         if (bytesperpixel == 4)
1257                         {
1258                                 for (y = 0;y < *height;y++)
1259                                 {
1260                                         for (x = 0;x < *width;x++)
1261                                         {
1262                                                 out[0] = (qbyte) ((in[0] + in[4]) >> 1);
1263                                                 out[1] = (qbyte) ((in[1] + in[5]) >> 1);
1264                                                 out[2] = (qbyte) ((in[2] + in[6]) >> 1);
1265                                                 out[3] = (qbyte) ((in[3] + in[7]) >> 1);
1266                                                 out += 4;
1267                                                 in += 8;
1268                                         }
1269                                 }
1270                         }
1271                         else if (bytesperpixel == 3)
1272                         {
1273                                 for (y = 0;y < *height;y++)
1274                                 {
1275                                         for (x = 0;x < *width;x++)
1276                                         {
1277                                                 out[0] = (qbyte) ((in[0] + in[3]) >> 1);
1278                                                 out[1] = (qbyte) ((in[1] + in[4]) >> 1);
1279                                                 out[2] = (qbyte) ((in[2] + in[5]) >> 1);
1280                                                 out += 3;
1281                                                 in += 6;
1282                                         }
1283                                 }
1284                         }
1285                         else
1286                                 Sys_Error("Image_MipReduce: unsupported bytesperpixel %i\n", bytesperpixel);
1287                 }
1288         }
1289         else
1290         {
1291                 if (*height > destheight)
1292                 {
1293                         // reduce height
1294                         *height >>= 1;
1295                         if (bytesperpixel == 4)
1296                         {
1297                                 for (y = 0;y < *height;y++)
1298                                 {
1299                                         for (x = 0;x < *width;x++)
1300                                         {
1301                                                 out[0] = (qbyte) ((in[0] + in[nextrow  ]) >> 1);
1302                                                 out[1] = (qbyte) ((in[1] + in[nextrow+1]) >> 1);
1303                                                 out[2] = (qbyte) ((in[2] + in[nextrow+2]) >> 1);
1304                                                 out[3] = (qbyte) ((in[3] + in[nextrow+3]) >> 1);
1305                                                 out += 4;
1306                                                 in += 4;
1307                                         }
1308                                         in += nextrow; // skip a line
1309                                 }
1310                         }
1311                         else if (bytesperpixel == 3)
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;
1321                                                 in += 3;
1322                                         }
1323                                         in += nextrow; // skip a line
1324                                 }
1325                         }
1326                         else
1327                                 Sys_Error("Image_MipReduce: unsupported bytesperpixel %i\n", bytesperpixel);
1328                 }
1329                 else
1330                         Sys_Error("Image_MipReduce: desired size already achieved\n");
1331         }
1332 }
1333
1334 extern cvar_t r_shadow_bumpscale;
1335 void Image_HeightmapToNormalmap(const unsigned char *inpixels, unsigned char *outpixels, int width, int height, int clamp, float bumpscale)
1336 {
1337         int x, y;
1338         const unsigned char *p0, *p1, *p2;
1339         unsigned char *out;
1340         float iwidth, iheight, ibumpscale, n[3];
1341         iwidth = 1.0f / width;
1342         iheight = 1.0f / height;
1343         ibumpscale = (255.0f * 3.0f) / (bumpscale * r_shadow_bumpscale.value);
1344         out = outpixels;
1345         for (y = 0;y < height;y++)
1346         {
1347                 for (x = 0;x < width;x++)
1348                 {
1349                         p0 = inpixels + (y * width + x) * 4;
1350                         if (x == width - 1)
1351                         {
1352                                 if (clamp)
1353                                         p1 = inpixels + (y * width + x) * 4;
1354                                 else
1355                                         p1 = inpixels + (y * width) * 4;
1356                         }
1357                         else
1358                                 p1 = inpixels + (y * width + x + 1) * 4;
1359                         if (y == height - 1)
1360                         {
1361                                 if (clamp)
1362                                         p2 = inpixels + (y * width + x) * 4;
1363                                 else
1364                                         p2 = inpixels + x * 4;
1365                         }
1366                         else
1367                                 p2 = inpixels + ((y + 1) * width + x) * 4;
1368                         /*
1369                         dv[0][0] = iwidth;
1370                         dv[0][1] = 0;
1371                         dv[0][2] = ((p1[0] + p1[1] + p1[2]) * ibumpscale) - ((p0[0] + p0[1] + p0[2]) * ibumpscale);
1372                         dv[1][0] = 0;
1373                         dv[1][1] = iheight;
1374                         dv[1][2] = ((p2[0] + p2[1] + p2[2]) * ibumpscale) - ((p0[0] + p0[1] + p0[2]) * ibumpscale);
1375                         n[0] = dv[0][1]*dv[1][2]-dv[0][2]*dv[1][1];
1376                         n[1] = dv[0][2]*dv[1][0]-dv[0][0]*dv[1][2];
1377                         n[2] = dv[0][0]*dv[1][1]-dv[0][1]*dv[1][0];
1378                         */
1379                         n[0] = ((p0[0] + p0[1] + p0[2]) - (p1[0] + p1[1] + p1[2]));
1380                         n[1] = ((p0[0] + p0[1] + p0[2]) - (p2[0] + p2[1] + p2[2]));
1381                         n[2] = ibumpscale;
1382                         VectorNormalize(n);
1383                         out[0] = 128.0f + n[0] * 127.0f;
1384                         out[1] = 128.0f + n[1] * 127.0f;
1385                         out[2] = 128.0f + n[2] * 127.0f;
1386                         out[3] = 255;
1387                         out += 4;
1388                 }
1389         }
1390 }