2 * Texture manipulation routines
4 * Copyright (c) Mark J. Kilgard, 1997.
5 * Code added in april 2003 by Erik Hofman
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.
14 #include <simgear/compiler.h>
25 #include "texture.hxx"
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.";
35 SGTexture::SGTexture()
43 SGTexture::SGTexture(unsigned int width, unsigned int height)
47 texture_data = new GLubyte[ width * height * 3 ];
50 SGTexture::~SGTexture()
56 if ( texture_id != 0 ) {
67 glGenTextures(1, &texture_id);
69 #elif GL_EXT_texture_object
70 glGenTexturesEXT(1, &texture_id);
76 glBindTexture(GL_TEXTURE_2D, texture_id);
78 #elif GL_EXT_texture_object
79 glBindTextureEXT(GL_TEXTURE_2D, texture_id);
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);
91 * A function to resize the OpenGL window which will be used by
92 * the dynamic texture generating routines.
94 * @param width The width of the new window
95 * @param height The height of the new window
98 SGTexture::resize(unsigned int width, unsigned int height)
102 // Make sure that we don't get a divide by zero exception
106 // Set the viewport for the OpenGL window
107 glViewport(0, 0, width, height);
109 // Calculate the aspect ratio of the window
110 aspect = width/height;
112 // Go to the projection matrix, this gets modified by the perspective
114 glMatrixMode(GL_PROJECTION);
117 // Do the perspective calculations
118 gluPerspective(45.0, aspect, 1.0, 400.0);
120 // Return to the modelview matrix
121 glMatrixMode(GL_MODELVIEW);
125 * A function to prepare the OpenGL state machine for dynamic
126 * texture generation.
128 * @param width The width of the texture
129 * @param height The height of the texture
132 SGTexture::prepare(unsigned int width, unsigned int height) {
134 texture_width = width;
135 texture_height = height;
137 // Resize the OpenGL window to the size of our dynamic texture
138 resize(texture_width, texture_height);
140 glClearColor(0.0, 0.0, 0.0, 1.0);
144 * A function to generate the dynamic texture.
146 * The actual texture can be accessed by calling get_texture()
148 * @param width The width of the previous OpenGL window
149 * @param height The height of the previous OpenGL window
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.
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);
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);
169 // Set the OpenGL window back to its previous size
170 resize(width, height);
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);
179 SGTexture::read_alpha_texture(const char *name)
182 SGTexture::ImageRec *image;
188 image = ImageOpen(name);
190 errstr = FILE_OPEN_ERROR;
194 texture_width = image->xsize;
195 texture_height = image->ysize;
197 // printf("image->zsize = %d\n", image->zsize);
199 if (image->zsize != 1) {
201 errstr = WRONG_COUNT;
205 texture_data = new GLubyte[ image->xsize * image->ysize ];
213 for(y=0; y<image->ysize; y++) {
214 ImageGetRow(image,lptr,y,0);
215 lptr += image->xsize;
221 SGTexture::read_rgb_texture(const char *name)
224 GLubyte *rbuf, *gbuf, *bbuf, *abuf;
225 SGTexture::ImageRec *image;
231 image = ImageOpen(name);
233 errstr = FILE_OPEN_ERROR;
237 texture_width = image->xsize;
238 texture_height = image->ysize;
239 if (image->zsize != 3 && image->zsize != 4) {
241 errstr = WRONG_COUNT;
245 texture_data = new GLubyte[ image->xsize * image->ysize * 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) {
257 errstr = OUT_OF_MEMORY;
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);
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);
289 SGTexture::read_rgba_texture(const char *name)
292 GLubyte *rbuf, *gbuf, *bbuf, *abuf;
293 SGTexture::ImageRec *image;
299 image = ImageOpen(name);
301 errstr = FILE_OPEN_ERROR;
305 texture_width = image->xsize;
306 texture_height = image->ysize;
307 if (image->zsize != 3 && image->zsize != 4) {
309 errstr = WRONG_COUNT;
313 texture_data = new GLubyte[ image->xsize * image->ysize * 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) {
325 errstr = OUT_OF_MEMORY;
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);
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);
356 SGTexture::read_raw_texture(const char *name)
359 SGTexture::ImageRec *image;
365 image = RawImageOpen(name);
368 errstr = FILE_OPEN_ERROR;
373 texture_height = 256;
375 texture_data = new GLubyte[ 256 * 256 * 3 ];
377 errstr = OUT_OF_MEMORY;
382 for(y=0; y<256; y++) {
383 gzread(image->file, ptr, 256*3);
390 SGTexture::read_r8_texture(const char *name)
394 SGTexture::ImageRec *image;
400 //it wouldn't make sense to write a new function ...
401 image = RawImageOpen(name);
404 errstr = FILE_OPEN_ERROR;
409 texture_height = 256;
411 texture_data = new GLubyte [ 256 * 256 * 3 ];
413 errstr = OUT_OF_MEMORY;
418 for(xy=0; xy<(256*256); xy++) {
419 gzread(image->file, c, 1);
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];
433 SGTexture::write_texture(const char *name) {
434 SGTexture::ImageRec *image = ImageWriteOpen(name);
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++) {
441 ptr = ptr + num_colors;
443 fwrite(image->tmp, 1, texture_width, file);
452 SGTexture::set_pixel(GLuint x, GLuint y, GLubyte *c)
459 unsigned int pos = (x + y*texture_width) * num_colors;
460 memcpy(texture_data+pos, c, num_colors);
465 SGTexture::get_pixel(GLuint x, GLuint y)
467 static GLubyte c[4] = {0, 0, 0, 0};
474 unsigned int pos = (x + y*texture_width)*num_colors;
475 memcpy(c, texture_data + pos, num_colors);
480 SGTexture::ImageRec *
481 SGTexture::ImageOpen(const char *fileName)
488 SGTexture::ImageRec *image;
492 endianTest.testWord = 1;
493 if (endianTest.testByte[0] == 1) {
499 image = new SGTexture::ImageRec;
500 memset(image, 0, sizeof(SGTexture::ImageRec));
502 errstr = OUT_OF_MEMORY;
505 if ((image->file = gzopen(fileName, "rb")) == 0) {
506 errstr = FILE_OPEN_ERROR;
510 gzread(image->file, image, 12);
513 ConvertShort(&image->imagic, 6);
516 image->tmp = new GLubyte[ image->xsize * 256 ];
517 if (image->tmp == 0) {
518 errstr = OUT_OF_MEMORY;
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;
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);
535 ConvertUint(image->rowStart, x/(int) sizeof(unsigned));
536 ConvertUint((unsigned *)image->rowSize, x/(int) sizeof(int));
544 SGTexture::ImageClose(SGTexture::ImageRec *image) {
545 if (image->file) gzclose(image->file);
546 if (file) fclose(file);
551 SGTexture::ImageRec *
552 SGTexture::RawImageOpen(const char *fileName)
559 SGTexture::ImageRec *image;
562 endianTest.testWord = 1;
563 if (endianTest.testByte[0] == 1) {
569 image = new SGTexture::ImageRec;
570 memset(image, 0, sizeof(SGTexture::ImageRec));
572 errstr = OUT_OF_MEMORY;
575 if ((image->file = gzopen(fileName, "rb")) == 0) {
576 errstr = FILE_OPEN_ERROR;
580 gzread(image->file, image, 12);
583 ConvertShort(&image->imagic, 6);
587 //just allocate a pseudo value as I'm too lazy to change ImageClose()...
588 image->tmp = new GLubyte;
590 if (image->tmp == 0) {
591 errstr = OUT_OF_MEMORY;
598 SGTexture::ImageRec *
599 SGTexture::ImageWriteOpen(const char *fileName)
609 endianTest.testWord = 1;
610 if (endianTest.testByte[0] == 1) {
616 image = new SGTexture::ImageRec;
617 memset(image, 0, sizeof(SGTexture::ImageRec));
619 errstr = OUT_OF_MEMORY;
622 if ((file = fopen(fileName, "wb")) == 0) {
623 errstr = FILE_OPEN_ERROR;
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;
635 ConvertShort(&image->imagic, 6);
638 fwrite(image, 1, 12, file);
639 fseek(file, 512, SEEK_SET);
641 image->tmp = new GLubyte[ image->xsize * 256 ];
642 if (image->tmp == 0) {
643 errstr = OUT_OF_MEMORY;
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;
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);
660 ConvertUint(image->rowStart, x/(int) sizeof(unsigned));
661 ConvertUint((unsigned *)image->rowSize, x/(int) sizeof(int));
670 SGTexture::ImageGetRow(SGTexture::ImageRec *image, GLubyte *buf, int y, int z) {
671 GLubyte *iPtr, *oPtr, pixel;
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]);
683 count = (int)(pixel & 0x7F);
685 errstr = WRONG_COUNT;
700 gzseek(image->file, 512+(y*image->xsize)+(z*image->xsize*image->ysize),
702 gzread(image->file, buf, image->xsize);
707 SGTexture::ImagePutRow(SGTexture::ImageRec *image, GLubyte *buf, int y, int z) {
708 GLubyte *iPtr, *oPtr, pixel;
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],
720 count = (int)(pixel & 0x7F);
722 errstr = WRONG_COUNT;
737 fseek(file, 512+(y*image->xsize)+(z*image->xsize*image->ysize),
739 fread(buf, 1, image->xsize, file);
745 SGTexture::rgbtorgb(GLubyte *r, GLubyte *g, GLubyte *b, GLubyte *l, int n) {
750 l += 3; r++; g++; b++;
755 SGTexture::rgbatorgba(GLubyte *r, GLubyte *g, GLubyte *b, GLubyte *a,
762 l += 4; r++; g++; b++; a++;
768 SGTexture::ConvertShort(unsigned short *array, unsigned int length) {
769 unsigned short b1, b2;
772 ptr = (unsigned char *)array;
776 *array++ = (b1 << 8) | (b2);
782 SGTexture::ConvertUint(unsigned *array, unsigned int length) {
783 unsigned int b1, b2, b3, b4;
786 ptr = (unsigned char *)array;
792 *array++ = (b1 << 24) | (b2 << 16) | (b3 << 8) | (b4);
798 SGTexture::make_monochrome(float contrast, GLubyte r, GLubyte g, GLubyte b) {
804 for (int y=0; y<texture_height; y++)
805 for (int x=0; x<texture_width; x++)
807 GLubyte *rgb = get_pixel(x,y);
808 GLubyte avg = (rgb[0] + rgb[1] + rgb[2]) / 3;
810 if (contrast != 1.0) {
811 float pixcol = -1.0 + (avg/128);
812 avg = 128 + int(128*pow(pixcol, contrast));
825 SGTexture::make_grayscale(float contrast) {
829 int colors = (num_colors == 3) ? 1 : 2;
830 GLubyte *map = (GLubyte *)malloc (texture_width * texture_height * colors);
832 for (int y=0; y<texture_height; y++)
833 for (int x=0; x<texture_width; x++)
835 GLubyte *rgb = get_pixel(x,y);
836 GLubyte avg = (rgb[0] + rgb[1] + rgb[2]) / 3;
838 if (contrast != 1.0) {
839 float pixcol = -1.0 + (avg/128);
840 avg = 128 + int(128*pow(pixcol, contrast));
843 int pos = (x + y*texture_width)*colors;
856 SGTexture::make_maxcolorwindow() {
857 GLubyte minmaxc[2] = {255, 0};
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;
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);
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]);
888 SGTexture::make_normalmap(float brightness, float contrast) {
889 make_grayscale(contrast);
890 make_maxcolorwindow();
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);
897 int mpos = 0, dpos = 0;
898 for (int y=0; y<texture_height; y++) {
899 int ytw = y*texture_width;
901 for (int x=0; x<texture_width; x++)
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;
908 GLubyte c = texture_data[dpos];
909 GLubyte cx1 = texture_data[posxp1];
910 GLubyte cy1 = texture_data[posyp1];
913 GLubyte a = texture_data[dpos+1];
914 GLubyte ax1 = texture_data[posxp1+1];
915 GLubyte ay1 = texture_data[posyp1+1];
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;
940 SGTexture::make_bumpmap(float brightness, float contrast) {
941 make_grayscale(contrast);
943 int colors = (num_colors == 1) ? 1 : 2;
944 GLubyte *map = (GLubyte *)malloc (texture_width * texture_height * colors);
946 for (int y=0; y<texture_height; y++)
947 for (int x=0; x<texture_width; x++)
949 int mpos = (x + y*texture_width)*colors;
950 int dpos = (x + y*texture_width)*num_colors;
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;
957 map[mpos] = (127 - ((texture_data[dpos]-texture_data[posxp1]) -
958 ((texture_data[dpos]-texture_data[posyp1]))/4))/2;
960 map[mpos+1] = texture_data[dpos+1];