rewrote memory system entirely (hunk, cache, and zone are gone, memory pools replaced...
[xonotic/darkplaces.git] / image.c
1
2 #include "quakedef.h"
3
4 int             image_width;
5 int             image_height;
6
7 void Image_GammaRemapRGB(byte *in, byte *out, int pixels, byte *gammar, byte *gammag, byte *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(byte *in, byte *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 byte* LoadPCX (byte *f, int matchwidth, int matchheight)
87 {
88         pcx_t   pcx;
89         byte    *palette, *a, *b, *image_rgba, *fin, *pbuf, *enddata;
90         int             x, y, x2, dataByte;
91
92         if (loadsize < sizeof(pcx) + 768)
93         {
94                 Con_Printf ("Bad pcx file\n");
95                 return NULL;
96         }
97
98         fin = f;
99
100         memcpy(&pcx, fin, sizeof(pcx));
101         fin += sizeof(pcx);
102
103         // LordHavoc: big-endian support ported from QF newtree
104         pcx.xmax = LittleShort (pcx.xmax);
105         pcx.xmin = LittleShort (pcx.xmin);
106         pcx.ymax = LittleShort (pcx.ymax);
107         pcx.ymin = LittleShort (pcx.ymin);
108         pcx.hres = LittleShort (pcx.hres);
109         pcx.vres = LittleShort (pcx.vres);
110         pcx.bytes_per_line = LittleShort (pcx.bytes_per_line);
111         pcx.palette_type = LittleShort (pcx.palette_type);
112
113         if (pcx.manufacturer != 0x0a || pcx.version != 5 || pcx.encoding != 1 || pcx.bits_per_pixel != 8 || pcx.xmax > 320 || pcx.ymax > 256)
114         {
115                 Con_Printf ("Bad pcx file\n");
116                 return NULL;
117         }
118
119         if (matchwidth && (pcx.xmax+1) != matchwidth)
120         {
121                 return NULL;
122         }
123         if (matchheight && (pcx.ymax+1) != matchheight)
124         {
125                 return NULL;
126         }
127
128         image_width = pcx.xmax+1;
129         image_height = pcx.ymax+1;
130
131         palette = f + loadsize - 768;
132
133         image_rgba = Mem_Alloc(tempmempool, image_width*image_height*4);
134         if (!image_rgba)
135         {
136                 Con_Printf("LoadPCX: not enough memory for %i by %i image\n", image_width, image_height);
137                 return NULL;
138         }
139         pbuf = image_rgba + image_width*image_height*3;
140         enddata = palette;
141
142         for (y = 0;y < image_height && fin < enddata;y++)
143         {
144                 a = pbuf + y * image_width;
145                 for (x = 0;x < image_width && fin < enddata;)
146                 {
147                         dataByte = *fin++;
148                         if(dataByte >= 0xC0)
149                         {
150                                 if (fin >= enddata)
151                                         break;
152                                 x2 = x + (dataByte & 0x3F);
153                                 dataByte = *fin++;
154                                 if (x2 > image_width)
155                                         x2 = image_width; // technically an error
156                                 while(x < x2)
157                                         a[x++] = dataByte;
158                         }
159                         else
160                                 a[x++] = dataByte;
161                 }
162                 while(x < image_width)
163                         a[x++] = 0;
164         }
165
166         a = image_rgba;
167         b = pbuf;
168
169         for(x = 0;x < image_width*image_height;x++)
170         {
171                 y = *b++ * 3;
172                 *a++ = palette[y];
173                 *a++ = palette[y+1];
174                 *a++ = palette[y+2];
175                 *a++ = 255;
176         }
177
178         return image_rgba;
179 }
180
181 /*
182 =========================================================
183
184 TARGA LOADING
185
186 =========================================================
187 */
188
189 typedef struct _TargaHeader
190 {
191         unsigned char   id_length, colormap_type, image_type;
192         unsigned short  colormap_index, colormap_length;
193         unsigned char   colormap_size;
194         unsigned short  x_origin, y_origin, width, height;
195         unsigned char   pixel_size, attributes;
196 }
197 TargaHeader;
198
199 TargaHeader             targa_header;
200
201
202 /*
203 =============
204 LoadTGA
205 =============
206 */
207 byte* LoadTGA (byte *f, int matchwidth, int matchheight)
208 {
209         int columns, rows, row, column;
210         byte *pixbuf, *image_rgba, *fin, *enddata;
211
212         if (loadsize < 18+3)
213                 return NULL;
214         targa_header.id_length = f[0];
215         targa_header.colormap_type = f[1];
216         targa_header.image_type = f[2];
217
218         targa_header.colormap_index = f[3] + f[4] * 256;
219         targa_header.colormap_length = f[5] + f[6] * 256;
220         targa_header.colormap_size = f[7];
221         targa_header.x_origin = f[8] + f[9] * 256;
222         targa_header.y_origin = f[10] + f[11] * 256;
223         targa_header.width = f[12] + f[13] * 256;
224         targa_header.height = f[14] + f[15] * 256;
225         if (matchwidth && targa_header.width != matchwidth)
226                 return NULL;
227         if (matchheight && targa_header.height != matchheight)
228                 return NULL;
229         targa_header.pixel_size = f[16];
230         targa_header.attributes = f[17];
231
232         if (targa_header.image_type != 2 && targa_header.image_type != 10)
233         {
234                 Con_Printf ("LoadTGA: Only type 2 and 10 targa RGB images supported\n");
235                 return NULL;
236         }
237
238         if (targa_header.colormap_type != 0     || (targa_header.pixel_size != 32 && targa_header.pixel_size != 24))
239         {
240                 Con_Printf ("LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n");
241                 return NULL;
242         }
243
244         enddata = f + loadsize;
245
246         columns = targa_header.width;
247         rows = targa_header.height;
248
249         image_rgba = Mem_Alloc(tempmempool, columns * rows * 4);
250         if (!image_rgba)
251         {
252                 Con_Printf ("LoadTGA: not enough memory for %i by %i image\n", columns, rows);
253                 return NULL;
254         }
255
256         fin = f + 18;
257         if (targa_header.id_length != 0)
258                 fin += targa_header.id_length;  // skip TARGA image comment
259
260         if (targa_header.image_type == 2)
261         {
262                 // Uncompressed, RGB images
263                 for(row = rows - 1;row >= 0;row--)
264                 {
265                         pixbuf = image_rgba + row*columns*4;
266                         for(column = 0;column < columns;column++)
267                         {
268                                 switch (targa_header.pixel_size)
269                                 {
270                                 case 24:
271                                         if (fin + 3 > enddata)
272                                                 break;
273                                         *pixbuf++ = fin[2];
274                                         *pixbuf++ = fin[1];
275                                         *pixbuf++ = fin[0];
276                                         *pixbuf++ = 255;
277                                         fin += 3;
278                                         break;
279                                 case 32:
280                                         if (fin + 4 > enddata)
281                                                 break;
282                                         *pixbuf++ = fin[2];
283                                         *pixbuf++ = fin[1];
284                                         *pixbuf++ = fin[0];
285                                         *pixbuf++ = fin[3];
286                                         fin += 4;
287                                         break;
288                                 }
289                         }
290                 }
291         }
292         else if (targa_header.image_type==10)
293         {
294                 // Runlength encoded RGB images
295                 unsigned char red = 0, green = 0, blue = 0, alphabyte = 0, packetHeader, packetSize, j;
296                 for(row = rows - 1;row >= 0;row--)
297                 {
298                         pixbuf = image_rgba + row * columns * 4;
299                         for(column = 0;column < columns;)
300                         {
301                                 if (fin >= enddata)
302                                         goto outofdata;
303                                 packetHeader = *fin++;
304                                 packetSize = 1 + (packetHeader & 0x7f);
305                                 if (packetHeader & 0x80)
306                                 {
307                                         // run-length packet
308                                         switch (targa_header.pixel_size)
309                                         {
310                                         case 24:
311                                                 if (fin + 3 > enddata)
312                                                         goto outofdata;
313                                                 blue = *fin++;
314                                                 green = *fin++;
315                                                 red = *fin++;
316                                                 alphabyte = 255;
317                                                 break;
318                                         case 32:
319                                                 if (fin + 4 > enddata)
320                                                         goto outofdata;
321                                                 blue = *fin++;
322                                                 green = *fin++;
323                                                 red = *fin++;
324                                                 alphabyte = *fin++;
325                                                 break;
326                                         }
327
328                                         for(j = 0;j < packetSize;j++)
329                                         {
330                                                 *pixbuf++ = red;
331                                                 *pixbuf++ = green;
332                                                 *pixbuf++ = blue;
333                                                 *pixbuf++ = alphabyte;
334                                                 column++;
335                                                 if (column == columns)
336                                                 {
337                                                         // run spans across rows
338                                                         column = 0;
339                                                         if (row > 0)
340                                                                 row--;
341                                                         else
342                                                                 goto breakOut;
343                                                         pixbuf = image_rgba + row * columns * 4;
344                                                 }
345                                         }
346                                 }
347                                 else
348                                 {
349                                         // non run-length packet
350                                         for(j = 0;j < packetSize;j++)
351                                         {
352                                                 switch (targa_header.pixel_size)
353                                                 {
354                                                 case 24:
355                                                         if (fin + 3 > enddata)
356                                                                 goto outofdata;
357                                                         *pixbuf++ = fin[2];
358                                                         *pixbuf++ = fin[1];
359                                                         *pixbuf++ = fin[0];
360                                                         *pixbuf++ = 255;
361                                                         fin += 3;
362                                                         break;
363                                                 case 32:
364                                                         if (fin + 4 > enddata)
365                                                                 goto outofdata;
366                                                         *pixbuf++ = fin[2];
367                                                         *pixbuf++ = fin[1];
368                                                         *pixbuf++ = fin[0];
369                                                         *pixbuf++ = fin[3];
370                                                         fin += 4;
371                                                         break;
372                                                 }
373                                                 column++;
374                                                 if (column == columns)
375                                                 {
376                                                         // pixel packet run spans across rows
377                                                         column = 0;
378                                                         if (row > 0)
379                                                                 row--;
380                                                         else
381                                                                 goto breakOut;
382                                                         pixbuf = image_rgba + row * columns * 4;
383                                                 }
384                                         }
385                                 }
386                         }
387                         breakOut:;
388                 }
389         }
390 outofdata:;
391
392         image_width = columns;
393         image_height = rows;
394         return image_rgba;
395 }
396
397 /*
398 ============
399 LoadLMP
400 ============
401 */
402 byte* LoadLMP (byte *f, int matchwidth, int matchheight)
403 {
404         byte    *image_rgba;
405         int             width, height;
406
407         if (loadsize < 9)
408         {
409                 Con_Printf("LoadLMP: invalid LMP file\n");
410                 return NULL;
411         }
412
413         // parse the very complicated header *chuckle*
414         width = f[0] + f[1] * 256 + f[2] * 65536 + f[3] * 16777216;
415         height = f[4] + f[5] * 256 + f[6] * 65536 + f[7] * 16777216;
416         if ((unsigned) width > 4096 || (unsigned) height > 4096)
417         {
418                 Con_Printf("LoadLMP: invalid size\n");
419                 return NULL;
420         }
421         if ((matchwidth && width != matchwidth) || (matchheight && height != matchheight))
422                 return NULL;
423
424         if (loadsize < 8 + width * height)
425         {
426                 Con_Printf("LoadLMP: invalid LMP file\n");
427                 return NULL;
428         }
429
430         image_width = width;
431         image_height = height;
432
433         image_rgba = Mem_Alloc(tempmempool, image_width * image_height * 4);
434         if (!image_rgba)
435         {
436                 Con_Printf("LoadLMP: not enough memory for %i by %i image\n", image_width, image_height);
437                 return NULL;
438         }
439         Image_Copy8bitRGBA(f + 8, image_rgba, image_width * image_height, d_8to24table);
440         return image_rgba;
441 }
442
443 void Image_StripImageExtension (char *in, char *out)
444 {
445         char *end, *temp;
446         end = in + strlen(in);
447         if ((end - in) >= 4)
448         {
449                 temp = end - 4;
450                 if (strcmp(temp, ".tga") == 0 || strcmp(temp, ".pcx") == 0 || strcmp(temp, ".lmp") == 0)
451                         end = temp;
452                 while (in < end)
453                         *out++ = *in++;
454                 *out++ = 0;
455         }
456         else
457                 strcpy(out, in);
458 }
459
460 byte* loadimagepixels (char* filename, qboolean complain, int matchwidth, int matchheight)
461 {
462         byte    *f, *data;
463         char    basename[256], name[256], *c;
464         Image_StripImageExtension(filename, basename); // strip .tga, .pcx and .lmp extensions to allow replacement by other types
465         // replace *'s with #, so commandline utils don't get confused when dealing with the external files
466         for (c = basename;*c;c++)
467                 if (*c == '*')
468                         *c = '#';
469         sprintf (name, "textures/%s.tga", basename);
470         f = COM_LoadFile(name, true);
471         if (f)
472         {
473                 data = LoadTGA (f, matchwidth, matchheight);
474                 Mem_Free(f);
475                 return data;
476         }
477         sprintf (name, "textures/%s.pcx", basename);
478         f = COM_LoadFile(name, true);
479         if (f)
480         {
481                 data = LoadPCX (f, matchwidth, matchheight);
482                 Mem_Free(f);
483                 return data;
484         }
485         sprintf (name, "%s.tga", basename);
486         f = COM_LoadFile(name, true);
487         if (f)
488         {
489                 data = LoadTGA (f, matchwidth, matchheight);
490                 Mem_Free(f);
491                 return data;
492         }
493         sprintf (name, "%s.pcx", basename);
494         f = COM_LoadFile(name, true);
495         if (f)
496         {
497                 data = LoadPCX (f, matchwidth, matchheight);
498                 Mem_Free(f);
499                 return data;
500         }
501         sprintf (name, "%s.lmp", basename);
502         f = COM_LoadFile(name, true);
503         if (f)
504         {
505                 data = LoadLMP (f, matchwidth, matchheight);
506                 Mem_Free(f);
507                 return data;
508         }
509         if (complain)
510                 Con_Printf ("Couldn't load %s.tga, .pcx, .lmp\n", filename);
511         return NULL;
512 }
513
514 int image_makemask (byte *in, byte *out, int size)
515 {
516         int             i, count;
517         count = 0;
518         for (i = 0;i < size;i++)
519         {
520                 out[0] = out[1] = out[2] = 255;
521                 out[3] = in[3];
522                 if (in[3] != 255)
523                         count++;
524                 in += 4;
525                 out += 4;
526         }
527         return count;
528 }
529
530 byte* loadimagepixelsmask (char* filename, qboolean complain, int matchwidth, int matchheight)
531 {
532         byte    *in, *data;
533         in = data = loadimagepixels(filename, complain, matchwidth, matchheight);
534         if (!data)
535                 return NULL;
536         if (image_makemask(data, data, image_width * image_height))
537                 return data; // some transparency
538         else
539         {
540                 Mem_Free(data);
541                 return NULL; // all opaque
542         }
543 }
544
545 rtexture_t *loadtextureimage (rtexturepool_t *pool, char* filename, int matchwidth, int matchheight, qboolean complain, qboolean mipmap, qboolean precache)
546 {
547         byte *data;
548         rtexture_t *rt;
549         if (!(data = loadimagepixels (filename, complain, matchwidth, matchheight)))
550                 return 0;
551         rt = R_LoadTexture (pool, filename, image_width, image_height, data, TEXTYPE_RGBA, TEXF_ALPHA | (mipmap ? TEXF_MIPMAP : 0) | (precache ? TEXF_PRECACHE : 0));
552         Mem_Free(data);
553         return rt;
554 }
555
556 rtexture_t *loadtextureimagemask (rtexturepool_t *pool, char* filename, int matchwidth, int matchheight, qboolean complain, qboolean mipmap, qboolean precache)
557 {
558         byte *data;
559         rtexture_t *rt;
560         if (!(data = loadimagepixelsmask (filename, complain, matchwidth, matchheight)))
561                 return 0;
562         rt = R_LoadTexture (pool, filename, image_width, image_height, data, TEXTYPE_RGBA, TEXF_ALPHA | (mipmap ? TEXF_MIPMAP : 0) | (precache ? TEXF_PRECACHE : 0));
563         Mem_Free(data);
564         return rt;
565 }
566
567 rtexture_t *image_masktex;
568 rtexture_t *loadtextureimagewithmask (rtexturepool_t *pool, char* filename, int matchwidth, int matchheight, qboolean complain, qboolean mipmap, qboolean precache)
569 {
570         int count;
571         byte *data;
572         char *filename2;
573         rtexture_t *rt;
574         image_masktex = NULL;
575         if (!(data = loadimagepixels (filename, complain, matchwidth, matchheight)))
576                 return 0;
577         rt = R_LoadTexture (pool, filename, image_width, image_height, data, TEXTYPE_RGBA, TEXF_ALPHA | (mipmap ? TEXF_MIPMAP : 0) | (precache ? TEXF_PRECACHE : 0));
578         count = image_makemask(data, data, image_width * image_height);
579         if (count)
580         {
581                 filename2 = Mem_Alloc(tempmempool, strlen(filename) + 6);
582                 sprintf(filename2, "%s_mask", filename);
583                 image_masktex = R_LoadTexture (pool, filename2, image_width, image_height, data, TEXTYPE_RGBA, TEXF_ALPHA | (mipmap ? TEXF_MIPMAP : 0) | (precache ? TEXF_PRECACHE : 0));
584                 Mem_Free(filename2);
585         }
586         Mem_Free(data);
587         return rt;
588 }
589
590 void Image_WriteTGARGB_preflipped (char *filename, int width, int height, byte *data)
591 {
592         byte *buffer, *in, *out, *end;
593
594         buffer = Mem_Alloc(tempmempool, width*height*3 + 18);
595
596         memset (buffer, 0, 18);
597         buffer[2] = 2;          // uncompressed type
598         buffer[12] = (width >> 0) & 0xFF;
599         buffer[13] = (width >> 8) & 0xFF;
600         buffer[14] = (height >> 0) & 0xFF;
601         buffer[15] = (height >> 8) & 0xFF;
602         buffer[16] = 24;        // pixel size
603
604         // swap rgb to bgr
605         in = data;
606         out = buffer + 18;
607         end = in + width*height*3;
608         for (;in < end;in += 3)
609         {
610                 *out++ = in[2];
611                 *out++ = in[1];
612                 *out++ = in[0];
613         }
614         COM_WriteFile (filename, buffer, width*height*3 + 18 );
615
616         Mem_Free(buffer);
617 }
618
619 void Image_WriteTGARGB (char *filename, int width, int height, byte *data)
620 {
621         int y;
622         byte *buffer, *in, *out, *end;
623
624         buffer = Mem_Alloc(tempmempool, width*height*3 + 18);
625
626         memset (buffer, 0, 18);
627         buffer[2] = 2;          // uncompressed type
628         buffer[12] = (width >> 0) & 0xFF;
629         buffer[13] = (width >> 8) & 0xFF;
630         buffer[14] = (height >> 0) & 0xFF;
631         buffer[15] = (height >> 8) & 0xFF;
632         buffer[16] = 24;        // pixel size
633
634         // swap rgb to bgr and flip upside down
635         out = buffer + 18;
636         for (y = height - 1;y >= 0;y--)
637         {
638                 in = data + y * width * 3;
639                 end = in + width * 3;
640                 for (;in < end;in += 3)
641                 {
642                         *out++ = in[2];
643                         *out++ = in[1];
644                         *out++ = in[0];
645                 }
646         }
647         COM_WriteFile (filename, buffer, width*height*3 + 18 );
648
649         Mem_Free(buffer);
650 }
651
652 void Image_WriteTGARGBA (char *filename, int width, int height, byte *data)
653 {
654         int y;
655         byte *buffer, *in, *out, *end;
656
657         buffer = Mem_Alloc(tempmempool, width*height*4 + 18);
658
659         memset (buffer, 0, 18);
660         buffer[2] = 2;          // uncompressed type
661         buffer[12] = (width >> 0) & 0xFF;
662         buffer[13] = (width >> 8) & 0xFF;
663         buffer[14] = (height >> 0) & 0xFF;
664         buffer[15] = (height >> 8) & 0xFF;
665         buffer[16] = 32;        // pixel size
666
667         // swap rgba to bgra and flip upside down
668         out = buffer + 18;
669         for (y = height - 1;y >= 0;y--)
670         {
671                 in = data + y * width * 4;
672                 end = in + width * 4;
673                 for (;in < end;in += 4)
674                 {
675                         *out++ = in[2];
676                         *out++ = in[1];
677                         *out++ = in[0];
678                         *out++ = in[3];
679                 }
680         }
681         COM_WriteFile (filename, buffer, width*height*4 + 18 );
682
683         Mem_Free(buffer);
684 }
685
686 qboolean Image_CheckAlpha(byte *data, int size, qboolean rgba)
687 {
688         byte *end;
689         if (rgba)
690         {
691                 // check alpha bytes
692                 for (end = data + size * 4, data += 3;data < end;data += 4)
693                         if (*data < 255)
694                                 return 1;
695         }
696         else
697         {
698                 // color 255 is transparent
699                 for (end = data + size;data < end;data++)
700                         if (*data == 255)
701                                 return 1;
702         }
703         return 0;
704 }