From 9481ac459551b552f0947156a421ade0726c7809 Mon Sep 17 00:00:00 2001 From: Erik Hofman Date: Tue, 3 Nov 2020 14:39:32 +0100 Subject: [PATCH] =?UTF-8?q?Add=20a=20utility=20to=20generate=20the=20k?= =?UTF-8?q?=C3=B6ppen-geiger=20climate=20database=20image?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- utils/climate/README | 21 + utils/climate/asc2a.cpp | 77 +++ utils/climate/colours.h | 289 ++++++++++++ utils/climate/texture.cxx | 970 ++++++++++++++++++++++++++++++++++++++ utils/climate/texture.hxx | 153 ++++++ 5 files changed, 1510 insertions(+) create mode 100644 utils/climate/README create mode 100644 utils/climate/asc2a.cpp create mode 100644 utils/climate/colours.h create mode 100644 utils/climate/texture.cxx create mode 100644 utils/climate/texture.hxx diff --git a/utils/climate/README b/utils/climate/README new file mode 100644 index 000000000..8c7608801 --- /dev/null +++ b/utils/climate/README @@ -0,0 +1,21 @@ +-- Data Source: +http://koeppen-geiger.vu-wien.ac.at/present.htm + +Download "code with raster file: Map_KG-Global.R" + + +-- Bulding: +g++ asc2a.cpp texture.cxx -o asc2a -lGL -lz -losg + + +-- Workflow: +1. Convert the ESRI grid to ASCII using R: + https://groups.google.com/forum/#!topic/maxent/OrTBUvqOXaE + +2. Run asc2a to convert the ASCII file to a single channel rgb file: + ./asc2a .asc .rgb + +3. Use your favorite image processing package to convert from rgb to png + to compress it 40x compared to the ESRI grid image and 110x compared to + the ASCII file. + diff --git a/utils/climate/asc2a.cpp b/utils/climate/asc2a.cpp new file mode 100644 index 000000000..128c84c79 --- /dev/null +++ b/utils/climate/asc2a.cpp @@ -0,0 +1,77 @@ + +#include +#include +#include +#include +#include + +#include "texture.hxx" + +#define NCOLS 4320 +#define NROWS 2160 + +enum STR2INT_ERROR { SUCCESS, OVERFLOW, UNDERFLOW, INCONVERTIBLE }; + +STR2INT_ERROR str2int (int &i, char const *s, int base = 0) +{ + char *end; + long l; + errno = 0; + l = strtol(s, &end, base); + if ((errno == ERANGE && l == LONG_MAX) || l > INT_MAX) { + std::cerr << "OVERFLOW" << std::endl; + return OVERFLOW; + } + if ((errno == ERANGE && l == LONG_MIN) || l < INT_MIN) { + std::cerr << "UNDERFLOW" << std::endl; + return UNDERFLOW; + } + if (*s == '\0' || end == s) { + std::cerr << "INCONVERTIBLE: " << s << std::endl; + return INCONVERTIBLE; + } + i = l; + return SUCCESS; +} + +int main(int argc, char **argv) +{ + if (argc < 3) { + printf("Uage: %s .asc .rgb\n\n", argv[0]); + exit(-1); + } + + const char *infile = argv[1]; + const char *outfile = argv[2]; + + SGTexture texture(NCOLS, NROWS); + std::ifstream file(infile); + std::string str; + + texture.prepare(NCOLS, NROWS); + texture.set_colors(1); + + GLuint row = 0; + unsigned int line = 0; + while (std::getline(file, str)) + { + GLuint col = 0; + int num = 0; + + if (++line < 7) continue; + + std::istringstream iss(str); + std::string token; + while (std::getline(iss, token, ' ')) + { + if (str2int(num, token.c_str(), 10) == SUCCESS) + { + GLubyte val = (num == 32) ? 0 : 4*num; + texture.set_pixel(col++, NROWS-row, &val); + } + } + row++; + } + + texture.write_texture(outfile); +} diff --git a/utils/climate/colours.h b/utils/climate/colours.h new file mode 100644 index 000000000..acb5da758 --- /dev/null +++ b/utils/climate/colours.h @@ -0,0 +1,289 @@ +// colours.h -- This header file contains colour definitions in the +// same way as MS FS5 does +// +// Contributed by "Christian Mayer" , started April 1999. +// +// Copyright (C) 1998 Christian Mayer - Vader@t-online.de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + + +#ifndef COLOURS_H +#define COLOURS_H + +unsigned char msfs_colour[256][3]= +{ + { 0, 0, 0}, + { 8, 8, 8}, + { 16, 16, 16}, + { 24, 24, 24}, + { 32, 32, 32}, + { 40, 40, 40}, + { 48, 48, 48}, + { 56, 56, 56}, + { 65, 65, 65}, + { 73, 73, 73}, + { 81, 81, 81}, + { 89, 89, 89}, + { 97, 97, 97}, + {105, 105, 105}, + {113, 113, 113}, + {121, 121, 121}, + {130, 130, 130}, + {138, 138, 138}, + {146, 146, 146}, + {154, 154, 154}, + {162, 162, 162}, + {170, 170, 170}, + {178, 178, 178}, + {186, 186, 186}, + {195, 195, 195}, + {203, 203, 203}, + {211, 211, 211}, + {219, 219, 219}, + {227, 227, 227}, + {235, 235, 235}, + {247, 247, 247}, + {255, 255, 255}, + { 21, 5, 5}, + { 42, 10, 10}, + { 63, 15, 15}, + { 84, 20, 20}, + {105, 25, 25}, + {126, 30, 30}, + {147, 35, 35}, + {168, 40, 40}, + {189, 45, 45}, + {210, 50, 50}, + {231, 55, 55}, + {252, 60, 60}, + { 5, 21, 5}, + { 10, 42, 10}, + { 15, 63, 15}, + { 20, 84, 20}, + { 25, 105, 25}, + { 30, 126, 30}, + { 35, 147, 35}, + { 40, 168, 40}, + { 45, 189, 45}, + { 50, 210, 50}, + { 55, 231, 55}, + { 60, 252, 60}, + { 0, 7, 23}, + { 0, 15, 40}, + { 0, 23, 58}, + { 0, 40, 84}, + { 0, 64, 104}, + { 0, 71, 122}, + { 0, 87, 143}, + { 0, 99, 156}, + { 0, 112, 179}, + { 0, 128, 199}, + { 0, 143, 215}, + { 0, 153, 230}, + { 28, 14, 0}, + { 56, 28, 0}, + { 84, 42, 0}, + {112, 56, 0}, + {140, 70, 0}, + {168, 84, 0}, + {196, 98, 0}, + {224, 112, 0}, + {252, 126, 0}, + { 28, 28, 0}, + { 56, 56, 0}, + { 84, 84, 0}, + {112, 112, 0}, + {140, 140, 0}, + {168, 168, 0}, + {196, 196, 0}, + {224, 224, 0}, + {252, 252, 0}, + { 25, 21, 16}, + { 50, 42, 32}, + { 75, 63, 48}, + {100, 84, 64}, + {125, 105, 80}, + {150, 126, 96}, + {175, 147, 112}, + {200, 168, 128}, + {225, 189, 144}, + { 28, 11, 7}, + { 56, 22, 14}, + { 84, 33, 21}, + {112, 44, 28}, + {140, 55, 35}, + {168, 66, 42}, + {196, 77, 49}, + {224, 88, 56}, + {252, 99, 63}, + { 17, 22, 9}, + { 34, 44, 18}, + { 51, 66, 27}, + { 68, 88, 36}, + { 85, 110, 45}, + {102, 132, 54}, + {119, 154, 63}, + {136, 176, 72}, + {153, 198, 81}, + { 0, 58, 104}, + { 0, 87, 112}, + { 43, 112, 128}, + {255, 0, 0}, + { 64, 255, 64}, + { 0, 0, 192}, + { 0, 105, 105}, + {255, 128, 0}, + {255, 255, 0}, + { 81, 81, 81}, + {121, 121, 121}, + {146, 146, 146}, + {170, 170, 170}, + {195, 195, 195}, + {227, 227, 227}, + { 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}, + {192, 192, 255}, + {200, 200, 255}, + {208, 208, 255}, + {216, 216, 255}, + {224, 224, 255}, + {232, 232, 255}, + {240, 240, 255}, + {248, 248, 255}, + {255, 255, 255}, + {255, 255, 255}, + {255, 255, 255}, + {255, 255, 255}, + {255, 255, 255}, + {255, 255, 255}, + {255, 255, 255}, + {255, 255, 255}, + { 16, 72, 16}, + { 32, 80, 32}, + { 48, 88, 48}, + { 64, 96, 64}, + { 80, 104, 80}, + { 96, 112, 96}, + {112, 120, 112}, + {120, 124, 120}, + {128, 128, 128}, + {128, 128, 128}, + {128, 128, 128}, + {128, 128, 128}, + {128, 128, 128}, + {128, 128, 128}, + {128, 128, 128}, + {128, 128, 128}, + { 33, 140, 189}, + { 57, 132, 165}, + {189, 66, 66}, + {156, 66, 66}, + {132, 74, 74}, + { 33, 82, 107}, + {214, 90, 82}, + {189, 90, 82}, + {165, 90, 82}, + {123, 57, 49}, + { 99, 57, 49}, + {107, 74, 66}, + {123, 90, 82}, + {181, 90, 66}, + { 74, 49, 41}, + {189, 115, 90}, + {140, 90, 49}, + { 33, 49, 74}, + {181, 115, 49}, + { 99, 66, 33}, + {165, 115, 66}, + { 49, 41, 33}, + {165, 140, 115}, + {189, 165, 140}, + { 57, 99, 123}, + {181, 107, 24}, + {206, 123, 33}, + {156, 99, 33}, + {148, 107, 49}, + {107, 82, 49}, + { 33, 33, 57}, + { 33, 115, 165}, + {214, 214, 33}, + {173, 173, 33}, + {198, 198, 41}, + {140, 140, 33}, + {115, 115, 33}, + {189, 189, 57}, + {156, 156, 49}, + {173, 173, 57}, + {123, 123, 49}, + {123, 123, 66}, + { 74, 74, 49}, + {123, 123, 90}, + { 41, 41, 33}, + { 90, 99, 57}, + {107, 115, 74}, + {123, 148, 82}, + {140, 173, 99}, + {132, 156, 99}, + { 49, 66, 41}, + { 99, 165, 90}, + { 74, 214, 74}, + { 57, 140, 57}, + { 74, 181, 74}, + { 90, 198, 90}, + { 57, 123, 57}, + { 49, 99, 49}, + { 90, 165, 90}, + { 82, 148, 82}, + { 74, 99, 74}, + { 57, 115, 132}, + { 33, 99, 123}, + { 74, 115, 132} +}; + + +#endif diff --git a/utils/climate/texture.cxx b/utils/climate/texture.cxx new file mode 100644 index 000000000..2794f45f5 --- /dev/null +++ b/utils/climate/texture.cxx @@ -0,0 +1,970 @@ +/* + * 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 + +#ifdef WIN32 +# include +#endif + +#include + +#include +#include + +#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) +{ + using namespace osg; + 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); + + // Do the perspective calculations + Matrixf proj = Matrixf::perspective(45.0, aspect, 1.0, 400.0); + glLoadMatrix(proj.ptr()); + + // 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; yysize; 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; yysize; 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; yysize; 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); + +printf("colors: %i, height: %i, width: %i\n", num_colors, texture_height, texture_width); + for (int c=0; ctmp[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 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 1) + map[mpos+1] = texture_data[dpos+1]; + } + + delete[] texture_data; + texture_data = map; + num_colors = colors; +} + diff --git a/utils/climate/texture.hxx b/utils/climate/texture.hxx new file mode 100644 index 000000000..7abf5984f --- /dev/null +++ b/utils/climate/texture.hxx @@ -0,0 +1,153 @@ +/* + * \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 +#include +#include + +#include + +/** + * 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 +