]> icculus.org git repositories - divverent/darkplaces.git/blob - image.c
split out RecursiveHullCheck and related code into collision.c (shared by client...
[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 qboolean Image_WriteTGARGB_preflipped (char *filename, int width, int height, qbyte *data)
591 {
592         qboolean ret;
593         qbyte *buffer, *in, *out, *end;
594
595         buffer = Mem_Alloc(tempmempool, width*height*3 + 18);
596
597         memset (buffer, 0, 18);
598         buffer[2] = 2;          // uncompressed type
599         buffer[12] = (width >> 0) & 0xFF;
600         buffer[13] = (width >> 8) & 0xFF;
601         buffer[14] = (height >> 0) & 0xFF;
602         buffer[15] = (height >> 8) & 0xFF;
603         buffer[16] = 24;        // pixel size
604
605         // swap rgb to bgr
606         in = data;
607         out = buffer + 18;
608         end = in + width*height*3;
609         for (;in < end;in += 3)
610         {
611                 *out++ = in[2];
612                 *out++ = in[1];
613                 *out++ = in[0];
614         }
615         ret = COM_WriteFile (filename, buffer, width*height*3 + 18 );
616
617         Mem_Free(buffer);
618         return ret;
619 }
620
621 void Image_WriteTGARGB (char *filename, int width, int height, qbyte *data)
622 {
623         int y;
624         qbyte *buffer, *in, *out, *end;
625
626         buffer = Mem_Alloc(tempmempool, width*height*3 + 18);
627
628         memset (buffer, 0, 18);
629         buffer[2] = 2;          // uncompressed type
630         buffer[12] = (width >> 0) & 0xFF;
631         buffer[13] = (width >> 8) & 0xFF;
632         buffer[14] = (height >> 0) & 0xFF;
633         buffer[15] = (height >> 8) & 0xFF;
634         buffer[16] = 24;        // pixel size
635
636         // swap rgb to bgr and flip upside down
637         out = buffer + 18;
638         for (y = height - 1;y >= 0;y--)
639         {
640                 in = data + y * width * 3;
641                 end = in + width * 3;
642                 for (;in < end;in += 3)
643                 {
644                         *out++ = in[2];
645                         *out++ = in[1];
646                         *out++ = in[0];
647                 }
648         }
649         COM_WriteFile (filename, buffer, width*height*3 + 18 );
650
651         Mem_Free(buffer);
652 }
653
654 void Image_WriteTGARGBA (char *filename, int width, int height, qbyte *data)
655 {
656         int y;
657         qbyte *buffer, *in, *out, *end;
658
659         buffer = Mem_Alloc(tempmempool, width*height*4 + 18);
660
661         memset (buffer, 0, 18);
662         buffer[2] = 2;          // uncompressed type
663         buffer[12] = (width >> 0) & 0xFF;
664         buffer[13] = (width >> 8) & 0xFF;
665         buffer[14] = (height >> 0) & 0xFF;
666         buffer[15] = (height >> 8) & 0xFF;
667         buffer[16] = 32;        // pixel size
668
669         // swap rgba to bgra and flip upside down
670         out = buffer + 18;
671         for (y = height - 1;y >= 0;y--)
672         {
673                 in = data + y * width * 4;
674                 end = in + width * 4;
675                 for (;in < end;in += 4)
676                 {
677                         *out++ = in[2];
678                         *out++ = in[1];
679                         *out++ = in[0];
680                         *out++ = in[3];
681                 }
682         }
683         COM_WriteFile (filename, buffer, width*height*4 + 18 );
684
685         Mem_Free(buffer);
686 }
687
688 qboolean Image_CheckAlpha(qbyte *data, int size, qboolean rgba)
689 {
690         qbyte *end;
691         if (rgba)
692         {
693                 // check alpha bytes
694                 for (end = data + size * 4, data += 3;data < end;data += 4)
695                         if (*data < 255)
696                                 return 1;
697         }
698         else
699         {
700                 // color 255 is transparent
701                 for (end = data + size;data < end;data++)
702                         if (*data == 255)
703                                 return 1;
704         }
705         return 0;
706 }
707
708 static void Image_Resample32LerpLine (qbyte *in, qbyte *out, int inwidth, int outwidth)
709 {
710         int             j, xi, oldx = 0, f, fstep, endx, lerp;
711         fstep = (int) (inwidth*65536.0f/outwidth);
712         endx = (inwidth-1);
713         for (j = 0,f = 0;j < outwidth;j++, f += fstep)
714         {
715                 xi = f >> 16;
716                 if (xi != oldx)
717                 {
718                         in += (xi - oldx) * 4;
719                         oldx = xi;
720                 }
721                 if (xi < endx)
722                 {
723                         lerp = f & 0xFFFF;
724                         *out++ = (qbyte) ((((in[4] - in[0]) * lerp) >> 16) + in[0]);
725                         *out++ = (qbyte) ((((in[5] - in[1]) * lerp) >> 16) + in[1]);
726                         *out++ = (qbyte) ((((in[6] - in[2]) * lerp) >> 16) + in[2]);
727                         *out++ = (qbyte) ((((in[7] - in[3]) * lerp) >> 16) + in[3]);
728                 }
729                 else // last pixel of the line has no pixel to lerp to
730                 {
731                         *out++ = in[0];
732                         *out++ = in[1];
733                         *out++ = in[2];
734                         *out++ = in[3];
735                 }
736         }
737 }
738
739 static void Image_Resample24LerpLine (qbyte *in, qbyte *out, int inwidth, int outwidth)
740 {
741         int             j, xi, oldx = 0, f, fstep, endx, lerp;
742         fstep = (int) (inwidth*65536.0f/outwidth);
743         endx = (inwidth-1);
744         for (j = 0,f = 0;j < outwidth;j++, f += fstep)
745         {
746                 xi = f >> 16;
747                 if (xi != oldx)
748                 {
749                         in += (xi - oldx) * 3;
750                         oldx = xi;
751                 }
752                 if (xi < endx)
753                 {
754                         lerp = f & 0xFFFF;
755                         *out++ = (qbyte) ((((in[3] - in[0]) * lerp) >> 16) + in[0]);
756                         *out++ = (qbyte) ((((in[4] - in[1]) * lerp) >> 16) + in[1]);
757                         *out++ = (qbyte) ((((in[5] - in[2]) * lerp) >> 16) + in[2]);
758                 }
759                 else // last pixel of the line has no pixel to lerp to
760                 {
761                         *out++ = in[0];
762                         *out++ = in[1];
763                         *out++ = in[2];
764                 }
765         }
766 }
767
768 int resamplerowsize = 0;
769 qbyte *resamplerow1 = NULL;
770 qbyte *resamplerow2 = NULL;
771 mempool_t *resamplemempool = NULL;
772
773 #define LERPBYTE(i) r = resamplerow1[i];out[i] = (qbyte) ((((resamplerow2[i] - r) * lerp) >> 16) + r)
774 void Image_Resample32Lerp(void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight)
775 {
776         int i, j, r, yi, oldy, f, fstep, lerp, endy = (inheight-1), inwidth4 = inwidth*4, outwidth4 = outwidth*4;
777         qbyte *inrow, *out;
778         out = outdata;
779         fstep = (int) (inheight*65536.0f/outheight);
780
781         inrow = indata;
782         oldy = 0;
783         Image_Resample32LerpLine (inrow, resamplerow1, inwidth, outwidth);
784         Image_Resample32LerpLine (inrow + inwidth4, resamplerow2, inwidth, outwidth);
785         for (i = 0, f = 0;i < outheight;i++,f += fstep)
786         {
787                 yi = f >> 16;
788                 if (yi < endy)
789                 {
790                         lerp = f & 0xFFFF;
791                         if (yi != oldy)
792                         {
793                                 inrow = (qbyte *)indata + inwidth4*yi;
794                                 if (yi == oldy+1)
795                                         memcpy(resamplerow1, resamplerow2, outwidth4);
796                                 else
797                                         Image_Resample32LerpLine (inrow, resamplerow1, inwidth, outwidth);
798                                 Image_Resample32LerpLine (inrow + inwidth4, resamplerow2, inwidth, outwidth);
799                                 oldy = yi;
800                         }
801                         j = outwidth - 4;
802                         while(j >= 0)
803                         {
804                                 LERPBYTE( 0);
805                                 LERPBYTE( 1);
806                                 LERPBYTE( 2);
807                                 LERPBYTE( 3);
808                                 LERPBYTE( 4);
809                                 LERPBYTE( 5);
810                                 LERPBYTE( 6);
811                                 LERPBYTE( 7);
812                                 LERPBYTE( 8);
813                                 LERPBYTE( 9);
814                                 LERPBYTE(10);
815                                 LERPBYTE(11);
816                                 LERPBYTE(12);
817                                 LERPBYTE(13);
818                                 LERPBYTE(14);
819                                 LERPBYTE(15);
820                                 out += 16;
821                                 resamplerow1 += 16;
822                                 resamplerow2 += 16;
823                                 j -= 4;
824                         }
825                         if (j & 2)
826                         {
827                                 LERPBYTE( 0);
828                                 LERPBYTE( 1);
829                                 LERPBYTE( 2);
830                                 LERPBYTE( 3);
831                                 LERPBYTE( 4);
832                                 LERPBYTE( 5);
833                                 LERPBYTE( 6);
834                                 LERPBYTE( 7);
835                                 out += 8;
836                                 resamplerow1 += 8;
837                                 resamplerow2 += 8;
838                         }
839                         if (j & 1)
840                         {
841                                 LERPBYTE( 0);
842                                 LERPBYTE( 1);
843                                 LERPBYTE( 2);
844                                 LERPBYTE( 3);
845                                 out += 4;
846                                 resamplerow1 += 4;
847                                 resamplerow2 += 4;
848                         }
849                         resamplerow1 -= outwidth4;
850                         resamplerow2 -= outwidth4;
851                 }
852                 else
853                 {
854                         if (yi != oldy)
855                         {
856                                 inrow = (qbyte *)indata + inwidth4*yi;
857                                 if (yi == oldy+1)
858                                         memcpy(resamplerow1, resamplerow2, outwidth4);
859                                 else
860                                         Image_Resample32LerpLine (inrow, resamplerow1, inwidth, outwidth);
861                                 oldy = yi;
862                         }
863                         memcpy(out, resamplerow1, outwidth4);
864                 }
865         }
866 }
867
868 void Image_Resample32Nearest(void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight)
869 {
870         int i, j;
871         unsigned frac, fracstep;
872         // relies on int being 4 bytes
873         int *inrow, *out;
874         out = outdata;
875
876         fracstep = inwidth*0x10000/outwidth;
877         for (i = 0;i < outheight;i++)
878         {
879                 inrow = (int *)indata + inwidth*(i*inheight/outheight);
880                 frac = fracstep >> 1;
881                 j = outwidth - 4;
882                 while (j >= 0)
883                 {
884                         out[0] = inrow[frac >> 16];frac += fracstep;
885                         out[1] = inrow[frac >> 16];frac += fracstep;
886                         out[2] = inrow[frac >> 16];frac += fracstep;
887                         out[3] = inrow[frac >> 16];frac += fracstep;
888                         out += 4;
889                         j -= 4;
890                 }
891                 if (j & 2)
892                 {
893                         out[0] = inrow[frac >> 16];frac += fracstep;
894                         out[1] = inrow[frac >> 16];frac += fracstep;
895                         out += 2;
896                 }
897                 if (j & 1)
898                 {
899                         out[0] = inrow[frac >> 16];frac += fracstep;
900                         out += 1;
901                 }
902         }
903 }
904
905 void Image_Resample24Lerp(void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight)
906 {
907         int i, j, r, yi, oldy, f, fstep, lerp, endy = (inheight-1), inwidth3 = inwidth * 3, outwidth3 = outwidth * 3;
908         qbyte *inrow, *out;
909         out = outdata;
910         fstep = (int) (inheight*65536.0f/outheight);
911
912         inrow = indata;
913         oldy = 0;
914         Image_Resample24LerpLine (inrow, resamplerow1, inwidth, outwidth);
915         Image_Resample24LerpLine (inrow + inwidth3, resamplerow2, inwidth, outwidth);
916         for (i = 0, f = 0;i < outheight;i++,f += fstep)
917         {
918                 yi = f >> 16;
919                 if (yi < endy)
920                 {
921                         lerp = f & 0xFFFF;
922                         if (yi != oldy)
923                         {
924                                 inrow = (qbyte *)indata + inwidth3*yi;
925                                 if (yi == oldy+1)
926                                         memcpy(resamplerow1, resamplerow2, outwidth3);
927                                 else
928                                         Image_Resample24LerpLine (inrow, resamplerow1, inwidth, outwidth);
929                                 Image_Resample24LerpLine (inrow + inwidth3, resamplerow2, inwidth, outwidth);
930                                 oldy = yi;
931                         }
932                         j = outwidth - 4;
933                         while(j >= 0)
934                         {
935                                 LERPBYTE( 0);
936                                 LERPBYTE( 1);
937                                 LERPBYTE( 2);
938                                 LERPBYTE( 3);
939                                 LERPBYTE( 4);
940                                 LERPBYTE( 5);
941                                 LERPBYTE( 6);
942                                 LERPBYTE( 7);
943                                 LERPBYTE( 8);
944                                 LERPBYTE( 9);
945                                 LERPBYTE(10);
946                                 LERPBYTE(11);
947                                 out += 12;
948                                 resamplerow1 += 12;
949                                 resamplerow2 += 12;
950                                 j -= 4;
951                         }
952                         if (j & 2)
953                         {
954                                 LERPBYTE( 0);
955                                 LERPBYTE( 1);
956                                 LERPBYTE( 2);
957                                 LERPBYTE( 3);
958                                 LERPBYTE( 4);
959                                 LERPBYTE( 5);
960                                 out += 6;
961                                 resamplerow1 += 6;
962                                 resamplerow2 += 6;
963                         }
964                         if (j & 1)
965                         {
966                                 LERPBYTE( 0);
967                                 LERPBYTE( 1);
968                                 LERPBYTE( 2);
969                                 out += 3;
970                                 resamplerow1 += 3;
971                                 resamplerow2 += 3;
972                         }
973                         resamplerow1 -= outwidth3;
974                         resamplerow2 -= outwidth3;
975                 }
976                 else
977                 {
978                         if (yi != oldy)
979                         {
980                                 inrow = (qbyte *)indata + inwidth3*yi;
981                                 if (yi == oldy+1)
982                                         memcpy(resamplerow1, resamplerow2, outwidth3);
983                                 else
984                                         Image_Resample24LerpLine (inrow, resamplerow1, inwidth, outwidth);
985                                 oldy = yi;
986                         }
987                         memcpy(out, resamplerow1, outwidth3);
988                 }
989         }
990 }
991
992 void Image_Resample24Nolerp(void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight)
993 {
994         int i, j, f, inwidth3 = inwidth * 3;
995         unsigned frac, fracstep;
996         qbyte *inrow, *out;
997         out = outdata;
998
999         fracstep = inwidth*0x10000/outwidth;
1000         for (i = 0;i < outheight;i++)
1001         {
1002                 inrow = (qbyte *)indata + inwidth3*(i*inheight/outheight);
1003                 frac = fracstep >> 1;
1004                 j = outwidth - 4;
1005                 while (j >= 0)
1006                 {
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                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1010                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1011                         j -= 4;
1012                 }
1013                 if (j & 2)
1014                 {
1015                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1016                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1017                         out += 2;
1018                 }
1019                 if (j & 1)
1020                 {
1021                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1022                         out += 1;
1023                 }
1024         }
1025 }
1026
1027 /*
1028 ================
1029 Image_Resample
1030 ================
1031 */
1032 void Image_Resample (void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight, int bytesperpixel, int quality)
1033 {
1034         if (resamplerowsize < outwidth*4)
1035         {
1036                 if (resamplerow1)
1037                         Mem_Free(resamplerow1);
1038                 resamplerowsize = outwidth*4;
1039                 if (!resamplemempool)
1040                         resamplemempool = Mem_AllocPool("Image Scaling Buffer");
1041                 resamplerow1 = Mem_Alloc(resamplemempool, resamplerowsize*2);
1042                 resamplerow2 = resamplerow1 + resamplerowsize;
1043         }
1044         if (bytesperpixel == 4)
1045         {
1046                 if (quality)
1047                         Image_Resample32Lerp(indata, inwidth, inheight, outdata, outwidth, outheight);
1048                 else
1049                         Image_Resample32Nearest(indata, inwidth, inheight, outdata, outwidth, outheight);
1050         }
1051         else if (bytesperpixel == 3)
1052         {
1053                 if (quality)
1054                         Image_Resample24Lerp(indata, inwidth, inheight, outdata, outwidth, outheight);
1055                 else
1056                         Image_Resample24Nolerp(indata, inwidth, inheight, outdata, outwidth, outheight);
1057         }
1058         else
1059                 Sys_Error("Image_Resample: unsupported bytesperpixel %i\n", bytesperpixel);
1060 }
1061
1062 // in can be the same as out
1063 void Image_MipReduce(qbyte *in, qbyte *out, int *width, int *height, int destwidth, int destheight, int bytesperpixel)
1064 {
1065         int x, y, nextrow;
1066         nextrow = *width * bytesperpixel;
1067         if (*width > destwidth)
1068         {
1069                 *width >>= 1;
1070                 if (*height > destheight)
1071                 {
1072                         // reduce both
1073                         *height >>= 1;
1074                         if (bytesperpixel == 4)
1075                         {
1076                                 for (y = 0;y < *height;y++)
1077                                 {
1078                                         for (x = 0;x < *width;x++)
1079                                         {
1080                                                 out[0] = (qbyte) ((in[0] + in[4] + in[nextrow  ] + in[nextrow+4]) >> 2);
1081                                                 out[1] = (qbyte) ((in[1] + in[5] + in[nextrow+1] + in[nextrow+5]) >> 2);
1082                                                 out[2] = (qbyte) ((in[2] + in[6] + in[nextrow+2] + in[nextrow+6]) >> 2);
1083                                                 out[3] = (qbyte) ((in[3] + in[7] + in[nextrow+3] + in[nextrow+7]) >> 2);
1084                                                 out += 4;
1085                                                 in += 8;
1086                                         }
1087                                         in += nextrow; // skip a line
1088                                 }
1089                         }
1090                         else if (bytesperpixel == 3)
1091                         {
1092                                 for (y = 0;y < *height;y++)
1093                                 {
1094                                         for (x = 0;x < *width;x++)
1095                                         {
1096                                                 out[0] = (qbyte) ((in[0] + in[3] + in[nextrow  ] + in[nextrow+3]) >> 2);
1097                                                 out[1] = (qbyte) ((in[1] + in[4] + in[nextrow+1] + in[nextrow+4]) >> 2);
1098                                                 out[2] = (qbyte) ((in[2] + in[5] + in[nextrow+2] + in[nextrow+5]) >> 2);
1099                                                 out += 3;
1100                                                 in += 6;
1101                                         }
1102                                         in += nextrow; // skip a line
1103                                 }
1104                         }
1105                         else
1106                                 Sys_Error("Image_MipReduce: unsupported bytesperpixel %i\n", bytesperpixel);
1107                 }
1108                 else
1109                 {
1110                         // reduce width
1111                         if (bytesperpixel == 4)
1112                         {
1113                                 for (y = 0;y < *height;y++)
1114                                 {
1115                                         for (x = 0;x < *width;x++)
1116                                         {
1117                                                 out[0] = (qbyte) ((in[0] + in[4]) >> 1);
1118                                                 out[1] = (qbyte) ((in[1] + in[5]) >> 1);
1119                                                 out[2] = (qbyte) ((in[2] + in[6]) >> 1);
1120                                                 out[3] = (qbyte) ((in[3] + in[7]) >> 1);
1121                                                 out += 4;
1122                                                 in += 8;
1123                                         }
1124                                 }
1125                         }
1126                         else if (bytesperpixel == 3)
1127                         {
1128                                 for (y = 0;y < *height;y++)
1129                                 {
1130                                         for (x = 0;x < *width;x++)
1131                                         {
1132                                                 out[0] = (qbyte) ((in[0] + in[3]) >> 1);
1133                                                 out[1] = (qbyte) ((in[1] + in[4]) >> 1);
1134                                                 out[2] = (qbyte) ((in[2] + in[5]) >> 1);
1135                                                 out += 3;
1136                                                 in += 6;
1137                                         }
1138                                 }
1139                         }
1140                         else
1141                                 Sys_Error("Image_MipReduce: unsupported bytesperpixel %i\n", bytesperpixel);
1142                 }
1143         }
1144         else
1145         {
1146                 if (*height > destheight)
1147                 {
1148                         // reduce height
1149                         *height >>= 1;
1150                         if (bytesperpixel == 4)
1151                         {
1152                                 for (y = 0;y < *height;y++)
1153                                 {
1154                                         for (x = 0;x < *width;x++)
1155                                         {
1156                                                 out[0] = (qbyte) ((in[0] + in[nextrow  ]) >> 1);
1157                                                 out[1] = (qbyte) ((in[1] + in[nextrow+1]) >> 1);
1158                                                 out[2] = (qbyte) ((in[2] + in[nextrow+2]) >> 1);
1159                                                 out[3] = (qbyte) ((in[3] + in[nextrow+3]) >> 1);
1160                                                 out += 4;
1161                                                 in += 4;
1162                                         }
1163                                         in += nextrow; // skip a line
1164                                 }
1165                         }
1166                         else if (bytesperpixel == 3)
1167                         {
1168                                 for (y = 0;y < *height;y++)
1169                                 {
1170                                         for (x = 0;x < *width;x++)
1171                                         {
1172                                                 out[0] = (qbyte) ((in[0] + in[nextrow  ]) >> 1);
1173                                                 out[1] = (qbyte) ((in[1] + in[nextrow+1]) >> 1);
1174                                                 out[2] = (qbyte) ((in[2] + in[nextrow+2]) >> 1);
1175                                                 out += 3;
1176                                                 in += 3;
1177                                         }
1178                                         in += nextrow; // skip a line
1179                                 }
1180                         }
1181                         else
1182                                 Sys_Error("Image_MipReduce: unsupported bytesperpixel %i\n", bytesperpixel);
1183                 }
1184                 else
1185                         Sys_Error("Image_MipReduce: desired size already achieved\n");
1186         }
1187 }