Project::OSiRiON - Git repositories
Project::OSiRiON
News . About . Screenshots . Downloads . Forum . Wiki . Tracker . Git
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStijn Buys <ingar@osirion.org>2008-03-26 23:24:26 +0000
committerStijn Buys <ingar@osirion.org>2008-03-26 23:24:26 +0000
commit3d993058e5078fbdfd92d479281ad93bb40a4bc6 (patch)
treefd578d5a44246a90fc08cdf6b9a18f02313ce431 /src/render
parent2b9f068e7ee4c0d249c715f9eb5a3c2c8a11e6f8 (diff)
improved TGA handling
Diffstat (limited to 'src/render')
-rw-r--r--src/render/Makefile.am4
-rw-r--r--src/render/gl.h1
-rw-r--r--src/render/image.cc64
-rw-r--r--src/render/image.h56
-rw-r--r--src/render/render.cc32
-rw-r--r--src/render/tga.cc400
-rw-r--r--src/render/tga.h35
7 files changed, 419 insertions, 173 deletions
diff --git a/src/render/Makefile.am b/src/render/Makefile.am
index 87f3ba3..75da9c7 100644
--- a/src/render/Makefile.am
+++ b/src/render/Makefile.am
@@ -3,5 +3,5 @@ METASOURCES = AUTO
noinst_LTLIBRARIES = librender.la
librender_la_LDFLAGS = -avoid-version -no-undefined @GL_LIBS@
librender_la_LIBADD = $(top_builddir)/src/math/libmath.la
-librender_la_SOURCES = draw.cc gl.cc render.cc text.cc tga.cc
-noinst_HEADERS = draw.h gl.h render.h text.h tga.h
+librender_la_SOURCES = draw.cc gl.cc image.cc render.cc text.cc tga.cc
+noinst_HEADERS = draw.h gl.h image.h render.h text.h tga.h
diff --git a/src/render/gl.h b/src/render/gl.h
index 9153435..69eb412 100644
--- a/src/render/gl.h
+++ b/src/render/gl.h
@@ -8,6 +8,7 @@
#define __INCLUDED_RENDER_GL_H__
#include <GL/gl.h>
+#include <GL/glu.h>
#include "math/vector3f.h"
#include "math/color.h"
diff --git a/src/render/image.cc b/src/render/image.cc
new file mode 100644
index 0000000..7ce8113
--- /dev/null
+++ b/src/render/image.cc
@@ -0,0 +1,64 @@
+/*
+ render/image.cc
+ This file is part of the Osirion project and is distributed under
+ the terms of the GNU General Public License version 2
+*/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "render/image.h"
+
+namespace render
+{
+
+Image::Image(unsigned int width, unsigned int height, unsigned int channels)
+{
+ image_width = width;
+ image_height = height;
+ image_channels = channels;
+
+ image_data = (unsigned char *) malloc(size());
+ clear();
+}
+
+Image::~Image()
+{
+ free(image_data);
+}
+
+void Image::clear()
+{
+ memset(image_data, 0, size());
+}
+
+void Image::swap_channels()
+{
+ for (size_t y =0; y < image_height; y++) {
+ for (size_t x = 0; x < image_width; x++) {
+ size_t offset = y * image_width * image_channels + x * image_channels;
+ unsigned char tmp = image_data[offset];
+ image_data[offset] = image_data[offset + 2];
+ image_data[offset + 2] = tmp;
+ }
+ }
+}
+
+void Image::flip()
+{
+ unsigned char line[image_width*image_channels];
+ for (size_t y=0; y < image_height /2; y++) {
+ memcpy(line,
+ &image_data[y*image_width*image_channels],
+ image_width*image_channels);
+ memcpy(&image_data[y*image_width*image_channels],
+ &image_data[(image_height-1-y)*image_width*image_channels],
+ image_width*image_channels);
+ memcpy(&image_data[(image_height-1-y)*image_width*image_channels],
+ line,
+ image_width*image_channels);
+ }
+}
+
+}
+
diff --git a/src/render/image.h b/src/render/image.h
new file mode 100644
index 0000000..0c1069b
--- /dev/null
+++ b/src/render/image.h
@@ -0,0 +1,56 @@
+/*
+ render/image.h
+ This file is part of the Osirion project and is distributed under
+ the terms of the GNU General Public License version 2
+*/
+
+#ifndef __INCLUDED_RENDER_IMAGE_H__
+#define __INCLUDED_RENDER_IMAGE_H__
+
+namespace render {
+
+/// RGB (24bpp) or RGBA (32bpp) image data
+class Image {
+public:
+ Image(unsigned int width, unsigned int height, unsigned int channels);
+ ~Image();
+
+ // pointer to the image data
+ inline unsigned char *data() { return image_data; }
+
+ // index into the image data
+ inline unsigned char *operator[](size_t index) { return &image_data[index]; }
+
+ // width of the image in pixels
+ inline unsigned int width() const { return image_width; }
+
+ // height of the image in pixels
+ inline unsigned int height() const { return image_height; }
+
+ // size of the image data in bytes
+ inline size_t size() const { return ((size_t) image_width * (size_t) image_height * (size_t) image_channels); }
+
+ // number of channels 3 (RGB) or 4 (RGBA)
+ inline unsigned int channels() const { return image_channels; }
+
+ // set image data to zero
+ void clear();
+
+ // swap the red and blue channel values of the image
+ void swap_channels();
+
+ // flip upside-down
+ void flip();
+
+private:
+ unsigned char *image_data;
+
+ unsigned int image_width;
+ unsigned int image_height;
+ unsigned int image_channels;
+};
+
+}
+
+#endif // __INCLUDED_RENDER_IMAGE_H__
+
diff --git a/src/render/render.cc b/src/render/render.cc
index 15a6d5c..3a9ebd3 100644
--- a/src/render/render.cc
+++ b/src/render/render.cc
@@ -10,6 +10,8 @@
#include <iomanip>
#include "render/render.h"
+#include "render/tga.h"
+#include "render/gl.h"
#include "core/core.h"
#include "filesystem/filesystem.h"
#include "sys/sys.h"
@@ -22,6 +24,32 @@ core::Cvar *r_drawradius = 0;
core::Cvar *r_drawstats = 0;
core::Cvar *r_wireframe = 0;
+bool texture(char * const filename, size_t id)
+{
+ Image *image = TGA::load(filename);
+ if (!image)
+ return false;
+
+ // FIXME - I don't get this part... yet
+
+ glGenTextures(1, &textures[id]);
+ glBindTexture(GL_TEXTURE_2D, textures[id]);
+
+ int texture_type;
+ if (image->channels() == 4)
+ texture_type = GL_RGBA;
+ else
+ texture_type = GL_RGB;
+
+ gluBuild2DMipmaps(GL_TEXTURE_2D, image->channels(),
+ image->width(), image->height(), texture_type, GL_UNSIGNED_BYTE, image->data());
+
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR_MIPMAP_LINEAR);
+
+ return true;
+}
+
void init()
{
con_print << "Initializing renderer..." << std::endl;
@@ -32,11 +60,11 @@ void init()
con_print << "Loading textures..." << std::endl;
- if (!TGA::texture(textures, "bitmaps/loader.tga", 0)) {
+ if (!texture( "bitmaps/loader.tga", 0)) {
con_error << "Essential file bitmaps/loader.tga missing" << std::endl;
core::application()->shutdown();
}
- if (!TGA::texture(textures, "bitmaps/conchars.tga", 1)) {
+ if (!texture("bitmaps/conchars.tga", 1)) {
con_error << "Essential file bitmaps/conchars.tga missing" << std::endl;
core::application()->shutdown();
}
diff --git a/src/render/tga.cc b/src/render/tga.cc
index 0eb2ef6..c7f4fc1 100644
--- a/src/render/tga.cc
+++ b/src/render/tga.cc
@@ -1,185 +1,285 @@
/*
- Ronny Andr�Reierstad
- http://www.morrowland.com
- http://www.morrowland.com/apron/tut_gl.php
- apron@morrowland.com
+ render/tga.cc
+ This file is part of the Osirion project and is distributed under
+ the terms of the GNU General Public License version 2
*/
+/*
+ Documentation and examples on the TGA file format:
+
+ http://www.dca.fee.unicamp.br/~martino/disciplinas/ea978/tgaffs.pdf
+ http://www.fileformat.info/format/tga/egff.htm
+ http://www.morrowland.com/apron/tut_gl.php
+
+ Notes
+
+ TGA image type Colormap RLE
+ 0 No image data included in file No No
+ 1 Colormapped image data Yes No
+ 2 Truecolor image data No No
+ 3 Monochrome image data No No
+ 9 Colormapped image data Yes Yes
+ 10 Truecolor image data No Yes
+ 11 Monochrome image data No Yes
+
+ TGA multi-byte integer values have LSB first
+*/
+
+
+
+#include <fstream>
+#include <sstream>
+#include <string>
+
#include "filesystem/filesystem.h"
#include "render/tga.h"
#include "sys/sys.h"
-#include "GL/gl.h"
-#include <GL/glu.h>
+
+const unsigned char TGA_NONE = 0;
+const unsigned char TGA_TRUECOLOR = 2;
+const unsigned char TGA_TRUECOLOR_RLE = 10;
namespace render
{
-/////////////////////////////////////////////////////////////////////////////////////////////////
-// TGA TEXTURE LOADER
-/////////////////////////////////////////////////////////////////////////////////////////////////
-bool TGA::texture(GLuint textureArray[], const char *filename, int ID)
+Image *TGA::load(const char *filename)
{
+ Image *image = 0;
+
if (!filename)
- return false;
-
- image *pBitMap = load(filename);
- if (!pBitMap) {
- //con_warn << "Could not load " << filename << std::endl;
- return false;
+ return 0;
+
+ filesystem::File *tga_file = filesystem::open(filename);
+ if (!tga_file) {
+ con_warn << "Could not open " << filename << std::endl;
+ return 0;
}
-
- glGenTextures(1, &textureArray[ID]);
- glBindTexture(GL_TEXTURE_2D, textureArray[ID]);
- int textureType = GL_RGB;
- if (pBitMap->channels == 4) textureType = GL_RGBA;
- gluBuild2DMipmaps(GL_TEXTURE_2D, pBitMap->channels, pBitMap->size_x, pBitMap->size_y, textureType, GL_UNSIGNED_BYTE, pBitMap->data);
- glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
- glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR_MIPMAP_LINEAR);
-
- if (pBitMap) {
- if (pBitMap->data) {
- free(pBitMap->data);
- }
- free(pBitMap);
+
+ // TGA header
+ unsigned char header[18];
+ memset(header, 0, sizeof(header));
+
+ if (!tga_file->read(header, 18)) {
+ con_warn << "Error reading " << filename << std::endl;
+ filesystem::close(tga_file);
+ return 0;
}
- return true;
-}
+ // byte 0 - image ID field lenght
+ unsigned int tga_idlength = header[0];
+ // byte 1 - color map type
+ unsigned int tga_colormap = header[1];
-TGA::image *TGA::load(const char *filename)
-{
- image *pImgData = NULL;
- //FILE *pFile = NULL;
- GLushort width = 0;
- GLushort height = 0;
- GLubyte length = 0;
- GLubyte imgType = 0;
- GLubyte bits = 0;
- GLushort channels = 0;
- GLushort stride = 0;
-
- filesystem::File *f = filesystem::open(filename);
- if (!f)
- return 0;
-
- pImgData = (image*)malloc(sizeof(image));
- pImgData->data = 0;
-
- f->read((void *)&length, sizeof(GLubyte));
- f->skip(1);
- f->read((void *)&imgType, sizeof(GLubyte));
- f->skip(9);
- f->read((void *)&width, sizeof(GLushort));
- f->read((void *)&height, sizeof(GLushort));
- f->read((void *)&bits, sizeof(GLubyte));
- f->skip(length +1);
+ // byte 2 - image type
+ unsigned int tga_type = header[2];
+
+ // byte 3+4 - color map first entry index
+ //unsigned int tga_colormap_first = header[3] + (header[4] << 8 );
+
+ // byte 5+6 - color map length (in bits)
+ unsigned int tga_color_map_length = header[5] +(header[6] << 8 );
+
+ // byte 7 - color map entry length
+ unsigned int tga_colormap_entry = header[7];
+
+ // byte 8+9 - image x origin
+ // byte 10+11 - image y origin
+ // byte 12+13 - image width (LSB first)
+ unsigned int tga_width = header[12] + (header[13] << 8);
+
+ // byte 14+15 - image height (LSB first)
+ unsigned int tga_height = header[14] + (header[15] << 8);
+
+ // byte 16 - image color depth (in bits)
+ unsigned int tga_depth = header[16];
+
+ // byte 17 - image descriptor byte
+ unsigned int tga_descriptor = header[17];
- con_debug << " " << filename << " " << width << "x" << height << "x" << (int) bits << "bpp" << std::endl;
+ con_debug << " " << filename << " "
+ << tga_width << "x" << tga_height << "x" << tga_depth << "bpp" << std::endl;
- if (imgType != TGA_RLE) {
- // Check for 24 or 32 Bit
- if (bits == 24 || bits == 32) {
+ // read the image id if there is one
+ if (tga_idlength)
+ tga_file->skip(tga_idlength);
+
+ // read color map data (even for non-color mapped images)
+ if (tga_colormap) {
+ if (tga_colormap > 1)
+ con_warn << filename << ": invalid color map type!" << std::endl;
- channels = bits / 8;
- stride = channels * width;
- pImgData->data = new unsigned char[stride * height];
-
- for (GLushort y = 0; y < height; y++) {
- unsigned char *pLine = &(pImgData->data[stride * y]);
-
- f->read((void *)pLine, stride);
-
- for (GLushort i = 0; i < stride; i += channels) {
- int temp = pLine[i];
- pLine[i] = pLine[i + 2];
- pLine[i + 2] = temp;
- }
+ tga_file->skip(tga_color_map_length*tga_colormap_entry);
+ }
+
+ unsigned int index = 0;
+ unsigned int channels = tga_depth / 8;
+
+ switch(tga_type) {
+
+ case TGA_NONE:
+ con_warn << "Error reading " << filename
+ << ": no image data!" << std::endl;
+ filesystem::close(tga_file);
+ return 0;
+ break;
+
+ case TGA_TRUECOLOR:
+ if ((tga_depth == 24) || (tga_depth == 32)) {
+
+ image = new Image(tga_width, tga_height, channels);
+
+ for (size_t i = 0; i < tga_width * tga_height; i++) {
+ tga_file->read((void *)(*image)[i*(size_t)channels], channels);
}
- }
-
- // Check for 16 Bit
- else if (bits == 16) {
- unsigned short pixels = 0;
- int r=0, g=0, b=0;
+
+ image->swap_channels();
+
+ } else if (tga_depth == 16) {
channels = 3;
- stride = channels * width;
- pImgData->data = new unsigned char[stride * height];
-
- for (int i = 0; i < width*height; i++) {
- f->read((void *)&pixels, sizeof(GLushort));
-
- b = (pixels & 0x1f) << 3;
- g = ((pixels >> 5) & 0x1f) << 3;
- r = ((pixels >> 10) & 0x1f) << 3;
+ image = new Image(tga_width, tga_height,channels);
+
+ for (size_t i =0; i < tga_width * tga_height; i++) {
+ // unpack one pixel
+ unsigned char pixel_data[2];
+ tga_file->read((void *)pixel_data, 2);
+ unsigned int unpacked = pixel_data[0] + pixel_data[1]* 0xff;
+
+ unsigned int b = (unpacked & 0x1f) << 3;
+ unsigned int g = ((unpacked >> 5) & 0x1f) << 3;
+ unsigned int r = ((unpacked >> 10) & 0x1f) << 3;
- pImgData->data[i * 3 + 0] = r;
- pImgData->data[i * 3 + 1] = g;
- pImgData->data[i * 3 + 2] = b;
+ // store it
+ image->data()[i * channels] = (unsigned char) b;
+ image->data()[i * channels+1] = (unsigned char) g;
+ image->data()[i * channels+2] = (unsigned char) r;
}
- } else
- return NULL;
- } else {
- GLubyte rleID = 0;
- int colorsRead = 0;
- channels = bits / 8;
- stride = channels * width;
-
- pImgData->data = new unsigned char[stride * height];
- GLubyte *pColors = new GLubyte [channels];
-
- int i = 0;
- while (i < width*height) {
+ } else {
+ con_warn << "Error reading " << filename
+ << ": unsupported image depth '" << tga_depth << "'!" << std::endl;
+ filesystem::close(tga_file);
+ return 0;
+ }
- f->read((void *)&rleID, sizeof(GLubyte));
-
+ break;
+
+ case TGA_TRUECOLOR_RLE:
+
+ image = new Image(tga_width, tga_height, channels);
+
+ while (index < tga_width * tga_height) {
+ unsigned char rle = 0;
+ unsigned char pixel_data[channels];
+
+ // read RLE packet byte
+ tga_file->read(&rle, 1);
- if (rleID < 128) {
- rleID++;
-
- while (rleID) {
- f->read((void *)pColors, sizeof(GLubyte) * channels);
-
- pImgData->data[colorsRead + 0] = pColors[2];
- pImgData->data[colorsRead + 1] = pColors[1];
- pImgData->data[colorsRead + 2] = pColors[0];
-
- if (bits == 32) pImgData->data[colorsRead + 3] = pColors[3];
-
- i++;
- rleID--;
- colorsRead += channels;
- }
+ if (rle < 128) {
+ rle++; // rle contains the number of pixels-1
+ tga_file->read((void *)(*image)[index*channels], rle*channels);
+ index += rle;
+
} else {
- rleID -= 127;
-
- f->read((void *)pColors, sizeof(GLubyte) * channels);
-
- while (rleID) {
- pImgData->data[colorsRead + 0] = pColors[2];
- pImgData->data[colorsRead + 1] = pColors[1];
- pImgData->data[colorsRead + 2] = pColors[0];
-
- if (bits == 32) pImgData->data[colorsRead + 3] = pColors[3];
-
- i++;
- rleID--;
- colorsRead += channels;
+ rle -= 127; // rle contains 128 + the number of identical pixels-1
+ tga_file->read(pixel_data, channels);
+
+ while (rle) {
+ memcpy((void *)(*image)[index*channels], (void *)pixel_data, channels);
+ index++;
+ rle--;
}
}
}
- delete pColors;
- }
-
- filesystem::close(f);
-
-
- pImgData->channels = channels;
- pImgData->size_x = width;
- pImgData->size_y = height;
+
+ image->swap_channels();
+
+ break;
- return pImgData;
+ default:
+ con_warn << "Error reading " << filename
+ << ": unsupported TGA type '" << (int) tga_type << "'!" << std::endl;
+ filesystem::close(tga_file);
+ return 0;
+ }
+
+ filesystem::close(tga_file);
+
+ if ((tga_descriptor & 0x20) == 0x0) {
+ // origin at bottom left
+ image->flip();
+ }
+
+ if ((tga_descriptor & 0x10) == 0x10) {
+ con_warn << filename << ": descriptor bit 4 (left-right) set!" << std::endl;
+ }
+
+ return image;
+}
+
+void TGA::save(const char *filename, Image & image)
+{
+ if (!filename)
+ return;
+
+ std::ofstream ofs(filename);
+
+ if (!ofs.is_open()) {
+ con_warn << "Could not write " << filename << std::endl;
+ return;
+ }
+
+ // write TGA header
+ unsigned char header[18];
+ memset(header, 0, sizeof(header));
+
+ // byte 0 - image ID field lenght = 0 (no image ID field present)
+ // byte 1 - color map type = 0 (no palette present)
+ // byte 2 - image type = 2 (truecolor without RLE)
+ header[2] = TGA_TRUECOLOR;
+ // byte 3-11 - palette data (not used)
+ // byte 12+13 - image width
+ header[12] = (image.width() & 0xff);
+ header[13] = ((image.width() >> 8) & 0xff);
+ // byte 14+15 - image height
+ header[14] = (image.height() & 0xff);
+ header[15] = ((image.height() >> 8) & 0xff);
+ // byte 16 - image color depth = 24 (RGB) or 32 (RGBA)
+ header[16] = image.channels() * 8;
+ // byte 17 - image descriptor byte = 0x20 (origin at bottom left)
+ header[17] = 0x20;
+
+ // write header
+ ofs.write((char *)header, sizeof(header));
+
+ // write image data
+ // TGA has the R and B channels switched
+ unsigned char buf[image.channels()];
+ for (int y = image.height()-1; y >= 0; y--) {
+ for (size_t x = 0; x < image.width(); x++) {
+ buf[0] = *image[y*image.width()*image.channels()+x*image.channels()+2];
+ buf[1] = *image[y*image.width()*image.channels()+x*image.channels()+1];
+ buf[2] = *image[y*image.width()*image.channels()+x*image.channels()];
+
+ if (image.channels() == 4)
+ buf[3] = *image[y*image.width()*image.channels()+x*image.channels()+3];
+
+ ofs.write((char *)buf, image.channels());
+ }
+ }
+
+ // write footer (optional, but the specification recommends it)
+ char footer[26];
+ memset(footer, 0, sizeof(footer));
+ strncpy(&footer[8] , "TRUEVISION-XFILE", 16);
+ footer[24] = '.';
+ footer[25] = 0;
+ ofs.write(footer, sizeof(footer));
+
+ // close file
+ ofs.close();
+
+ con_print << "Wrote " << filename << std::endl;
}
}
diff --git a/src/render/tga.h b/src/render/tga.h
index d4449a8..0da65fb 100644
--- a/src/render/tga.h
+++ b/src/render/tga.h
@@ -1,36 +1,33 @@
/*
- www.morrowland.com
- apron@morrowland.com
+ render/tga.h
+ This file is part of the Osirion project and is distributed under
+ the terms of the GNU General Public License version 2
*/
+
#ifndef _INCLUDED_RENDER_TGA_H__
#define _INCLUDED_RENDER_TGA_H__
-#include "GL/gl.h"
-
-#define TGA_RGB 2
-#define TGA_A 3
-#define TGA_RLE 10
+#include "render/image.h"
namespace render
{
class TGA {
+
public:
- typedef struct {
- int channels;
- int size_x;
- int size_y;
- unsigned char *data;
- } image;
-
-
- static bool texture(GLuint textureArray[], const char *filename, int textureID);
-
-protected:
- static image *load(const char *filename);
+ /// load a TGA image from disk
+ /** @param filename short path to the filename to be loaded
+ */
+ static Image *load(const char * filename);
+
+ /// write an image to a TGA file
+ /** @param filename short path to the file to write the image data to
+ */
+ static void save(const char *filename, Image & image);
};
}
#endif //_INCLUDED_RENDER_TGA_H__
+