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