From 3d993058e5078fbdfd92d479281ad93bb40a4bc6 Mon Sep 17 00:00:00 2001 From: Stijn Buys Date: Wed, 26 Mar 2008 23:24:26 +0000 Subject: improved TGA handling --- src/render/tga.cc | 400 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 250 insertions(+), 150 deletions(-) (limited to 'src/render/tga.cc') 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 +#include +#include + #include "filesystem/filesystem.h" #include "render/tga.h" #include "sys/sys.h" -#include "GL/gl.h" -#include + +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; } } -- cgit v1.2.3