xref: /haiku/src/add-ons/translators/pcx/PCX.cpp (revision ed24eb5ff12640d052171c6a7feba37fab8a75d1)
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 
28 using namespace PCX;
29 
30 
31 class 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 
43 bool
44 pcx_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 
57 void
58 pcx_header::SwapToHost()
59 {
60 	swap_data(B_UINT16_TYPE, this, sizeof(pcx_header), B_SWAP_LENDIAN_TO_HOST);
61 }
62 
63 
64 void
65 pcx_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 
74 static status_t
75 convert_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 
229 status_t
230 PCX::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 
256 status_t
257 PCX::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