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/tga.cc
parent2b9f068e7ee4c0d249c715f9eb5a3c2c8a11e6f8 (diff)
improved TGA handling
Diffstat (limited to 'src/render/tga.cc')
-rw-r--r--src/render/tga.cc400
1 files changed, 250 insertions, 150 deletions
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;
}
}