fixed menu player colormapping
[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 /*
457 ============
458 LoadLMP
459 ============
460 */
461 qbyte *LoadLMPAs8Bit (qbyte *f, int matchwidth, int matchheight)
462 {
463         qbyte *image_8bit;
464         int width, height;
465
466         if (loadsize < 9)
467         {
468                 Con_Printf("LoadLMPAs8Bit: invalid LMP file\n");
469                 return NULL;
470         }
471
472         // parse the very complicated header *chuckle*
473         width = f[0] + f[1] * 256 + f[2] * 65536 + f[3] * 16777216;
474         height = f[4] + f[5] * 256 + f[6] * 65536 + f[7] * 16777216;
475         if ((unsigned) width > 4096 || (unsigned) height > 4096)
476         {
477                 Con_Printf("LoadLMPAs8Bit: invalid size\n");
478                 return NULL;
479         }
480         if ((matchwidth && width != matchwidth) || (matchheight && height != matchheight))
481                 return NULL;
482
483         if (loadsize < 8 + width * height)
484         {
485                 Con_Printf("LoadLMPAs8Bit: invalid LMP file\n");
486                 return NULL;
487         }
488
489         image_width = width;
490         image_height = height;
491
492         image_8bit = Mem_Alloc(tempmempool, image_width * image_height);
493         if (!image_8bit)
494         {
495                 Con_Printf("LoadLMPAs8Bit: not enough memory for %i by %i image\n", image_width, image_height);
496                 return NULL;
497         }
498         memcpy(image_8bit, f + 8, image_width * image_height);
499         return image_8bit;
500 }
501
502 void Image_StripImageExtension (char *in, char *out)
503 {
504         char *end, *temp;
505         end = in + strlen(in);
506         if ((end - in) >= 4)
507         {
508                 temp = end - 4;
509                 if (strcmp(temp, ".tga") == 0 || strcmp(temp, ".pcx") == 0 || strcmp(temp, ".lmp") == 0)
510                         end = temp;
511                 while (in < end)
512                         *out++ = *in++;
513                 *out++ = 0;
514         }
515         else
516                 strcpy(out, in);
517 }
518
519 qbyte *loadimagepixels (char *filename, qboolean complain, int matchwidth, int matchheight)
520 {
521         qbyte *f, *data;
522         char basename[256], name[256], *c;
523         Image_StripImageExtension(filename, basename); // strip .tga, .pcx and .lmp extensions to allow replacement by other types
524         // replace *'s with #, so commandline utils don't get confused when dealing with the external files
525         for (c = basename;*c;c++)
526                 if (*c == '*')
527                         *c = '#';
528         sprintf (name, "textures/%s.tga", basename);
529         f = COM_LoadFile(name, true);
530         if (f)
531         {
532                 data = LoadTGA (f, matchwidth, matchheight);
533                 Mem_Free(f);
534                 return data;
535         }
536         sprintf (name, "textures/%s.pcx", basename);
537         f = COM_LoadFile(name, true);
538         if (f)
539         {
540                 data = LoadPCX (f, matchwidth, matchheight);
541                 Mem_Free(f);
542                 return data;
543         }
544         sprintf (name, "%s.tga", basename);
545         f = COM_LoadFile(name, true);
546         if (f)
547         {
548                 data = LoadTGA (f, matchwidth, matchheight);
549                 Mem_Free(f);
550                 return data;
551         }
552         sprintf (name, "%s.pcx", basename);
553         f = COM_LoadFile(name, true);
554         if (f)
555         {
556                 data = LoadPCX (f, matchwidth, matchheight);
557                 Mem_Free(f);
558                 return data;
559         }
560         sprintf (name, "%s.lmp", basename);
561         f = COM_LoadFile(name, true);
562         if (f)
563         {
564                 data = LoadLMP (f, matchwidth, matchheight);
565                 Mem_Free(f);
566                 return data;
567         }
568         if (complain)
569                 Con_Printf ("Couldn't load %s.tga, .pcx, .lmp\n", filename);
570         return NULL;
571 }
572
573 int image_makemask (qbyte *in, qbyte *out, int size)
574 {
575         int             i, count;
576         count = 0;
577         for (i = 0;i < size;i++)
578         {
579                 out[0] = out[1] = out[2] = 255;
580                 out[3] = in[3];
581                 if (in[3] != 255)
582                         count++;
583                 in += 4;
584                 out += 4;
585         }
586         return count;
587 }
588
589 qbyte* loadimagepixelsmask (char* filename, qboolean complain, int matchwidth, int matchheight)
590 {
591         qbyte *in, *data;
592         in = data = loadimagepixels(filename, complain, matchwidth, matchheight);
593         if (!data)
594                 return NULL;
595         if (image_makemask(data, data, image_width * image_height))
596                 return data; // some transparency
597         else
598         {
599                 Mem_Free(data);
600                 return NULL; // all opaque
601         }
602 }
603
604 rtexture_t *loadtextureimage (rtexturepool_t *pool, char* filename, int matchwidth, int matchheight, qboolean complain, qboolean mipmap, qboolean precache)
605 {
606         qbyte *data;
607         rtexture_t *rt;
608         if (!(data = loadimagepixels (filename, complain, matchwidth, matchheight)))
609                 return 0;
610         rt = R_LoadTexture (pool, filename, image_width, image_height, data, TEXTYPE_RGBA, TEXF_ALPHA | (mipmap ? TEXF_MIPMAP : 0) | (precache ? TEXF_PRECACHE : 0));
611         Mem_Free(data);
612         return rt;
613 }
614
615 rtexture_t *loadtextureimagemask (rtexturepool_t *pool, char* filename, int matchwidth, int matchheight, qboolean complain, qboolean mipmap, qboolean precache)
616 {
617         qbyte *data;
618         rtexture_t *rt;
619         if (!(data = loadimagepixelsmask (filename, complain, matchwidth, matchheight)))
620                 return 0;
621         rt = R_LoadTexture (pool, filename, image_width, image_height, data, TEXTYPE_RGBA, TEXF_ALPHA | (mipmap ? TEXF_MIPMAP : 0) | (precache ? TEXF_PRECACHE : 0));
622         Mem_Free(data);
623         return rt;
624 }
625
626 rtexture_t *image_masktex;
627 rtexture_t *loadtextureimagewithmask (rtexturepool_t *pool, char* filename, int matchwidth, int matchheight, qboolean complain, qboolean mipmap, qboolean precache)
628 {
629         int count;
630         qbyte *data;
631         char *filename2;
632         rtexture_t *rt;
633         image_masktex = NULL;
634         if (!(data = loadimagepixels (filename, complain, matchwidth, matchheight)))
635                 return 0;
636         rt = R_LoadTexture (pool, filename, image_width, image_height, data, TEXTYPE_RGBA, TEXF_ALPHA | (mipmap ? TEXF_MIPMAP : 0) | (precache ? TEXF_PRECACHE : 0));
637         count = image_makemask(data, data, image_width * image_height);
638         if (count)
639         {
640                 filename2 = Mem_Alloc(tempmempool, strlen(filename) + 6);
641                 sprintf(filename2, "%s_mask", filename);
642                 image_masktex = R_LoadTexture (pool, filename2, image_width, image_height, data, TEXTYPE_RGBA, TEXF_ALPHA | (mipmap ? TEXF_MIPMAP : 0) | (precache ? TEXF_PRECACHE : 0));
643                 Mem_Free(filename2);
644         }
645         Mem_Free(data);
646         return rt;
647 }
648
649 qboolean Image_WriteTGARGB_preflipped (char *filename, int width, int height, qbyte *data)
650 {
651         qboolean ret;
652         qbyte *buffer, *in, *out, *end;
653
654         buffer = Mem_Alloc(tempmempool, width*height*3 + 18);
655
656         memset (buffer, 0, 18);
657         buffer[2] = 2;          // uncompressed type
658         buffer[12] = (width >> 0) & 0xFF;
659         buffer[13] = (width >> 8) & 0xFF;
660         buffer[14] = (height >> 0) & 0xFF;
661         buffer[15] = (height >> 8) & 0xFF;
662         buffer[16] = 24;        // pixel size
663
664         // swap rgb to bgr
665         in = data;
666         out = buffer + 18;
667         end = in + width*height*3;
668         for (;in < end;in += 3)
669         {
670                 *out++ = in[2];
671                 *out++ = in[1];
672                 *out++ = in[0];
673         }
674         ret = COM_WriteFile (filename, buffer, width*height*3 + 18 );
675
676         Mem_Free(buffer);
677         return ret;
678 }
679
680 void Image_WriteTGARGB (char *filename, int width, int height, qbyte *data)
681 {
682         int y;
683         qbyte *buffer, *in, *out, *end;
684
685         buffer = Mem_Alloc(tempmempool, width*height*3 + 18);
686
687         memset (buffer, 0, 18);
688         buffer[2] = 2;          // uncompressed type
689         buffer[12] = (width >> 0) & 0xFF;
690         buffer[13] = (width >> 8) & 0xFF;
691         buffer[14] = (height >> 0) & 0xFF;
692         buffer[15] = (height >> 8) & 0xFF;
693         buffer[16] = 24;        // pixel size
694
695         // swap rgb to bgr and flip upside down
696         out = buffer + 18;
697         for (y = height - 1;y >= 0;y--)
698         {
699                 in = data + y * width * 3;
700                 end = in + width * 3;
701                 for (;in < end;in += 3)
702                 {
703                         *out++ = in[2];
704                         *out++ = in[1];
705                         *out++ = in[0];
706                 }
707         }
708         COM_WriteFile (filename, buffer, width*height*3 + 18 );
709
710         Mem_Free(buffer);
711 }
712
713 void Image_WriteTGARGBA (char *filename, int width, int height, qbyte *data)
714 {
715         int y;
716         qbyte *buffer, *in, *out, *end;
717
718         buffer = Mem_Alloc(tempmempool, width*height*4 + 18);
719
720         memset (buffer, 0, 18);
721         buffer[2] = 2;          // uncompressed type
722         buffer[12] = (width >> 0) & 0xFF;
723         buffer[13] = (width >> 8) & 0xFF;
724         buffer[14] = (height >> 0) & 0xFF;
725         buffer[15] = (height >> 8) & 0xFF;
726         buffer[16] = 32;        // pixel size
727
728         // swap rgba to bgra and flip upside down
729         out = buffer + 18;
730         for (y = height - 1;y >= 0;y--)
731         {
732                 in = data + y * width * 4;
733                 end = in + width * 4;
734                 for (;in < end;in += 4)
735                 {
736                         *out++ = in[2];
737                         *out++ = in[1];
738                         *out++ = in[0];
739                         *out++ = in[3];
740                 }
741         }
742         COM_WriteFile (filename, buffer, width*height*4 + 18 );
743
744         Mem_Free(buffer);
745 }
746
747 qboolean Image_CheckAlpha(qbyte *data, int size, qboolean rgba)
748 {
749         qbyte *end;
750         if (rgba)
751         {
752                 // check alpha bytes
753                 for (end = data + size * 4, data += 3;data < end;data += 4)
754                         if (*data < 255)
755                                 return 1;
756         }
757         else
758         {
759                 // color 255 is transparent
760                 for (end = data + size;data < end;data++)
761                         if (*data == 255)
762                                 return 1;
763         }
764         return 0;
765 }
766
767 static void Image_Resample32LerpLine (qbyte *in, qbyte *out, int inwidth, int outwidth)
768 {
769         int             j, xi, oldx = 0, f, fstep, endx, lerp;
770         fstep = (int) (inwidth*65536.0f/outwidth);
771         endx = (inwidth-1);
772         for (j = 0,f = 0;j < outwidth;j++, f += fstep)
773         {
774                 xi = f >> 16;
775                 if (xi != oldx)
776                 {
777                         in += (xi - oldx) * 4;
778                         oldx = xi;
779                 }
780                 if (xi < endx)
781                 {
782                         lerp = f & 0xFFFF;
783                         *out++ = (qbyte) ((((in[4] - in[0]) * lerp) >> 16) + in[0]);
784                         *out++ = (qbyte) ((((in[5] - in[1]) * lerp) >> 16) + in[1]);
785                         *out++ = (qbyte) ((((in[6] - in[2]) * lerp) >> 16) + in[2]);
786                         *out++ = (qbyte) ((((in[7] - in[3]) * lerp) >> 16) + in[3]);
787                 }
788                 else // last pixel of the line has no pixel to lerp to
789                 {
790                         *out++ = in[0];
791                         *out++ = in[1];
792                         *out++ = in[2];
793                         *out++ = in[3];
794                 }
795         }
796 }
797
798 static void Image_Resample24LerpLine (qbyte *in, qbyte *out, int inwidth, int outwidth)
799 {
800         int             j, xi, oldx = 0, f, fstep, endx, lerp;
801         fstep = (int) (inwidth*65536.0f/outwidth);
802         endx = (inwidth-1);
803         for (j = 0,f = 0;j < outwidth;j++, f += fstep)
804         {
805                 xi = f >> 16;
806                 if (xi != oldx)
807                 {
808                         in += (xi - oldx) * 3;
809                         oldx = xi;
810                 }
811                 if (xi < endx)
812                 {
813                         lerp = f & 0xFFFF;
814                         *out++ = (qbyte) ((((in[3] - in[0]) * lerp) >> 16) + in[0]);
815                         *out++ = (qbyte) ((((in[4] - in[1]) * lerp) >> 16) + in[1]);
816                         *out++ = (qbyte) ((((in[5] - in[2]) * lerp) >> 16) + in[2]);
817                 }
818                 else // last pixel of the line has no pixel to lerp to
819                 {
820                         *out++ = in[0];
821                         *out++ = in[1];
822                         *out++ = in[2];
823                 }
824         }
825 }
826
827 int resamplerowsize = 0;
828 qbyte *resamplerow1 = NULL;
829 qbyte *resamplerow2 = NULL;
830 mempool_t *resamplemempool = NULL;
831
832 #define LERPBYTE(i) r = resamplerow1[i];out[i] = (qbyte) ((((resamplerow2[i] - r) * lerp) >> 16) + r)
833 void Image_Resample32Lerp(void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight)
834 {
835         int i, j, r, yi, oldy, f, fstep, lerp, endy = (inheight-1), inwidth4 = inwidth*4, outwidth4 = outwidth*4;
836         qbyte *inrow, *out;
837         out = outdata;
838         fstep = (int) (inheight*65536.0f/outheight);
839
840         inrow = indata;
841         oldy = 0;
842         Image_Resample32LerpLine (inrow, resamplerow1, inwidth, outwidth);
843         Image_Resample32LerpLine (inrow + inwidth4, resamplerow2, inwidth, outwidth);
844         for (i = 0, f = 0;i < outheight;i++,f += fstep)
845         {
846                 yi = f >> 16;
847                 if (yi < endy)
848                 {
849                         lerp = f & 0xFFFF;
850                         if (yi != oldy)
851                         {
852                                 inrow = (qbyte *)indata + inwidth4*yi;
853                                 if (yi == oldy+1)
854                                         memcpy(resamplerow1, resamplerow2, outwidth4);
855                                 else
856                                         Image_Resample32LerpLine (inrow, resamplerow1, inwidth, outwidth);
857                                 Image_Resample32LerpLine (inrow + inwidth4, resamplerow2, inwidth, outwidth);
858                                 oldy = yi;
859                         }
860                         j = outwidth - 4;
861                         while(j >= 0)
862                         {
863                                 LERPBYTE( 0);
864                                 LERPBYTE( 1);
865                                 LERPBYTE( 2);
866                                 LERPBYTE( 3);
867                                 LERPBYTE( 4);
868                                 LERPBYTE( 5);
869                                 LERPBYTE( 6);
870                                 LERPBYTE( 7);
871                                 LERPBYTE( 8);
872                                 LERPBYTE( 9);
873                                 LERPBYTE(10);
874                                 LERPBYTE(11);
875                                 LERPBYTE(12);
876                                 LERPBYTE(13);
877                                 LERPBYTE(14);
878                                 LERPBYTE(15);
879                                 out += 16;
880                                 resamplerow1 += 16;
881                                 resamplerow2 += 16;
882                                 j -= 4;
883                         }
884                         if (j & 2)
885                         {
886                                 LERPBYTE( 0);
887                                 LERPBYTE( 1);
888                                 LERPBYTE( 2);
889                                 LERPBYTE( 3);
890                                 LERPBYTE( 4);
891                                 LERPBYTE( 5);
892                                 LERPBYTE( 6);
893                                 LERPBYTE( 7);
894                                 out += 8;
895                                 resamplerow1 += 8;
896                                 resamplerow2 += 8;
897                         }
898                         if (j & 1)
899                         {
900                                 LERPBYTE( 0);
901                                 LERPBYTE( 1);
902                                 LERPBYTE( 2);
903                                 LERPBYTE( 3);
904                                 out += 4;
905                                 resamplerow1 += 4;
906                                 resamplerow2 += 4;
907                         }
908                         resamplerow1 -= outwidth4;
909                         resamplerow2 -= outwidth4;
910                 }
911                 else
912                 {
913                         if (yi != oldy)
914                         {
915                                 inrow = (qbyte *)indata + inwidth4*yi;
916                                 if (yi == oldy+1)
917                                         memcpy(resamplerow1, resamplerow2, outwidth4);
918                                 else
919                                         Image_Resample32LerpLine (inrow, resamplerow1, inwidth, outwidth);
920                                 oldy = yi;
921                         }
922                         memcpy(out, resamplerow1, outwidth4);
923                 }
924         }
925 }
926
927 void Image_Resample32Nearest(void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight)
928 {
929         int i, j;
930         unsigned frac, fracstep;
931         // relies on int being 4 bytes
932         int *inrow, *out;
933         out = outdata;
934
935         fracstep = inwidth*0x10000/outwidth;
936         for (i = 0;i < outheight;i++)
937         {
938                 inrow = (int *)indata + inwidth*(i*inheight/outheight);
939                 frac = fracstep >> 1;
940                 j = outwidth - 4;
941                 while (j >= 0)
942                 {
943                         out[0] = inrow[frac >> 16];frac += fracstep;
944                         out[1] = inrow[frac >> 16];frac += fracstep;
945                         out[2] = inrow[frac >> 16];frac += fracstep;
946                         out[3] = inrow[frac >> 16];frac += fracstep;
947                         out += 4;
948                         j -= 4;
949                 }
950                 if (j & 2)
951                 {
952                         out[0] = inrow[frac >> 16];frac += fracstep;
953                         out[1] = inrow[frac >> 16];frac += fracstep;
954                         out += 2;
955                 }
956                 if (j & 1)
957                 {
958                         out[0] = inrow[frac >> 16];frac += fracstep;
959                         out += 1;
960                 }
961         }
962 }
963
964 void Image_Resample24Lerp(void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight)
965 {
966         int i, j, r, yi, oldy, f, fstep, lerp, endy = (inheight-1), inwidth3 = inwidth * 3, outwidth3 = outwidth * 3;
967         qbyte *inrow, *out;
968         out = outdata;
969         fstep = (int) (inheight*65536.0f/outheight);
970
971         inrow = indata;
972         oldy = 0;
973         Image_Resample24LerpLine (inrow, resamplerow1, inwidth, outwidth);
974         Image_Resample24LerpLine (inrow + inwidth3, resamplerow2, inwidth, outwidth);
975         for (i = 0, f = 0;i < outheight;i++,f += fstep)
976         {
977                 yi = f >> 16;
978                 if (yi < endy)
979                 {
980                         lerp = f & 0xFFFF;
981                         if (yi != oldy)
982                         {
983                                 inrow = (qbyte *)indata + inwidth3*yi;
984                                 if (yi == oldy+1)
985                                         memcpy(resamplerow1, resamplerow2, outwidth3);
986                                 else
987                                         Image_Resample24LerpLine (inrow, resamplerow1, inwidth, outwidth);
988                                 Image_Resample24LerpLine (inrow + inwidth3, resamplerow2, inwidth, outwidth);
989                                 oldy = yi;
990                         }
991                         j = outwidth - 4;
992                         while(j >= 0)
993                         {
994                                 LERPBYTE( 0);
995                                 LERPBYTE( 1);
996                                 LERPBYTE( 2);
997                                 LERPBYTE( 3);
998                                 LERPBYTE( 4);
999                                 LERPBYTE( 5);
1000                                 LERPBYTE( 6);
1001                                 LERPBYTE( 7);
1002                                 LERPBYTE( 8);
1003                                 LERPBYTE( 9);
1004                                 LERPBYTE(10);
1005                                 LERPBYTE(11);
1006                                 out += 12;
1007                                 resamplerow1 += 12;
1008                                 resamplerow2 += 12;
1009                                 j -= 4;
1010                         }
1011                         if (j & 2)
1012                         {
1013                                 LERPBYTE( 0);
1014                                 LERPBYTE( 1);
1015                                 LERPBYTE( 2);
1016                                 LERPBYTE( 3);
1017                                 LERPBYTE( 4);
1018                                 LERPBYTE( 5);
1019                                 out += 6;
1020                                 resamplerow1 += 6;
1021                                 resamplerow2 += 6;
1022                         }
1023                         if (j & 1)
1024                         {
1025                                 LERPBYTE( 0);
1026                                 LERPBYTE( 1);
1027                                 LERPBYTE( 2);
1028                                 out += 3;
1029                                 resamplerow1 += 3;
1030                                 resamplerow2 += 3;
1031                         }
1032                         resamplerow1 -= outwidth3;
1033                         resamplerow2 -= outwidth3;
1034                 }
1035                 else
1036                 {
1037                         if (yi != oldy)
1038                         {
1039                                 inrow = (qbyte *)indata + inwidth3*yi;
1040                                 if (yi == oldy+1)
1041                                         memcpy(resamplerow1, resamplerow2, outwidth3);
1042                                 else
1043                                         Image_Resample24LerpLine (inrow, resamplerow1, inwidth, outwidth);
1044                                 oldy = yi;
1045                         }
1046                         memcpy(out, resamplerow1, outwidth3);
1047                 }
1048         }
1049 }
1050
1051 void Image_Resample24Nolerp(void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight)
1052 {
1053         int i, j, f, inwidth3 = inwidth * 3;
1054         unsigned frac, fracstep;
1055         qbyte *inrow, *out;
1056         out = outdata;
1057
1058         fracstep = inwidth*0x10000/outwidth;
1059         for (i = 0;i < outheight;i++)
1060         {
1061                 inrow = (qbyte *)indata + inwidth3*(i*inheight/outheight);
1062                 frac = fracstep >> 1;
1063                 j = outwidth - 4;
1064                 while (j >= 0)
1065                 {
1066                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1067                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1068                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1069                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1070                         j -= 4;
1071                 }
1072                 if (j & 2)
1073                 {
1074                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1075                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1076                         out += 2;
1077                 }
1078                 if (j & 1)
1079                 {
1080                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1081                         out += 1;
1082                 }
1083         }
1084 }
1085
1086 /*
1087 ================
1088 Image_Resample
1089 ================
1090 */
1091 void Image_Resample (void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight, int bytesperpixel, int quality)
1092 {
1093         if (resamplerowsize < outwidth*4)
1094         {
1095                 if (resamplerow1)
1096                         Mem_Free(resamplerow1);
1097                 resamplerowsize = outwidth*4;
1098                 if (!resamplemempool)
1099                         resamplemempool = Mem_AllocPool("Image Scaling Buffer");
1100                 resamplerow1 = Mem_Alloc(resamplemempool, resamplerowsize*2);
1101                 resamplerow2 = resamplerow1 + resamplerowsize;
1102         }
1103         if (bytesperpixel == 4)
1104         {
1105                 if (quality)
1106                         Image_Resample32Lerp(indata, inwidth, inheight, outdata, outwidth, outheight);
1107                 else
1108                         Image_Resample32Nearest(indata, inwidth, inheight, outdata, outwidth, outheight);
1109         }
1110         else if (bytesperpixel == 3)
1111         {
1112                 if (quality)
1113                         Image_Resample24Lerp(indata, inwidth, inheight, outdata, outwidth, outheight);
1114                 else
1115                         Image_Resample24Nolerp(indata, inwidth, inheight, outdata, outwidth, outheight);
1116         }
1117         else
1118                 Sys_Error("Image_Resample: unsupported bytesperpixel %i\n", bytesperpixel);
1119 }
1120
1121 // in can be the same as out
1122 void Image_MipReduce(qbyte *in, qbyte *out, int *width, int *height, int destwidth, int destheight, int bytesperpixel)
1123 {
1124         int x, y, nextrow;
1125         nextrow = *width * bytesperpixel;
1126         if (*width > destwidth)
1127         {
1128                 *width >>= 1;
1129                 if (*height > destheight)
1130                 {
1131                         // reduce both
1132                         *height >>= 1;
1133                         if (bytesperpixel == 4)
1134                         {
1135                                 for (y = 0;y < *height;y++)
1136                                 {
1137                                         for (x = 0;x < *width;x++)
1138                                         {
1139                                                 out[0] = (qbyte) ((in[0] + in[4] + in[nextrow  ] + in[nextrow+4]) >> 2);
1140                                                 out[1] = (qbyte) ((in[1] + in[5] + in[nextrow+1] + in[nextrow+5]) >> 2);
1141                                                 out[2] = (qbyte) ((in[2] + in[6] + in[nextrow+2] + in[nextrow+6]) >> 2);
1142                                                 out[3] = (qbyte) ((in[3] + in[7] + in[nextrow+3] + in[nextrow+7]) >> 2);
1143                                                 out += 4;
1144                                                 in += 8;
1145                                         }
1146                                         in += nextrow; // skip a line
1147                                 }
1148                         }
1149                         else if (bytesperpixel == 3)
1150                         {
1151                                 for (y = 0;y < *height;y++)
1152                                 {
1153                                         for (x = 0;x < *width;x++)
1154                                         {
1155                                                 out[0] = (qbyte) ((in[0] + in[3] + in[nextrow  ] + in[nextrow+3]) >> 2);
1156                                                 out[1] = (qbyte) ((in[1] + in[4] + in[nextrow+1] + in[nextrow+4]) >> 2);
1157                                                 out[2] = (qbyte) ((in[2] + in[5] + in[nextrow+2] + in[nextrow+5]) >> 2);
1158                                                 out += 3;
1159                                                 in += 6;
1160                                         }
1161                                         in += nextrow; // skip a line
1162                                 }
1163                         }
1164                         else
1165                                 Sys_Error("Image_MipReduce: unsupported bytesperpixel %i\n", bytesperpixel);
1166                 }
1167                 else
1168                 {
1169                         // reduce width
1170                         if (bytesperpixel == 4)
1171                         {
1172                                 for (y = 0;y < *height;y++)
1173                                 {
1174                                         for (x = 0;x < *width;x++)
1175                                         {
1176                                                 out[0] = (qbyte) ((in[0] + in[4]) >> 1);
1177                                                 out[1] = (qbyte) ((in[1] + in[5]) >> 1);
1178                                                 out[2] = (qbyte) ((in[2] + in[6]) >> 1);
1179                                                 out[3] = (qbyte) ((in[3] + in[7]) >> 1);
1180                                                 out += 4;
1181                                                 in += 8;
1182                                         }
1183                                 }
1184                         }
1185                         else if (bytesperpixel == 3)
1186                         {
1187                                 for (y = 0;y < *height;y++)
1188                                 {
1189                                         for (x = 0;x < *width;x++)
1190                                         {
1191                                                 out[0] = (qbyte) ((in[0] + in[3]) >> 1);
1192                                                 out[1] = (qbyte) ((in[1] + in[4]) >> 1);
1193                                                 out[2] = (qbyte) ((in[2] + in[5]) >> 1);
1194                                                 out += 3;
1195                                                 in += 6;
1196                                         }
1197                                 }
1198                         }
1199                         else
1200                                 Sys_Error("Image_MipReduce: unsupported bytesperpixel %i\n", bytesperpixel);
1201                 }
1202         }
1203         else
1204         {
1205                 if (*height > destheight)
1206                 {
1207                         // reduce height
1208                         *height >>= 1;
1209                         if (bytesperpixel == 4)
1210                         {
1211                                 for (y = 0;y < *height;y++)
1212                                 {
1213                                         for (x = 0;x < *width;x++)
1214                                         {
1215                                                 out[0] = (qbyte) ((in[0] + in[nextrow  ]) >> 1);
1216                                                 out[1] = (qbyte) ((in[1] + in[nextrow+1]) >> 1);
1217                                                 out[2] = (qbyte) ((in[2] + in[nextrow+2]) >> 1);
1218                                                 out[3] = (qbyte) ((in[3] + in[nextrow+3]) >> 1);
1219                                                 out += 4;
1220                                                 in += 4;
1221                                         }
1222                                         in += nextrow; // skip a line
1223                                 }
1224                         }
1225                         else if (bytesperpixel == 3)
1226                         {
1227                                 for (y = 0;y < *height;y++)
1228                                 {
1229                                         for (x = 0;x < *width;x++)
1230                                         {
1231                                                 out[0] = (qbyte) ((in[0] + in[nextrow  ]) >> 1);
1232                                                 out[1] = (qbyte) ((in[1] + in[nextrow+1]) >> 1);
1233                                                 out[2] = (qbyte) ((in[2] + in[nextrow+2]) >> 1);
1234                                                 out += 3;
1235                                                 in += 3;
1236                                         }
1237                                         in += nextrow; // skip a line
1238                                 }
1239                         }
1240                         else
1241                                 Sys_Error("Image_MipReduce: unsupported bytesperpixel %i\n", bytesperpixel);
1242                 }
1243                 else
1244                         Sys_Error("Image_MipReduce: desired size already achieved\n");
1245         }
1246 }
1247