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()
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)
100 // Make sure that we don't get a divide by zero exception
104 // Set the viewport for the OpenGL window
105 glViewport(0, 0, width, height);
107 // Calculate the aspect ratio of the window
108 aspect = width/height;
110 // Go to the projection matrix, this gets modified by the perspective
112 glMatrixMode(GL_PROJECTION);
115 // Do the perspective calculations
116 gluPerspective(45.0, aspect, 1.0, 400.0);
118 // Return to the modelview matrix
119 glMatrixMode(GL_MODELVIEW);
123 * A function to prepare the OpenGL state machine for dynamic
124 * texture generation.
126 * @param width The width of the texture
127 * @param height The height of the texture
130 SGTexture::prepare(unsigned int width, unsigned int height) {
132 texture_width = width;
133 texture_height = height;
135 // Resize the OpenGL window to the size of our dynamic texture
136 resize(texture_width, texture_height);
138 glClearColor(0.0, 0.0, 0.0, 1.0);
142 * A function to generate the dynamic texture.
144 * The actual texture can be accessed by calling get_texture()
146 * @param width The width of the previous OpenGL window
147 * @param height The height of the previous OpenGL window
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.
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);
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);
167 // Set the OpenGL window back to its previous size
168 resize(width, height);
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);
177 SGTexture::read_alpha_texture(const char *name)
180 SGTexture::ImageRec *image;
183 delete[] texture_data;
185 image = ImageOpen(name);
187 errstr = FILE_OPEN_ERROR;
191 texture_width = image->xsize;
192 texture_height = image->ysize;
194 // printf("image->zsize = %d\n", image->zsize);
196 if (image->zsize != 1) {
198 errstr = WRONG_COUNT;
202 texture_data = new GLubyte[ image->xsize * image->ysize ];
210 for(y=0; y<image->ysize; y++) {
211 ImageGetRow(image,lptr,y,0);
212 lptr += image->xsize;
218 SGTexture::read_rgb_texture(const char *name)
221 GLubyte *rbuf, *gbuf, *bbuf, *abuf;
222 SGTexture::ImageRec *image;
225 delete[] texture_data;
227 image = ImageOpen(name);
229 errstr = FILE_OPEN_ERROR;
233 texture_width = image->xsize;
234 texture_height = image->ysize;
235 if (image->zsize != 3 && image->zsize != 4) {
237 errstr = WRONG_COUNT;
241 texture_data = new GLubyte[ image->xsize * image->ysize * 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;
253 errstr = OUT_OF_MEMORY;
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);
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);
285 SGTexture::read_rgba_texture(const char *name)
288 GLubyte *rbuf, *gbuf, *bbuf, *abuf;
289 SGTexture::ImageRec *image;
292 delete[] texture_data;
294 image = ImageOpen(name);
296 errstr = FILE_OPEN_ERROR;
300 texture_width = image->xsize;
301 texture_height = image->ysize;
302 if (image->zsize != 3 && image->zsize != 4) {
304 errstr = WRONG_COUNT;
308 texture_data = new GLubyte[ image->xsize * image->ysize * 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;
320 errstr = OUT_OF_MEMORY;
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);
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);
351 SGTexture::read_raw_texture(const char *name)
354 SGTexture::ImageRec *image;
357 delete[] texture_data;
359 image = RawImageOpen(name);
362 errstr = FILE_OPEN_ERROR;
367 texture_height = 256;
369 texture_data = new GLubyte[ 256 * 256 * 3 ];
371 errstr = OUT_OF_MEMORY;
376 for(y=0; y<256; y++) {
377 gzread(image->file, ptr, 256*3);
384 SGTexture::read_r8_texture(const char *name)
388 SGTexture::ImageRec *image;
391 delete[] texture_data;
393 //it wouldn't make sense to write a new function ...
394 image = RawImageOpen(name);
397 errstr = FILE_OPEN_ERROR;
402 texture_height = 256;
404 texture_data = new GLubyte [ 256 * 256 * 3 ];
406 errstr = OUT_OF_MEMORY;
411 for(xy=0; xy<(256*256); xy++) {
412 gzread(image->file, c, 1);
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];
426 SGTexture::write_texture(const char *name) {
427 SGTexture::ImageRec *image = ImageWriteOpen(name);
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++) {
434 ptr = ptr + num_colors;
436 fwrite(image->tmp, 1, texture_width, file);
445 SGTexture::set_pixel(GLuint x, GLuint y, GLubyte *c)
452 unsigned int pos = (x + y*texture_width) * num_colors;
453 memcpy(texture_data+pos, c, num_colors);
458 SGTexture::get_pixel(GLuint x, GLuint y)
460 static GLubyte c[4] = {0, 0, 0, 0};
467 unsigned int pos = (x + y*texture_width)*num_colors;
468 memcpy(c, texture_data + pos, num_colors);
473 SGTexture::ImageRec *
474 SGTexture::ImageOpen(const char *fileName)
481 SGTexture::ImageRec *image;
485 endianTest.testWord = 1;
486 if (endianTest.testByte[0] == 1) {
492 image = new SGTexture::ImageRec;
493 memset(image, 0, sizeof(SGTexture::ImageRec));
495 errstr = OUT_OF_MEMORY;
498 if ((image->file = gzopen(fileName, "rb")) == 0) {
499 errstr = FILE_OPEN_ERROR;
503 gzread(image->file, image, 12);
506 ConvertShort(&image->imagic, 6);
509 image->tmp = new GLubyte[ image->xsize * 256 ];
510 if (image->tmp == 0) {
511 errstr = OUT_OF_MEMORY;
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;
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);
528 ConvertUint(image->rowStart, x/(int) sizeof(unsigned));
529 ConvertUint((unsigned *)image->rowSize, x/(int) sizeof(int));
537 SGTexture::ImageClose(SGTexture::ImageRec *image) {
538 if (image->file) gzclose(image->file);
539 if (file) fclose(file);
541 delete[] image->rowStart;
542 delete[] image->rowSize;
546 SGTexture::ImageRec *
547 SGTexture::RawImageOpen(const char *fileName)
554 SGTexture::ImageRec *image;
557 endianTest.testWord = 1;
558 if (endianTest.testByte[0] == 1) {
564 image = new SGTexture::ImageRec;
565 memset(image, 0, sizeof(SGTexture::ImageRec));
567 errstr = OUT_OF_MEMORY;
570 if ((image->file = gzopen(fileName, "rb")) == 0) {
571 errstr = FILE_OPEN_ERROR;
575 gzread(image->file, image, 12);
578 ConvertShort(&image->imagic, 6);
582 //just allocate a pseudo value as I'm too lazy to change ImageClose()...
583 image->tmp = new GLubyte[1];
585 if (image->tmp == 0) {
586 errstr = OUT_OF_MEMORY;
593 SGTexture::ImageRec *
594 SGTexture::ImageWriteOpen(const char *fileName)
604 endianTest.testWord = 1;
605 if (endianTest.testByte[0] == 1) {
611 image = new SGTexture::ImageRec;
612 memset(image, 0, sizeof(SGTexture::ImageRec));
614 errstr = OUT_OF_MEMORY;
617 if ((file = fopen(fileName, "wb")) == 0) {
618 errstr = FILE_OPEN_ERROR;
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;
630 ConvertShort(&image->imagic, 6);
633 fwrite(image, 1, 12, file);
634 fseek(file, 512, SEEK_SET);
636 image->tmp = new GLubyte[ image->xsize * 256 ];
637 if (image->tmp == 0) {
638 errstr = OUT_OF_MEMORY;
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;
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);
655 ConvertUint(image->rowStart, x/(int) sizeof(unsigned));
656 ConvertUint((unsigned *)image->rowSize, x/(int) sizeof(int));
665 SGTexture::ImageGetRow(SGTexture::ImageRec *image, GLubyte *buf, int y, int z) {
666 GLubyte *iPtr, *oPtr, pixel;
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]);
678 count = (int)(pixel & 0x7F);
680 errstr = WRONG_COUNT;
695 gzseek(image->file, 512+(y*image->xsize)+(z*image->xsize*image->ysize),
697 gzread(image->file, buf, image->xsize);
702 SGTexture::ImagePutRow(SGTexture::ImageRec *image, GLubyte *buf, int y, int z) {
703 GLubyte *iPtr, *oPtr, pixel;
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],
715 count = (int)(pixel & 0x7F);
717 errstr = WRONG_COUNT;
732 fseek(file, 512+(y*image->xsize)+(z*image->xsize*image->ysize),
734 fread(buf, 1, image->xsize, file);
740 SGTexture::rgbtorgb(GLubyte *r, GLubyte *g, GLubyte *b, GLubyte *l, int n) {
745 l += 3; r++; g++; b++;
750 SGTexture::rgbatorgba(GLubyte *r, GLubyte *g, GLubyte *b, GLubyte *a,
757 l += 4; r++; g++; b++; a++;
763 SGTexture::ConvertShort(unsigned short *array, unsigned int length) {
764 unsigned short b1, b2;
767 ptr = (unsigned char *)array;
771 *array++ = (b1 << 8) | (b2);
777 SGTexture::ConvertUint(unsigned *array, unsigned int length) {
778 unsigned int b1, b2, b3, b4;
781 ptr = (unsigned char *)array;
787 *array++ = (b1 << 24) | (b2 << 16) | (b3 << 8) | (b4);
793 SGTexture::make_monochrome(float contrast, GLubyte r, GLubyte g, GLubyte b) {
799 for (int y=0; y<texture_height; y++)
800 for (int x=0; x<texture_width; x++)
802 GLubyte *rgb = get_pixel(x,y);
803 GLubyte avg = (rgb[0] + rgb[1] + rgb[2]) / 3;
805 if (contrast != 1.0) {
806 float pixcol = -1.0 + (avg/128);
807 avg = 128 + int(128*pow(pixcol, contrast));
820 SGTexture::make_grayscale(float contrast) {
824 int colors = (num_colors == 3) ? 1 : 2;
825 GLubyte *map = new GLubyte[ texture_width * texture_height * colors ];
827 for (int y=0; y<texture_height; y++)
828 for (int x=0; x<texture_width; x++)
830 GLubyte *rgb = get_pixel(x,y);
831 GLubyte avg = (rgb[0] + rgb[1] + rgb[2]) / 3;
833 if (contrast != 1.0) {
834 float pixcol = -1.0 + (avg/128);
835 avg = 128 + int(128*pow(pixcol, contrast));
838 int pos = (x + y*texture_width)*colors;
844 delete[] texture_data;
851 SGTexture::make_maxcolorwindow() {
852 GLubyte minmaxc[2] = {255, 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;
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);
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]);
883 SGTexture::make_normalmap(float brightness, float contrast) {
884 make_grayscale(contrast);
885 make_maxcolorwindow();
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 ];
892 int mpos = 0, dpos = 0;
893 for (int y=0; y<texture_height; y++) {
894 int ytw = y*texture_width;
896 for (int x=0; x<texture_width; x++)
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;
903 GLubyte c = texture_data[dpos];
904 GLubyte cx1 = texture_data[posxp1];
905 GLubyte cy1 = texture_data[posyp1];
908 GLubyte a = texture_data[dpos+1];
909 GLubyte ax1 = texture_data[posxp1+1];
910 GLubyte ay1 = texture_data[posyp1+1];
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;
928 delete[] texture_data;
935 SGTexture::make_bumpmap(float brightness, float contrast) {
936 make_grayscale(contrast);
938 int colors = (num_colors == 1) ? 1 : 2;
939 GLubyte *map = new GLubyte[ texture_width * texture_height * colors ];
941 for (int y=0; y<texture_height; y++)
942 for (int x=0; x<texture_width; x++)
944 int mpos = (x + y*texture_width)*colors;
945 int dpos = (x + y*texture_width)*num_colors;
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;
952 map[mpos] = (127 - ((texture_data[dpos]-texture_data[posxp1]) -
953 ((texture_data[dpos]-texture_data[posyp1]))/4))/2;
955 map[mpos+1] = texture_data[dpos+1];
958 delete[] texture_data;