/* Copyright (C) 1999-2006 Id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. 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 "pcx.h" #include "ifilesystem.h" typedef unsigned char byte; #include #include "imagelib.h" #include "bytestreamutils.h" /* ================================================================= PCX LOADING ================================================================= */ typedef struct { unsigned char manufacturer; unsigned char version; unsigned char encoding; unsigned char bits_per_pixel; unsigned short xmin, ymin, xmax, ymax; unsigned short hres, vres; unsigned char palette[48]; unsigned char reserved; unsigned char color_planes; unsigned short bytes_per_line; unsigned short palette_type; unsigned char filler[58]; unsigned char data; // unbounded } pcx_t; /* ============== LoadPCX ============== */ struct PCXRLEPacket { byte data; int length; }; inline void ByteStream_readPCXRLEPacket( PointerInputStream& inputStream, PCXRLEPacket& packet ){ byte d; inputStream.read( &d, 1 ); if ( ( d & 0xC0 ) == 0xC0 ) { packet.length = d & 0x3F; inputStream.read( &packet.data, 1 ); } else { packet.length = 1; packet.data = d; } } void LoadPCXBuff( byte* buffer, std::size_t len, byte **pic, byte **palette, int *width, int *height ){ *pic = 0; pcx_t pcx; int x, y, lsize; byte *out, *pix; /* parse the PCX file */ PointerInputStream inputStream( buffer ); pcx.manufacturer = istream_read_byte( inputStream ); pcx.version = istream_read_byte( inputStream ); pcx.encoding = istream_read_byte( inputStream ); pcx.bits_per_pixel = istream_read_byte( inputStream ); pcx.xmin = istream_read_int16_le( inputStream ); pcx.ymin = istream_read_int16_le( inputStream ); pcx.xmax = istream_read_int16_le( inputStream ); pcx.ymax = istream_read_int16_le( inputStream ); pcx.hres = istream_read_int16_le( inputStream ); pcx.vres = istream_read_int16_le( inputStream ); inputStream.read( pcx.palette, 48 ); pcx.reserved = istream_read_byte( inputStream ); pcx.color_planes = istream_read_byte( inputStream ); pcx.bytes_per_line = istream_read_int16_le( inputStream ); pcx.palette_type = istream_read_int16_le( inputStream ); inputStream.read( pcx.filler, 58 ); if ( pcx.manufacturer != 0x0a || pcx.version != 5 || pcx.encoding != 1 || pcx.bits_per_pixel != 8 ) { return; } if ( width ) { *width = pcx.xmax + 1; } if ( height ) { *height = pcx.ymax + 1; } if ( !pic ) { return; } out = (byte *)malloc( ( pcx.ymax + 1 ) * ( pcx.xmax + 1 ) ); *pic = out; pix = out; /* RR2DO2: pcx fix */ lsize = pcx.color_planes * pcx.bytes_per_line; /* go scanline by scanline */ for ( y = 0; y <= pcx.ymax; y++, pix += pcx.xmax + 1 ) { /* do a scanline */ for ( x = 0; x <= pcx.xmax; ) { /* RR2DO2 */ PCXRLEPacket packet; ByteStream_readPCXRLEPacket( inputStream, packet ); while ( packet.length-- > 0 ) { pix[ x++ ] = packet.data; } } /* RR2DO2: discard any other data */ PCXRLEPacket packet; while ( x < lsize ) { ByteStream_readPCXRLEPacket( inputStream, packet ); x++; } while ( packet.length-- > 0 ) { x++; } } /* validity check */ if ( std::size_t( inputStream.get() - buffer ) > len ) { *pic = 0; } if ( palette ) { *palette = (byte *)malloc( 768 ); memcpy( *palette, buffer + len - 768, 768 ); } } /* ============== LoadPCX32 ============== */ Image* LoadPCX32Buff( byte* buffer, std::size_t length ){ byte *palette; byte *pic8; int i, c, p, width, height; byte *pic32; LoadPCXBuff( buffer, length, &pic8, &palette, &width, &height ); if ( !pic8 ) { return 0; } RGBAImage* image = new RGBAImage( width, height ); c = ( width ) * ( height ); pic32 = image->getRGBAPixels(); for ( i = 0; i < c; i++ ) { p = pic8[i]; pic32[0] = palette[p * 3]; pic32[1] = palette[p * 3 + 1]; pic32[2] = palette[p * 3 + 2]; pic32[3] = 255; pic32 += 4; } free( pic8 ); free( palette ); return image; } Image* LoadPCX32( ArchiveFile& file ){ ScopedArchiveBuffer buffer( file ); return LoadPCX32Buff( buffer.buffer, buffer.length ); }