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