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