]> git.mxchange.org Git - simgear.git/blob - simgear/screen/texture.cxx
Alexander Powell:
[simgear.git] / simgear / screen / 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 SG_GLU_H
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, *abuf;
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 != 3 && image->zsize != 4) {
236       ImageClose(image);
237       errstr = WRONG_COUNT;
238       return;
239     }
240
241     texture_data = new GLubyte[ image->xsize * image->ysize * 3 ];
242     num_colors = 3;
243     rbuf = new GLubyte[ image->xsize ];
244     gbuf = new GLubyte[ image->xsize ];
245     bbuf = new GLubyte[ image->xsize ];
246     abuf = new GLubyte[ image->xsize ];
247     if(!texture_data || !rbuf || !gbuf || !bbuf || !abuf) {
248       delete[] texture_data;
249       delete[] rbuf;
250       delete[] gbuf;
251       delete[] bbuf;
252       delete[] abuf;
253       errstr = OUT_OF_MEMORY;
254       return;
255     }
256
257     ptr = texture_data;
258     for(y=0; y<image->ysize; y++) {
259         if(image->zsize == 4) {
260             ImageGetRow(image,rbuf,y,0);
261             ImageGetRow(image,gbuf,y,1);
262             ImageGetRow(image,bbuf,y,2);
263             ImageGetRow(image,abuf,y,3); // discard
264             rgbtorgb(rbuf,gbuf,bbuf,ptr,image->xsize);
265             ptr += (image->xsize * 3);
266         } else {
267             ImageGetRow(image,rbuf,y,0);
268             ImageGetRow(image,gbuf,y,1);
269             ImageGetRow(image,bbuf,y,2);
270             rgbtorgb(rbuf,gbuf,bbuf,ptr,image->xsize);
271             ptr += (image->xsize * 3);
272         }
273     }
274
275     ImageClose(image);
276     delete[] rbuf;
277     delete[] gbuf;
278     delete[] bbuf;
279     delete[] abuf;
280 }
281
282
283
284 void
285 SGTexture::read_rgba_texture(const char *name)
286 {
287     GLubyte *ptr;
288     GLubyte *rbuf, *gbuf, *bbuf, *abuf;
289     SGTexture::ImageRec *image;
290     int y;
291
292     delete[] texture_data;
293
294     image = ImageOpen(name);
295     if(!image) {
296         errstr = FILE_OPEN_ERROR;
297         return;
298     }
299
300     texture_width = image->xsize;
301     texture_height = image->ysize;
302     if (image->zsize != 3 && image->zsize != 4) {
303       ImageClose(image);
304       errstr = WRONG_COUNT;
305       return;
306     }
307
308     texture_data = new GLubyte[ image->xsize * image->ysize * 4 ];
309     num_colors = 4;
310     rbuf = new GLubyte[ image->xsize ];
311     gbuf = new GLubyte[ image->xsize ];
312     bbuf = new GLubyte[ image->xsize ];
313     abuf = new GLubyte[ image->xsize ];
314     if(!texture_data || !rbuf || !gbuf || !bbuf || !abuf) {
315       delete[] texture_data;
316       delete[] rbuf;
317       delete[] gbuf;
318       delete[] bbuf;
319       delete[] abuf;
320       errstr = OUT_OF_MEMORY;
321       return;
322     }
323
324     ptr = texture_data;
325     memset(abuf, 255, image->xsize);
326     for(y=0; y<image->ysize; y++) {
327         if(image->zsize == 4) {
328             ImageGetRow(image,rbuf,y,0);
329             ImageGetRow(image,gbuf,y,1);
330             ImageGetRow(image,bbuf,y,2);
331             ImageGetRow(image,abuf,y,3);
332             rgbatorgba(rbuf,gbuf,bbuf,abuf,ptr,image->xsize);
333             ptr += (image->xsize * 4);
334         } else {
335             ImageGetRow(image,rbuf,y,0);
336             ImageGetRow(image,gbuf,y,1);
337             ImageGetRow(image,bbuf,y,2);
338             rgbatorgba(rbuf,gbuf,bbuf,abuf,ptr,image->xsize);
339             ptr += (image->xsize * 3);
340         }
341     }
342
343     ImageClose(image);
344     delete[] rbuf;
345     delete[] gbuf;
346     delete[] bbuf;
347     delete[] abuf;
348 }
349
350 void
351 SGTexture::read_raw_texture(const char *name)
352 {
353     GLubyte *ptr;
354     SGTexture::ImageRec *image;
355     int y;
356
357     delete[] texture_data;
358
359     image = RawImageOpen(name);
360
361     if(!image) {
362         errstr = FILE_OPEN_ERROR;
363         return;
364     }
365
366     texture_width = 256;
367     texture_height = 256;
368
369     texture_data = new GLubyte[ 256 * 256 * 3 ];
370     if(!texture_data) {
371       errstr = OUT_OF_MEMORY;
372       return;
373     }
374
375     ptr = texture_data;
376     for(y=0; y<256; y++) {
377         gzread(image->file, ptr, 256*3);
378         ptr+=256*3;
379     }
380     ImageClose(image);
381 }
382
383 void
384 SGTexture::read_r8_texture(const char *name)
385 {
386     unsigned char c[1];
387     GLubyte *ptr;
388     SGTexture::ImageRec *image;
389     int xy;
390
391     delete[] texture_data;
392
393     //it wouldn't make sense to write a new function ...
394     image = RawImageOpen(name);
395
396     if(!image) {
397         errstr = FILE_OPEN_ERROR;
398         return;
399     }
400
401     texture_width = 256;
402     texture_height = 256;
403
404     texture_data = new GLubyte [ 256 * 256 * 3 ];
405     if(!texture_data) {
406         errstr = OUT_OF_MEMORY;
407         return;
408     }
409
410     ptr = texture_data;
411     for(xy=0; xy<(256*256); xy++) {
412         gzread(image->file, c, 1);
413
414         //look in the table for the right colours
415         ptr[0]=msfs_colour[c[0]][0];
416         ptr[1]=msfs_colour[c[0]][1];
417         ptr[2]=msfs_colour[c[0]][2];
418
419         ptr+=3;
420     }
421     ImageClose(image);
422 }
423
424
425 void
426 SGTexture::write_texture(const char *name) {
427    SGTexture::ImageRec *image = ImageWriteOpen(name);
428
429    for (int c=0; c<num_colors; c++) {
430       GLubyte *ptr = texture_data + c;
431       for (int y=0; y<texture_height; y++) {
432          for (int x=0; x<texture_width; x++) {
433             image->tmp[x]=*ptr;
434             ptr = ptr + num_colors;
435          }
436          fwrite(image->tmp, 1, texture_width, file);
437       }
438    }
439
440    ImageClose(image);
441 }
442
443
444 void
445 SGTexture::set_pixel(GLuint x, GLuint y, GLubyte *c)
446 {
447     if (!texture_data) {
448         errstr = NO_TEXTURE;
449         return;
450     }
451
452     unsigned int pos = (x + y*texture_width) * num_colors;
453     memcpy(texture_data+pos, c, num_colors);
454 }
455
456
457 GLubyte *
458 SGTexture::get_pixel(GLuint x, GLuint y)
459 {
460     static GLubyte c[4] = {0, 0, 0, 0};
461
462     if (!texture_data) {
463         errstr = NO_TEXTURE;
464         return c;
465     }
466
467     unsigned int pos = (x + y*texture_width)*num_colors;
468     memcpy(c, texture_data + pos, num_colors);
469
470     return c;
471 }
472
473 SGTexture::ImageRec *
474 SGTexture::ImageOpen(const char *fileName)
475 {
476      union {
477        int testWord;
478        char testByte[4];
479      } endianTest;
480
481     SGTexture::ImageRec *image;
482     int swapFlag;
483     int x;
484
485     endianTest.testWord = 1;
486     if (endianTest.testByte[0] == 1) {
487         swapFlag = 1;
488     } else {
489         swapFlag = 0;
490     }
491
492     image = new SGTexture::ImageRec;
493     memset(image, 0, sizeof(SGTexture::ImageRec));
494     if (image == 0) {
495         errstr = OUT_OF_MEMORY;
496         return 0;
497     }
498     if ((image->file = gzopen(fileName, "rb")) == 0) {
499       errstr = FILE_OPEN_ERROR;
500       return 0;
501     }
502
503     gzread(image->file, image, 12);
504
505     if (swapFlag) {
506         ConvertShort(&image->imagic, 6);
507     }
508
509     image->tmp = new GLubyte[ image->xsize * 256 ];
510     if (image->tmp == 0) {
511         errstr = OUT_OF_MEMORY;
512         return 0;
513     }
514
515     if ((image->type & 0xFF00) == 0x0100) {
516         x = image->ysize * image->zsize * (int) sizeof(unsigned);
517         image->rowStart = new unsigned[x];
518         image->rowSize = new int[x];
519         if (image->rowStart == 0 || image->rowSize == 0) {
520             errstr = OUT_OF_MEMORY;
521             return 0;
522         }
523         image->rleEnd = 512 + (2 * x);
524         gzseek(image->file, 512, SEEK_SET);
525         gzread(image->file, image->rowStart, x);
526         gzread(image->file, image->rowSize, x);
527         if (swapFlag) {
528             ConvertUint(image->rowStart, x/(int) sizeof(unsigned));
529             ConvertUint((unsigned *)image->rowSize, x/(int) sizeof(int));
530         }
531     }
532     return image;
533 }
534
535
536 void
537 SGTexture::ImageClose(SGTexture::ImageRec *image) {
538     if (image->file)  gzclose(image->file);
539     if (file) fclose(file);
540     delete[] image->tmp;
541     delete[] image->rowStart;
542     delete[] image->rowSize;
543     delete image;
544 }
545
546 SGTexture::ImageRec *
547 SGTexture::RawImageOpen(const char *fileName)
548 {
549      union {
550        int testWord;
551        char testByte[4];
552      } endianTest;
553
554     SGTexture::ImageRec *image;
555     int swapFlag;
556
557     endianTest.testWord = 1;
558     if (endianTest.testByte[0] == 1) {
559         swapFlag = 1;
560     } else {
561         swapFlag = 0;
562     }
563
564     image = new SGTexture::ImageRec;
565     memset(image, 0, sizeof(SGTexture::ImageRec));
566     if (image == 0) {
567         errstr = OUT_OF_MEMORY;
568         return 0;
569     }
570     if ((image->file = gzopen(fileName, "rb")) == 0) {
571       errstr = FILE_OPEN_ERROR;
572       return 0;
573     }
574
575     gzread(image->file, image, 12);
576
577     if (swapFlag) {
578         ConvertShort(&image->imagic, 6);
579     }
580
581
582     //just allocate a pseudo value as I'm too lazy to change ImageClose()...
583     image->tmp = new GLubyte[1];
584
585     if (image->tmp == 0) {
586         errstr = OUT_OF_MEMORY;
587         return 0;
588     }
589
590     return image;
591 }
592
593 SGTexture::ImageRec *
594 SGTexture::ImageWriteOpen(const char *fileName)
595 {
596     union {
597         int testWord;
598         char testByte[4];
599     } endianTest;
600     ImageRec* image;
601     int swapFlag;
602     int x;
603
604     endianTest.testWord = 1;
605     if (endianTest.testByte[0] == 1) {
606         swapFlag = 1;
607     } else {
608         swapFlag = 0;
609     }
610
611     image = new SGTexture::ImageRec;
612     memset(image, 0, sizeof(SGTexture::ImageRec));
613     if (image == 0) {
614         errstr = OUT_OF_MEMORY;
615         return 0;
616     }
617     if ((file = fopen(fileName, "wb")) == 0) {
618         errstr = FILE_OPEN_ERROR;
619         return 0;
620     }
621
622     image->imagic = 474;
623     image->type = 0x0001;
624     image->dim = (num_colors > 1) ? 3 : 2;
625     image->xsize = texture_width;
626     image->ysize = texture_height;
627     image->zsize = num_colors;
628
629     if (swapFlag) {
630         ConvertShort(&image->imagic, 6);
631     }
632
633     fwrite(image, 1, 12, file);
634     fseek(file, 512, SEEK_SET);
635
636     image->tmp = new GLubyte[ image->xsize * 256 ];
637     if (image->tmp == 0) {
638         errstr = OUT_OF_MEMORY;
639         return 0;
640     }
641
642     if ((image->type & 0xFF00) == 0x0100) {
643         x = image->ysize * image->zsize * (int) sizeof(unsigned);
644         image->rowStart = new unsigned[x];
645         image->rowSize = new int[x];
646         if (image->rowStart == 0 || image->rowSize == 0) {
647             errstr = OUT_OF_MEMORY;
648             return 0;
649         }
650         image->rleEnd = 512 + (2 * x);
651         fseek(file, 512, SEEK_SET);
652         fread(image->rowStart, 1, x, file);
653         fread(image->rowSize, 1, x, file);
654         if (swapFlag) {
655             ConvertUint(image->rowStart, x/(int) sizeof(unsigned));
656             ConvertUint((unsigned *)image->rowSize, x/(int) sizeof(int));
657         }
658     }
659
660     return image;
661
662 }
663
664 void
665 SGTexture::ImageGetRow(SGTexture::ImageRec *image, GLubyte *buf, int y, int z) {
666     GLubyte *iPtr, *oPtr, pixel;
667     int count;
668
669     if ((image->type & 0xFF00) == 0x0100) {
670         gzseek(image->file, (long) image->rowStart[y+z*image->ysize], SEEK_SET);
671         gzread(image->file, image->tmp,
672                (unsigned int)image->rowSize[y+z*image->ysize]);
673
674         iPtr = image->tmp;
675         oPtr = buf;
676         for (;;) {
677             pixel = *iPtr++;
678             count = (int)(pixel & 0x7F);
679             if (!count) {
680                 errstr = WRONG_COUNT;
681                 return;
682             }
683             if (pixel & 0x80) {
684                 while (count--) {
685                     *oPtr++ = *iPtr++;
686                 }
687             } else {
688                 pixel = *iPtr++;
689                 while (count--) {
690                     *oPtr++ = pixel;
691                 }
692             }
693         }
694     } else {
695         gzseek(image->file, 512+(y*image->xsize)+(z*image->xsize*image->ysize),
696               SEEK_SET);
697         gzread(image->file, buf, image->xsize);
698     }
699 }
700
701 void
702 SGTexture::ImagePutRow(SGTexture::ImageRec *image, GLubyte *buf, int y, int z) {
703     GLubyte *iPtr, *oPtr, pixel;
704     int count;
705
706     if ((image->type & 0xFF00) == 0x0100) {
707         fseek(file, (long) image->rowStart[y+z*image->ysize], SEEK_SET);
708         fread( image->tmp, 1, (unsigned int)image->rowSize[y+z*image->ysize],
709                file);
710
711         iPtr = image->tmp;
712         oPtr = buf;
713         for (;;) {
714             pixel = *iPtr++;
715             count = (int)(pixel & 0x7F);
716             if (!count) {
717                 errstr = WRONG_COUNT;
718                 return;
719             }
720             if (pixel & 0x80) {
721                 while (count--) {
722                     *oPtr++ = *iPtr++;
723                 }
724             } else {
725                 pixel = *iPtr++;
726                 while (count--) {
727                     *oPtr++ = pixel;
728                 }
729             }
730         }
731     } else {
732         fseek(file, 512+(y*image->xsize)+(z*image->xsize*image->ysize),
733               SEEK_SET);
734         fread(buf, 1, image->xsize, file);
735     }
736 }
737
738
739 void
740 SGTexture::rgbtorgb(GLubyte *r, GLubyte *g, GLubyte *b, GLubyte *l, int n) {
741     while(n--) {
742         l[0] = r[0];
743         l[1] = g[0];
744         l[2] = b[0];
745         l += 3; r++; g++; b++;
746     }
747 }
748
749 void
750 SGTexture::rgbatorgba(GLubyte *r, GLubyte *g, GLubyte *b, GLubyte *a,
751                       GLubyte *l, int n) {
752     while(n--) {
753         l[0] = r[0];
754         l[1] = g[0];
755         l[2] = b[0];
756         l[3] = a[0];
757         l += 4; r++; g++; b++; a++;
758     }
759 }
760
761
762 void
763 SGTexture::ConvertShort(unsigned short *array, unsigned int length) {
764     unsigned short b1, b2;
765     unsigned char *ptr;
766
767     ptr = (unsigned char *)array;
768     while (length--) {
769         b1 = *ptr++;
770         b2 = *ptr++;
771         *array++ = (b1 << 8) | (b2);
772     }
773 }
774
775
776 void
777 SGTexture::ConvertUint(unsigned *array, unsigned int length) {
778     unsigned int b1, b2, b3, b4;
779     unsigned char *ptr;
780
781     ptr = (unsigned char *)array;
782     while (length--) {
783         b1 = *ptr++;
784         b2 = *ptr++;
785         b3 = *ptr++;
786         b4 = *ptr++;
787         *array++ = (b1 << 24) | (b2 << 16) | (b3 << 8) | (b4);
788     }
789 }
790
791
792 void
793 SGTexture::make_monochrome(float contrast, GLubyte r, GLubyte g, GLubyte b) {
794
795    if (num_colors >= 3)
796       return;
797
798    GLubyte ap[3];
799    for (int y=0; y<texture_height; y++)
800       for (int x=0; x<texture_width; x++)
801       {
802          GLubyte *rgb = get_pixel(x,y);
803          GLubyte avg = (rgb[0] + rgb[1] + rgb[2]) / 3;
804
805          if (contrast != 1.0) {
806             float pixcol = -1.0 + (avg/128);
807             avg = 128 + int(128*pow(pixcol, contrast));
808          }
809
810          ap[0] = avg*r/255;
811          ap[1] = avg*g/255;
812          ap[2] = avg*b/255;
813
814          set_pixel(x,y,ap);
815       }
816 }
817
818
819 void
820 SGTexture::make_grayscale(float contrast) {
821    if (num_colors < 3)
822       return;
823
824    int colors = (num_colors == 3) ? 1 : 2;
825    GLubyte *map = new GLubyte[ texture_width * texture_height * colors ];
826
827    for (int y=0; y<texture_height; y++)
828       for (int x=0; x<texture_width; x++)
829       {
830          GLubyte *rgb = get_pixel(x,y);
831          GLubyte avg = (rgb[0] + rgb[1] + rgb[2]) / 3;
832
833          if (contrast != 1.0) {
834             float pixcol = -1.0 + (avg/128);
835             avg = 128 + int(128*pow(pixcol, contrast));
836          }
837
838          int pos = (x + y*texture_width)*colors;
839          map[pos] = avg;
840          if (colors > 1)
841             map[pos+1] = rgb[3];
842       }
843
844    delete[] texture_data;
845    texture_data = map;
846    num_colors = colors;
847 }
848
849
850 void
851 SGTexture::make_maxcolorwindow() {
852    GLubyte minmaxc[2] = {255, 0};
853
854    int pos = 0;
855    int max = num_colors;
856    if (num_colors == 2) max = 1;
857    if (num_colors == 4) max = 3; 
858    while (pos < texture_width * texture_height * num_colors) {
859       for (int i=0; i < max; i++) {
860          GLubyte c = texture_data[pos+i];
861          if (c < minmaxc[0]) minmaxc[0] = c;
862          if (c > minmaxc[1]) minmaxc[1] = c;
863       }
864       pos += num_colors;
865    }
866
867    GLubyte offs = minmaxc[0];
868    float factor = 255.0 / float(minmaxc[1] - minmaxc[0]);
869    // printf("Min: %i, Max: %i, Factor: %f\n", offs, minmaxc[1], factor);
870
871    pos = 0;
872    while (pos < texture_width * texture_height * num_colors) {
873       for (int i=0; i < max; i++) {
874          texture_data[pos+i] -= offs;
875          texture_data[pos+i] = int(factor * texture_data[pos+i]);
876       }
877       pos += num_colors;
878    }
879 }
880
881
882 void
883 SGTexture::make_normalmap(float brightness, float contrast) {
884    make_grayscale(contrast);
885    make_maxcolorwindow();
886
887    int colors = (num_colors == 1) ? 3 : 4;
888    bool alpha = (colors > 3);
889    int tsize = texture_width * texture_height * colors;
890    GLubyte *map = new GLubyte[ tsize ];
891
892    int mpos = 0, dpos = 0;
893    for (int y=0; y<texture_height; y++) {
894       int ytw = y*texture_width;
895
896       for (int x=0; x<texture_width; x++)
897       {
898          int xp1 = (x < (texture_width-1)) ? x+1 : 0;
899          int yp1 = (y < (texture_height-1)) ? y+1 : 0;
900          int posxp1 = (xp1 + ytw)*num_colors;
901          int posyp1 = (x + yp1*texture_width)*num_colors;
902
903          GLubyte c = texture_data[dpos];
904          GLubyte cx1 = texture_data[posxp1];
905          GLubyte cy1 = texture_data[posyp1];
906
907          if (alpha) {
908             GLubyte a = texture_data[dpos+1];
909             GLubyte ax1 = texture_data[posxp1+1];
910             GLubyte ay1 = texture_data[posyp1+1];
911
912             c = (c + a)/2;
913             cx1 = (cx1 + ax1)/2;
914             cy1 = (cy1 + ay1)/2;
915
916             map[mpos+3] = a;
917          }
918
919          map[mpos+0] = (128+(cx1-c)/2);
920          map[mpos+1] = (128+(cy1-c)/2);
921          map[mpos+2] = 127+int(brightness*128); // 255-c/2;
922
923          mpos += colors;
924          dpos += num_colors;
925       }
926    }
927
928    delete[] texture_data;
929    texture_data = map;
930    num_colors = colors;
931 }
932
933
934 void
935 SGTexture::make_bumpmap(float brightness, float contrast) {
936    make_grayscale(contrast);
937
938    int colors = (num_colors == 1) ? 1 : 2;
939    GLubyte *map = new GLubyte[ texture_width * texture_height * colors ];
940
941    for (int y=0; y<texture_height; y++)
942       for (int x=0; x<texture_width; x++)
943       {
944          int mpos = (x + y*texture_width)*colors;
945          int dpos = (x + y*texture_width)*num_colors;
946
947          int xp1 = (x < (texture_width-1)) ? x+1 : 0;
948          int yp1 = (y < (texture_height-1)) ? y+1 : 0;
949          int posxp1 = (xp1 + y*texture_width)*num_colors;
950          int posyp1 = (x + yp1*texture_width)*num_colors;
951
952          map[mpos] = (127 - ((texture_data[dpos]-texture_data[posxp1]) -
953                             ((texture_data[dpos]-texture_data[posyp1]))/4))/2;
954          if (colors > 1)
955             map[mpos+1] = texture_data[dpos+1];
956       }
957
958    delete[] texture_data;
959    texture_data = map;
960    num_colors = colors;
961 }
962