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