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