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