/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant 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. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "tga.h" #include "ifilesystem.h" #include "iarchive.h" #include "idatastream.h" typedef unsigned char byte; #include #include "generic/bitfield.h" #include "imagelib.h" #include "bytestreamutils.h" // represents x,y origin of tga image being decoded class Flip00 {}; // no flip class Flip01 {}; // vertical flip only class Flip10 {}; // horizontal flip only class Flip11 {}; // both template void image_decode(PointerInputStream& istream, PixelDecoder& decode, RGBAImage& image, const Flip00&) { RGBAPixel* end = image.pixels + (image.height * image.width); for(RGBAPixel* row = end; row != image.pixels; row -= image.width) { for(RGBAPixel* pixel = row - image.width; pixel != row; ++pixel) { decode(istream, *pixel); } } } template void image_decode(PointerInputStream& istream, PixelDecoder& decode, RGBAImage& image, const Flip01&) { RGBAPixel* end = image.pixels + (image.height * image.width); for(RGBAPixel* row = image.pixels; row != end; row += image.width) { for(RGBAPixel* pixel = row; pixel != row + image.width; ++pixel) { decode(istream, *pixel); } } } template void image_decode(PointerInputStream& istream, PixelDecoder& decode, RGBAImage& image, const Flip10&) { RGBAPixel* end = image.pixels + (image.height * image.width); for(RGBAPixel* row = end; row != image.pixels; row -= image.width) { for(RGBAPixel* pixel = row; pixel != row - image.width;) { decode(istream, *--pixel); } } } template void image_decode(PointerInputStream& istream, PixelDecoder& decode, RGBAImage& image, const Flip11&) { RGBAPixel* end = image.pixels + (image.height * image.width); for(RGBAPixel* row = image.pixels; row != end; row += image.width) { for(RGBAPixel* pixel = row + image.width; pixel != row;) { decode(istream, *--pixel); } } } inline void istream_read_gray(PointerInputStream& istream, RGBAPixel& pixel) { istream.read(&pixel.blue, 1); pixel.red = pixel.green = pixel.blue; pixel.alpha = 0xff; } inline void istream_read_rgb(PointerInputStream& istream, RGBAPixel& pixel) { istream.read(&pixel.blue, 1); istream.read(&pixel.green, 1); istream.read(&pixel.red, 1); pixel.alpha = 0xff; } inline void istream_read_rgba(PointerInputStream& istream, RGBAPixel& pixel) { istream.read(&pixel.blue, 1); istream.read(&pixel.green, 1); istream.read(&pixel.red, 1); istream.read(&pixel.alpha, 1); } class TargaDecodeGrayPixel { public: void operator()(PointerInputStream& istream, RGBAPixel& pixel) { istream_read_gray(istream, pixel); } }; template void targa_decode_grayscale(PointerInputStream& istream, RGBAImage& image, const Flip& flip) { TargaDecodeGrayPixel decode; image_decode(istream, decode, image, flip); } class TargaDecodeRGBPixel { public: void operator()(PointerInputStream& istream, RGBAPixel& pixel) { istream_read_rgb(istream, pixel); } }; template void targa_decode_rgb(PointerInputStream& istream, RGBAImage& image, const Flip& flip) { TargaDecodeRGBPixel decode; image_decode(istream, decode, image, flip); } class TargaDecodeRGBAPixel { public: void operator()(PointerInputStream& istream, RGBAPixel& pixel) { istream_read_rgba(istream, pixel); } }; template void targa_decode_rgba(PointerInputStream& istream, RGBAImage& image, const Flip& flip) { TargaDecodeRGBAPixel decode; image_decode(istream, decode, image, flip); } typedef byte TargaPacket; typedef byte TargaPacketSize; inline void targa_packet_read_istream(TargaPacket& packet, PointerInputStream& istream) { istream.read(&packet, 1); } inline bool targa_packet_is_rle(const TargaPacket& packet) { return (packet & 0x80) != 0; } inline TargaPacketSize targa_packet_size(const TargaPacket& packet) { return 1 + (packet & 0x7f); } class TargaDecodeRGBPixelRLE { TargaPacketSize m_packetSize; RGBAPixel m_pixel; TargaPacket m_packet; public: TargaDecodeRGBPixelRLE() : m_packetSize(0) { } void operator()(PointerInputStream& istream, RGBAPixel& pixel) { if(m_packetSize == 0) { targa_packet_read_istream(m_packet, istream); m_packetSize = targa_packet_size(m_packet); if(targa_packet_is_rle(m_packet)) { istream_read_rgb(istream, m_pixel); } } if(targa_packet_is_rle(m_packet)) { pixel = m_pixel; } else { istream_read_rgb(istream, pixel); } --m_packetSize; } }; template void targa_decode_rle_rgb(PointerInputStream& istream, RGBAImage& image, const Flip& flip) { TargaDecodeRGBPixelRLE decode; image_decode(istream, decode, image, flip); } class TargaDecodeRGBAPixelRLE { TargaPacketSize m_packetSize; RGBAPixel m_pixel; TargaPacket m_packet; public: TargaDecodeRGBAPixelRLE() : m_packetSize(0) { } void operator()(PointerInputStream& istream, RGBAPixel& pixel) { if(m_packetSize == 0) { targa_packet_read_istream(m_packet, istream); m_packetSize = targa_packet_size(m_packet); if(targa_packet_is_rle(m_packet)) { istream_read_rgba(istream, m_pixel); } } if(targa_packet_is_rle(m_packet)) { pixel = m_pixel; } else { istream_read_rgba(istream, pixel); } --m_packetSize; } }; template void targa_decode_rle_rgba(PointerInputStream& istream, RGBAImage& image, const Flip& flip) { TargaDecodeRGBAPixelRLE decode; image_decode(istream, decode, image, flip); } struct TargaHeader { unsigned char id_length, colormap_type, image_type; unsigned short colormap_index, colormap_length; unsigned char colormap_size; unsigned short x_origin, y_origin, width, height; unsigned char pixel_size, attributes; }; inline void targa_header_read_istream(TargaHeader& targa_header, PointerInputStream& istream) { targa_header.id_length = istream_read_byte(istream); targa_header.colormap_type = istream_read_byte(istream); targa_header.image_type = istream_read_byte(istream); targa_header.colormap_index = istream_read_int16_le(istream); targa_header.colormap_length = istream_read_int16_le(istream); targa_header.colormap_size = istream_read_byte(istream); targa_header.x_origin = istream_read_int16_le(istream); targa_header.y_origin = istream_read_int16_le(istream); targa_header.width = istream_read_int16_le(istream); targa_header.height = istream_read_int16_le(istream); targa_header.pixel_size = istream_read_byte(istream); targa_header.attributes = istream_read_byte(istream); if (targa_header.id_length != 0) istream.seek(targa_header.id_length); // skip TARGA image comment } template class ScopeDelete { Type* m_value; ScopeDelete(const ScopeDelete&); ScopeDelete& operator=(const ScopeDelete&); public: ScopeDelete(Type* value) : m_value(value) { } ~ScopeDelete() { delete m_value; } Type* get_pointer() const { return m_value; } }; template Image* Targa_decodeImageData(const TargaHeader& targa_header, PointerInputStream& istream, const Flip& flip) { RGBAImage* image = new RGBAImage(targa_header.width, targa_header.height); if (targa_header.image_type == 2 || targa_header.image_type == 3) { switch (targa_header.pixel_size) { case 8: targa_decode_grayscale(istream, *image, flip); break; case 24: targa_decode_rgb(istream, *image, flip); break; case 32: targa_decode_rgba(istream, *image, flip); break; default: globalErrorStream() << "LoadTGA: illegal pixel_size '" << targa_header.pixel_size << "'\n"; image->release(); return 0; } } else if (targa_header.image_type == 10) { switch (targa_header.pixel_size) { case 24: targa_decode_rle_rgb(istream, *image, flip); break; case 32: targa_decode_rle_rgba(istream, *image, flip); break; default: globalErrorStream() << "LoadTGA: illegal pixel_size '" << targa_header.pixel_size << "'\n"; image->release(); return 0; } } return image; } const unsigned int TGA_FLIP_HORIZONTAL = 0x10; const unsigned int TGA_FLIP_VERTICAL = 0x20; Image* LoadTGABuff(const byte* buffer) { PointerInputStream istream(buffer); TargaHeader targa_header; targa_header_read_istream(targa_header, istream); if (targa_header.image_type != 2 && targa_header.image_type != 10 && targa_header.image_type != 3) { globalErrorStream() << "LoadTGA: TGA type " << targa_header.image_type << " not supported\n"; globalErrorStream() << "LoadTGA: Only type 2 (RGB), 3 (gray), and 10 (RGB) TGA images supported\n"; return 0; } if (targa_header.colormap_type != 0) { globalErrorStream() << "LoadTGA: colormaps not supported\n"; return 0; } if ((targa_header.pixel_size != 32 && targa_header.pixel_size != 24) && targa_header.image_type != 3) { globalErrorStream() << "LoadTGA: Only 32 or 24 bit images supported\n"; return 0; } if(!bitfield_enabled(targa_header.attributes, TGA_FLIP_HORIZONTAL) && !bitfield_enabled(targa_header.attributes, TGA_FLIP_VERTICAL)) { return Targa_decodeImageData(targa_header, istream, Flip00()); } if(!bitfield_enabled(targa_header.attributes, TGA_FLIP_HORIZONTAL) && bitfield_enabled(targa_header.attributes, TGA_FLIP_VERTICAL)) { return Targa_decodeImageData(targa_header, istream, Flip01()); } if(bitfield_enabled(targa_header.attributes, TGA_FLIP_HORIZONTAL) && !bitfield_enabled(targa_header.attributes, TGA_FLIP_VERTICAL)) { return Targa_decodeImageData(targa_header, istream, Flip10()); } if(bitfield_enabled(targa_header.attributes, TGA_FLIP_HORIZONTAL) && bitfield_enabled(targa_header.attributes, TGA_FLIP_VERTICAL)) { return Targa_decodeImageData(targa_header, istream, Flip11()); } // unreachable return 0; } Image* LoadTGA(ArchiveFile& file) { ScopedArchiveBuffer buffer(file); return LoadTGABuff(buffer.buffer); }