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>
20 #include <osg/Matrixf>
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()
52 delete[] texture_data;
54 if ( texture_id != 0 ) {
65 glGenTextures(1, &texture_id);
67 #elif GL_EXT_texture_object
68 glGenTexturesEXT(1, &texture_id);
74 glBindTexture(GL_TEXTURE_2D, texture_id);
76 #elif GL_EXT_texture_object
77 glBindTextureEXT(GL_TEXTURE_2D, texture_id);
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);
89 * A function to resize the OpenGL window which will be used by
90 * the dynamic texture generating routines.
92 * @param width The width of the new window
93 * @param height The height of the new window
96 SGTexture::resize(unsigned int width, unsigned int height)
101 // Make sure that we don't get a divide by zero exception
105 // Set the viewport for the OpenGL window
106 glViewport(0, 0, width, height);
108 // Calculate the aspect ratio of the window
109 aspect = width/height;
111 // Go to the projection matrix, this gets modified by the perspective
113 glMatrixMode(GL_PROJECTION);
115 // Do the perspective calculations
116 Matrixf proj = Matrixf::perspective(45.0, aspect, 1.0, 400.0);
117 glLoadMatrix(proj.ptr());
119 // Return to the modelview matrix
120 glMatrixMode(GL_MODELVIEW);
124 * A function to prepare the OpenGL state machine for dynamic
125 * texture generation.
127 * @param width The width of the texture
128 * @param height The height of the texture
131 SGTexture::prepare(unsigned int width, unsigned int height) {
133 texture_width = width;
134 texture_height = height;
136 // Resize the OpenGL window to the size of our dynamic texture
137 resize(texture_width, texture_height);
139 glClearColor(0.0, 0.0, 0.0, 1.0);
143 * A function to generate the dynamic texture.
145 * The actual texture can be accessed by calling get_texture()
147 * @param width The width of the previous OpenGL window
148 * @param height The height of the previous OpenGL window
151 SGTexture::finish(unsigned int width, unsigned int height) {
152 // If a texture hasn't been created then it gets created, and the contents
153 // of the frame buffer gets copied into it. If the texture has already been
154 // created then its contents just get updated.
158 // Copies the contents of the frame buffer into the texture
159 glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0,
160 texture_width, texture_height, 0);
163 // Copies the contents of the frame buffer into the texture
164 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0,
165 texture_width, texture_height);
168 // Set the OpenGL window back to its previous size
169 resize(width, height);
171 // Clear the window back to black
172 glClearColor(0.0, 0.0, 0.0, 1.0);
173 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
178 SGTexture::read_alpha_texture(const char *name)
181 SGTexture::ImageRec *image;
184 delete[] texture_data;
186 image = ImageOpen(name);
188 errstr = FILE_OPEN_ERROR;
192 texture_width = image->xsize;
193 texture_height = image->ysize;
195 // printf("image->zsize = %d\n", image->zsize);
197 if (image->zsize != 1) {
199 errstr = WRONG_COUNT;
203 texture_data = new GLubyte[ image->xsize * image->ysize ];
211 for(y=0; y<image->ysize; y++) {
212 ImageGetRow(image,lptr,y,0);
213 lptr += image->xsize;
219 SGTexture::read_rgb_texture(const char *name)
222 GLubyte *rbuf, *gbuf, *bbuf;
223 SGTexture::ImageRec *image;
226 delete[] texture_data;
228 image = ImageOpen(name);
230 errstr = FILE_OPEN_ERROR;
234 texture_width = image->xsize;
235 texture_height = image->ysize;
236 if (image->zsize < 1 || image->zsize > 4) {
238 errstr = WRONG_COUNT;
243 texture_data = new GLubyte[ image->xsize * image->ysize * num_colors ];
244 rbuf = new GLubyte[ image->xsize ];
245 gbuf = new GLubyte[ image->xsize ];
246 bbuf = new GLubyte[ image->xsize ];
247 if(!texture_data || !rbuf || !gbuf || !bbuf) {
248 delete[] texture_data;
252 errstr = OUT_OF_MEMORY;
257 for(y=0; y<image->ysize; y++) {
258 if(image->zsize == 4 || image->zsize == 3) {
259 ImageGetRow(image,rbuf,y,0);
260 ImageGetRow(image,gbuf,y,1);
261 ImageGetRow(image,bbuf,y,2);
263 ImageGetRow(image,rbuf,y,0);
264 memcpy(gbuf,rbuf,image->xsize);
265 memcpy(bbuf,rbuf,image->xsize);
267 rgbtorgb(rbuf,gbuf,bbuf,ptr,image->xsize);
268 ptr += (image->xsize * num_colors);
280 SGTexture::read_rgba_texture(const char *name)
283 GLubyte *rbuf, *gbuf, *bbuf, *abuf;
284 SGTexture::ImageRec *image;
287 delete[] texture_data;
289 image = ImageOpen(name);
291 errstr = FILE_OPEN_ERROR;
295 texture_width = image->xsize;
296 texture_height = image->ysize;
297 if (image->zsize < 1 || image->zsize > 4) {
299 errstr = WRONG_COUNT;
304 texture_data = new GLubyte[ image->xsize * image->ysize * num_colors ];
305 rbuf = new GLubyte[ image->xsize ];
306 gbuf = new GLubyte[ image->xsize ];
307 bbuf = new GLubyte[ image->xsize ];
308 abuf = new GLubyte[ image->xsize ];
309 if(!texture_data || !rbuf || !gbuf || !bbuf || !abuf) {
310 delete[] texture_data;
315 errstr = OUT_OF_MEMORY;
320 for(y=0; y<image->ysize; y++) {
321 if(image->zsize == 4) {
322 ImageGetRow(image,rbuf,y,0);
323 ImageGetRow(image,gbuf,y,1);
324 ImageGetRow(image,bbuf,y,2);
325 ImageGetRow(image,abuf,y,3);
326 } else if(image->zsize == 3) {
327 ImageGetRow(image,rbuf,y,0);
328 ImageGetRow(image,gbuf,y,1);
329 ImageGetRow(image,bbuf,y,2);
330 memset(abuf, 255, image->xsize);
331 } else if(image->zsize == 2) {
332 ImageGetRow(image,rbuf,y,0);
333 memcpy(gbuf,rbuf,image->xsize);
334 memcpy(bbuf,rbuf,image->xsize);
335 ImageGetRow(image,abuf,y,1);
337 ImageGetRow(image,rbuf,y,0);
338 memcpy(gbuf,rbuf,image->xsize);
339 memcpy(bbuf,rbuf,image->xsize);
340 memset(abuf, 255, image->xsize);
342 rgbatorgba(rbuf,gbuf,bbuf,abuf,ptr,image->xsize);
343 ptr += (image->xsize * num_colors);
354 SGTexture::read_raw_texture(const char *name)
357 SGTexture::ImageRec *image;
360 delete[] texture_data;
362 image = RawImageOpen(name);
365 errstr = FILE_OPEN_ERROR;
370 texture_height = 256;
372 texture_data = new GLubyte[ 256 * 256 * 3 ];
374 errstr = OUT_OF_MEMORY;
379 for(y=0; y<256; y++) {
380 gzread(image->file, ptr, 256*3);
387 SGTexture::read_r8_texture(const char *name)
391 SGTexture::ImageRec *image;
394 delete[] texture_data;
396 //it wouldn't make sense to write a new function ...
397 image = RawImageOpen(name);
400 errstr = FILE_OPEN_ERROR;
405 texture_height = 256;
407 texture_data = new GLubyte [ 256 * 256 * 3 ];
409 errstr = OUT_OF_MEMORY;
414 for(xy=0; xy<(256*256); xy++) {
415 gzread(image->file, c, 1);
417 //look in the table for the right colours
418 ptr[0]=msfs_colour[c[0]][0];
419 ptr[1]=msfs_colour[c[0]][1];
420 ptr[2]=msfs_colour[c[0]][2];
429 SGTexture::write_texture(const char *name) {
430 SGTexture::ImageRec *image = ImageWriteOpen(name);
432 for (int c=0; c<num_colors; c++) {
433 GLubyte *ptr = texture_data + c;
434 for (int y=0; y<texture_height; y++) {
435 for (int x=0; x<texture_width; x++) {
437 ptr = ptr + num_colors;
439 fwrite(image->tmp, 1, texture_width, file);
448 SGTexture::set_pixel(GLuint x, GLuint y, GLubyte *c)
455 unsigned int pos = (x + y*texture_width) * num_colors;
456 memcpy(texture_data+pos, c, num_colors);
461 SGTexture::get_pixel(GLuint x, GLuint y)
463 static GLubyte c[4] = {0, 0, 0, 0};
470 unsigned int pos = (x + y*texture_width)*num_colors;
471 memcpy(c, texture_data + pos, num_colors);
476 SGTexture::ImageRec *
477 SGTexture::ImageOpen(const char *fileName)
484 SGTexture::ImageRec *image;
488 endianTest.testWord = 1;
489 if (endianTest.testByte[0] == 1) {
495 image = new SGTexture::ImageRec;
496 memset(image, 0, sizeof(SGTexture::ImageRec));
498 errstr = OUT_OF_MEMORY;
501 if ((image->file = gzopen(fileName, "rb")) == 0) {
502 errstr = FILE_OPEN_ERROR;
506 gzread(image->file, image, 12);
509 ConvertShort(&image->imagic, 6);
512 image->tmp = new GLubyte[ image->xsize * 256 ];
513 if (image->tmp == 0) {
514 errstr = OUT_OF_MEMORY;
518 if ((image->type & 0xFF00) == 0x0100) {
519 x = image->ysize * image->zsize * (int) sizeof(unsigned);
520 image->rowStart = new unsigned[x];
521 image->rowSize = new int[x];
522 if (image->rowStart == 0 || image->rowSize == 0) {
523 errstr = OUT_OF_MEMORY;
526 image->rleEnd = 512 + (2 * x);
527 gzseek(image->file, 512, SEEK_SET);
528 gzread(image->file, image->rowStart, x);
529 gzread(image->file, image->rowSize, x);
531 ConvertUint(image->rowStart, x/(int) sizeof(unsigned));
532 ConvertUint((unsigned *)image->rowSize, x/(int) sizeof(int));
540 SGTexture::ImageClose(SGTexture::ImageRec *image) {
541 if (image->file) gzclose(image->file);
542 if (file) fclose(file);
544 delete[] image->rowStart;
545 delete[] image->rowSize;
549 SGTexture::ImageRec *
550 SGTexture::RawImageOpen(const char *fileName)
557 SGTexture::ImageRec *image;
560 endianTest.testWord = 1;
561 if (endianTest.testByte[0] == 1) {
567 image = new SGTexture::ImageRec;
568 memset(image, 0, sizeof(SGTexture::ImageRec));
570 errstr = OUT_OF_MEMORY;
573 if ((image->file = gzopen(fileName, "rb")) == 0) {
574 errstr = FILE_OPEN_ERROR;
578 gzread(image->file, image, 12);
581 ConvertShort(&image->imagic, 6);
585 //just allocate a pseudo value as I'm too lazy to change ImageClose()...
586 image->tmp = new GLubyte[1];
588 if (image->tmp == 0) {
589 errstr = OUT_OF_MEMORY;
596 SGTexture::ImageRec *
597 SGTexture::ImageWriteOpen(const char *fileName)
607 endianTest.testWord = 1;
608 if (endianTest.testByte[0] == 1) {
614 image = new SGTexture::ImageRec;
615 memset(image, 0, sizeof(SGTexture::ImageRec));
617 errstr = OUT_OF_MEMORY;
620 if ((file = fopen(fileName, "wb")) == 0) {
621 errstr = FILE_OPEN_ERROR;
626 image->type = 0x0001;
627 image->dim = (num_colors > 1) ? 3 : 2;
628 image->xsize = texture_width;
629 image->ysize = texture_height;
630 image->zsize = num_colors;
633 ConvertShort(&image->imagic, 6);
636 fwrite(image, 1, 12, file);
637 fseek(file, 512, SEEK_SET);
639 image->tmp = new GLubyte[ image->xsize * 256 ];
640 if (image->tmp == 0) {
641 errstr = OUT_OF_MEMORY;
645 if ((image->type & 0xFF00) == 0x0100) {
646 x = image->ysize * image->zsize * (int) sizeof(unsigned);
647 image->rowStart = new unsigned[x];
648 image->rowSize = new int[x];
649 if (image->rowStart == 0 || image->rowSize == 0) {
650 errstr = OUT_OF_MEMORY;
653 image->rleEnd = 512 + (2 * x);
654 fseek(file, 512, SEEK_SET);
655 fread(image->rowStart, 1, x, file);
656 fread(image->rowSize, 1, x, file);
658 ConvertUint(image->rowStart, x/(int) sizeof(unsigned));
659 ConvertUint((unsigned *)image->rowSize, x/(int) sizeof(int));
668 SGTexture::ImageGetRow(SGTexture::ImageRec *image, GLubyte *buf, int y, int z) {
669 GLubyte *iPtr, *oPtr, pixel;
672 if ((image->type & 0xFF00) == 0x0100) {
673 gzseek(image->file, (long) image->rowStart[y+z*image->ysize], SEEK_SET);
674 int size = image->rowSize[y+z*image->ysize];
675 gzread(image->file, image->tmp, size);
679 for (GLubyte *limit = iPtr + size; iPtr < limit;) {
681 count = (int)(pixel & 0x7F);
683 errstr = WRONG_COUNT;
687 while (iPtr < limit && count--) {
690 } else if (iPtr < limit) {
698 gzseek(image->file, 512+(y*image->xsize)+(z*image->xsize*image->ysize),
700 gzread(image->file, buf, image->xsize);
705 SGTexture::ImagePutRow(SGTexture::ImageRec *image, GLubyte *buf, int y, int z) {
706 GLubyte *iPtr, *oPtr, pixel;
709 if ((image->type & 0xFF00) == 0x0100) {
710 fseek(file, (long) image->rowStart[y+z*image->ysize], SEEK_SET);
711 fread( image->tmp, 1, (unsigned int)image->rowSize[y+z*image->ysize],
718 count = (int)(pixel & 0x7F);
720 errstr = WRONG_COUNT;
735 fseek(file, 512+(y*image->xsize)+(z*image->xsize*image->ysize),
737 fread(buf, 1, image->xsize, file);
743 SGTexture::rgbtorgb(GLubyte *r, GLubyte *g, GLubyte *b, GLubyte *l, int n) {
748 l += 3; r++; g++; b++;
753 SGTexture::rgbatorgba(GLubyte *r, GLubyte *g, GLubyte *b, GLubyte *a,
760 l += 4; r++; g++; b++; a++;
766 SGTexture::ConvertShort(unsigned short *array, unsigned int length) {
767 unsigned short b1, b2;
770 ptr = (unsigned char *)array;
774 *array++ = (b1 << 8) | (b2);
780 SGTexture::ConvertUint(unsigned *array, unsigned int length) {
781 unsigned int b1, b2, b3, b4;
784 ptr = (unsigned char *)array;
790 *array++ = (b1 << 24) | (b2 << 16) | (b3 << 8) | (b4);
796 SGTexture::make_monochrome(float contrast, GLubyte r, GLubyte g, GLubyte b) {
802 for (int y=0; y<texture_height; y++)
803 for (int x=0; x<texture_width; x++)
805 GLubyte *rgb = get_pixel(x,y);
806 GLubyte avg = (rgb[0] + rgb[1] + rgb[2]) / 3;
808 if (contrast != 1.0) {
809 float pixcol = -1.0 + (avg/128);
810 avg = 128 + int(128*pow(pixcol, contrast));
823 SGTexture::make_grayscale(float contrast) {
827 int colors = (num_colors == 3) ? 1 : 2;
828 GLubyte *map = new GLubyte[ texture_width * texture_height * colors ];
830 for (int y=0; y<texture_height; y++)
831 for (int x=0; x<texture_width; x++)
833 GLubyte *rgb = get_pixel(x,y);
834 GLubyte avg = (rgb[0] + rgb[1] + rgb[2]) / 3;
836 if (contrast != 1.0) {
837 float pixcol = -1.0 + (avg/128);
838 avg = 128 + int(128*pow(pixcol, contrast));
841 int pos = (x + y*texture_width)*colors;
847 delete[] texture_data;
854 SGTexture::make_maxcolorwindow() {
855 GLubyte minmaxc[2] = {255, 0};
858 int max = num_colors;
859 if (num_colors == 2) max = 1;
860 if (num_colors == 4) max = 3;
861 while (pos < texture_width * texture_height * num_colors) {
862 for (int i=0; i < max; i++) {
863 GLubyte c = texture_data[pos+i];
864 if (c < minmaxc[0]) minmaxc[0] = c;
865 if (c > minmaxc[1]) minmaxc[1] = c;
870 GLubyte offs = minmaxc[0];
871 float factor = 255.0 / float(minmaxc[1] - minmaxc[0]);
872 // printf("Min: %i, Max: %i, Factor: %f\n", offs, minmaxc[1], factor);
875 while (pos < texture_width * texture_height * num_colors) {
876 for (int i=0; i < max; i++) {
877 texture_data[pos+i] -= offs;
878 texture_data[pos+i] = int(factor * texture_data[pos+i]);
886 SGTexture::make_normalmap(float brightness, float contrast) {
887 make_grayscale(contrast);
888 make_maxcolorwindow();
890 int colors = (num_colors == 1) ? 3 : 4;
891 bool alpha = (colors > 3);
892 int tsize = texture_width * texture_height * colors;
893 GLubyte *map = new GLubyte[ tsize ];
895 int mpos = 0, dpos = 0;
896 for (int y=0; y<texture_height; y++) {
897 int ytw = y*texture_width;
899 for (int x=0; x<texture_width; x++)
901 int xp1 = (x < (texture_width-1)) ? x+1 : 0;
902 int yp1 = (y < (texture_height-1)) ? y+1 : 0;
903 int posxp1 = (xp1 + ytw)*num_colors;
904 int posyp1 = (x + yp1*texture_width)*num_colors;
907 GLubyte c = texture_data[dpos];
908 GLubyte cx1 = texture_data[posxp1];
909 GLubyte cy1 = texture_data[posyp1];
912 GLubyte a = texture_data[dpos+1];
913 GLubyte ax1 = texture_data[posxp1+1];
914 GLubyte ay1 = texture_data[posyp1+1];
923 fx = asin((c/256.0-cx1/256.0))/1.57079633;
924 fy = asin((cy1/256.0-c/256.0))/1.57079633;
926 map[mpos+0] = (GLuint)(fx*256.0)-128;
927 map[mpos+1] = (GLuint)(fy*256.0)-128;
928 map[mpos+2] = 127+int(brightness*128); // 255-c/2;
935 delete[] texture_data;
942 SGTexture::make_bumpmap(float brightness, float contrast) {
943 make_grayscale(contrast);
945 int colors = (num_colors == 1) ? 1 : 2;
946 GLubyte *map = new GLubyte[ texture_width * texture_height * colors ];
948 for (int y=0; y<texture_height; y++)
949 for (int x=0; x<texture_width; x++)
951 int mpos = (x + y*texture_width)*colors;
952 int dpos = (x + y*texture_width)*num_colors;
954 int xp1 = (x < (texture_width-1)) ? x+1 : 0;
955 int yp1 = (y < (texture_height-1)) ? y+1 : 0;
956 int posxp1 = (xp1 + y*texture_width)*num_colors;
957 int posyp1 = (x + yp1*texture_width)*num_colors;
959 map[mpos] = (127 - ((texture_data[dpos]-texture_data[posxp1]) -
960 ((texture_data[dpos]-texture_data[posyp1]))/4))/2;
962 map[mpos+1] = texture_data[dpos+1];
965 delete[] texture_data;