1 /*
2  * Texture manipulation routines
3  *
4  * Copyright (c) Mark J. Kilgard, 1997.
5  * Code added in april 2003 by Erik Hofman
6  *
7  * This program is freely distributable without licensing fees
8  * and is provided without guarantee or warrantee expressed or
9  * implied. This program is -not- in the public domain.
10  *
11  * $Id$
12  */
13 
14 #include <simgear/compiler.h>
15 
16 #ifdef WIN32
17 # include <windows.h>
18 #endif
19 
20 #include <osg/Matrixf>
21 
22 #include <math.h>
23 #include <zlib.h>
24 
25 #include "texture.hxx"
26 #include "colours.h"
27 
28 
29 const char *FILE_OPEN_ERROR = "Unable to open file.";
30 const char *WRONG_COUNT = "Unsupported number of color channels.";
31 const char *NO_TEXTURE = "No texture data resident.";
32 const char *OUT_OF_MEMORY = "Out of memory.";
33 
34 
SGTexture()35 SGTexture::SGTexture()
36    : texture_id(0),
37      texture_data(0),
38      num_colors(3),
39      file(0)
40 {
41 }
42 
SGTexture(unsigned int width,unsigned int height)43 SGTexture::SGTexture(unsigned int width, unsigned int height)
44    : texture_id(0),
45      errstr("")
46 {
47     texture_data = new GLubyte[ width * height * 3 ];
48 }
49 
~SGTexture()50 SGTexture::~SGTexture()
51 {
52     delete[] texture_data;
53 
54     if ( texture_id != 0 ) {
55         free_id();
56     }
57 }
58 
59 void
bind()60 SGTexture::bind()
61 {
62     bool gen = false;
63     if (!texture_id) {
64 #ifdef GL_VERSION_1_1
65         glGenTextures(1, &texture_id);
66 
67 #elif GL_EXT_texture_object
68         glGenTexturesEXT(1, &texture_id);
69 #endif
70         gen = true;
71     }
72 
73 #ifdef GL_VERSION_1_1
74     glBindTexture(GL_TEXTURE_2D, texture_id);
75 
76 #elif GL_EXT_texture_object
77     glBindTextureEXT(GL_TEXTURE_2D, texture_id);
78 #endif
79 
80     if (gen) {
81         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
82         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
83         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
84         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
85     }
86 }
87 
88 /**
89  * A function to resize the OpenGL window which will be used by
90  * the dynamic texture generating routines.
91  *
92  * @param width The width of the new window
93  * @param height The height of the new window
94  */
95 void
resize(unsigned int width,unsigned int height)96 SGTexture::resize(unsigned int width, unsigned int height)
97 {
98     using namespace osg;
99     GLfloat aspect;
100 
101     // Make sure that we don't get a divide by zero exception
102     if (height == 0)
103         height = 1;
104 
105     // Set the viewport for the OpenGL window
106     glViewport(0, 0, width, height);
107 
108     // Calculate the aspect ratio of the window
109     aspect = width/height;
110 
111     // Go to the projection matrix, this gets modified by the perspective
112     // calulations
113     glMatrixMode(GL_PROJECTION);
114 
115     // Do the perspective calculations
116     Matrixf proj = Matrixf::perspective(45.0, aspect, 1.0, 400.0);
117     glLoadMatrix(proj.ptr());
118 
119     // Return to the modelview matrix
120     glMatrixMode(GL_MODELVIEW);
121 }
122 
123 /**
124  * A function to prepare the OpenGL state machine for dynamic
125  * texture generation.
126  *
127  * @param width The width of the texture
128  * @param height The height of the texture
129  */
130 void
prepare(unsigned int width,unsigned int height)131 SGTexture::prepare(unsigned int width, unsigned int height) {
132 
133     texture_width = width;
134     texture_height = height;
135 
136     // Resize the OpenGL window to the size of our dynamic texture
137     resize(texture_width, texture_height);
138 
139     glClearColor(0.0, 0.0, 0.0, 1.0);
140 }
141 
142 /**
143  * A function to generate the dynamic texture.
144  *
145  * The actual texture can be accessed by calling get_texture()
146  *
147  * @param width The width of the previous OpenGL window
148  * @param height The height of the previous OpenGL window
149  */
150 void
finish(unsigned int width,unsigned int height)151 SGTexture::finish(unsigned int width, unsigned int height) {
152     // If a texture hasn't been created then it gets created, and the contents
153     // of the frame buffer gets copied into it. If the texture has already been
154     // created then its contents just get updated.
155     bind();
156     if (!texture_data)
157     {
158       // Copies the contents of the frame buffer into the texture
159       glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0,
160                                       texture_width, texture_height, 0);
161 
162     } else {
163       // Copies the contents of the frame buffer into the texture
164       glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0,
165                                          texture_width, texture_height);
166     }
167 
168     // Set the OpenGL window back to its previous size
169     resize(width, height);
170 
171     // Clear the window back to black
172     glClearColor(0.0, 0.0, 0.0, 1.0);
173     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
174 }
175 
176 
177 void
read_alpha_texture(const char * name)178 SGTexture::read_alpha_texture(const char *name)
179 {
180     GLubyte *lptr;
181     SGTexture::ImageRec *image;
182     int y;
183 
184     delete[] texture_data;
185 
186     image = ImageOpen(name);
187     if(!image) {
188         errstr = FILE_OPEN_ERROR;
189         return;
190     }
191 
192     texture_width = image->xsize;
193     texture_height = image->ysize;
194 
195     // printf("image->zsize = %d\n", image->zsize);
196 
197     if (image->zsize != 1) {
198       ImageClose(image);
199       errstr = WRONG_COUNT;
200       return;
201     }
202 
203     texture_data = new GLubyte[ image->xsize * image->ysize ];
204     num_colors = 1;
205     if (!texture_data) {
206         errstr = NO_TEXTURE;
207         return;
208     }
209 
210     lptr = texture_data;
211     for(y=0; y<image->ysize; y++) {
212         ImageGetRow(image,lptr,y,0);
213         lptr += image->xsize;
214     }
215     ImageClose(image);
216 }
217 
218 void
read_rgb_texture(const char * name)219 SGTexture::read_rgb_texture(const char *name)
220 {
221     GLubyte *ptr;
222     GLubyte *rbuf, *gbuf, *bbuf;
223     SGTexture::ImageRec *image;
224     int y;
225 
226     delete[] texture_data;
227 
228     image = ImageOpen(name);
229     if(!image) {
230         errstr = FILE_OPEN_ERROR;
231         return;
232     }
233 
234     texture_width = image->xsize;
235     texture_height = image->ysize;
236     if (image->zsize < 1 || image->zsize > 4) {
237       ImageClose(image);
238       errstr = WRONG_COUNT;
239       return;
240     }
241 
242     num_colors = 3;
243     texture_data = new GLubyte[ image->xsize * image->ysize * num_colors ];
244     rbuf = new GLubyte[ image->xsize ];
245     gbuf = new GLubyte[ image->xsize ];
246     bbuf = new GLubyte[ image->xsize ];
247     if(!texture_data || !rbuf || !gbuf || !bbuf) {
248       delete[] texture_data;
249       delete[] rbuf;
250       delete[] gbuf;
251       delete[] bbuf;
252       errstr = OUT_OF_MEMORY;
253       return;
254     }
255 
256     ptr = texture_data;
257     for(y=0; y<image->ysize; y++) {
258         if(image->zsize == 4 || image->zsize == 3) {
259             ImageGetRow(image,rbuf,y,0);
260             ImageGetRow(image,gbuf,y,1);
261             ImageGetRow(image,bbuf,y,2);
262         } else {
263             ImageGetRow(image,rbuf,y,0);
264             memcpy(gbuf,rbuf,image->xsize);
265             memcpy(bbuf,rbuf,image->xsize);
266         }
267         rgbtorgb(rbuf,gbuf,bbuf,ptr,image->xsize);
268         ptr += (image->xsize * num_colors);
269     }
270 
271     ImageClose(image);
272     delete[] rbuf;
273     delete[] gbuf;
274     delete[] bbuf;
275 }
276 
277 
278 
279 void
read_rgba_texture(const char * name)280 SGTexture::read_rgba_texture(const char *name)
281 {
282     GLubyte *ptr;
283     GLubyte *rbuf, *gbuf, *bbuf, *abuf;
284     SGTexture::ImageRec *image;
285     int y;
286 
287     delete[] texture_data;
288 
289     image = ImageOpen(name);
290     if(!image) {
291         errstr = FILE_OPEN_ERROR;
292         return;
293     }
294 
295     texture_width = image->xsize;
296     texture_height = image->ysize;
297     if (image->zsize < 1 || image->zsize > 4) {
298       ImageClose(image);
299       errstr = WRONG_COUNT;
300       return;
301     }
302 
303     num_colors = 4;
304     texture_data = new GLubyte[ image->xsize * image->ysize * num_colors ];
305     rbuf = new GLubyte[ image->xsize ];
306     gbuf = new GLubyte[ image->xsize ];
307     bbuf = new GLubyte[ image->xsize ];
308     abuf = new GLubyte[ image->xsize ];
309     if(!texture_data || !rbuf || !gbuf || !bbuf || !abuf) {
310       delete[] texture_data;
311       delete[] rbuf;
312       delete[] gbuf;
313       delete[] bbuf;
314       delete[] abuf;
315       errstr = OUT_OF_MEMORY;
316       return;
317     }
318 
319     ptr = texture_data;
320     for(y=0; y<image->ysize; y++) {
321         if(image->zsize == 4) {
322             ImageGetRow(image,rbuf,y,0);
323             ImageGetRow(image,gbuf,y,1);
324             ImageGetRow(image,bbuf,y,2);
325             ImageGetRow(image,abuf,y,3);
326         } else if(image->zsize == 3) {
327             ImageGetRow(image,rbuf,y,0);
328             ImageGetRow(image,gbuf,y,1);
329             ImageGetRow(image,bbuf,y,2);
330             memset(abuf, 255, image->xsize);
331         } else if(image->zsize == 2) {
332             ImageGetRow(image,rbuf,y,0);
333             memcpy(gbuf,rbuf,image->xsize);
334             memcpy(bbuf,rbuf,image->xsize);
335             ImageGetRow(image,abuf,y,1);
336         } else {
337             ImageGetRow(image,rbuf,y,0);
338             memcpy(gbuf,rbuf,image->xsize);
339             memcpy(bbuf,rbuf,image->xsize);
340             memset(abuf, 255, image->xsize);
341         }
342         rgbatorgba(rbuf,gbuf,bbuf,abuf,ptr,image->xsize);
343         ptr += (image->xsize * num_colors);
344     }
345 
346     ImageClose(image);
347     delete[] rbuf;
348     delete[] gbuf;
349     delete[] bbuf;
350     delete[] abuf;
351 }
352 
353 void
read_raw_texture(const char * name)354 SGTexture::read_raw_texture(const char *name)
355 {
356     GLubyte *ptr;
357     SGTexture::ImageRec *image;
358     int y;
359 
360     delete[] texture_data;
361 
362     image = RawImageOpen(name);
363 
364     if(!image) {
365         errstr = FILE_OPEN_ERROR;
366         return;
367     }
368 
369     texture_width = 256;
370     texture_height = 256;
371 
372     texture_data = new GLubyte[ 256 * 256 * 3 ];
373     if(!texture_data) {
374       errstr = OUT_OF_MEMORY;
375       return;
376     }
377 
378     ptr = texture_data;
379     for(y=0; y<256; y++) {
380         gzread(image->file, ptr, 256*3);
381         ptr+=256*3;
382     }
383     ImageClose(image);
384 }
385 
386 void
read_r8_texture(const char * name)387 SGTexture::read_r8_texture(const char *name)
388 {
389     unsigned char c[1];
390     GLubyte *ptr;
391     SGTexture::ImageRec *image;
392     int xy;
393 
394     delete[] texture_data;
395 
396     //it wouldn't make sense to write a new function ...
397     image = RawImageOpen(name);
398 
399     if(!image) {
400         errstr = FILE_OPEN_ERROR;
401         return;
402     }
403 
404     texture_width = 256;
405     texture_height = 256;
406 
407     texture_data = new GLubyte [ 256 * 256 * 3 ];
408     if(!texture_data) {
409         errstr = OUT_OF_MEMORY;
410         return;
411     }
412 
413     ptr = texture_data;
414     for(xy=0; xy<(256*256); xy++) {
415         gzread(image->file, c, 1);
416 
417         //look in the table for the right colours
418         ptr[0]=msfs_colour[c[0]][0];
419         ptr[1]=msfs_colour[c[0]][1];
420         ptr[2]=msfs_colour[c[0]][2];
421 
422         ptr+=3;
423     }
424     ImageClose(image);
425 }
426 
427 
428 void
write_texture(const char * name)429 SGTexture::write_texture(const char *name) {
430    SGTexture::ImageRec *image = ImageWriteOpen(name);
431 
432    for (int c=0; c<num_colors; c++) {
433       GLubyte *ptr = texture_data + c;
434       for (int y=0; y<texture_height; y++) {
435          for (int x=0; x<texture_width; x++) {
436 	    image->tmp[x]=*ptr;
437             ptr = ptr + num_colors;
438          }
439          fwrite(image->tmp, 1, texture_width, file);
440       }
441    }
442 
443    ImageClose(image);
444 }
445 
446 
447 void
set_pixel(GLuint x,GLuint y,GLubyte * c)448 SGTexture::set_pixel(GLuint x, GLuint y, GLubyte *c)
449 {
450     if (!texture_data) {
451         errstr = NO_TEXTURE;
452         return;
453     }
454 
455     unsigned int pos = (x + y*texture_width) * num_colors;
456     memcpy(texture_data+pos, c, num_colors);
457 }
458 
459 
460 GLubyte *
get_pixel(GLuint x,GLuint y)461 SGTexture::get_pixel(GLuint x, GLuint y)
462 {
463     static GLubyte c[4] = {0, 0, 0, 0};
464 
465     if (!texture_data) {
466         errstr = NO_TEXTURE;
467         return c;
468     }
469 
470     unsigned int pos = (x + y*texture_width)*num_colors;
471     memcpy(c, texture_data + pos, num_colors);
472 
473     return c;
474 }
475 
476 SGTexture::ImageRec *
ImageOpen(const char * fileName)477 SGTexture::ImageOpen(const char *fileName)
478 {
479      union {
480        int testWord;
481        char testByte[4];
482      } endianTest;
483 
484     SGTexture::ImageRec *image;
485     int swapFlag;
486     int x;
487 
488     endianTest.testWord = 1;
489     if (endianTest.testByte[0] == 1) {
490         swapFlag = 1;
491     } else {
492         swapFlag = 0;
493     }
494 
495     image = new SGTexture::ImageRec;
496     memset(image, 0, sizeof(SGTexture::ImageRec));
497     if (image == 0) {
498         errstr = OUT_OF_MEMORY;
499         return 0;
500     }
501     if ((image->file = gzopen(fileName, "rb")) == 0) {
502       errstr = FILE_OPEN_ERROR;
503       return 0;
504     }
505 
506     gzread(image->file, image, 12);
507 
508     if (swapFlag) {
509         ConvertShort(&image->imagic, 6);
510     }
511 
512     image->tmp = new GLubyte[ image->xsize * 256 ];
513     if (image->tmp == 0) {
514         errstr = OUT_OF_MEMORY;
515         return 0;
516     }
517 
518     if ((image->type & 0xFF00) == 0x0100) {
519         x = image->ysize * image->zsize * (int) sizeof(unsigned);
520         image->rowStart = new unsigned[x];
521         image->rowSize = new int[x];
522         if (image->rowStart == 0 || image->rowSize == 0) {
523             errstr = OUT_OF_MEMORY;
524             return 0;
525         }
526         image->rleEnd = 512 + (2 * x);
527         gzseek(image->file, 512, SEEK_SET);
528         gzread(image->file, image->rowStart, x);
529         gzread(image->file, image->rowSize, x);
530         if (swapFlag) {
531             ConvertUint(image->rowStart, x/(int) sizeof(unsigned));
532             ConvertUint((unsigned *)image->rowSize, x/(int) sizeof(int));
533         }
534     }
535     return image;
536 }
537 
538 
539 void
ImageClose(SGTexture::ImageRec * image)540 SGTexture::ImageClose(SGTexture::ImageRec *image) {
541     if (image->file)  gzclose(image->file);
542     if (file) fclose(file);
543     delete[] image->tmp;
544     delete[] image->rowStart;
545     delete[] image->rowSize;
546     delete image;
547 }
548 
549 SGTexture::ImageRec *
RawImageOpen(const char * fileName)550 SGTexture::RawImageOpen(const char *fileName)
551 {
552      union {
553        int testWord;
554        char testByte[4];
555      } endianTest;
556 
557     SGTexture::ImageRec *image;
558     int swapFlag;
559 
560     endianTest.testWord = 1;
561     if (endianTest.testByte[0] == 1) {
562         swapFlag = 1;
563     } else {
564         swapFlag = 0;
565     }
566 
567     image = new SGTexture::ImageRec;
568     memset(image, 0, sizeof(SGTexture::ImageRec));
569     if (image == 0) {
570         errstr = OUT_OF_MEMORY;
571         return 0;
572     }
573     if ((image->file = gzopen(fileName, "rb")) == 0) {
574       errstr = FILE_OPEN_ERROR;
575       return 0;
576     }
577 
578     gzread(image->file, image, 12);
579 
580     if (swapFlag) {
581         ConvertShort(&image->imagic, 6);
582     }
583 
584 
585     //just allocate a pseudo value as I'm too lazy to change ImageClose()...
586     image->tmp = new GLubyte[1];
587 
588     if (image->tmp == 0) {
589         errstr = OUT_OF_MEMORY;
590         return 0;
591     }
592 
593     return image;
594 }
595 
596 SGTexture::ImageRec *
ImageWriteOpen(const char * fileName)597 SGTexture::ImageWriteOpen(const char *fileName)
598 {
599     union {
600         int testWord;
601         char testByte[4];
602     } endianTest;
603     ImageRec* image;
604     int swapFlag;
605     int x;
606 
607     endianTest.testWord = 1;
608     if (endianTest.testByte[0] == 1) {
609         swapFlag = 1;
610     } else {
611         swapFlag = 0;
612     }
613 
614     image = new SGTexture::ImageRec;
615     memset(image, 0, sizeof(SGTexture::ImageRec));
616     if (image == 0) {
617         errstr = OUT_OF_MEMORY;
618         return 0;
619     }
620     if ((file = fopen(fileName, "wb")) == 0) {
621         errstr = FILE_OPEN_ERROR;
622         return 0;
623     }
624 
625     image->imagic = 474;
626     image->type = 0x0001;
627     image->dim = (num_colors > 1) ? 3 : 2;
628     image->xsize = texture_width;
629     image->ysize = texture_height;
630     image->zsize = num_colors;
631 
632     if (swapFlag) {
633         ConvertShort(&image->imagic, 6);
634     }
635 
636     fwrite(image, 1, 12, file);
637     fseek(file, 512, SEEK_SET);
638 
639     image->tmp = new GLubyte[ image->xsize * 256 ];
640     if (image->tmp == 0) {
641         errstr = OUT_OF_MEMORY;
642         return 0;
643     }
644 
645     if ((image->type & 0xFF00) == 0x0100) {
646         x = image->ysize * image->zsize * (int) sizeof(unsigned);
647         image->rowStart = new unsigned[x];
648         image->rowSize = new int[x];
649         if (image->rowStart == 0 || image->rowSize == 0) {
650             errstr = OUT_OF_MEMORY;
651             return 0;
652         }
653         image->rleEnd = 512 + (2 * x);
654         fseek(file, 512, SEEK_SET);
655         fread(image->rowStart, 1, x, file);
656         fread(image->rowSize, 1, x, file);
657         if (swapFlag) {
658             ConvertUint(image->rowStart, x/(int) sizeof(unsigned));
659             ConvertUint((unsigned *)image->rowSize, x/(int) sizeof(int));
660         }
661     }
662 
663     return image;
664 
665 }
666 
667 void
ImageGetRow(SGTexture::ImageRec * image,GLubyte * buf,int y,int z)668 SGTexture::ImageGetRow(SGTexture::ImageRec *image, GLubyte *buf, int y, int z) {
669     GLubyte *iPtr, *oPtr, pixel;
670     int count;
671 
672     if ((image->type & 0xFF00) == 0x0100) {
673         gzseek(image->file, (long) image->rowStart[y+z*image->ysize], SEEK_SET);
674         int size = image->rowSize[y+z*image->ysize];
675         gzread(image->file, image->tmp, size);
676 
677         iPtr = image->tmp;
678         oPtr = buf;
679         for (GLubyte *limit = iPtr + size; iPtr < limit;) {
680             pixel = *iPtr++;
681             count = (int)(pixel & 0x7F);
682             if (!count) {
683                 errstr = WRONG_COUNT;
684                 return;
685             }
686             if (pixel & 0x80) {
687                 while (iPtr < limit && count--) {
688                     *oPtr++ = *iPtr++;
689                 }
690             } else if (iPtr < limit) {
691                 pixel = *iPtr++;
692                 while (count--) {
693                     *oPtr++ = pixel;
694                 }
695             }
696         }
697     } else {
698         gzseek(image->file, 512+(y*image->xsize)+(z*image->xsize*image->ysize),
699               SEEK_SET);
700         gzread(image->file, buf, image->xsize);
701     }
702 }
703 
704 void
ImagePutRow(SGTexture::ImageRec * image,GLubyte * buf,int y,int z)705 SGTexture::ImagePutRow(SGTexture::ImageRec *image, GLubyte *buf, int y, int z) {
706     GLubyte *iPtr, *oPtr, pixel;
707     int count;
708 
709     if ((image->type & 0xFF00) == 0x0100) {
710         fseek(file, (long) image->rowStart[y+z*image->ysize], SEEK_SET);
711         fread( image->tmp, 1, (unsigned int)image->rowSize[y+z*image->ysize],
712                file);
713 
714         iPtr = image->tmp;
715         oPtr = buf;
716         for (;;) {
717             pixel = *iPtr++;
718             count = (int)(pixel & 0x7F);
719             if (!count) {
720                 errstr = WRONG_COUNT;
721                 return;
722             }
723             if (pixel & 0x80) {
724                 while (count--) {
725                     *oPtr++ = *iPtr++;
726                 }
727             } else {
728                 pixel = *iPtr++;
729                 while (count--) {
730                     *oPtr++ = pixel;
731                 }
732             }
733         }
734     } else {
735         fseek(file, 512+(y*image->xsize)+(z*image->xsize*image->ysize),
736               SEEK_SET);
737         fread(buf, 1, image->xsize, file);
738     }
739 }
740 
741 
742 void
rgbtorgb(GLubyte * r,GLubyte * g,GLubyte * b,GLubyte * l,int n)743 SGTexture::rgbtorgb(GLubyte *r, GLubyte *g, GLubyte *b, GLubyte *l, int n) {
744     while(n--) {
745         l[0] = r[0];
746         l[1] = g[0];
747         l[2] = b[0];
748         l += 3; r++; g++; b++;
749     }
750 }
751 
752 void
rgbatorgba(GLubyte * r,GLubyte * g,GLubyte * b,GLubyte * a,GLubyte * l,int n)753 SGTexture::rgbatorgba(GLubyte *r, GLubyte *g, GLubyte *b, GLubyte *a,
754                       GLubyte *l, int n) {
755     while(n--) {
756         l[0] = r[0];
757         l[1] = g[0];
758         l[2] = b[0];
759         l[3] = a[0];
760         l += 4; r++; g++; b++; a++;
761     }
762 }
763 
764 
765 void
ConvertShort(unsigned short * array,unsigned int length)766 SGTexture::ConvertShort(unsigned short *array, unsigned int length) {
767     unsigned short b1, b2;
768     unsigned char *ptr;
769 
770     ptr = (unsigned char *)array;
771     while (length--) {
772         b1 = *ptr++;
773         b2 = *ptr++;
774         *array++ = (b1 << 8) | (b2);
775     }
776 }
777 
778 
779 void
ConvertUint(unsigned * array,unsigned int length)780 SGTexture::ConvertUint(unsigned *array, unsigned int length) {
781     unsigned int b1, b2, b3, b4;
782     unsigned char *ptr;
783 
784     ptr = (unsigned char *)array;
785     while (length--) {
786         b1 = *ptr++;
787         b2 = *ptr++;
788         b3 = *ptr++;
789         b4 = *ptr++;
790         *array++ = (b1 << 24) | (b2 << 16) | (b3 << 8) | (b4);
791     }
792 }
793 
794 
795 void
make_monochrome(float contrast,GLubyte r,GLubyte g,GLubyte b)796 SGTexture::make_monochrome(float contrast, GLubyte r, GLubyte g, GLubyte b) {
797 
798    if (num_colors >= 3)
799       return;
800 
801    GLubyte ap[3];
802    for (int y=0; y<texture_height; y++)
803       for (int x=0; x<texture_width; x++)
804       {
805          GLubyte *rgb = get_pixel(x,y);
806          GLubyte avg = (rgb[0] + rgb[1] + rgb[2]) / 3;
807 
808          if (contrast != 1.0) {
809             float pixcol = -1.0 + (avg/128);
810             avg = 128 + int(128*pow(pixcol, contrast));
811          }
812 
813          ap[0] = avg*r/255;
814          ap[1] = avg*g/255;
815          ap[2] = avg*b/255;
816 
817          set_pixel(x,y,ap);
818       }
819 }
820 
821 
822 void
make_grayscale(float contrast)823 SGTexture::make_grayscale(float contrast) {
824    if (num_colors < 3)
825       return;
826 
827    int colors = (num_colors == 3) ? 1 : 2;
828    GLubyte *map = new GLubyte[ texture_width * texture_height * colors ];
829 
830    for (int y=0; y<texture_height; y++)
831       for (int x=0; x<texture_width; x++)
832       {
833          GLubyte *rgb = get_pixel(x,y);
834          GLubyte avg = (rgb[0] + rgb[1] + rgb[2]) / 3;
835 
836          if (contrast != 1.0) {
837             float pixcol = -1.0 + (avg/128);
838             avg = 128 + int(128*pow(pixcol, contrast));
839          }
840 
841          int pos = (x + y*texture_width)*colors;
842          map[pos] = avg;
843          if (colors > 1)
844             map[pos+1] = rgb[3];
845       }
846 
847    delete[] texture_data;
848    texture_data = map;
849    num_colors = colors;
850 }
851 
852 
853 void
make_maxcolorwindow()854 SGTexture::make_maxcolorwindow() {
855    GLubyte minmaxc[2] = {255, 0};
856 
857    int pos = 0;
858    int max = num_colors;
859    if (num_colors == 2) max = 1;
860    if (num_colors == 4) max = 3;
861    while (pos < texture_width * texture_height * num_colors) {
862       for (int i=0; i < max; i++) {
863          GLubyte c = texture_data[pos+i];
864          if (c < minmaxc[0]) minmaxc[0] = c;
865          if (c > minmaxc[1]) minmaxc[1] = c;
866       }
867       pos += num_colors;
868    }
869 
870    GLubyte offs = minmaxc[0];
871    float factor = 255.0 / float(minmaxc[1] - minmaxc[0]);
872    // printf("Min: %i, Max: %i, Factor: %f\n", offs, minmaxc[1], factor);
873 
874    pos = 0;
875    while (pos < texture_width * texture_height * num_colors) {
876       for (int i=0; i < max; i++) {
877          texture_data[pos+i] -= offs;
878          texture_data[pos+i] = int(factor * texture_data[pos+i]);
879       }
880       pos += num_colors;
881    }
882 }
883 
884 
885 void
make_normalmap(float brightness,float contrast)886 SGTexture::make_normalmap(float brightness, float contrast) {
887    make_grayscale(contrast);
888    make_maxcolorwindow();
889 
890    int colors = (num_colors == 1) ? 3 : 4;
891    bool alpha = (colors > 3);
892    int tsize = texture_width * texture_height * colors;
893    GLubyte *map = new GLubyte[ tsize ];
894 
895    int mpos = 0, dpos = 0;
896    for (int y=0; y<texture_height; y++) {
897       int ytw = y*texture_width;
898 
899       for (int x=0; x<texture_width; x++)
900       {
901          int xp1 = (x < (texture_width-1)) ? x+1 : 0;
902          int yp1 = (y < (texture_height-1)) ? y+1 : 0;
903          int posxp1 = (xp1 + ytw)*num_colors;
904          int posyp1 = (x + yp1*texture_width)*num_colors;
905          float fx,fy;
906 
907          GLubyte c = texture_data[dpos];
908          GLubyte cx1 = texture_data[posxp1];
909          GLubyte cy1 = texture_data[posyp1];
910 
911          if (alpha) {
912             GLubyte a = texture_data[dpos+1];
913             GLubyte ax1 = texture_data[posxp1+1];
914             GLubyte ay1 = texture_data[posyp1+1];
915 
916             c = (c + a)/2;
917             cx1 = (cx1 + ax1)/2;
918             cy1 = (cy1 + ay1)/2;
919 
920             map[mpos+3] = a;
921          }
922 
923          fx = asin((c/256.0-cx1/256.0))/1.57079633;
924          fy = asin((cy1/256.0-c/256.0))/1.57079633;
925 
926          map[mpos+0] = (GLuint)(fx*256.0)-128;
927          map[mpos+1] = (GLuint)(fy*256.0)-128;
928          map[mpos+2] = 127+int(brightness*128); // 255-c/2;
929 
930          mpos += colors;
931          dpos += num_colors;
932       }
933    }
934 
935    delete[] texture_data;
936    texture_data = map;
937    num_colors = colors;
938 }
939 
940 
941 void
make_bumpmap(float brightness,float contrast)942 SGTexture::make_bumpmap(float brightness, float contrast) {
943    make_grayscale(contrast);
944 
945    int colors = (num_colors == 1) ? 1 : 2;
946    GLubyte *map = new GLubyte[ texture_width * texture_height * colors ];
947 
948    for (int y=0; y<texture_height; y++)
949       for (int x=0; x<texture_width; x++)
950       {
951          int mpos = (x + y*texture_width)*colors;
952          int dpos = (x + y*texture_width)*num_colors;
953 
954          int xp1 = (x < (texture_width-1)) ? x+1 : 0;
955          int yp1 = (y < (texture_height-1)) ? y+1 : 0;
956          int posxp1 = (xp1 + y*texture_width)*num_colors;
957          int posyp1 = (x + yp1*texture_width)*num_colors;
958 
959          map[mpos] = (127 - ((texture_data[dpos]-texture_data[posxp1]) -
960                             ((texture_data[dpos]-texture_data[posyp1]))/4))/2;
961          if (colors > 1)
962             map[mpos+1] = texture_data[dpos+1];
963       }
964 
965    delete[] texture_data;
966    texture_data = map;
967    num_colors = colors;
968 }
969 
970