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()
41 SGTexture::SGTexture(unsigned int width, unsigned int height)
45 texture_data = new GLubyte[ width * height * 3 ];
48 SGTexture::~SGTexture()
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;
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, *abuf;
223 SGTexture::ImageRec *image;
229 image = ImageOpen(name);
231 errstr = FILE_OPEN_ERROR;
235 texture_width = image->xsize;
236 texture_height = image->ysize;
237 if (image->zsize != 3 && image->zsize != 4) {
239 errstr = WRONG_COUNT;
243 texture_data = new GLubyte[ image->xsize * image->ysize * 3 ];
245 rbuf = new GLubyte[ image->xsize ];
246 gbuf = new GLubyte[ image->xsize ];
247 bbuf = new GLubyte[ image->xsize ];
248 abuf = new GLubyte[ image->xsize ];
249 if(!texture_data || !rbuf || !gbuf || !bbuf || !abuf) {
255 errstr = OUT_OF_MEMORY;
260 for(y=0; y<image->ysize; y++) {
261 if(image->zsize == 4) {
262 ImageGetRow(image,rbuf,y,0);
263 ImageGetRow(image,gbuf,y,1);
264 ImageGetRow(image,bbuf,y,2);
265 ImageGetRow(image,abuf,y,3); // discard
266 rgbtorgb(rbuf,gbuf,bbuf,ptr,image->xsize);
267 ptr += (image->xsize * 3);
269 ImageGetRow(image,rbuf,y,0);
270 ImageGetRow(image,gbuf,y,1);
271 ImageGetRow(image,bbuf,y,2);
272 rgbtorgb(rbuf,gbuf,bbuf,ptr,image->xsize);
273 ptr += (image->xsize * 3);
287 SGTexture::read_rgba_texture(const char *name)
290 GLubyte *rbuf, *gbuf, *bbuf, *abuf;
291 SGTexture::ImageRec *image;
297 image = ImageOpen(name);
299 errstr = FILE_OPEN_ERROR;
303 texture_width = image->xsize;
304 texture_height = image->ysize;
305 if (image->zsize != 3 && image->zsize != 4) {
307 errstr = WRONG_COUNT;
311 texture_data = new GLubyte[ image->xsize * image->ysize * 4 ];
313 rbuf = new GLubyte[ image->xsize ];
314 gbuf = new GLubyte[ image->xsize ];
315 bbuf = new GLubyte[ image->xsize ];
316 abuf = new GLubyte[ image->xsize ];
317 if(!texture_data || !rbuf || !gbuf || !bbuf || !abuf) {
323 errstr = OUT_OF_MEMORY;
328 memset(abuf, 255, image->xsize);
329 for(y=0; y<image->ysize; y++) {
330 if(image->zsize == 4) {
331 ImageGetRow(image,rbuf,y,0);
332 ImageGetRow(image,gbuf,y,1);
333 ImageGetRow(image,bbuf,y,2);
334 ImageGetRow(image,abuf,y,3);
335 rgbatorgba(rbuf,gbuf,bbuf,abuf,ptr,image->xsize);
336 ptr += (image->xsize * 4);
338 ImageGetRow(image,rbuf,y,0);
339 ImageGetRow(image,gbuf,y,1);
340 ImageGetRow(image,bbuf,y,2);
341 rgbatorgba(rbuf,gbuf,bbuf,abuf,ptr,image->xsize);
342 ptr += (image->xsize * 3);
354 SGTexture::read_raw_texture(const char *name)
357 SGTexture::ImageRec *image;
363 image = RawImageOpen(name);
366 errstr = FILE_OPEN_ERROR;
371 texture_height = 256;
373 texture_data = new GLubyte[ 256 * 256 * 3 ];
375 errstr = OUT_OF_MEMORY;
380 for(y=0; y<256; y++) {
381 gzread(image->gzfile, ptr, 256*3);
388 SGTexture::read_r8_texture(const char *name)
392 SGTexture::ImageRec *image;
398 //it wouldn't make sense to write a new function ...
399 image = RawImageOpen(name);
402 errstr = FILE_OPEN_ERROR;
407 texture_height = 256;
409 texture_data = new GLubyte [ 256 * 256 * 3 ];
411 errstr = OUT_OF_MEMORY;
416 for(xy=0; xy<(256*256); xy++) {
417 gzread(image->gzfile, c, 1);
419 //look in the table for the right colours
420 ptr[0]=msfs_colour[c[0]][0];
421 ptr[1]=msfs_colour[c[0]][1];
422 ptr[2]=msfs_colour[c[0]][2];
431 SGTexture::write_texture(const char *name) {
432 SGTexture::ImageRec *image;
435 image=ImageWriteOpen(name);
437 GLubyte *ptr = texture_data;
438 for (y=0; y<texture_height; y++) {
439 for (x=0; x<texture_width; x++) {
441 ptr = ptr + num_colors;
443 fwrite(image->tmp, 1, texture_width, image->file);
446 if (num_colors > 1) {
447 ptr = texture_data + 1;
448 for (y=0; y<texture_height; y++) {
449 for (x=0; x<texture_width; x++) {
451 ptr = ptr + num_colors;
453 fwrite(image->tmp, 1, texture_width, image->file);
456 if (num_colors > 2) {
457 ptr = texture_data + 2;
458 for (y=0; y<texture_height; y++) {
459 for (x=0; x<texture_width; x++) {
461 ptr = ptr + num_colors;
463 fwrite(image->tmp, 1, texture_width, image->file);
466 if (num_colors > 3) {
467 ptr = texture_data + 3;
468 for (y=0; y<texture_height; y++) {
469 for (x=0; x<texture_width; x++) {
471 ptr = ptr + num_colors;
473 fwrite(image->tmp, 1, texture_width, image->file);
484 SGTexture::set_pixel(GLuint x, GLuint y, sgVec3 &c)
491 unsigned int pos = (x + y*texture_width)*3;
492 texture_data[pos] = (unsigned char)(c[0] * 256);
493 texture_data[pos+1] = (unsigned char)(c[1] * 256);
494 texture_data[pos+2] = (unsigned char)(c[2] * 256);
499 SGTexture::get_pixel(GLuint x, GLuint y)
503 sgSetVec3(c, 0, 0, 0);
509 unsigned int pos = (x + y*texture_width)*3;
511 sgSetVec3(c, texture_data[pos], texture_data[pos+1], texture_data[pos+2]);
516 SGTexture::ImageRec *
517 SGTexture::ImageOpen(const char *fileName)
524 SGTexture::ImageRec *image;
528 endianTest.testWord = 1;
529 if (endianTest.testByte[0] == 1) {
535 image = new SGTexture::ImageRec;
536 memset(image, 0, sizeof(SGTexture::ImageRec));
538 errstr = OUT_OF_MEMORY;
541 if ((image->gzfile = gzopen(fileName, "rb")) == 0) {
542 errstr = FILE_OPEN_ERROR;
546 gzread(image->gzfile, image, 12);
549 ConvertShort(&image->imagic, 6);
552 image->tmp = new GLubyte[ image->xsize * 256 ];
553 if (image->tmp == 0) {
554 errstr = OUT_OF_MEMORY;
558 if ((image->type & 0xFF00) == 0x0100) {
559 x = image->ysize * image->zsize * (int) sizeof(unsigned);
560 image->rowStart = new unsigned[x];
561 image->rowSize = new int[x];
562 if (image->rowStart == 0 || image->rowSize == 0) {
563 errstr = OUT_OF_MEMORY;
566 image->rleEnd = 512 + (2 * x);
567 gzseek(image->gzfile, 512, SEEK_SET);
568 gzread(image->gzfile, image->rowStart, x);
569 gzread(image->gzfile, image->rowSize, x);
571 ConvertUint(image->rowStart, x/(int) sizeof(unsigned));
572 ConvertUint((unsigned *)image->rowSize, x/(int) sizeof(int));
580 SGTexture::ImageClose(SGTexture::ImageRec *image) {
581 if (image->gzfile) gzclose(image->gzfile);
582 if (image->file) fclose(image->file);
587 SGTexture::ImageRec *
588 SGTexture::RawImageOpen(const char *fileName)
595 SGTexture::ImageRec *image;
598 endianTest.testWord = 1;
599 if (endianTest.testByte[0] == 1) {
605 image = new SGTexture::ImageRec;
606 memset(image, 0, sizeof(SGTexture::ImageRec));
608 errstr = OUT_OF_MEMORY;
611 if ((image->gzfile = gzopen(fileName, "rb")) == 0) {
612 errstr = FILE_OPEN_ERROR;
616 gzread(image->gzfile, image, 12);
619 ConvertShort(&image->imagic, 6);
623 //just allocate a pseudo value as I'm too lazy to change ImageClose()...
624 image->tmp = new GLubyte;
626 if (image->tmp == 0) {
627 errstr = OUT_OF_MEMORY;
634 SGTexture::ImageRec *
635 SGTexture::ImageWriteOpen(const char *fileName)
645 endianTest.testWord = 1;
646 if (endianTest.testByte[0] == 1) {
652 image = new SGTexture::ImageRec;
653 memset(image, 0, sizeof(SGTexture::ImageRec));
655 errstr = OUT_OF_MEMORY;
658 if ((image->file = fopen(fileName, "w")) == 0) {
659 errstr = FILE_OPEN_ERROR;
664 image->type = 0x0001;
666 image->xsize = texture_width;
667 image->ysize = texture_height;
668 image->zsize = num_colors;
670 fwrite(image, 1, 12, image->file);
672 fseek(image->file, 512, SEEK_SET);
674 ConvertShort(&image->imagic, 6);
677 image->tmp = new GLubyte[ image->xsize * 256 ];
678 if (image->tmp == 0) {
679 errstr = OUT_OF_MEMORY;
683 if ((image->type & 0xFF00) == 0x0100) {
684 x = image->ysize * image->zsize * (int) sizeof(unsigned);
685 image->rowStart = new unsigned[x];
686 image->rowSize = new int[x];
687 if (image->rowStart == 0 || image->rowSize == 0) {
688 errstr = OUT_OF_MEMORY;
691 image->rleEnd = 512 + (2 * x);
692 fseek(image->file, 512, SEEK_SET);
693 fread(image->rowStart, 1, x, image->file);
694 fread(image->rowSize, 1, x, image->file);
696 ConvertUint(image->rowStart, x/(int) sizeof(unsigned));
697 ConvertUint((unsigned *)image->rowSize, x/(int) sizeof(int));
705 SGTexture::ImageGetRow(SGTexture::ImageRec *image, GLubyte *buf, int y, int z) {
706 GLubyte *iPtr, *oPtr, pixel;
709 if ((image->type & 0xFF00) == 0x0100) {
710 gzseek(image->gzfile, (long) image->rowStart[y+z*image->ysize], SEEK_SET);
711 gzread(image->gzfile, image->tmp,
712 (unsigned int)image->rowSize[y+z*image->ysize]);
718 count = (int)(pixel & 0x7F);
720 errstr = WRONG_COUNT;
735 gzseek(image->gzfile, 512+(y*image->xsize)+(z*image->xsize*image->ysize),
737 gzread(image->gzfile, buf, image->xsize);
742 SGTexture::ImagePutRow(SGTexture::ImageRec *image, GLubyte *buf, int y, int z) {
743 GLubyte *iPtr, *oPtr, pixel;
746 if ((image->type & 0xFF00) == 0x0100) {
747 fseek(image->file, (long) image->rowStart[y+z*image->ysize], SEEK_SET);
748 fread( image->tmp, 1, (unsigned int)image->rowSize[y+z*image->ysize],
755 count = (int)(pixel & 0x7F);
757 errstr = WRONG_COUNT;
772 fseek(image->file, 512+(y*image->xsize)+(z*image->xsize*image->ysize),
774 fread(buf, 1, image->xsize, image->file);
780 SGTexture::rgbtorgb(GLubyte *r, GLubyte *g, GLubyte *b, GLubyte *l, int n) {
785 l += 3; r++; g++; b++;
790 SGTexture::rgbatorgba(GLubyte *r, GLubyte *g, GLubyte *b, GLubyte *a,
797 l += 4; r++; g++; b++; a++;
803 SGTexture::ConvertShort(unsigned short *array, unsigned int length) {
804 unsigned short b1, b2;
807 ptr = (unsigned char *)array;
811 *array++ = (b1 << 8) | (b2);
817 SGTexture::ConvertUint(unsigned *array, unsigned int length) {
818 unsigned int b1, b2, b3, b4;
821 ptr = (unsigned char *)array;
827 *array++ = (b1 << 24) | (b2 << 16) | (b3 << 8) | (b4);