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