/* 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 ); } } } void image_fix_fully_transparent_alpha( RGBAImage& image ){ const RGBAPixel* end = image.pixels + ( image.height * image.width ); for( RGBAPixel* pixel = image.pixels; pixel != end; ++pixel ) if( pixel->alpha != 0 ) return; for( RGBAPixel* pixel = image.pixels; pixel != end; ++pixel ) pixel->alpha = 0xff; } template inline void istream_read_pixel( PointerInputStream& istream, RGBAPixel& pixel ); template<> inline void istream_read_pixel<1>( PointerInputStream& istream, RGBAPixel& pixel ){ pixel.red = pixel.green = pixel.blue = istream_read_byte( istream ); pixel.alpha = 0xff; } template<> inline void istream_read_pixel<3>( PointerInputStream& istream, RGBAPixel& pixel ){ istream.read( &pixel.blue, 1 ); istream.read( &pixel.green, 1 ); istream.read( &pixel.red, 1 ); pixel.alpha = 0xff; } template<> inline void istream_read_pixel<4>( PointerInputStream& istream, RGBAPixel& pixel ){ istream.read( &pixel.blue, 1 ); istream.read( &pixel.green, 1 ); istream.read( &pixel.red, 1 ); istream.read( &pixel.alpha, 1 ); } template inline void istream_read_paletted( PointerInputStream& istream, RGBAPixel& pixel, const byte* colormap ); template<> inline void istream_read_paletted<3>( PointerInputStream& istream, RGBAPixel& pixel, const byte* colormap ){ const byte* color = colormap + istream_read_byte( istream ) * 3; pixel.blue = *color++; pixel.green = *color++; pixel.red = *color; pixel.alpha = 0xff; } template<> inline void istream_read_paletted<4>( PointerInputStream& istream, RGBAPixel& pixel, const byte* colormap ){ const byte* color = colormap + istream_read_byte( istream ) * 4; pixel.blue = *color++; pixel.green = *color++; pixel.red = *color++; pixel.alpha = *color; } template class TargaDecodePalettedPixel { const byte* m_colormap; public: TargaDecodePalettedPixel( const byte* colormap ) : m_colormap( colormap ){ } void operator()( PointerInputStream& istream, RGBAPixel& pixel ) const { istream_read_paletted( istream, pixel, m_colormap ); } }; template void targa_decode_paletted( PointerInputStream& istream, RGBAImage& image, const Flip& flip, const byte* colormap ){ TargaDecodePalettedPixel decode( colormap ); image_decode( istream, decode, image, flip ); } template class TargaDecodePixel { public: void operator()( PointerInputStream& istream, RGBAPixel& pixel ) const { istream_read_pixel( istream, pixel ); } }; template void targa_decode( PointerInputStream& istream, RGBAImage& image, const Flip& flip ){ TargaDecodePixel 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 ); } template class TargaDecodePixelRLE { TargaPacketSize m_packetSize; RGBAPixel m_pixel; TargaPacket m_packet; const PixelReadFunctor& m_pixelRead; public: TargaDecodePixelRLE( const PixelReadFunctor& pixelRead ) : m_packetSize( 0 ), m_pixelRead( pixelRead ){ } 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 ) ) { m_pixelRead( istream, m_pixel ); } } if ( targa_packet_is_rle( m_packet ) ) { pixel = m_pixel; } else { m_pixelRead( istream, pixel ); } --m_packetSize; } }; template void targa_decode_rle( PointerInputStream& istream, RGBAImage& image, const Flip& flip ){ const TargaDecodePixel pixelRead; TargaDecodePixelRLE> decode( pixelRead ); image_decode( istream, decode, image, flip ); } template void targa_decode_paletted_rle( PointerInputStream& istream, RGBAImage& image, const Flip& flip, const byte* colormap ){ const TargaDecodePalettedPixel pixelRead( colormap ); TargaDecodePixelRLE> decode( pixelRead ); 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; byte *colormap = nullptr; void colormap_read( PointerInputStream& istream ){ const std::size_t size = colormap_size / 8 * colormap_length; colormap = new byte[size]; istream.read( colormap, size ); } ~TargaHeader(){ delete[] colormap; } }; 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 } if( ( targa_header.image_type == 1 || targa_header.image_type == 9 ) && targa_header.colormap_type == 1 ){ targa_header.colormap_read( istream ); } } 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( istream, *image, flip ); break; case 24: targa_decode( istream, *image, flip ); break; case 32: targa_decode( istream, *image, flip ); image_fix_fully_transparent_alpha( *image ); break; default: globalErrorStream() << "LoadTGA: illegal pixel_size '" << targa_header.pixel_size << "'\n"; image->release(); return 0; } } else if ( targa_header.image_type == 10 || targa_header.image_type == 11 ) { switch ( targa_header.pixel_size ) { case 8: targa_decode_rle( istream, *image, flip ); break; case 24: targa_decode_rle( istream, *image, flip ); break; case 32: targa_decode_rle( istream, *image, flip ); image_fix_fully_transparent_alpha( *image ); break; default: globalErrorStream() << "LoadTGA: illegal pixel_size '" << targa_header.pixel_size << "'\n"; image->release(); return 0; } } else if ( targa_header.image_type == 1 ) { switch ( targa_header.colormap_size ) { case 24: targa_decode_paletted( istream, *image, flip, targa_header.colormap ); break; case 32: targa_decode_paletted( istream, *image, flip, targa_header.colormap ); image_fix_fully_transparent_alpha( *image ); break; default: globalErrorStream() << "LoadTGA: illegal colormap_size '" << targa_header.colormap_size << "'\n"; image->release(); return 0; } } else if ( targa_header.image_type == 9 ) { switch ( targa_header.colormap_size ) { case 24: targa_decode_paletted_rle( istream, *image, flip, targa_header.colormap ); break; case 32: targa_decode_paletted_rle( istream, *image, flip, targa_header.colormap ); image_fix_fully_transparent_alpha( *image ); break; default: globalErrorStream() << "LoadTGA: illegal colormap_size '" << targa_header.colormap_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 != 1 && targa_header.image_type != 2 && targa_header.image_type != 3 && targa_header.image_type != 9 && targa_header.image_type != 10 && targa_header.image_type != 11 ) { globalErrorStream() << "LoadTGA: TGA type " << targa_header.image_type << " not supported\n"; globalErrorStream() << "LoadTGA: Only uncompressed types: 1 (paletted), 2 (RGB), 3 (gray) and compressed: 9 (paletted), 10 (RGB), 11 (gray) of TGA images supported\n"; return 0; } if ( targa_header.image_type == 1 || targa_header.image_type == 9 ) { if( targa_header.colormap_type != 1 ){ globalErrorStream() << "LoadTGA: only type 1 colormaps are supported\n"; return 0; } else if( targa_header.colormap_index != 0 ){ globalErrorStream() << "LoadTGA: colormap_index not supported\n"; return 0; } } if ( ( ( targa_header.image_type == 2 || targa_header.image_type == 10 ) && targa_header.pixel_size != 32 && targa_header.pixel_size != 24 ) || ( ( targa_header.image_type == 3 || targa_header.image_type == 11 ) && targa_header.pixel_size != 8 ) || ( ( targa_header.image_type == 1 || targa_header.image_type == 9 ) && targa_header.pixel_size != 8 ) ) { globalErrorStream() << "LoadTGA: Only 32, 24 or 8 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 ); }