--- /dev/null
+/*
+ * Texture manipulation routines
+ *
+ * Copyright (c) Mark J. Kilgard, 1997.
+ * Code added in april 2003 by Erik Hofman
+ *
+ * This program is freely distributable without licensing fees
+ * and is provided without guarantee or warrantee expressed or
+ * implied. This program is -not- in the public domain.
+ *
+ * $Id$
+ */
+
+#include <simgear/compiler.h>
+
+#ifdef WIN32
+# include <windows.h>
+#endif
+
+#include <osg/GLU>
+
+#include <math.h>
+#include <zlib.h>
+
+#include "texture.hxx"
+#include "colours.h"
+
+
+const char *FILE_OPEN_ERROR = "Unable to open file.";
+const char *WRONG_COUNT = "Unsupported number of color channels.";
+const char *NO_TEXTURE = "No texture data resident.";
+const char *OUT_OF_MEMORY = "Out of memory.";
+
+
+SGTexture::SGTexture()
+ : texture_id(0),
+ texture_data(0),
+ num_colors(3),
+ file(0)
+{
+}
+
+SGTexture::SGTexture(unsigned int width, unsigned int height)
+ : texture_id(0),
+ errstr("")
+{
+ texture_data = new GLubyte[ width * height * 3 ];
+}
+
+SGTexture::~SGTexture()
+{
+ delete[] texture_data;
+
+ if ( texture_id != 0 ) {
+ free_id();
+ }
+}
+
+void
+SGTexture::bind()
+{
+ bool gen = false;
+ if (!texture_id) {
+#ifdef GL_VERSION_1_1
+ glGenTextures(1, &texture_id);
+
+#elif GL_EXT_texture_object
+ glGenTexturesEXT(1, &texture_id);
+#endif
+ gen = true;
+ }
+
+#ifdef GL_VERSION_1_1
+ glBindTexture(GL_TEXTURE_2D, texture_id);
+
+#elif GL_EXT_texture_object
+ glBindTextureEXT(GL_TEXTURE_2D, texture_id);
+#endif
+
+ if (gen) {
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ }
+}
+
+/**
+ * A function to resize the OpenGL window which will be used by
+ * the dynamic texture generating routines.
+ *
+ * @param width The width of the new window
+ * @param height The height of the new window
+ */
+void
+SGTexture::resize(unsigned int width, unsigned int height)
+{
+ GLfloat aspect;
+
+ // Make sure that we don't get a divide by zero exception
+ if (height == 0)
+ height = 1;
+
+ // Set the viewport for the OpenGL window
+ glViewport(0, 0, width, height);
+
+ // Calculate the aspect ratio of the window
+ aspect = width/height;
+
+ // Go to the projection matrix, this gets modified by the perspective
+ // calulations
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+
+ // Do the perspective calculations
+ gluPerspective(45.0, aspect, 1.0, 400.0);
+
+ // Return to the modelview matrix
+ glMatrixMode(GL_MODELVIEW);
+}
+
+/**
+ * A function to prepare the OpenGL state machine for dynamic
+ * texture generation.
+ *
+ * @param width The width of the texture
+ * @param height The height of the texture
+ */
+void
+SGTexture::prepare(unsigned int width, unsigned int height) {
+
+ texture_width = width;
+ texture_height = height;
+
+ // Resize the OpenGL window to the size of our dynamic texture
+ resize(texture_width, texture_height);
+
+ glClearColor(0.0, 0.0, 0.0, 1.0);
+}
+
+/**
+ * A function to generate the dynamic texture.
+ *
+ * The actual texture can be accessed by calling get_texture()
+ *
+ * @param width The width of the previous OpenGL window
+ * @param height The height of the previous OpenGL window
+ */
+void
+SGTexture::finish(unsigned int width, unsigned int height) {
+ // If a texture hasn't been created then it gets created, and the contents
+ // of the frame buffer gets copied into it. If the texture has already been
+ // created then its contents just get updated.
+ bind();
+ if (!texture_data)
+ {
+ // Copies the contents of the frame buffer into the texture
+ glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0,
+ texture_width, texture_height, 0);
+
+ } else {
+ // Copies the contents of the frame buffer into the texture
+ glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0,
+ texture_width, texture_height);
+ }
+
+ // Set the OpenGL window back to its previous size
+ resize(width, height);
+
+ // Clear the window back to black
+ glClearColor(0.0, 0.0, 0.0, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+}
+
+
+void
+SGTexture::read_alpha_texture(const char *name)
+{
+ GLubyte *lptr;
+ SGTexture::ImageRec *image;
+ int y;
+
+ delete[] texture_data;
+
+ image = ImageOpen(name);
+ if(!image) {
+ errstr = FILE_OPEN_ERROR;
+ return;
+ }
+
+ texture_width = image->xsize;
+ texture_height = image->ysize;
+
+ // printf("image->zsize = %d\n", image->zsize);
+
+ if (image->zsize != 1) {
+ ImageClose(image);
+ errstr = WRONG_COUNT;
+ return;
+ }
+
+ texture_data = new GLubyte[ image->xsize * image->ysize ];
+ num_colors = 1;
+ if (!texture_data) {
+ errstr = NO_TEXTURE;
+ return;
+ }
+
+ lptr = texture_data;
+ for(y=0; y<image->ysize; y++) {
+ ImageGetRow(image,lptr,y,0);
+ lptr += image->xsize;
+ }
+ ImageClose(image);
+}
+
+void
+SGTexture::read_rgb_texture(const char *name)
+{
+ GLubyte *ptr;
+ GLubyte *rbuf, *gbuf, *bbuf;
+ SGTexture::ImageRec *image;
+ int y;
+
+ delete[] texture_data;
+
+ image = ImageOpen(name);
+ if(!image) {
+ errstr = FILE_OPEN_ERROR;
+ return;
+ }
+
+ texture_width = image->xsize;
+ texture_height = image->ysize;
+ if (image->zsize < 1 || image->zsize > 4) {
+ ImageClose(image);
+ errstr = WRONG_COUNT;
+ return;
+ }
+
+ num_colors = 3;
+ texture_data = new GLubyte[ image->xsize * image->ysize * num_colors ];
+ rbuf = new GLubyte[ image->xsize ];
+ gbuf = new GLubyte[ image->xsize ];
+ bbuf = new GLubyte[ image->xsize ];
+ if(!texture_data || !rbuf || !gbuf || !bbuf) {
+ delete[] texture_data;
+ delete[] rbuf;
+ delete[] gbuf;
+ delete[] bbuf;
+ errstr = OUT_OF_MEMORY;
+ return;
+ }
+
+ ptr = texture_data;
+ for(y=0; y<image->ysize; y++) {
+ if(image->zsize == 4 || image->zsize == 3) {
+ ImageGetRow(image,rbuf,y,0);
+ ImageGetRow(image,gbuf,y,1);
+ ImageGetRow(image,bbuf,y,2);
+ } else {
+ ImageGetRow(image,rbuf,y,0);
+ memcpy(gbuf,rbuf,image->xsize);
+ memcpy(bbuf,rbuf,image->xsize);
+ }
+ rgbtorgb(rbuf,gbuf,bbuf,ptr,image->xsize);
+ ptr += (image->xsize * num_colors);
+ }
+
+ ImageClose(image);
+ delete[] rbuf;
+ delete[] gbuf;
+ delete[] bbuf;
+}
+
+
+
+void
+SGTexture::read_rgba_texture(const char *name)
+{
+ GLubyte *ptr;
+ GLubyte *rbuf, *gbuf, *bbuf, *abuf;
+ SGTexture::ImageRec *image;
+ int y;
+
+ delete[] texture_data;
+
+ image = ImageOpen(name);
+ if(!image) {
+ errstr = FILE_OPEN_ERROR;
+ return;
+ }
+
+ texture_width = image->xsize;
+ texture_height = image->ysize;
+ if (image->zsize < 1 || image->zsize > 4) {
+ ImageClose(image);
+ errstr = WRONG_COUNT;
+ return;
+ }
+
+ num_colors = 4;
+ texture_data = new GLubyte[ image->xsize * image->ysize * num_colors ];
+ rbuf = new GLubyte[ image->xsize ];
+ gbuf = new GLubyte[ image->xsize ];
+ bbuf = new GLubyte[ image->xsize ];
+ abuf = new GLubyte[ image->xsize ];
+ if(!texture_data || !rbuf || !gbuf || !bbuf || !abuf) {
+ delete[] texture_data;
+ delete[] rbuf;
+ delete[] gbuf;
+ delete[] bbuf;
+ delete[] abuf;
+ errstr = OUT_OF_MEMORY;
+ return;
+ }
+
+ ptr = texture_data;
+ for(y=0; y<image->ysize; y++) {
+ if(image->zsize == 4) {
+ ImageGetRow(image,rbuf,y,0);
+ ImageGetRow(image,gbuf,y,1);
+ ImageGetRow(image,bbuf,y,2);
+ ImageGetRow(image,abuf,y,3);
+ } else if(image->zsize == 3) {
+ ImageGetRow(image,rbuf,y,0);
+ ImageGetRow(image,gbuf,y,1);
+ ImageGetRow(image,bbuf,y,2);
+ memset(abuf, 255, image->xsize);
+ } else if(image->zsize == 2) {
+ ImageGetRow(image,rbuf,y,0);
+ memcpy(gbuf,rbuf,image->xsize);
+ memcpy(bbuf,rbuf,image->xsize);
+ ImageGetRow(image,abuf,y,1);
+ } else {
+ ImageGetRow(image,rbuf,y,0);
+ memcpy(gbuf,rbuf,image->xsize);
+ memcpy(bbuf,rbuf,image->xsize);
+ memset(abuf, 255, image->xsize);
+ }
+ rgbatorgba(rbuf,gbuf,bbuf,abuf,ptr,image->xsize);
+ ptr += (image->xsize * num_colors);
+ }
+
+ ImageClose(image);
+ delete[] rbuf;
+ delete[] gbuf;
+ delete[] bbuf;
+ delete[] abuf;
+}
+
+void
+SGTexture::read_raw_texture(const char *name)
+{
+ GLubyte *ptr;
+ SGTexture::ImageRec *image;
+ int y;
+
+ delete[] texture_data;
+
+ image = RawImageOpen(name);
+
+ if(!image) {
+ errstr = FILE_OPEN_ERROR;
+ return;
+ }
+
+ texture_width = 256;
+ texture_height = 256;
+
+ texture_data = new GLubyte[ 256 * 256 * 3 ];
+ if(!texture_data) {
+ errstr = OUT_OF_MEMORY;
+ return;
+ }
+
+ ptr = texture_data;
+ for(y=0; y<256; y++) {
+ gzread(image->file, ptr, 256*3);
+ ptr+=256*3;
+ }
+ ImageClose(image);
+}
+
+void
+SGTexture::read_r8_texture(const char *name)
+{
+ unsigned char c[1];
+ GLubyte *ptr;
+ SGTexture::ImageRec *image;
+ int xy;
+
+ delete[] texture_data;
+
+ //it wouldn't make sense to write a new function ...
+ image = RawImageOpen(name);
+
+ if(!image) {
+ errstr = FILE_OPEN_ERROR;
+ return;
+ }
+
+ texture_width = 256;
+ texture_height = 256;
+
+ texture_data = new GLubyte [ 256 * 256 * 3 ];
+ if(!texture_data) {
+ errstr = OUT_OF_MEMORY;
+ return;
+ }
+
+ ptr = texture_data;
+ for(xy=0; xy<(256*256); xy++) {
+ gzread(image->file, c, 1);
+
+ //look in the table for the right colours
+ ptr[0]=msfs_colour[c[0]][0];
+ ptr[1]=msfs_colour[c[0]][1];
+ ptr[2]=msfs_colour[c[0]][2];
+
+ ptr+=3;
+ }
+ ImageClose(image);
+}
+
+
+void
+SGTexture::write_texture(const char *name) {
+ SGTexture::ImageRec *image = ImageWriteOpen(name);
+
+ for (int c=0; c<num_colors; c++) {
+ GLubyte *ptr = texture_data + c;
+ for (int y=0; y<texture_height; y++) {
+ for (int x=0; x<texture_width; x++) {
+ image->tmp[x]=*ptr;
+ ptr = ptr + num_colors;
+ }
+ fwrite(image->tmp, 1, texture_width, file);
+ }
+ }
+
+ ImageClose(image);
+}
+
+
+void
+SGTexture::set_pixel(GLuint x, GLuint y, GLubyte *c)
+{
+ if (!texture_data) {
+ errstr = NO_TEXTURE;
+ return;
+ }
+
+ unsigned int pos = (x + y*texture_width) * num_colors;
+ memcpy(texture_data+pos, c, num_colors);
+}
+
+
+GLubyte *
+SGTexture::get_pixel(GLuint x, GLuint y)
+{
+ static GLubyte c[4] = {0, 0, 0, 0};
+
+ if (!texture_data) {
+ errstr = NO_TEXTURE;
+ return c;
+ }
+
+ unsigned int pos = (x + y*texture_width)*num_colors;
+ memcpy(c, texture_data + pos, num_colors);
+
+ return c;
+}
+
+SGTexture::ImageRec *
+SGTexture::ImageOpen(const char *fileName)
+{
+ union {
+ int testWord;
+ char testByte[4];
+ } endianTest;
+
+ SGTexture::ImageRec *image;
+ int swapFlag;
+ int x;
+
+ endianTest.testWord = 1;
+ if (endianTest.testByte[0] == 1) {
+ swapFlag = 1;
+ } else {
+ swapFlag = 0;
+ }
+
+ image = new SGTexture::ImageRec;
+ memset(image, 0, sizeof(SGTexture::ImageRec));
+ if (image == 0) {
+ errstr = OUT_OF_MEMORY;
+ return 0;
+ }
+ if ((image->file = gzopen(fileName, "rb")) == 0) {
+ errstr = FILE_OPEN_ERROR;
+ return 0;
+ }
+
+ gzread(image->file, image, 12);
+
+ if (swapFlag) {
+ ConvertShort(&image->imagic, 6);
+ }
+
+ image->tmp = new GLubyte[ image->xsize * 256 ];
+ if (image->tmp == 0) {
+ errstr = OUT_OF_MEMORY;
+ return 0;
+ }
+
+ if ((image->type & 0xFF00) == 0x0100) {
+ x = image->ysize * image->zsize * (int) sizeof(unsigned);
+ image->rowStart = new unsigned[x];
+ image->rowSize = new int[x];
+ if (image->rowStart == 0 || image->rowSize == 0) {
+ errstr = OUT_OF_MEMORY;
+ return 0;
+ }
+ image->rleEnd = 512 + (2 * x);
+ gzseek(image->file, 512, SEEK_SET);
+ gzread(image->file, image->rowStart, x);
+ gzread(image->file, image->rowSize, x);
+ if (swapFlag) {
+ ConvertUint(image->rowStart, x/(int) sizeof(unsigned));
+ ConvertUint((unsigned *)image->rowSize, x/(int) sizeof(int));
+ }
+ }
+ return image;
+}
+
+
+void
+SGTexture::ImageClose(SGTexture::ImageRec *image) {
+ if (image->file) gzclose(image->file);
+ if (file) fclose(file);
+ delete[] image->tmp;
+ delete[] image->rowStart;
+ delete[] image->rowSize;
+ delete image;
+}
+
+SGTexture::ImageRec *
+SGTexture::RawImageOpen(const char *fileName)
+{
+ union {
+ int testWord;
+ char testByte[4];
+ } endianTest;
+
+ SGTexture::ImageRec *image;
+ int swapFlag;
+
+ endianTest.testWord = 1;
+ if (endianTest.testByte[0] == 1) {
+ swapFlag = 1;
+ } else {
+ swapFlag = 0;
+ }
+
+ image = new SGTexture::ImageRec;
+ memset(image, 0, sizeof(SGTexture::ImageRec));
+ if (image == 0) {
+ errstr = OUT_OF_MEMORY;
+ return 0;
+ }
+ if ((image->file = gzopen(fileName, "rb")) == 0) {
+ errstr = FILE_OPEN_ERROR;
+ return 0;
+ }
+
+ gzread(image->file, image, 12);
+
+ if (swapFlag) {
+ ConvertShort(&image->imagic, 6);
+ }
+
+
+ //just allocate a pseudo value as I'm too lazy to change ImageClose()...
+ image->tmp = new GLubyte[1];
+
+ if (image->tmp == 0) {
+ errstr = OUT_OF_MEMORY;
+ return 0;
+ }
+
+ return image;
+}
+
+SGTexture::ImageRec *
+SGTexture::ImageWriteOpen(const char *fileName)
+{
+ union {
+ int testWord;
+ char testByte[4];
+ } endianTest;
+ ImageRec* image;
+ int swapFlag;
+ int x;
+
+ endianTest.testWord = 1;
+ if (endianTest.testByte[0] == 1) {
+ swapFlag = 1;
+ } else {
+ swapFlag = 0;
+ }
+
+ image = new SGTexture::ImageRec;
+ memset(image, 0, sizeof(SGTexture::ImageRec));
+ if (image == 0) {
+ errstr = OUT_OF_MEMORY;
+ return 0;
+ }
+ if ((file = fopen(fileName, "wb")) == 0) {
+ errstr = FILE_OPEN_ERROR;
+ return 0;
+ }
+
+ image->imagic = 474;
+ image->type = 0x0001;
+ image->dim = (num_colors > 1) ? 3 : 2;
+ image->xsize = texture_width;
+ image->ysize = texture_height;
+ image->zsize = num_colors;
+
+ if (swapFlag) {
+ ConvertShort(&image->imagic, 6);
+ }
+
+ fwrite(image, 1, 12, file);
+ fseek(file, 512, SEEK_SET);
+
+ image->tmp = new GLubyte[ image->xsize * 256 ];
+ if (image->tmp == 0) {
+ errstr = OUT_OF_MEMORY;
+ return 0;
+ }
+
+ if ((image->type & 0xFF00) == 0x0100) {
+ x = image->ysize * image->zsize * (int) sizeof(unsigned);
+ image->rowStart = new unsigned[x];
+ image->rowSize = new int[x];
+ if (image->rowStart == 0 || image->rowSize == 0) {
+ errstr = OUT_OF_MEMORY;
+ return 0;
+ }
+ image->rleEnd = 512 + (2 * x);
+ fseek(file, 512, SEEK_SET);
+ fread(image->rowStart, 1, x, file);
+ fread(image->rowSize, 1, x, file);
+ if (swapFlag) {
+ ConvertUint(image->rowStart, x/(int) sizeof(unsigned));
+ ConvertUint((unsigned *)image->rowSize, x/(int) sizeof(int));
+ }
+ }
+
+ return image;
+
+}
+
+void
+SGTexture::ImageGetRow(SGTexture::ImageRec *image, GLubyte *buf, int y, int z) {
+ GLubyte *iPtr, *oPtr, pixel;
+ int count;
+
+ if ((image->type & 0xFF00) == 0x0100) {
+ gzseek(image->file, (long) image->rowStart[y+z*image->ysize], SEEK_SET);
+ int size = image->rowSize[y+z*image->ysize];
+ gzread(image->file, image->tmp, size);
+
+ iPtr = image->tmp;
+ oPtr = buf;
+ for (GLubyte *limit = iPtr + size; iPtr < limit;) {
+ pixel = *iPtr++;
+ count = (int)(pixel & 0x7F);
+ if (!count) {
+ errstr = WRONG_COUNT;
+ return;
+ }
+ if (pixel & 0x80) {
+ while (iPtr < limit && count--) {
+ *oPtr++ = *iPtr++;
+ }
+ } else if (iPtr < limit) {
+ pixel = *iPtr++;
+ while (count--) {
+ *oPtr++ = pixel;
+ }
+ }
+ }
+ } else {
+ gzseek(image->file, 512+(y*image->xsize)+(z*image->xsize*image->ysize),
+ SEEK_SET);
+ gzread(image->file, buf, image->xsize);
+ }
+}
+
+void
+SGTexture::ImagePutRow(SGTexture::ImageRec *image, GLubyte *buf, int y, int z) {
+ GLubyte *iPtr, *oPtr, pixel;
+ int count;
+
+ if ((image->type & 0xFF00) == 0x0100) {
+ fseek(file, (long) image->rowStart[y+z*image->ysize], SEEK_SET);
+ fread( image->tmp, 1, (unsigned int)image->rowSize[y+z*image->ysize],
+ file);
+
+ iPtr = image->tmp;
+ oPtr = buf;
+ for (;;) {
+ pixel = *iPtr++;
+ count = (int)(pixel & 0x7F);
+ if (!count) {
+ errstr = WRONG_COUNT;
+ return;
+ }
+ if (pixel & 0x80) {
+ while (count--) {
+ *oPtr++ = *iPtr++;
+ }
+ } else {
+ pixel = *iPtr++;
+ while (count--) {
+ *oPtr++ = pixel;
+ }
+ }
+ }
+ } else {
+ fseek(file, 512+(y*image->xsize)+(z*image->xsize*image->ysize),
+ SEEK_SET);
+ fread(buf, 1, image->xsize, file);
+ }
+}
+
+
+void
+SGTexture::rgbtorgb(GLubyte *r, GLubyte *g, GLubyte *b, GLubyte *l, int n) {
+ while(n--) {
+ l[0] = r[0];
+ l[1] = g[0];
+ l[2] = b[0];
+ l += 3; r++; g++; b++;
+ }
+}
+
+void
+SGTexture::rgbatorgba(GLubyte *r, GLubyte *g, GLubyte *b, GLubyte *a,
+ GLubyte *l, int n) {
+ while(n--) {
+ l[0] = r[0];
+ l[1] = g[0];
+ l[2] = b[0];
+ l[3] = a[0];
+ l += 4; r++; g++; b++; a++;
+ }
+}
+
+
+void
+SGTexture::ConvertShort(unsigned short *array, unsigned int length) {
+ unsigned short b1, b2;
+ unsigned char *ptr;
+
+ ptr = (unsigned char *)array;
+ while (length--) {
+ b1 = *ptr++;
+ b2 = *ptr++;
+ *array++ = (b1 << 8) | (b2);
+ }
+}
+
+
+void
+SGTexture::ConvertUint(unsigned *array, unsigned int length) {
+ unsigned int b1, b2, b3, b4;
+ unsigned char *ptr;
+
+ ptr = (unsigned char *)array;
+ while (length--) {
+ b1 = *ptr++;
+ b2 = *ptr++;
+ b3 = *ptr++;
+ b4 = *ptr++;
+ *array++ = (b1 << 24) | (b2 << 16) | (b3 << 8) | (b4);
+ }
+}
+
+
+void
+SGTexture::make_monochrome(float contrast, GLubyte r, GLubyte g, GLubyte b) {
+
+ if (num_colors >= 3)
+ return;
+
+ GLubyte ap[3];
+ for (int y=0; y<texture_height; y++)
+ for (int x=0; x<texture_width; x++)
+ {
+ GLubyte *rgb = get_pixel(x,y);
+ GLubyte avg = (rgb[0] + rgb[1] + rgb[2]) / 3;
+
+ if (contrast != 1.0) {
+ float pixcol = -1.0 + (avg/128);
+ avg = 128 + int(128*pow(pixcol, contrast));
+ }
+
+ ap[0] = avg*r/255;
+ ap[1] = avg*g/255;
+ ap[2] = avg*b/255;
+
+ set_pixel(x,y,ap);
+ }
+}
+
+
+void
+SGTexture::make_grayscale(float contrast) {
+ if (num_colors < 3)
+ return;
+
+ int colors = (num_colors == 3) ? 1 : 2;
+ GLubyte *map = new GLubyte[ texture_width * texture_height * colors ];
+
+ for (int y=0; y<texture_height; y++)
+ for (int x=0; x<texture_width; x++)
+ {
+ GLubyte *rgb = get_pixel(x,y);
+ GLubyte avg = (rgb[0] + rgb[1] + rgb[2]) / 3;
+
+ if (contrast != 1.0) {
+ float pixcol = -1.0 + (avg/128);
+ avg = 128 + int(128*pow(pixcol, contrast));
+ }
+
+ int pos = (x + y*texture_width)*colors;
+ map[pos] = avg;
+ if (colors > 1)
+ map[pos+1] = rgb[3];
+ }
+
+ delete[] texture_data;
+ texture_data = map;
+ num_colors = colors;
+}
+
+
+void
+SGTexture::make_maxcolorwindow() {
+ GLubyte minmaxc[2] = {255, 0};
+
+ int pos = 0;
+ int max = num_colors;
+ if (num_colors == 2) max = 1;
+ if (num_colors == 4) max = 3;
+ while (pos < texture_width * texture_height * num_colors) {
+ for (int i=0; i < max; i++) {
+ GLubyte c = texture_data[pos+i];
+ if (c < minmaxc[0]) minmaxc[0] = c;
+ if (c > minmaxc[1]) minmaxc[1] = c;
+ }
+ pos += num_colors;
+ }
+
+ GLubyte offs = minmaxc[0];
+ float factor = 255.0 / float(minmaxc[1] - minmaxc[0]);
+ // printf("Min: %i, Max: %i, Factor: %f\n", offs, minmaxc[1], factor);
+
+ pos = 0;
+ while (pos < texture_width * texture_height * num_colors) {
+ for (int i=0; i < max; i++) {
+ texture_data[pos+i] -= offs;
+ texture_data[pos+i] = int(factor * texture_data[pos+i]);
+ }
+ pos += num_colors;
+ }
+}
+
+
+void
+SGTexture::make_normalmap(float brightness, float contrast) {
+ make_grayscale(contrast);
+ make_maxcolorwindow();
+
+ int colors = (num_colors == 1) ? 3 : 4;
+ bool alpha = (colors > 3);
+ int tsize = texture_width * texture_height * colors;
+ GLubyte *map = new GLubyte[ tsize ];
+
+ int mpos = 0, dpos = 0;
+ for (int y=0; y<texture_height; y++) {
+ int ytw = y*texture_width;
+
+ for (int x=0; x<texture_width; x++)
+ {
+ int xp1 = (x < (texture_width-1)) ? x+1 : 0;
+ int yp1 = (y < (texture_height-1)) ? y+1 : 0;
+ int posxp1 = (xp1 + ytw)*num_colors;
+ int posyp1 = (x + yp1*texture_width)*num_colors;
+
+ GLubyte c = texture_data[dpos];
+ GLubyte cx1 = texture_data[posxp1];
+ GLubyte cy1 = texture_data[posyp1];
+
+ if (alpha) {
+ GLubyte a = texture_data[dpos+1];
+ GLubyte ax1 = texture_data[posxp1+1];
+ GLubyte ay1 = texture_data[posyp1+1];
+
+ c = (c + a)/2;
+ cx1 = (cx1 + ax1)/2;
+ cy1 = (cy1 + ay1)/2;
+
+ map[mpos+3] = a;
+ }
+
+ map[mpos+0] = (128+(cx1-c)/2);
+ map[mpos+1] = (128+(cy1-c)/2);
+ map[mpos+2] = 127+int(brightness*128); // 255-c/2;
+
+ mpos += colors;
+ dpos += num_colors;
+ }
+ }
+
+ delete[] texture_data;
+ texture_data = map;
+ num_colors = colors;
+}
+
+
+void
+SGTexture::make_bumpmap(float brightness, float contrast) {
+ make_grayscale(contrast);
+
+ int colors = (num_colors == 1) ? 1 : 2;
+ GLubyte *map = new GLubyte[ texture_width * texture_height * colors ];
+
+ for (int y=0; y<texture_height; y++)
+ for (int x=0; x<texture_width; x++)
+ {
+ int mpos = (x + y*texture_width)*colors;
+ int dpos = (x + y*texture_width)*num_colors;
+
+ int xp1 = (x < (texture_width-1)) ? x+1 : 0;
+ int yp1 = (y < (texture_height-1)) ? y+1 : 0;
+ int posxp1 = (xp1 + y*texture_width)*num_colors;
+ int posyp1 = (x + yp1*texture_width)*num_colors;
+
+ map[mpos] = (127 - ((texture_data[dpos]-texture_data[posxp1]) -
+ ((texture_data[dpos]-texture_data[posyp1]))/4))/2;
+ if (colors > 1)
+ map[mpos+1] = texture_data[dpos+1];
+ }
+
+ delete[] texture_data;
+ texture_data = map;
+ num_colors = colors;
+}
+
--- /dev/null
+/*
+ * \file texture.hxx
+ * Texture manipulation routines
+ *
+ * Copyright (c) Mark J. Kilgard, 1997.
+ * Code added in april 2003 by Erik Hofman
+ *
+ * This program is freely distributable without licensing fees
+ * and is provided without guarantee or warrantee expressed or
+ * implied. This program is -not- in the public domain.
+ */
+
+#ifndef __SG_TEXTURE_HXX
+#define __SG_TEXTURE_HXX 1
+
+#include <simgear/compiler.h>
+#include <osg/GL>
+#include <zlib.h>
+
+#include <plib/sg.h>
+
+/**
+ * A class to encapsulate all the info surrounding an OpenGL texture
+ * and also query various info and load various file formats
+ */
+class SGTexture {
+
+private:
+
+ GLuint texture_id;
+ GLubyte *texture_data;
+
+ GLsizei texture_width;
+ GLsizei texture_height;
+ GLsizei num_colors;
+
+ void resize(unsigned int width = 256, unsigned int height = 256);
+
+ const char *errstr;
+
+protected:
+
+ FILE *file;
+ typedef struct _ImageRec {
+ _ImageRec(void) : tmp(0), rowStart(0), rowSize(0) {}
+ unsigned short imagic;
+ unsigned short type;
+ unsigned short dim;
+ unsigned short xsize, ysize, zsize;
+ unsigned int min, max;
+ unsigned int wasteBytes;
+ char name[80];
+ unsigned long colorMap;
+ gzFile file;
+ GLubyte *tmp;
+ unsigned long rleEnd;
+ unsigned int *rowStart;
+ int *rowSize;
+ } ImageRec;
+
+ void ConvertUint(unsigned *array, unsigned int length);
+ void ConvertShort(unsigned short *array, unsigned int length);
+ void rgbtorgb(GLubyte *r, GLubyte *g, GLubyte *b, GLubyte *l, int n);
+ void rgbatorgba(GLubyte *r, GLubyte *g, GLubyte *b, GLubyte *a,
+ GLubyte *l, int n);
+
+ ImageRec *ImageOpen(const char *fileName);
+ ImageRec *ImageWriteOpen(const char *fileName);
+ ImageRec *RawImageOpen(const char *fileName);
+ void ImageClose(ImageRec *image);
+ void ImageGetRow(ImageRec *image, GLubyte *buf, int y, int z);
+ void ImagePutRow(ImageRec *image, GLubyte *buf, int y, int z);
+
+ inline void free_id() {
+#ifdef GL_VERSION_1_1
+ glDeleteTextures(1, &texture_id);
+#else
+ glDeleteTexturesEXT(1, &texture_id);
+#endif
+ texture_id = 0;
+ }
+
+
+public:
+
+ SGTexture();
+ SGTexture(unsigned int width, unsigned int height);
+ ~SGTexture();
+
+ /* Copyright (c) Mark J. Kilgard, 1997. */
+ void read_alpha_texture(const char *name);
+ void read_rgb_texture(const char *name);
+ void read_rgba_texture(const char *name);
+ void read_raw_texture(const char *name);
+ void read_r8_texture(const char *name);
+ void write_texture(const char *name);
+
+ inline bool usable() { return (texture_id > 0) ? true : false; }
+
+ inline GLuint id() { return texture_id; }
+ inline GLubyte *texture() { return texture_data; }
+ inline void set_data(GLubyte *data) { texture_data = data; }
+ // inline void set_colors(int c) { num_colors = c; }
+
+ inline int width() { return texture_width; }
+ inline int height() { return texture_height; }
+ inline int colors() { return num_colors; }
+
+ // dynamic texture functions.
+ // everything drawn to the OpenGL screen after prepare is
+ // called and before finish is called will be included
+ // in the new texture.
+ void prepare(unsigned int width = 256, unsigned int height = 256);
+ void finish(unsigned int width, unsigned int height);
+
+ // texture pixel manipulation functions.
+ void set_pixel(GLuint x, GLuint y, GLubyte *c);
+ GLubyte *get_pixel(GLuint x, GLuint y);
+
+ void bind();
+ inline void select(bool keep_data = false) {
+ glTexImage2D( GL_TEXTURE_2D, 0, num_colors,
+ texture_width, texture_height, 0,
+ (num_colors==1)?GL_LUMINANCE:(num_colors==3)?GL_RGB:GL_RGBA, GL_UNSIGNED_BYTE, texture_data );
+
+ if (!keep_data) {
+ delete[] texture_data;
+ texture_data = 0;
+ }
+ }
+
+ // Nowhere does it say that resident textures have to be in video memory!
+ // NVidia's OpenGL drivers implement glAreTexturesResident() correctly,
+ // but the Matrox G400, for example, doesn't.
+ inline bool is_resident() {
+ GLboolean is_res;
+ glAreTexturesResident(1, &texture_id, &is_res);
+ return is_res != 0;
+ }
+
+ inline const char *err_str() { return errstr; }
+ inline void clear_err_str() { errstr = ""; }
+
+ void make_maxcolorwindow();
+ void make_grayscale(float contrast = 1.0);
+ void make_monochrome(float contrast = 1.0,
+ GLubyte r=255, GLubyte g=255, GLubyte b=255);
+ void make_normalmap(float brightness = 1.0, float contrast = 1.0);
+ void make_bumpmap(float brightness = 1.0, float contrast = 1.0);
+};
+
+#endif
+