]> icculus.org git repositories - divverent/darkplaces.git/blob - image.c
fix polygon distance checks (the radius comparison was completely wrong)
[divverent/darkplaces.git] / image.c
1
2 #include "quakedef.h"
3 #include "image.h"
4
5 int             image_width;
6 int             image_height;
7
8 void Image_GammaRemapRGB(const qbyte *in, qbyte *out, int pixels, const qbyte *gammar, const qbyte *gammag, const qbyte *gammab)
9 {
10         while (pixels--)
11         {
12                 out[0] = gammar[in[0]];
13                 out[1] = gammag[in[1]];
14                 out[2] = gammab[in[2]];
15                 in += 3;
16                 out += 3;
17         }
18 }
19
20 // note: pal must be 32bit color
21 void Image_Copy8bitRGBA(const qbyte *in, qbyte *out, int pixels, const unsigned int *pal)
22 {
23         int *iout = (void *)out;
24         while (pixels >= 8)
25         {
26                 iout[0] = pal[in[0]];
27                 iout[1] = pal[in[1]];
28                 iout[2] = pal[in[2]];
29                 iout[3] = pal[in[3]];
30                 iout[4] = pal[in[4]];
31                 iout[5] = pal[in[5]];
32                 iout[6] = pal[in[6]];
33                 iout[7] = pal[in[7]];
34                 in += 8;
35                 iout += 8;
36                 pixels -= 8;
37         }
38         if (pixels & 4)
39         {
40                 iout[0] = pal[in[0]];
41                 iout[1] = pal[in[1]];
42                 iout[2] = pal[in[2]];
43                 iout[3] = pal[in[3]];
44                 in += 4;
45                 iout += 4;
46         }
47         if (pixels & 2)
48         {
49                 iout[0] = pal[in[0]];
50                 iout[1] = pal[in[1]];
51                 in += 2;
52                 iout += 2;
53         }
54         if (pixels & 1)
55                 iout[0] = pal[in[0]];
56 }
57
58 /*
59 =================================================================
60
61   PCX Loading
62
63 =================================================================
64 */
65
66 typedef struct
67 {
68     char        manufacturer;
69     char        version;
70     char        encoding;
71     char        bits_per_pixel;
72     unsigned short      xmin,ymin,xmax,ymax;
73     unsigned short      hres,vres;
74     unsigned char       palette[48];
75     char        reserved;
76     char        color_planes;
77     unsigned short      bytes_per_line;
78     unsigned short      palette_type;
79     char        filler[58];
80 } pcx_t;
81
82 /*
83 ============
84 LoadPCX
85 ============
86 */
87 qbyte* LoadPCX (const qbyte *f, int matchwidth, int matchheight)
88 {
89         pcx_t pcx;
90         qbyte *a, *b, *image_rgba, *pbuf;
91         const qbyte *palette, *fin, *enddata;
92         int x, y, x2, dataByte;
93
94         if (loadsize < (int)sizeof(pcx) + 768)
95         {
96                 Con_Printf ("Bad pcx file\n");
97                 return NULL;
98         }
99
100         fin = f;
101
102         memcpy(&pcx, fin, sizeof(pcx));
103         fin += sizeof(pcx);
104
105         // LordHavoc: big-endian support ported from QF newtree
106         pcx.xmax = LittleShort (pcx.xmax);
107         pcx.xmin = LittleShort (pcx.xmin);
108         pcx.ymax = LittleShort (pcx.ymax);
109         pcx.ymin = LittleShort (pcx.ymin);
110         pcx.hres = LittleShort (pcx.hres);
111         pcx.vres = LittleShort (pcx.vres);
112         pcx.bytes_per_line = LittleShort (pcx.bytes_per_line);
113         pcx.palette_type = LittleShort (pcx.palette_type);
114
115         if (pcx.manufacturer != 0x0a || pcx.version != 5 || pcx.encoding != 1 || pcx.bits_per_pixel != 8 || pcx.xmax > 320 || pcx.ymax > 256)
116         {
117                 Con_Printf ("Bad pcx file\n");
118                 return NULL;
119         }
120
121         if (matchwidth && (pcx.xmax+1) != matchwidth)
122         {
123                 return NULL;
124         }
125         if (matchheight && (pcx.ymax+1) != matchheight)
126         {
127                 return NULL;
128         }
129
130         image_width = pcx.xmax+1;
131         image_height = pcx.ymax+1;
132
133         palette = f + loadsize - 768;
134
135         image_rgba = Mem_Alloc(tempmempool, image_width*image_height*4);
136         if (!image_rgba)
137         {
138                 Con_Printf("LoadPCX: not enough memory for %i by %i image\n", image_width, image_height);
139                 return NULL;
140         }
141         pbuf = image_rgba + image_width*image_height*3;
142         enddata = palette;
143
144         for (y = 0;y < image_height && fin < enddata;y++)
145         {
146                 a = pbuf + y * image_width;
147                 for (x = 0;x < image_width && fin < enddata;)
148                 {
149                         dataByte = *fin++;
150                         if(dataByte >= 0xC0)
151                         {
152                                 if (fin >= enddata)
153                                         break;
154                                 x2 = x + (dataByte & 0x3F);
155                                 dataByte = *fin++;
156                                 if (x2 > image_width)
157                                         x2 = image_width; // technically an error
158                                 while(x < x2)
159                                         a[x++] = dataByte;
160                         }
161                         else
162                                 a[x++] = dataByte;
163                 }
164                 fin += pcx.bytes_per_line - image_width; // the number of bytes per line is always forced to an even number
165                 while(x < image_width)
166                         a[x++] = 0;
167         }
168
169         a = image_rgba;
170         b = pbuf;
171
172         for(x = 0;x < image_width*image_height;x++)
173         {
174                 y = *b++ * 3;
175                 *a++ = palette[y];
176                 *a++ = palette[y+1];
177                 *a++ = palette[y+2];
178                 *a++ = 255;
179         }
180
181         return image_rgba;
182 }
183
184 /*
185 =========================================================
186
187 TARGA LOADING
188
189 =========================================================
190 */
191
192 typedef struct _TargaHeader
193 {
194         unsigned char   id_length, colormap_type, image_type;
195         unsigned short  colormap_index, colormap_length;
196         unsigned char   colormap_size;
197         unsigned short  x_origin, y_origin, width, height;
198         unsigned char   pixel_size, attributes;
199 }
200 TargaHeader;
201
202 TargaHeader             targa_header;
203
204
205 /*
206 =============
207 LoadTGA
208 =============
209 */
210 qbyte *LoadTGA (const qbyte *f, int matchwidth, int matchheight)
211 {
212         int x, y, row_inc;
213         unsigned char red, green, blue, alpha, run, runlen;
214         qbyte *pixbuf, *image_rgba;
215         const qbyte *fin, *enddata;
216
217         if (loadsize < 18+3)
218                 return NULL;
219         targa_header.id_length = f[0];
220         targa_header.colormap_type = f[1];
221         targa_header.image_type = f[2];
222
223         targa_header.colormap_index = f[3] + f[4] * 256;
224         targa_header.colormap_length = f[5] + f[6] * 256;
225         targa_header.colormap_size = f[7];
226         targa_header.x_origin = f[8] + f[9] * 256;
227         targa_header.y_origin = f[10] + f[11] * 256;
228         targa_header.width = f[12] + f[13] * 256;
229         targa_header.height = f[14] + f[15] * 256;
230         if (matchwidth && targa_header.width != matchwidth)
231                 return NULL;
232         if (matchheight && targa_header.height != matchheight)
233                 return NULL;
234         targa_header.pixel_size = f[16];
235         targa_header.attributes = f[17];
236
237         if (targa_header.image_type != 2 && targa_header.image_type != 10)
238         {
239                 Con_Printf ("LoadTGA: Only type 2 and 10 targa RGB images supported\n");
240                 return NULL;
241         }
242
243         if (targa_header.colormap_type != 0     || (targa_header.pixel_size != 32 && targa_header.pixel_size != 24))
244         {
245                 Con_Printf ("LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n");
246                 return NULL;
247         }
248
249         enddata = f + loadsize;
250
251         image_width = targa_header.width;
252         image_height = targa_header.height;
253
254         image_rgba = Mem_Alloc(tempmempool, image_width * image_height * 4);
255         if (!image_rgba)
256         {
257                 Con_Printf ("LoadTGA: not enough memory for %i by %i image\n", image_width, image_height);
258                 return NULL;
259         }
260
261         fin = f + 18;
262         if (targa_header.id_length != 0)
263                 fin += targa_header.id_length;  // skip TARGA image comment
264
265         // If bit 5 of attributes isn't set, the image has been stored from bottom to top
266         if ((targa_header.attributes & 0x20) == 0)
267         {
268                 pixbuf = image_rgba + (image_height - 1)*image_width*4;
269                 row_inc = -image_width*4*2;
270         }
271         else
272         {
273                 pixbuf = image_rgba;
274                 row_inc = 0;
275         }
276
277         if (targa_header.image_type == 2)
278         {
279                 // Uncompressed, RGB images
280                 if (targa_header.pixel_size == 24)
281                 {
282                         if (fin + image_width * image_height * 3 <= enddata)
283                         {
284                                 for(y = 0;y < image_height;y++)
285                                 {
286                                         for(x = 0;x < image_width;x++)
287                                         {
288                                                 *pixbuf++ = fin[2];
289                                                 *pixbuf++ = fin[1];
290                                                 *pixbuf++ = fin[0];
291                                                 *pixbuf++ = 255;
292                                                 fin += 3;
293                                         }
294                                         pixbuf += row_inc;
295                                 }
296                         }
297                 }
298                 else
299                 {
300                         if (fin + image_width * image_height * 4 <= enddata)
301                         {
302                                 for(y = 0;y < image_height;y++)
303                                 {
304                                         for(x = 0;x < image_width;x++)
305                                         {
306                                                 *pixbuf++ = fin[2];
307                                                 *pixbuf++ = fin[1];
308                                                 *pixbuf++ = fin[0];
309                                                 *pixbuf++ = fin[3];
310                                                 fin += 4;
311                                         }
312                                         pixbuf += row_inc;
313                                 }
314                         }
315                 }
316         }
317         else if (targa_header.image_type==10)
318         {
319                 // Runlength encoded RGB images
320                 x = 0;
321                 y = 0;
322                 while (y < image_height && fin < enddata)
323                 {
324                         runlen = *fin++;
325                         if (runlen & 0x80)
326                         {
327                                 // RLE compressed run
328                                 runlen = 1 + (runlen & 0x7f);
329                                 if (targa_header.pixel_size == 24)
330                                 {
331                                         if (fin + 3 > enddata)
332                                                 break;
333                                         blue = *fin++;
334                                         green = *fin++;
335                                         red = *fin++;
336                                         alpha = 255;
337                                 }
338                                 else
339                                 {
340                                         if (fin + 4 > enddata)
341                                                 break;
342                                         blue = *fin++;
343                                         green = *fin++;
344                                         red = *fin++;
345                                         alpha = *fin++;
346                                 }
347
348                                 while (runlen && y < image_height)
349                                 {
350                                         run = runlen;
351                                         if (run > image_width - x)
352                                                 run = image_width - x;
353                                         x += run;
354                                         runlen -= run;
355                                         while(run--)
356                                         {
357                                                 *pixbuf++ = red;
358                                                 *pixbuf++ = green;
359                                                 *pixbuf++ = blue;
360                                                 *pixbuf++ = alpha;
361                                         }
362                                         if (x == image_width)
363                                         {
364                                                 // end of line, advance to next
365                                                 x = 0;
366                                                 y++;
367                                                 pixbuf += row_inc;
368                                         }
369                                 }
370                         }
371                         else
372                         {
373                                 // RLE uncompressed run
374                                 runlen = 1 + (runlen & 0x7f);
375                                 while (runlen && y < image_height)
376                                 {
377                                         run = runlen;
378                                         if (run > image_width - x)
379                                                 run = image_width - x;
380                                         x += run;
381                                         runlen -= run;
382                                         if (targa_header.pixel_size == 24)
383                                         {
384                                                 if (fin + run * 3 > enddata)
385                                                         break;
386                                                 while(run--)
387                                                 {
388                                                         *pixbuf++ = fin[2];
389                                                         *pixbuf++ = fin[1];
390                                                         *pixbuf++ = fin[0];
391                                                         *pixbuf++ = 255;
392                                                         fin += 3;
393                                                 }
394                                         }
395                                         else
396                                         {
397                                                 if (fin + run * 4 > enddata)
398                                                         break;
399                                                 while(run--)
400                                                 {
401                                                         *pixbuf++ = fin[2];
402                                                         *pixbuf++ = fin[1];
403                                                         *pixbuf++ = fin[0];
404                                                         *pixbuf++ = fin[3];
405                                                         fin += 4;
406                                                 }
407                                         }
408                                         if (x == image_width)
409                                         {
410                                                 // end of line, advance to next
411                                                 x = 0;
412                                                 y++;
413                                                 pixbuf += row_inc;
414                                         }
415                                 }
416                         }
417                 }
418         }
419         return image_rgba;
420 }
421
422 /*
423 ============
424 LoadLMP
425 ============
426 */
427 qbyte *LoadLMP (const qbyte *f, int matchwidth, int matchheight)
428 {
429         qbyte *image_rgba;
430         int width, height;
431
432         if (loadsize < 9)
433         {
434                 Con_Printf("LoadLMP: invalid LMP file\n");
435                 return NULL;
436         }
437
438         // parse the very complicated header *chuckle*
439         width = f[0] + f[1] * 256 + f[2] * 65536 + f[3] * 16777216;
440         height = f[4] + f[5] * 256 + f[6] * 65536 + f[7] * 16777216;
441         if ((unsigned) width > 4096 || (unsigned) height > 4096)
442         {
443                 Con_Printf("LoadLMP: invalid size\n");
444                 return NULL;
445         }
446         if ((matchwidth && width != matchwidth) || (matchheight && height != matchheight))
447                 return NULL;
448
449         if (loadsize < 8 + width * height)
450         {
451                 Con_Printf("LoadLMP: invalid LMP file\n");
452                 return NULL;
453         }
454
455         image_width = width;
456         image_height = height;
457
458         image_rgba = Mem_Alloc(tempmempool, image_width * image_height * 4);
459         if (!image_rgba)
460         {
461                 Con_Printf("LoadLMP: not enough memory for %i by %i image\n", image_width, image_height);
462                 return NULL;
463         }
464         Image_Copy8bitRGBA(f + 8, image_rgba, image_width * image_height, palette_complete);
465         return image_rgba;
466 }
467
468 /*
469 ============
470 LoadLMP
471 ============
472 */
473 qbyte *LoadLMPAs8Bit (const qbyte *f, int matchwidth, int matchheight)
474 {
475         qbyte *image_8bit;
476         int width, height;
477
478         if (loadsize < 9)
479         {
480                 Con_Printf("LoadLMPAs8Bit: invalid LMP file\n");
481                 return NULL;
482         }
483
484         // parse the very complicated header *chuckle*
485         width = f[0] + f[1] * 256 + f[2] * 65536 + f[3] * 16777216;
486         height = f[4] + f[5] * 256 + f[6] * 65536 + f[7] * 16777216;
487         if ((unsigned) width > 4096 || (unsigned) height > 4096)
488         {
489                 Con_Printf("LoadLMPAs8Bit: invalid size\n");
490                 return NULL;
491         }
492         if ((matchwidth && width != matchwidth) || (matchheight && height != matchheight))
493                 return NULL;
494
495         if (loadsize < 8 + width * height)
496         {
497                 Con_Printf("LoadLMPAs8Bit: invalid LMP file\n");
498                 return NULL;
499         }
500
501         image_width = width;
502         image_height = height;
503
504         image_8bit = Mem_Alloc(tempmempool, image_width * image_height);
505         if (!image_8bit)
506         {
507                 Con_Printf("LoadLMPAs8Bit: not enough memory for %i by %i image\n", image_width, image_height);
508                 return NULL;
509         }
510         memcpy(image_8bit, f + 8, image_width * image_height);
511         return image_8bit;
512 }
513
514 void Image_StripImageExtension (const char *in, char *out)
515 {
516         const char *end, *temp;
517         end = in + strlen(in);
518         if ((end - in) >= 4)
519         {
520                 temp = end - 4;
521                 if (strcmp(temp, ".tga") == 0 || strcmp(temp, ".pcx") == 0 || strcmp(temp, ".lmp") == 0)
522                         end = temp;
523                 while (in < end)
524                         *out++ = *in++;
525                 *out++ = 0;
526         }
527         else
528                 strcpy(out, in);
529 }
530
531 qbyte *loadimagepixels (const char *filename, qboolean complain, int matchwidth, int matchheight)
532 {
533         qbyte *f, *data;
534         char basename[256], name[256], *c;
535         Image_StripImageExtension(filename, basename); // strip .tga, .pcx and .lmp extensions to allow replacement by other types
536         // replace *'s with #, so commandline utils don't get confused when dealing with the external files
537         for (c = basename;*c;c++)
538                 if (*c == '*')
539                         *c = '#';
540         sprintf (name, "override/%s.tga", basename);
541         f = COM_LoadFile(name, true);
542         if (f)
543         {
544                 data = LoadTGA (f, matchwidth, matchheight);
545                 Mem_Free(f);
546                 return data;
547         }
548         sprintf (name, "textures/%s.tga", basename);
549         f = COM_LoadFile(name, true);
550         if (f)
551         {
552                 data = LoadTGA (f, matchwidth, matchheight);
553                 Mem_Free(f);
554                 return data;
555         }
556         sprintf (name, "textures/%s.pcx", basename);
557         f = COM_LoadFile(name, true);
558         if (f)
559         {
560                 data = LoadPCX (f, matchwidth, matchheight);
561                 Mem_Free(f);
562                 return data;
563         }
564         sprintf (name, "%s.tga", basename);
565         f = COM_LoadFile(name, true);
566         if (f)
567         {
568                 data = LoadTGA (f, matchwidth, matchheight);
569                 Mem_Free(f);
570                 return data;
571         }
572         sprintf (name, "%s.pcx", basename);
573         f = COM_LoadFile(name, true);
574         if (f)
575         {
576                 data = LoadPCX (f, matchwidth, matchheight);
577                 Mem_Free(f);
578                 return data;
579         }
580         sprintf (name, "%s.lmp", basename);
581         f = COM_LoadFile(name, true);
582         if (f)
583         {
584                 data = LoadLMP (f, matchwidth, matchheight);
585                 Mem_Free(f);
586                 return data;
587         }
588         if (complain)
589                 Con_Printf ("Couldn't load %s.tga, .pcx, .lmp\n", filename);
590         return NULL;
591 }
592
593 int image_makemask (const qbyte *in, qbyte *out, int size)
594 {
595         int i, count;
596         count = 0;
597         for (i = 0;i < size;i++)
598         {
599                 out[0] = out[1] = out[2] = 255;
600                 out[3] = in[3];
601                 if (in[3] != 255)
602                         count++;
603                 in += 4;
604                 out += 4;
605         }
606         return count;
607 }
608
609 qbyte* loadimagepixelsmask (const char *filename, qboolean complain, int matchwidth, int matchheight)
610 {
611         qbyte *in, *data;
612         in = data = loadimagepixels(filename, complain, matchwidth, matchheight);
613         if (!data)
614                 return NULL;
615         if (image_makemask(data, data, image_width * image_height))
616                 return data; // some transparency
617         else
618         {
619                 Mem_Free(data);
620                 return NULL; // all opaque
621         }
622 }
623
624 rtexture_t *loadtextureimage (rtexturepool_t *pool, const char *filename, int matchwidth, int matchheight, qboolean complain, int flags)
625 {
626         qbyte *data;
627         rtexture_t *rt;
628         if (!(data = loadimagepixels (filename, complain, matchwidth, matchheight)))
629                 return 0;
630         rt = R_LoadTexture2D(pool, filename, image_width, image_height, data, TEXTYPE_RGBA, flags, NULL);
631         Mem_Free(data);
632         return rt;
633 }
634
635 rtexture_t *loadtextureimagemask (rtexturepool_t *pool, const char *filename, int matchwidth, int matchheight, qboolean complain, int flags)
636 {
637         qbyte *data;
638         rtexture_t *rt;
639         if (!(data = loadimagepixelsmask (filename, complain, matchwidth, matchheight)))
640                 return 0;
641         rt = R_LoadTexture2D(pool, filename, image_width, image_height, data, TEXTYPE_RGBA, flags, NULL);
642         Mem_Free(data);
643         return rt;
644 }
645
646 rtexture_t *image_masktex;
647 rtexture_t *image_nmaptex;
648 rtexture_t *loadtextureimagewithmask (rtexturepool_t *pool, const char *filename, int matchwidth, int matchheight, qboolean complain, int flags)
649 {
650         qbyte *data;
651         rtexture_t *rt;
652         image_masktex = NULL;
653         image_nmaptex = NULL;
654         if (!(data = loadimagepixels (filename, complain, matchwidth, matchheight)))
655                 return 0;
656
657         rt = R_LoadTexture2D(pool, filename, image_width, image_height, data, TEXTYPE_RGBA, flags, NULL);
658
659         if (flags & TEXF_ALPHA && image_makemask(data, data, image_width * image_height))
660                 image_masktex = R_LoadTexture2D(pool, va("%s_mask", filename), image_width, image_height, data, TEXTYPE_RGBA, flags, NULL);
661
662         Mem_Free(data);
663         return rt;
664 }
665
666 rtexture_t *loadtextureimagewithmaskandnmap (rtexturepool_t *pool, const char *filename, int matchwidth, int matchheight, qboolean complain, int flags, float bumpscale)
667 {
668         qbyte *data, *data2;
669         rtexture_t *rt;
670         image_masktex = NULL;
671         image_nmaptex = NULL;
672         if (!(data = loadimagepixels (filename, complain, matchwidth, matchheight)))
673                 return 0;
674
675         data2 = Mem_Alloc(tempmempool, image_width * image_height * 4);
676
677         rt = R_LoadTexture2D(pool, filename, image_width, image_height, data, TEXTYPE_RGBA, flags, NULL);
678
679         Image_HeightmapToNormalmap(data, data2, image_width, image_height, (flags & TEXF_CLAMP) != 0, bumpscale);
680         image_nmaptex = R_LoadTexture2D(pool, va("%s_nmap", filename), image_width, image_height, data2, TEXTYPE_RGBA, flags, NULL);
681
682         if (flags & TEXF_ALPHA && image_makemask(data, data2, image_width * image_height))
683                 image_masktex = R_LoadTexture2D(pool, va("%s_mask", filename), image_width, image_height, data2, TEXTYPE_RGBA, flags, NULL);
684
685         Mem_Free(data2);
686
687         Mem_Free(data);
688         return rt;
689 }
690
691 qboolean Image_WriteTGARGB_preflipped (const char *filename, int width, int height, const qbyte *data)
692 {
693         qboolean ret;
694         qbyte *buffer, *out;
695         const qbyte *in, *end;
696
697         buffer = Mem_Alloc(tempmempool, width*height*3 + 18);
698
699         memset (buffer, 0, 18);
700         buffer[2] = 2;          // uncompressed type
701         buffer[12] = (width >> 0) & 0xFF;
702         buffer[13] = (width >> 8) & 0xFF;
703         buffer[14] = (height >> 0) & 0xFF;
704         buffer[15] = (height >> 8) & 0xFF;
705         buffer[16] = 24;        // pixel size
706
707         // swap rgb to bgr
708         in = data;
709         out = buffer + 18;
710         end = in + width*height*3;
711         for (;in < end;in += 3)
712         {
713                 *out++ = in[2];
714                 *out++ = in[1];
715                 *out++ = in[0];
716         }
717         ret = COM_WriteFile (filename, buffer, width*height*3 + 18 );
718
719         Mem_Free(buffer);
720         return ret;
721 }
722
723 void Image_WriteTGARGB (const char *filename, int width, int height, const qbyte *data)
724 {
725         int y;
726         qbyte *buffer, *out;
727         const qbyte *in, *end;
728
729         buffer = Mem_Alloc(tempmempool, width*height*3 + 18);
730
731         memset (buffer, 0, 18);
732         buffer[2] = 2;          // uncompressed type
733         buffer[12] = (width >> 0) & 0xFF;
734         buffer[13] = (width >> 8) & 0xFF;
735         buffer[14] = (height >> 0) & 0xFF;
736         buffer[15] = (height >> 8) & 0xFF;
737         buffer[16] = 24;        // pixel size
738
739         // swap rgb to bgr and flip upside down
740         out = buffer + 18;
741         for (y = height - 1;y >= 0;y--)
742         {
743                 in = data + y * width * 3;
744                 end = in + width * 3;
745                 for (;in < end;in += 3)
746                 {
747                         *out++ = in[2];
748                         *out++ = in[1];
749                         *out++ = in[0];
750                 }
751         }
752         COM_WriteFile (filename, buffer, width*height*3 + 18 );
753
754         Mem_Free(buffer);
755 }
756
757 void Image_WriteTGARGBA (const char *filename, int width, int height, const qbyte *data)
758 {
759         int y;
760         qbyte *buffer, *out;
761         const qbyte *in, *end;
762
763         buffer = Mem_Alloc(tempmempool, width*height*4 + 18);
764
765         memset (buffer, 0, 18);
766         buffer[2] = 2;          // uncompressed type
767         buffer[12] = (width >> 0) & 0xFF;
768         buffer[13] = (width >> 8) & 0xFF;
769         buffer[14] = (height >> 0) & 0xFF;
770         buffer[15] = (height >> 8) & 0xFF;
771         buffer[16] = 32;        // pixel size
772
773         // swap rgba to bgra and flip upside down
774         out = buffer + 18;
775         for (y = height - 1;y >= 0;y--)
776         {
777                 in = data + y * width * 4;
778                 end = in + width * 4;
779                 for (;in < end;in += 4)
780                 {
781                         *out++ = in[2];
782                         *out++ = in[1];
783                         *out++ = in[0];
784                         *out++ = in[3];
785                 }
786         }
787         COM_WriteFile (filename, buffer, width*height*4 + 18 );
788
789         Mem_Free(buffer);
790 }
791
792 qboolean Image_CheckAlpha(const qbyte *data, int size, qboolean rgba)
793 {
794         const qbyte *end;
795         if (rgba)
796         {
797                 // check alpha bytes
798                 for (end = data + size * 4, data += 3;data < end;data += 4)
799                         if (*data < 255)
800                                 return 1;
801         }
802         else
803         {
804                 // color 255 is transparent
805                 for (end = data + size;data < end;data++)
806                         if (*data == 255)
807                                 return 1;
808         }
809         return 0;
810 }
811
812 static void Image_Resample32LerpLine (const qbyte *in, qbyte *out, int inwidth, int outwidth)
813 {
814         int             j, xi, oldx = 0, f, fstep, endx, lerp;
815         fstep = (int) (inwidth*65536.0f/outwidth);
816         endx = (inwidth-1);
817         for (j = 0,f = 0;j < outwidth;j++, f += fstep)
818         {
819                 xi = f >> 16;
820                 if (xi != oldx)
821                 {
822                         in += (xi - oldx) * 4;
823                         oldx = xi;
824                 }
825                 if (xi < endx)
826                 {
827                         lerp = f & 0xFFFF;
828                         *out++ = (qbyte) ((((in[4] - in[0]) * lerp) >> 16) + in[0]);
829                         *out++ = (qbyte) ((((in[5] - in[1]) * lerp) >> 16) + in[1]);
830                         *out++ = (qbyte) ((((in[6] - in[2]) * lerp) >> 16) + in[2]);
831                         *out++ = (qbyte) ((((in[7] - in[3]) * lerp) >> 16) + in[3]);
832                 }
833                 else // last pixel of the line has no pixel to lerp to
834                 {
835                         *out++ = in[0];
836                         *out++ = in[1];
837                         *out++ = in[2];
838                         *out++ = in[3];
839                 }
840         }
841 }
842
843 static void Image_Resample24LerpLine (const qbyte *in, qbyte *out, int inwidth, int outwidth)
844 {
845         int             j, xi, oldx = 0, f, fstep, endx, lerp;
846         fstep = (int) (inwidth*65536.0f/outwidth);
847         endx = (inwidth-1);
848         for (j = 0,f = 0;j < outwidth;j++, f += fstep)
849         {
850                 xi = f >> 16;
851                 if (xi != oldx)
852                 {
853                         in += (xi - oldx) * 3;
854                         oldx = xi;
855                 }
856                 if (xi < endx)
857                 {
858                         lerp = f & 0xFFFF;
859                         *out++ = (qbyte) ((((in[3] - in[0]) * lerp) >> 16) + in[0]);
860                         *out++ = (qbyte) ((((in[4] - in[1]) * lerp) >> 16) + in[1]);
861                         *out++ = (qbyte) ((((in[5] - in[2]) * lerp) >> 16) + in[2]);
862                 }
863                 else // last pixel of the line has no pixel to lerp to
864                 {
865                         *out++ = in[0];
866                         *out++ = in[1];
867                         *out++ = in[2];
868                 }
869         }
870 }
871
872 int resamplerowsize = 0;
873 qbyte *resamplerow1 = NULL;
874 qbyte *resamplerow2 = NULL;
875 mempool_t *resamplemempool = NULL;
876
877 #define LERPBYTE(i) r = resamplerow1[i];out[i] = (qbyte) ((((resamplerow2[i] - r) * lerp) >> 16) + r)
878 void Image_Resample32Lerp(const void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight)
879 {
880         int i, j, r, yi, oldy, f, fstep, lerp, endy = (inheight-1), inwidth4 = inwidth*4, outwidth4 = outwidth*4;
881         qbyte *out;
882         const qbyte *inrow;
883         out = outdata;
884         fstep = (int) (inheight*65536.0f/outheight);
885
886         inrow = indata;
887         oldy = 0;
888         Image_Resample32LerpLine (inrow, resamplerow1, inwidth, outwidth);
889         Image_Resample32LerpLine (inrow + inwidth4, resamplerow2, inwidth, outwidth);
890         for (i = 0, f = 0;i < outheight;i++,f += fstep)
891         {
892                 yi = f >> 16;
893                 if (yi < endy)
894                 {
895                         lerp = f & 0xFFFF;
896                         if (yi != oldy)
897                         {
898                                 inrow = (qbyte *)indata + inwidth4*yi;
899                                 if (yi == oldy+1)
900                                         memcpy(resamplerow1, resamplerow2, outwidth4);
901                                 else
902                                         Image_Resample32LerpLine (inrow, resamplerow1, inwidth, outwidth);
903                                 Image_Resample32LerpLine (inrow + inwidth4, resamplerow2, inwidth, outwidth);
904                                 oldy = yi;
905                         }
906                         j = outwidth - 4;
907                         while(j >= 0)
908                         {
909                                 LERPBYTE( 0);
910                                 LERPBYTE( 1);
911                                 LERPBYTE( 2);
912                                 LERPBYTE( 3);
913                                 LERPBYTE( 4);
914                                 LERPBYTE( 5);
915                                 LERPBYTE( 6);
916                                 LERPBYTE( 7);
917                                 LERPBYTE( 8);
918                                 LERPBYTE( 9);
919                                 LERPBYTE(10);
920                                 LERPBYTE(11);
921                                 LERPBYTE(12);
922                                 LERPBYTE(13);
923                                 LERPBYTE(14);
924                                 LERPBYTE(15);
925                                 out += 16;
926                                 resamplerow1 += 16;
927                                 resamplerow2 += 16;
928                                 j -= 4;
929                         }
930                         if (j & 2)
931                         {
932                                 LERPBYTE( 0);
933                                 LERPBYTE( 1);
934                                 LERPBYTE( 2);
935                                 LERPBYTE( 3);
936                                 LERPBYTE( 4);
937                                 LERPBYTE( 5);
938                                 LERPBYTE( 6);
939                                 LERPBYTE( 7);
940                                 out += 8;
941                                 resamplerow1 += 8;
942                                 resamplerow2 += 8;
943                         }
944                         if (j & 1)
945                         {
946                                 LERPBYTE( 0);
947                                 LERPBYTE( 1);
948                                 LERPBYTE( 2);
949                                 LERPBYTE( 3);
950                                 out += 4;
951                                 resamplerow1 += 4;
952                                 resamplerow2 += 4;
953                         }
954                         resamplerow1 -= outwidth4;
955                         resamplerow2 -= outwidth4;
956                 }
957                 else
958                 {
959                         if (yi != oldy)
960                         {
961                                 inrow = (qbyte *)indata + inwidth4*yi;
962                                 if (yi == oldy+1)
963                                         memcpy(resamplerow1, resamplerow2, outwidth4);
964                                 else
965                                         Image_Resample32LerpLine (inrow, resamplerow1, inwidth, outwidth);
966                                 oldy = yi;
967                         }
968                         memcpy(out, resamplerow1, outwidth4);
969                 }
970         }
971 }
972
973 void Image_Resample32Nearest(const void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight)
974 {
975         int i, j;
976         unsigned frac, fracstep;
977         // relies on int being 4 bytes
978         int *inrow, *out;
979         out = outdata;
980
981         fracstep = inwidth*0x10000/outwidth;
982         for (i = 0;i < outheight;i++)
983         {
984                 inrow = (int *)indata + inwidth*(i*inheight/outheight);
985                 frac = fracstep >> 1;
986                 j = outwidth - 4;
987                 while (j >= 0)
988                 {
989                         out[0] = inrow[frac >> 16];frac += fracstep;
990                         out[1] = inrow[frac >> 16];frac += fracstep;
991                         out[2] = inrow[frac >> 16];frac += fracstep;
992                         out[3] = inrow[frac >> 16];frac += fracstep;
993                         out += 4;
994                         j -= 4;
995                 }
996                 if (j & 2)
997                 {
998                         out[0] = inrow[frac >> 16];frac += fracstep;
999                         out[1] = inrow[frac >> 16];frac += fracstep;
1000                         out += 2;
1001                 }
1002                 if (j & 1)
1003                 {
1004                         out[0] = inrow[frac >> 16];frac += fracstep;
1005                         out += 1;
1006                 }
1007         }
1008 }
1009
1010 void Image_Resample24Lerp(const void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight)
1011 {
1012         int i, j, r, yi, oldy, f, fstep, lerp, endy = (inheight-1), inwidth3 = inwidth * 3, outwidth3 = outwidth * 3;
1013         qbyte *out;
1014         const qbyte *inrow;
1015         out = outdata;
1016         fstep = (int) (inheight*65536.0f/outheight);
1017
1018         inrow = indata;
1019         oldy = 0;
1020         Image_Resample24LerpLine (inrow, resamplerow1, inwidth, outwidth);
1021         Image_Resample24LerpLine (inrow + inwidth3, resamplerow2, inwidth, outwidth);
1022         for (i = 0, f = 0;i < outheight;i++,f += fstep)
1023         {
1024                 yi = f >> 16;
1025                 if (yi < endy)
1026                 {
1027                         lerp = f & 0xFFFF;
1028                         if (yi != oldy)
1029                         {
1030                                 inrow = (qbyte *)indata + inwidth3*yi;
1031                                 if (yi == oldy+1)
1032                                         memcpy(resamplerow1, resamplerow2, outwidth3);
1033                                 else
1034                                         Image_Resample24LerpLine (inrow, resamplerow1, inwidth, outwidth);
1035                                 Image_Resample24LerpLine (inrow + inwidth3, resamplerow2, inwidth, outwidth);
1036                                 oldy = yi;
1037                         }
1038                         j = outwidth - 4;
1039                         while(j >= 0)
1040                         {
1041                                 LERPBYTE( 0);
1042                                 LERPBYTE( 1);
1043                                 LERPBYTE( 2);
1044                                 LERPBYTE( 3);
1045                                 LERPBYTE( 4);
1046                                 LERPBYTE( 5);
1047                                 LERPBYTE( 6);
1048                                 LERPBYTE( 7);
1049                                 LERPBYTE( 8);
1050                                 LERPBYTE( 9);
1051                                 LERPBYTE(10);
1052                                 LERPBYTE(11);
1053                                 out += 12;
1054                                 resamplerow1 += 12;
1055                                 resamplerow2 += 12;
1056                                 j -= 4;
1057                         }
1058                         if (j & 2)
1059                         {
1060                                 LERPBYTE( 0);
1061                                 LERPBYTE( 1);
1062                                 LERPBYTE( 2);
1063                                 LERPBYTE( 3);
1064                                 LERPBYTE( 4);
1065                                 LERPBYTE( 5);
1066                                 out += 6;
1067                                 resamplerow1 += 6;
1068                                 resamplerow2 += 6;
1069                         }
1070                         if (j & 1)
1071                         {
1072                                 LERPBYTE( 0);
1073                                 LERPBYTE( 1);
1074                                 LERPBYTE( 2);
1075                                 out += 3;
1076                                 resamplerow1 += 3;
1077                                 resamplerow2 += 3;
1078                         }
1079                         resamplerow1 -= outwidth3;
1080                         resamplerow2 -= outwidth3;
1081                 }
1082                 else
1083                 {
1084                         if (yi != oldy)
1085                         {
1086                                 inrow = (qbyte *)indata + inwidth3*yi;
1087                                 if (yi == oldy+1)
1088                                         memcpy(resamplerow1, resamplerow2, outwidth3);
1089                                 else
1090                                         Image_Resample24LerpLine (inrow, resamplerow1, inwidth, outwidth);
1091                                 oldy = yi;
1092                         }
1093                         memcpy(out, resamplerow1, outwidth3);
1094                 }
1095         }
1096 }
1097
1098 void Image_Resample24Nolerp(const void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight)
1099 {
1100         int i, j, f, inwidth3 = inwidth * 3;
1101         unsigned frac, fracstep;
1102         qbyte *inrow, *out;
1103         out = outdata;
1104
1105         fracstep = inwidth*0x10000/outwidth;
1106         for (i = 0;i < outheight;i++)
1107         {
1108                 inrow = (qbyte *)indata + inwidth3*(i*inheight/outheight);
1109                 frac = fracstep >> 1;
1110                 j = outwidth - 4;
1111                 while (j >= 0)
1112                 {
1113                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1114                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1115                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1116                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1117                         j -= 4;
1118                 }
1119                 if (j & 2)
1120                 {
1121                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1122                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1123                         out += 2;
1124                 }
1125                 if (j & 1)
1126                 {
1127                         f = (frac >> 16)*3;*out++ = inrow[f+0];*out++ = inrow[f+1];*out++ = inrow[f+2];frac += fracstep;
1128                         out += 1;
1129                 }
1130         }
1131 }
1132
1133 /*
1134 ================
1135 Image_Resample
1136 ================
1137 */
1138 void Image_Resample (const void *indata, int inwidth, int inheight, int indepth, void *outdata, int outwidth, int outheight, int outdepth, int bytesperpixel, int quality)
1139 {
1140         if (indepth != 1 || outdepth != 1)
1141                 Sys_Error("Image_Resample: 3D resampling not supported\n");
1142         if (resamplerowsize < outwidth*4)
1143         {
1144                 if (resamplerow1)
1145                         Mem_Free(resamplerow1);
1146                 resamplerowsize = outwidth*4;
1147                 if (!resamplemempool)
1148                         resamplemempool = Mem_AllocPool("Image Scaling Buffer");
1149                 resamplerow1 = Mem_Alloc(resamplemempool, resamplerowsize*2);
1150                 resamplerow2 = resamplerow1 + resamplerowsize;
1151         }
1152         if (bytesperpixel == 4)
1153         {
1154                 if (quality)
1155                         Image_Resample32Lerp(indata, inwidth, inheight, outdata, outwidth, outheight);
1156                 else
1157                         Image_Resample32Nearest(indata, inwidth, inheight, outdata, outwidth, outheight);
1158         }
1159         else if (bytesperpixel == 3)
1160         {
1161                 if (quality)
1162                         Image_Resample24Lerp(indata, inwidth, inheight, outdata, outwidth, outheight);
1163                 else
1164                         Image_Resample24Nolerp(indata, inwidth, inheight, outdata, outwidth, outheight);
1165         }
1166         else
1167                 Sys_Error("Image_Resample: unsupported bytesperpixel %i\n", bytesperpixel);
1168 }
1169
1170 // in can be the same as out
1171 void Image_MipReduce(const qbyte *in, qbyte *out, int *width, int *height, int *depth, int destwidth, int destheight, int destdepth, int bytesperpixel)
1172 {
1173         int x, y, nextrow;
1174         if (*depth != 1 || destdepth != 1)
1175                 Sys_Error("Image_Resample: 3D resampling not supported\n");
1176         nextrow = *width * bytesperpixel;
1177         if (*width > destwidth)
1178         {
1179                 *width >>= 1;
1180                 if (*height > destheight)
1181                 {
1182                         // reduce both
1183                         *height >>= 1;
1184                         if (bytesperpixel == 4)
1185                         {
1186                                 for (y = 0;y < *height;y++)
1187                                 {
1188                                         for (x = 0;x < *width;x++)
1189                                         {
1190                                                 out[0] = (qbyte) ((in[0] + in[4] + in[nextrow  ] + in[nextrow+4]) >> 2);
1191                                                 out[1] = (qbyte) ((in[1] + in[5] + in[nextrow+1] + in[nextrow+5]) >> 2);
1192                                                 out[2] = (qbyte) ((in[2] + in[6] + in[nextrow+2] + in[nextrow+6]) >> 2);
1193                                                 out[3] = (qbyte) ((in[3] + in[7] + in[nextrow+3] + in[nextrow+7]) >> 2);
1194                                                 out += 4;
1195                                                 in += 8;
1196                                         }
1197                                         in += nextrow; // skip a line
1198                                 }
1199                         }
1200                         else if (bytesperpixel == 3)
1201                         {
1202                                 for (y = 0;y < *height;y++)
1203                                 {
1204                                         for (x = 0;x < *width;x++)
1205                                         {
1206                                                 out[0] = (qbyte) ((in[0] + in[3] + in[nextrow  ] + in[nextrow+3]) >> 2);
1207                                                 out[1] = (qbyte) ((in[1] + in[4] + in[nextrow+1] + in[nextrow+4]) >> 2);
1208                                                 out[2] = (qbyte) ((in[2] + in[5] + in[nextrow+2] + in[nextrow+5]) >> 2);
1209                                                 out += 3;
1210                                                 in += 6;
1211                                         }
1212                                         in += nextrow; // skip a line
1213                                 }
1214                         }
1215                         else
1216                                 Sys_Error("Image_MipReduce: unsupported bytesperpixel %i\n", bytesperpixel);
1217                 }
1218                 else
1219                 {
1220                         // reduce width
1221                         if (bytesperpixel == 4)
1222                         {
1223                                 for (y = 0;y < *height;y++)
1224                                 {
1225                                         for (x = 0;x < *width;x++)
1226                                         {
1227                                                 out[0] = (qbyte) ((in[0] + in[4]) >> 1);
1228                                                 out[1] = (qbyte) ((in[1] + in[5]) >> 1);
1229                                                 out[2] = (qbyte) ((in[2] + in[6]) >> 1);
1230                                                 out[3] = (qbyte) ((in[3] + in[7]) >> 1);
1231                                                 out += 4;
1232                                                 in += 8;
1233                                         }
1234                                 }
1235                         }
1236                         else if (bytesperpixel == 3)
1237                         {
1238                                 for (y = 0;y < *height;y++)
1239                                 {
1240                                         for (x = 0;x < *width;x++)
1241                                         {
1242                                                 out[0] = (qbyte) ((in[0] + in[3]) >> 1);
1243                                                 out[1] = (qbyte) ((in[1] + in[4]) >> 1);
1244                                                 out[2] = (qbyte) ((in[2] + in[5]) >> 1);
1245                                                 out += 3;
1246                                                 in += 6;
1247                                         }
1248                                 }
1249                         }
1250                         else
1251                                 Sys_Error("Image_MipReduce: unsupported bytesperpixel %i\n", bytesperpixel);
1252                 }
1253         }
1254         else
1255         {
1256                 if (*height > destheight)
1257                 {
1258                         // reduce height
1259                         *height >>= 1;
1260                         if (bytesperpixel == 4)
1261                         {
1262                                 for (y = 0;y < *height;y++)
1263                                 {
1264                                         for (x = 0;x < *width;x++)
1265                                         {
1266                                                 out[0] = (qbyte) ((in[0] + in[nextrow  ]) >> 1);
1267                                                 out[1] = (qbyte) ((in[1] + in[nextrow+1]) >> 1);
1268                                                 out[2] = (qbyte) ((in[2] + in[nextrow+2]) >> 1);
1269                                                 out[3] = (qbyte) ((in[3] + in[nextrow+3]) >> 1);
1270                                                 out += 4;
1271                                                 in += 4;
1272                                         }
1273                                         in += nextrow; // skip a line
1274                                 }
1275                         }
1276                         else if (bytesperpixel == 3)
1277                         {
1278                                 for (y = 0;y < *height;y++)
1279                                 {
1280                                         for (x = 0;x < *width;x++)
1281                                         {
1282                                                 out[0] = (qbyte) ((in[0] + in[nextrow  ]) >> 1);
1283                                                 out[1] = (qbyte) ((in[1] + in[nextrow+1]) >> 1);
1284                                                 out[2] = (qbyte) ((in[2] + in[nextrow+2]) >> 1);
1285                                                 out += 3;
1286                                                 in += 3;
1287                                         }
1288                                         in += nextrow; // skip a line
1289                                 }
1290                         }
1291                         else
1292                                 Sys_Error("Image_MipReduce: unsupported bytesperpixel %i\n", bytesperpixel);
1293                 }
1294                 else
1295                         Sys_Error("Image_MipReduce: desired size already achieved\n");
1296         }
1297 }
1298
1299 extern cvar_t r_shadow_bumpscale;
1300 void Image_HeightmapToNormalmap(const unsigned char *inpixels, unsigned char *outpixels, int width, int height, int clamp, float bumpscale)
1301 {
1302         int x, y;
1303         const unsigned char *p0, *p1, *p2;
1304         unsigned char *out;
1305         float iwidth, iheight, ibumpscale, n[3];
1306         iwidth = 1.0f / width;
1307         iheight = 1.0f / height;
1308         ibumpscale = (255.0f * 3.0f) / (bumpscale * r_shadow_bumpscale.value);
1309         out = outpixels;
1310         for (y = 0;y < height;y++)
1311         {
1312                 for (x = 0;x < width;x++)
1313                 {
1314                         p0 = inpixels + (y * width + x) * 4;
1315                         if (x == width - 1)
1316                         {
1317                                 if (clamp)
1318                                         p1 = inpixels + (y * width + x) * 4;
1319                                 else
1320                                         p1 = inpixels + (y * width) * 4;
1321                         }
1322                         else
1323                                 p1 = inpixels + (y * width + x + 1) * 4;
1324                         if (y == height - 1)
1325                         {
1326                                 if (clamp)
1327                                         p2 = inpixels + (y * width + x) * 4;
1328                                 else
1329                                         p2 = inpixels + x * 4;
1330                         }
1331                         else
1332                                 p2 = inpixels + ((y + 1) * width + x) * 4;
1333                         /*
1334                         dv[0][0] = iwidth;
1335                         dv[0][1] = 0;
1336                         dv[0][2] = ((p1[0] + p1[1] + p1[2]) * ibumpscale) - ((p0[0] + p0[1] + p0[2]) * ibumpscale);
1337                         dv[1][0] = 0;
1338                         dv[1][1] = iheight;
1339                         dv[1][2] = ((p2[0] + p2[1] + p2[2]) * ibumpscale) - ((p0[0] + p0[1] + p0[2]) * ibumpscale);
1340                         n[0] = dv[0][1]*dv[1][2]-dv[0][2]*dv[1][1];
1341                         n[1] = dv[0][2]*dv[1][0]-dv[0][0]*dv[1][2];
1342                         n[2] = dv[0][0]*dv[1][1]-dv[0][1]*dv[1][0];
1343                         */
1344                         n[0] = ((p0[0] + p0[1] + p0[2]) - (p1[0] + p1[1] + p1[2]));
1345                         n[1] = ((p0[0] + p0[1] + p0[2]) - (p2[0] + p2[1] + p2[2]));
1346                         n[2] = ibumpscale;
1347                         VectorNormalize(n);
1348                         out[0] = 128.0f + n[0] * 127.0f;
1349                         out[1] = 128.0f + n[1] * 127.0f;
1350                         out[2] = 128.0f + n[2] * 127.0f;
1351                         out[3] = 255;
1352                         out += 4;
1353                 }
1354         }
1355 }