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