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