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>
24 #include "texture.hxx"
28 const char *FILE_OPEN_ERROR = "Unable to open file.";
29 const char *WRONG_COUNT = "Unsupported number of color channels.";
30 const char *NO_TEXTURE = "No texture data resident.";
31 const char *OUT_OF_MEMORY = "Out of memory.";
34 SGTexture::SGTexture()
42 SGTexture::SGTexture(unsigned int width, unsigned int height)
46 texture_data = new GLubyte[ width * height * 3 ];
49 SGTexture::~SGTexture()
55 if ( texture_id != 0 ) {
66 glGenTextures(1, &texture_id);
68 #elif GL_EXT_texture_object
69 glGenTexturesEXT(1, &texture_id);
75 glBindTexture(GL_TEXTURE_2D, texture_id);
77 #elif GL_EXT_texture_object
78 glBindTextureEXT(GL_TEXTURE_2D, texture_id);
82 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
83 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
84 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
85 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
90 * A function to resize the OpenGL window which will be used by
91 * the dynamic texture generating routines.
93 * @param width The width of the new window
94 * @param height The height of the new window
97 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);
116 // Do the perspective calculations
117 gluPerspective(45.0, aspect, 1.0, 400.0);
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;
187 image = ImageOpen(name);
189 errstr = FILE_OPEN_ERROR;
193 texture_width = image->xsize;
194 texture_height = image->ysize;
196 // printf("image->zsize = %d\n", image->zsize);
198 if (image->zsize != 1) {
200 errstr = WRONG_COUNT;
204 texture_data = new GLubyte[ image->xsize * image->ysize ];
212 for(y=0; y<image->ysize; y++) {
213 ImageGetRow(image,lptr,y,0);
214 lptr += image->xsize;
220 SGTexture::read_rgb_texture(const char *name)
223 GLubyte *rbuf, *gbuf, *bbuf, *abuf;
224 SGTexture::ImageRec *image;
230 image = ImageOpen(name);
232 errstr = FILE_OPEN_ERROR;
236 texture_width = image->xsize;
237 texture_height = image->ysize;
238 if (image->zsize != 3 && image->zsize != 4) {
240 errstr = WRONG_COUNT;
244 texture_data = new GLubyte[ image->xsize * image->ysize * 3 ];
246 rbuf = new GLubyte[ image->xsize ];
247 gbuf = new GLubyte[ image->xsize ];
248 bbuf = new GLubyte[ image->xsize ];
249 abuf = new GLubyte[ image->xsize ];
250 if(!texture_data || !rbuf || !gbuf || !bbuf || !abuf) {
256 errstr = OUT_OF_MEMORY;
261 for(y=0; y<image->ysize; y++) {
262 if(image->zsize == 4) {
263 ImageGetRow(image,rbuf,y,0);
264 ImageGetRow(image,gbuf,y,1);
265 ImageGetRow(image,bbuf,y,2);
266 ImageGetRow(image,abuf,y,3); // discard
267 rgbtorgb(rbuf,gbuf,bbuf,ptr,image->xsize);
268 ptr += (image->xsize * 3);
270 ImageGetRow(image,rbuf,y,0);
271 ImageGetRow(image,gbuf,y,1);
272 ImageGetRow(image,bbuf,y,2);
273 rgbtorgb(rbuf,gbuf,bbuf,ptr,image->xsize);
274 ptr += (image->xsize * 3);
288 SGTexture::read_rgba_texture(const char *name)
291 GLubyte *rbuf, *gbuf, *bbuf, *abuf;
292 SGTexture::ImageRec *image;
298 image = ImageOpen(name);
300 errstr = FILE_OPEN_ERROR;
304 texture_width = image->xsize;
305 texture_height = image->ysize;
306 if (image->zsize != 3 && image->zsize != 4) {
308 errstr = WRONG_COUNT;
312 texture_data = new GLubyte[ image->xsize * image->ysize * 4 ];
314 rbuf = new GLubyte[ image->xsize ];
315 gbuf = new GLubyte[ image->xsize ];
316 bbuf = new GLubyte[ image->xsize ];
317 abuf = new GLubyte[ image->xsize ];
318 if(!texture_data || !rbuf || !gbuf || !bbuf || !abuf) {
324 errstr = OUT_OF_MEMORY;
329 memset(abuf, 255, image->xsize);
330 for(y=0; y<image->ysize; y++) {
331 if(image->zsize == 4) {
332 ImageGetRow(image,rbuf,y,0);
333 ImageGetRow(image,gbuf,y,1);
334 ImageGetRow(image,bbuf,y,2);
335 ImageGetRow(image,abuf,y,3);
336 rgbatorgba(rbuf,gbuf,bbuf,abuf,ptr,image->xsize);
337 ptr += (image->xsize * 4);
339 ImageGetRow(image,rbuf,y,0);
340 ImageGetRow(image,gbuf,y,1);
341 ImageGetRow(image,bbuf,y,2);
342 rgbatorgba(rbuf,gbuf,bbuf,abuf,ptr,image->xsize);
343 ptr += (image->xsize * 3);
355 SGTexture::read_raw_texture(const char *name)
358 SGTexture::ImageRec *image;
364 image = RawImageOpen(name);
367 errstr = FILE_OPEN_ERROR;
372 texture_height = 256;
374 texture_data = new GLubyte[ 256 * 256 * 3 ];
376 errstr = OUT_OF_MEMORY;
381 for(y=0; y<256; y++) {
382 gzread(image->file, ptr, 256*3);
389 SGTexture::read_r8_texture(const char *name)
393 SGTexture::ImageRec *image;
399 //it wouldn't make sense to write a new function ...
400 image = RawImageOpen(name);
403 errstr = FILE_OPEN_ERROR;
408 texture_height = 256;
410 texture_data = new GLubyte [ 256 * 256 * 3 ];
412 errstr = OUT_OF_MEMORY;
417 for(xy=0; xy<(256*256); xy++) {
418 gzread(image->file, c, 1);
420 //look in the table for the right colours
421 ptr[0]=msfs_colour[c[0]][0];
422 ptr[1]=msfs_colour[c[0]][1];
423 ptr[2]=msfs_colour[c[0]][2];
432 SGTexture::write_texture(const char *name) {
433 SGTexture::ImageRec *image = ImageWriteOpen(name);
435 for (int c=0; c<num_colors; c++) {
436 GLubyte *ptr = texture_data + c;
437 for (int y=0; y<texture_height; y++) {
438 for (int x=0; x<texture_width; x++) {
440 ptr = ptr + num_colors;
442 fwrite(image->tmp, 1, texture_width, file);
451 SGTexture::set_pixel(GLuint x, GLuint y, GLubyte *c)
458 unsigned int pos = (x + y*texture_width) * num_colors;
459 memcpy(texture_data+pos, c, num_colors);
464 SGTexture::get_pixel(GLuint x, GLuint y)
466 static GLubyte c[4] = {0, 0, 0, 0};
473 unsigned int pos = (x + y*texture_width)*num_colors;
474 memcpy(c, texture_data + pos, num_colors);
479 SGTexture::ImageRec *
480 SGTexture::ImageOpen(const char *fileName)
487 SGTexture::ImageRec *image;
491 endianTest.testWord = 1;
492 if (endianTest.testByte[0] == 1) {
498 image = new SGTexture::ImageRec;
499 memset(image, 0, sizeof(SGTexture::ImageRec));
501 errstr = OUT_OF_MEMORY;
504 if ((image->file = gzopen(fileName, "rb")) == 0) {
505 errstr = FILE_OPEN_ERROR;
509 gzread(image->file, image, 12);
512 ConvertShort(&image->imagic, 6);
515 image->tmp = new GLubyte[ image->xsize * 256 ];
516 if (image->tmp == 0) {
517 errstr = OUT_OF_MEMORY;
521 if ((image->type & 0xFF00) == 0x0100) {
522 x = image->ysize * image->zsize * (int) sizeof(unsigned);
523 image->rowStart = new unsigned[x];
524 image->rowSize = new int[x];
525 if (image->rowStart == 0 || image->rowSize == 0) {
526 errstr = OUT_OF_MEMORY;
529 image->rleEnd = 512 + (2 * x);
530 gzseek(image->file, 512, SEEK_SET);
531 gzread(image->file, image->rowStart, x);
532 gzread(image->file, image->rowSize, x);
534 ConvertUint(image->rowStart, x/(int) sizeof(unsigned));
535 ConvertUint((unsigned *)image->rowSize, x/(int) sizeof(int));
543 SGTexture::ImageClose(SGTexture::ImageRec *image) {
544 if (image->file) gzclose(image->file);
545 if (file) fclose(file);
550 SGTexture::ImageRec *
551 SGTexture::RawImageOpen(const char *fileName)
558 SGTexture::ImageRec *image;
561 endianTest.testWord = 1;
562 if (endianTest.testByte[0] == 1) {
568 image = new SGTexture::ImageRec;
569 memset(image, 0, sizeof(SGTexture::ImageRec));
571 errstr = OUT_OF_MEMORY;
574 if ((image->file = gzopen(fileName, "rb")) == 0) {
575 errstr = FILE_OPEN_ERROR;
579 gzread(image->file, image, 12);
582 ConvertShort(&image->imagic, 6);
586 //just allocate a pseudo value as I'm too lazy to change ImageClose()...
587 image->tmp = new GLubyte;
589 if (image->tmp == 0) {
590 errstr = OUT_OF_MEMORY;
597 SGTexture::ImageRec *
598 SGTexture::ImageWriteOpen(const char *fileName)
608 endianTest.testWord = 1;
609 if (endianTest.testByte[0] == 1) {
615 image = new SGTexture::ImageRec;
616 memset(image, 0, sizeof(SGTexture::ImageRec));
618 errstr = OUT_OF_MEMORY;
621 if ((file = fopen(fileName, "wb")) == 0) {
622 errstr = FILE_OPEN_ERROR;
627 image->type = 0x0001;
628 image->dim = (num_colors > 1) ? 3 : 2;
629 image->xsize = texture_width;
630 image->ysize = texture_height;
631 image->zsize = num_colors;
633 fwrite(image, 1, 12, file);
635 fseek(file, 512, SEEK_SET);
638 ConvertShort(&image->imagic, 6);
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 fwrite(image->rowStart, 1, x, file);
658 fwrite(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;
820 SGTexture::make_grayscale(float contrast) {
824 GLubyte *map = (GLubyte *)malloc (texture_width * texture_height);
825 for (int y=0; y<texture_height; y++)
826 for (int x=0; x<texture_width; x++)
828 GLubyte *rgb = get_pixel(x,y);
829 GLubyte avg = (rgb[0] + rgb[1] + rgb[2]) / 3;
831 map[x +y*texture_height] = avg;
841 SGTexture::make_normalmap(float brightness, float contrast) {
842 make_grayscale(contrast);
844 GLubyte *map = (GLubyte *)malloc (texture_width * texture_height * 3);
846 for (int y=0; y<texture_height; y++)
847 for (int x=0; x<texture_width; x++)
849 int mpos = (x + y*texture_width)*3;
850 int dpos = (x + y*texture_width)*num_colors;
852 int xp1 = (x < (texture_width-1)) ? x+1 : 0;
853 int yp1 = (y < (texture_height-1)) ? y+1 : 0;
854 int posxp1 = (xp1 + y*texture_width)*num_colors;
855 int posyp1 = (x + yp1*texture_width)*num_colors;
857 map[mpos+0] = (128+(texture_data[posxp1]-texture_data[dpos])/2);
858 map[mpos+1] = (128+(texture_data[posyp1]-texture_data[dpos])/2);
859 map[mpos+2] = 127 + GLubyte(128*brightness);