1/*
2 * Copyright 2008, Jérôme Duval, korli@users.berlios.de. All rights reserved.
3 * Copyright 2005-2009, Axel Dörfler, axeld@pinc-software.de.
4 * Distributed under the terms of the MIT License.
5 */
6
7
8#include "PCX.h"
9
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13
14#include <ByteOrder.h>
15
16#include "StreamBuffer.h"
17#include "PCXTranslator.h"
18
19
20//#define TRACE_PCX
21#ifdef TRACE_PCX
22#	define TRACE(x...) printf(x)
23#else
24#	define TRACE(x...) ;
25#endif
26
27
28using namespace PCX;
29
30
31class TempAllocator {
32	public:
33		TempAllocator() : fMemory(NULL) {}
34		~TempAllocator() { free(fMemory); }
35
36		void *Allocate(size_t size) { return fMemory = malloc(size); }
37
38	private:
39		void	*fMemory;
40};
41
42
43bool
44pcx_header::IsValid() const
45{
46	TRACE("manufacturer:%u version:%u encoding:%u bitsPerPixel:%u numPlanes:%u bytesPerLine:%u\n", manufacturer, version, encoding, bitsPerPixel, numPlanes, bytesPerLine);
47	return manufacturer == 10
48		&& version == 5
49		&& encoding == 1
50		&& (bitsPerPixel == 1 || bitsPerPixel == 4 || bitsPerPixel == 8)
51		&& (numPlanes == 1 || numPlanes == 3)
52		&& (bitsPerPixel == 8 || numPlanes == 1)
53		&& (bytesPerLine & 1) == 0;
54}
55
56
57void
58pcx_header::SwapToHost()
59{
60	swap_data(B_UINT16_TYPE, this, sizeof(pcx_header), B_SWAP_LENDIAN_TO_HOST);
61}
62
63
64void
65pcx_header::SwapFromHost()
66{
67	swap_data(B_UINT16_TYPE, this, sizeof(pcx_header), B_SWAP_HOST_TO_LENDIAN);
68}
69
70
71//	#pragma mark -
72
73
74static status_t
75convert_data_to_bits(pcx_header &header, StreamBuffer &source,
76	BPositionIO &target)
77{
78	uint16 bitsPerPixel = header.bitsPerPixel;
79	uint16 bytesPerLine = header.bytesPerLine;
80	uint32 width = header.xMax - header.xMin + 1;
81	uint32 height = header.yMax - header.yMin + 1;
82	uint16 numPlanes = header.numPlanes;
83	uint32 scanLineLength = numPlanes * bytesPerLine;
84
85	// allocate buffers
86	TempAllocator scanLineAllocator;
87	TempAllocator paletteAllocator;
88	uint8 *scanLineData[height];
89	uint8 *palette = (uint8 *)paletteAllocator.Allocate(3 * 256);
90	status_t status = B_OK;
91
92	for (uint32 row = 0; row < height; row++) {
93		TRACE("scanline %ld\n", row);
94		scanLineData[row] = (uint8 *)scanLineAllocator.Allocate(scanLineLength);
95		if (scanLineData[row] == NULL)
96			return B_NO_MEMORY;
97		uint8 *line = scanLineData[row];
98		uint32 index = 0;
99		uint8 x;
100		do {
101			if (source.Read(&x, 1) != 1) {
102				status = B_IO_ERROR;
103				break;
104			}
105			if ((x & 0xc0) == 0xc0) {
106				uint32 count = x & 0x3f;
107				if (index + count - 1 > scanLineLength) {
108					status = B_BAD_DATA;
109					break;
110				}
111				if (source.Read(&x, 1) != 1) {
112					status = B_IO_ERROR;
113					break;
114				}
115				for (uint32 i = 0; i < count; i++)
116					line[index++] = x;
117			} else {
118				line[index++] = x;
119			}
120		} while (index < scanLineLength);
121
122		if (status != B_OK) {
123			// If we've already read more than a third of the file, display
124			// what we have, ie. ignore the error.
125			if (row < height / 3)
126				return status;
127
128			memset(scanLineData + row, 0, sizeof(uint8*) * (height - row));
129			break;
130		}
131	}
132
133	if (bitsPerPixel == 8 && numPlanes == 1) {
134		TRACE("palette reading %p 8\n", palette);
135		uint8 x;
136		if (status != B_OK || source.Read(&x, 1) != 1 || x != 12) {
137			// Try again by repositioning the file stream
138			if (source.Seek(-3 * 256 - 1, SEEK_END) < 0)
139				return B_BAD_DATA;
140			if (source.Read(&x, 1) != 1)
141				return B_IO_ERROR;
142			if (x != 12)
143				return B_BAD_DATA;
144		}
145		if (source.Read(palette, 256 * 3) != 256 * 3)
146			return B_IO_ERROR;
147	} else {
148		TRACE("palette reading %p palette\n", palette);
149		memcpy(palette, &header.paletteInfo, 48);
150	}
151
152	uint8 alpha = 255;
153	if (bitsPerPixel == 1 && numPlanes == 1) {
154		TRACE("target writing 1\n");
155		palette[0] = palette[1] = palette[2] = 0;
156		palette[3] = palette[4] = palette[5] = 0xff;
157		for (uint32 row = 0; row < height; row++) {
158			uint8 *line = scanLineData[row];
159			if (line == NULL)
160				break;
161			uint8 mask[] = { 128, 64, 32, 16, 8, 4, 2, 1 };
162			for (uint32 i = 0; i < width; i++) {
163				bool isBit = ((line[i >> 3] & mask[i & 7]) != 0) ? true : false;
164				target.Write(&palette[!isBit ? 2 : 5], 1);
165				target.Write(&palette[!isBit ? 1 : 4], 1);
166				target.Write(&palette[!isBit ? 0 : 3], 1);
167				target.Write(&alpha, 1);
168			}
169		}
170	} else if (bitsPerPixel == 4 && numPlanes == 1) {
171		TRACE("target writing 4\n");
172		for (uint32 row = 0; row < height; row++) {
173			uint8 *line = scanLineData[row];
174			if (line == NULL)
175				break;
176			for (uint32 i = 0; i < width; i++) {
177				uint16 index;
178				if ((i & 1) == 0)
179					index = (line[i >> 1] >> 4) & 15;
180				else
181					index = line[i >> 1] & 15;
182				TRACE("target writing 4 i %d index %d\n", i, index);
183				index += (index + index);
184				target.Write(&palette[index+2], 1);
185				target.Write(&palette[index+1], 1);
186				target.Write(&palette[index], 1);
187				target.Write(&alpha, 1);
188			}
189		}
190	} else if (bitsPerPixel == 8 && numPlanes == 1) {
191		TRACE("target writing 8\n");
192		for (uint32 row = 0; row < height; row++) {
193			TRACE("target writing 8 row %ld\n", row);
194			uint8 *line = scanLineData[row];
195			if (line == NULL)
196				break;
197			for (uint32 i = 0; i < width; i++) {
198				uint16 index = line[i];
199				index += (index + index);
200				target.Write(&palette[index+2], 1);
201				target.Write(&palette[index+1], 1);
202				target.Write(&palette[index], 1);
203				target.Write(&alpha, 1);
204			}
205
206		}
207	} else {
208		TRACE("target writing raw\n");
209		for (uint32 row = 0; row < height; row++) {
210			uint8 *line = scanLineData[row];
211			if (line == NULL)
212				break;
213			for (uint32 i = 0; i < width; i++) {
214				target.Write(&line[i + 2 * bytesPerLine], 1);
215				target.Write(&line[i + bytesPerLine], 1);
216				target.Write(&line[i], 1);
217				target.Write(&alpha, 1);
218			}
219		}
220	}
221
222	return B_OK;
223}
224
225
226//	#pragma mark -
227
228
229status_t
230PCX::identify(BMessage *settings, BPositionIO &stream, uint8 &type, int32 &bitsPerPixel)
231{
232	// read in the header
233
234	pcx_header header;
235	if (stream.Read(&header, sizeof(pcx_header)) != (ssize_t)sizeof(pcx_header))
236		return B_BAD_VALUE;
237
238	header.SwapToHost();
239
240	// check header
241
242	if (!header.IsValid())
243		return B_BAD_VALUE;
244
245	bitsPerPixel = header.bitsPerPixel;
246
247	TRACE("PCX::identify OK\n");
248
249	return B_OK;
250}
251
252
253/**	Converts an PCX image of any type into a B_RGBA32 B_TRANSLATOR_BITMAP.
254 */
255
256status_t
257PCX::convert_pcx_to_bits(BMessage *settings, BPositionIO &source, BPositionIO &target)
258{
259	StreamBuffer sourceBuf(&source, 2048);
260	if (sourceBuf.InitCheck() != B_OK)
261		return B_IO_ERROR;
262
263	pcx_header header;
264	if (sourceBuf.Read(&header, sizeof(pcx_header)) != (ssize_t)sizeof(pcx_header))
265		return B_BAD_VALUE;
266
267	header.SwapToHost();
268
269	// check header
270
271	if (!header.IsValid())
272		return B_BAD_VALUE;
273
274	uint16 width = header.xMax - header.xMin + 1;
275	uint16 height = header.yMax - header.yMin + 1;
276
277	TranslatorBitmap bitsHeader;
278	bitsHeader.magic = B_TRANSLATOR_BITMAP;
279	bitsHeader.bounds.left = 0;
280	bitsHeader.bounds.top = 0;
281	bitsHeader.bounds.right = width - 1;
282	bitsHeader.bounds.bottom = height - 1;
283	bitsHeader.bounds.Set(0, 0, width - 1, height - 1);
284	bitsHeader.rowBytes = width * 4;
285	bitsHeader.colors = B_RGB32;
286	bitsHeader.dataSize = bitsHeader.rowBytes * height;
287
288	// write out Be's Bitmap header
289	swap_data(B_UINT32_TYPE, &bitsHeader, sizeof(TranslatorBitmap), B_SWAP_HOST_TO_BENDIAN);
290	target.Write(&bitsHeader, sizeof(TranslatorBitmap));
291
292	return convert_data_to_bits(header, sourceBuf, target);
293}
294