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