/* 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); }