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-11-11 19:11:57 +0000
committerStijn Buys <ingar@osirion.org>2008-11-11 19:11:57 +0000
commit773c1bafe0f1d8b706e0f72e235f8466e7a9ccf5 (patch)
treeab4b3058f436a4e4c54618f132a9179ee40e330c /src/render/tgafile.cc
parent3082cb197fb6af7d069f9ad211ff6ea5657d924a (diff)
cleanups
Diffstat (limited to 'src/render/tgafile.cc')
-rw-r--r--src/render/tgafile.cc367
1 files changed, 367 insertions, 0 deletions
diff --git a/src/render/tgafile.cc b/src/render/tgafile.cc
new file mode 100644
index 0000000..5a27648
--- /dev/null
+++ b/src/render/tgafile.cc
@@ -0,0 +1,367 @@
+/*
+ render/tgafile.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 <string.h>
+
+#include <fstream>
+#include <sstream>
+#include <string>
+
+#include "filesystem/filesystem.h"
+#include "render/tgafile.h"
+#include "sys/sys.h"
+
+const unsigned char TGA_NONE = 0;
+const unsigned char TGA_TRUECOLOR = 2;
+const unsigned char TGA_TRUECOLOR_RLE = 10;
+
+namespace render
+{
+
+Image *TGA::load(const char *filename)
+{
+ Image *image = 0;
+
+ if (!filename)
+ return 0;
+
+ filesystem::File *tga_file = filesystem::open(filename);
+ if (!tga_file) {
+ //con_warn << "Could not open " << filename << std::endl;
+ return 0;
+ }
+
+ // 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;
+ }
+
+ // byte 0 - image ID field length
+ unsigned int tga_idlength = header[0];
+
+ // byte 1 - color map type
+ unsigned int tga_colormap = header[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];
+
+ // 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;
+
+ 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);
+ }
+
+ image->swap_channels();
+
+ } else if (tga_depth == 16) {
+
+ channels = 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;
+
+ // 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 {
+ con_warn << "Error reading " << filename
+ << ": unsupported image depth '" << tga_depth << "'!" << std::endl;
+ filesystem::close(tga_file);
+ return 0;
+ }
+
+ 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[3];
+
+ // read RLE packet byte
+ tga_file->read(&rle, 1);
+
+ if (rle < 128) {
+ rle++; // rle contains the number of pixels-1
+ tga_file->read((void *)(*image)[index*channels], rle*channels);
+ index += rle;
+
+ } else {
+ 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--;
+ }
+ }
+ }
+
+ image->swap_channels();
+
+ break;
+
+ 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;
+ }
+
+ con_debug << " " << filename << " " << image->width() << "x" << image->height() << "x" << image->bpp() << "bpp" << std::endl;
+ return image;
+}
+
+void TGA::save(const char *filename, Image & image)
+{
+ if (!filename)
+ return;
+
+ std::ofstream ofs(filename, std::ios_base::out | std::ios_base::binary );
+
+ 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 length = 0 (no image ID field present)
+ // byte 1 - color map type = 0 (no palette present)
+ // byte 2 - image type = 10 (truecolor RLE encoded)
+ header[2] = TGA_TRUECOLOR_RLE;
+ // 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 pixel_data[image.channels()];
+ unsigned char block_data[image.channels()*128];
+ unsigned char rle_packet;
+ bool compress = false;
+ size_t block_length = 0;
+
+ for (int y = image.height()-1; y >= 0; y--) {
+ for (size_t x = 0; x < image.width(); x++) {
+ size_t index = y*image.width()*image.channels() + x * image.channels();
+
+ pixel_data[0] = *image[index+2];
+ pixel_data[1] = *image[index+1];
+ pixel_data[2] = *image[index];
+ if (image.channels() == 4)
+ pixel_data[3] = *image[index+3];
+
+ if (block_length == 0) {
+ memcpy(block_data, pixel_data, image.channels());
+ block_length++;
+ compress = false;
+ } else {
+ if (!compress) {
+
+ // uncompressed block and pixel_data differs from the last pixel
+ if (memcmp(&block_data[(block_length-1)*image.channels()], pixel_data, image.channels()) != 0) {
+ // append pixel
+ memcpy(&block_data[block_length*image.channels()], pixel_data, image.channels());
+
+ block_length++;
+ } else {
+
+ // uncompressed block and pixel data is identical
+ if (block_length > 1 ) {
+ // write the uncompressed block
+ rle_packet = block_length - 2;
+ ofs.write((char *)&rle_packet, 1);
+ ofs.write((char *)block_data, (block_length-1) * image.channels());
+ block_length = 1;
+ }
+ memcpy(block_data, pixel_data, image.channels());
+ block_length++;
+ compress = true;
+ }
+
+ } else {
+
+ // compressed block and pixel data is identical
+ if (memcmp(block_data, pixel_data, image.channels()) == 0) {
+ block_length++;
+
+ } else {
+
+ // compressed block and pixel data differs
+ if (block_length > 1) {
+ // write the compressed block
+ rle_packet = block_length + 127;
+ ofs.write((char *)&rle_packet, 1);
+ ofs.write((char *)block_data, image.channels());
+ block_length = 0;
+ }
+ memcpy(&block_data[block_length * image.channels()], pixel_data, image.channels());
+ block_length++;
+ compress = false;
+ }
+ }
+ }
+
+ if (block_length == 128) {
+ rle_packet = block_length - 1;
+ if (!compress) {
+ ofs.write((char *)&rle_packet, 1);
+ ofs.write((char *)block_data, 128 * image.channels());
+ } else {
+ rle_packet += 128;
+ ofs.write((char *)&rle_packet, 1);
+ ofs.write((char *)block_data, image.channels());
+ }
+
+ block_length = 0;
+ compress = false;
+ }
+ }
+ }
+
+ // write remaining bytes
+ if (block_length) {
+ rle_packet = block_length - 1;
+ if (!compress) {
+ ofs.write((char *)&rle_packet, 1);
+ ofs.write((char *)block_data, block_length * image.channels());
+ } else {
+ rle_packet += 128;
+ ofs.write((char *)&rle_packet, 1);
+ ofs.write((char *)block_data, 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;
+}
+
+}