/src/FreeImage/Source/FreeImage/PluginPCX.cpp
C++ | 659 lines | 338 code | 123 blank | 198 comment | 85 complexity | db57a5407fa3bf7c59b2a5aa15cf386c MD5 | raw file
1// ========================================================== 2// PCX Loader 3// 4// Design and implementation by 5// - Floris van den Berg (flvdberg@wxs.nl) 6// - Jani Kajala (janik@remedy.fi) 7// - Markus Loibl (markus.loibl@epost.de) 8// - Hervé Drolon (drolon@infonie.fr) 9// - Juergen Riecker (j.riecker@gmx.de) 10// 11// This file is part of FreeImage 3 12// 13// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY 14// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES 15// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE 16// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED 17// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT 18// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY 19// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL 20// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER 21// THIS DISCLAIMER. 22// 23// Use at your own risk! 24// ========================================================== 25 26#include "FreeImage.h" 27#include "Utilities.h" 28 29// ---------------------------------------------------------- 30// Constants + headers 31// ---------------------------------------------------------- 32 33#define IO_BUF_SIZE 2048 34 35// ---------------------------------------------------------- 36 37#ifdef _WIN32 38#pragma pack(push, 1) 39#else 40#pragma pack(1) 41#endif 42 43typedef struct tagPCXHEADER { 44 BYTE manufacturer; // Magic number (0x0A = ZSoft Z) 45 BYTE version; // Version 0 == 2.5 46 // 2 == 2.8 with palette info 47 // 3 == 2.8 without palette info 48 // 5 == 3.0 with palette info 49 BYTE encoding; // Encoding: 0 = uncompressed, 1 = PCX rle compressed 50 BYTE bpp; // Bits per pixel per plane (only 1 or 8) 51 WORD window[4]; // left, upper, right,lower pixel coord. 52 WORD hdpi; // Horizontal resolution 53 WORD vdpi; // Vertical resolution 54 BYTE color_map[48]; // Colormap for 16-color images 55 BYTE reserved; 56 BYTE planes; // Number of planes (1, 3 or 4) 57 WORD bytes_per_line; // Bytes per row (always even) 58 WORD palette_info; // Palette information (1 = color or b&w; 2 = gray scale) 59 WORD h_screen_size; 60 WORD v_screen_size; 61 BYTE filler[54]; // Reserved filler 62} PCXHEADER; 63 64#ifdef _WIN32 65#pragma pack(pop) 66#else 67#pragma pack() 68#endif 69 70// ========================================================== 71// Internal functions 72// ========================================================== 73 74static BOOL 75pcx_validate(FreeImageIO *io, fi_handle handle) { 76 BYTE pcx_signature = 0x0A; 77 BYTE signature[4] = { 0, 0, 0, 0 }; 78 79 if(io->read_proc(&signature, 1, 4, handle) != 4) { 80 return FALSE; 81 } 82 // magic number (0x0A = ZSoft Z) 83 if(signature[0] == pcx_signature) { 84 // version 85 if(signature[1] <= 5) { 86 // encoding 87 if((signature[2] == 0) || (signature[2] == 1)) { 88 // bits per pixel per plane 89 if((signature[3] == 1) || (signature[3] == 8)) { 90 return TRUE; 91 } 92 } 93 } 94 } 95 96 return FALSE; 97} 98 99static unsigned 100readline(FreeImageIO &io, fi_handle handle, BYTE *buffer, unsigned length, BOOL rle, BYTE * ReadBuf, int * ReadPos) { 101 // -----------------------------------------------------------// 102 // Read either run-length encoded or normal image data // 103 // // 104 // THIS IS HOW RUNTIME LENGTH ENCODING WORKS IN PCX: // 105 // // 106 // 1) If the upper 2 bits of a byte are set, // 107 // the lower 6 bits specify the count for the next byte // 108 // // 109 // 2) If the upper 2 bits of the byte are clear, // 110 // the byte is actual data with a count of 1 // 111 // // 112 // Note that a scanline always has an even number of bytes // 113 // ------------------------------------------------------------- 114 115 BYTE count = 0, value = 0; 116 unsigned written = 0; 117 118 if (rle) { 119 // run-length encoded read 120 121 while (length--) { 122 if (count == 0) { 123 if (*ReadPos >= IO_BUF_SIZE - 1 ) { 124 if (*ReadPos == IO_BUF_SIZE - 1) { 125 // we still have one BYTE, copy it to the start pos 126 127 *ReadBuf = ReadBuf[IO_BUF_SIZE - 1]; 128 129 io.read_proc(ReadBuf + 1, 1, IO_BUF_SIZE - 1, handle); 130 } else { 131 // read the complete buffer 132 133 io.read_proc(ReadBuf, 1, IO_BUF_SIZE, handle); 134 } 135 136 *ReadPos = 0; 137 } 138 139 value = *(ReadBuf + (*ReadPos)++); 140 141 if ((value & 0xC0) == 0xC0) { 142 count = value & 0x3F; 143 value = *(ReadBuf + (*ReadPos)++); 144 } else { 145 count = 1; 146 } 147 } 148 149 count--; 150 151 *(buffer + written++) = value; 152 } 153 154 } else { 155 // normal read 156 157 written = io.read_proc(buffer, length, 1, handle); 158 } 159 160 return written; 161} 162 163#ifdef FREEIMAGE_BIGENDIAN 164static void 165SwapHeader(PCXHEADER *header) { 166 SwapShort(&header->window[0]); 167 SwapShort(&header->window[1]); 168 SwapShort(&header->window[2]); 169 SwapShort(&header->window[3]); 170 SwapShort(&header->hdpi); 171 SwapShort(&header->vdpi); 172 SwapShort(&header->bytes_per_line); 173 SwapShort(&header->palette_info); 174 SwapShort(&header->h_screen_size); 175 SwapShort(&header->v_screen_size); 176} 177#endif 178 179// ========================================================== 180// Plugin Interface 181// ========================================================== 182 183static int s_format_id; 184 185// ========================================================== 186// Plugin Implementation 187// ========================================================== 188 189/*! 190 Returns the format string for the plugin. Each plugin, 191 both internal in the DLL and external in a .fip file, must have 192 a unique format string to be addressable. 193*/ 194 195static const char * DLL_CALLCONV 196Format() { 197 return "PCX"; 198} 199 200/*! 201 Returns a description string for the plugin. Though a 202 description is not necessary per-se, 203 it is advised to return an unique string in order to tell the 204 user what type of bitmaps this plugin will read and/or write. 205*/ 206 207static const char * DLL_CALLCONV 208Description() { 209 return "Zsoft Paintbrush"; 210} 211 212/*! 213 Returns a comma separated list of file 214 extensions indicating what files this plugin can open. The 215 list, being used by FreeImage_GetFIFFromFilename, is usually 216 used as a last resort in finding the type of the bitmap we 217 are dealing with. Best is to check the first few bytes on 218 the low-level bits level first and compare them with a known 219 signature . If this fails, FreeImage_GetFIFFromFilename can be 220 used. 221*/ 222 223static const char * DLL_CALLCONV 224Extension() { 225 return "pcx"; 226} 227 228/*! 229 Returns an (optional) regular expression to help 230 software identifying a bitmap type. The 231 expression can be applied to the first few bytes (header) of 232 the bitmap. FreeImage is not capable of processing regular expression itself, 233 but FreeImageQt, the FreeImage Trolltech support library, can. If RegExpr 234 returns NULL, FreeImageQt will automatically bypass Trolltech's regular 235 expression support and use its internal functions to find the bitmap type. 236*/ 237 238static const char * DLL_CALLCONV 239RegExpr() { 240 return NULL; 241} 242 243static const char * DLL_CALLCONV 244MimeType() { 245 return "image/x-pcx"; 246} 247 248/*! 249 Validates a bitmap by reading the first few bytes 250 and comparing them with a known bitmap signature. 251 TRUE is returned if the bytes match the signature, FALSE otherwise. 252 The Validate function is used by using FreeImage_GetFileType. 253 254 Note: a plugin can safely read data any data from the bitmap without seeking back 255 to the original entry point; the entry point is stored prior to calling this 256 function and restored after. 257 258 Note: because of FreeImage's io redirection support, the header for the bitmap 259 must be on the start of the bitmap or at least on a known part in the bitmap. It is 260 forbidden to seek to the end of the bitmap or to a point relative to the end of a bitmap, 261 because the end of the bitmap is not always known. 262*/ 263 264static BOOL DLL_CALLCONV 265Validate(FreeImageIO *io, fi_handle handle) { 266 return pcx_validate(io, handle); 267} 268 269/*! 270 This function is used to 'ask' the plugin if it can write 271 a bitmap in a certain bitdepth. Different bitmap types have different 272 capabilities, for example not all formats allow writing in palettized mode. 273 This function is there provide an uniform interface to the plugin's 274 capabilities. SupportsExportDepth returns TRUE if the plugin support writing 275 in the asked bitdepth, or FALSE if it doesn't. The function also 276 returns FALSE if bitmap saving is not supported by the plugin at all. 277*/ 278 279static BOOL DLL_CALLCONV 280SupportsExportDepth(int depth) { 281 return FALSE; 282} 283 284static BOOL DLL_CALLCONV 285SupportsExportType(FREE_IMAGE_TYPE type) { 286 return FALSE; 287} 288 289static BOOL DLL_CALLCONV 290SupportsNoPixels() { 291 return TRUE; 292} 293 294// ---------------------------------------------------------- 295 296/*! 297 Loads a bitmap into memory. On entry it is assumed that 298 the bitmap to be loaded is of the correct type. If the bitmap 299 is of an incorrect type, the plugin might not gracefully fail but 300 crash or enter an endless loop. It is also assumed that all 301 the bitmap data is available at one time. If the bitmap is not complete, 302 for example because it is being downloaded while loaded, the plugin 303 might also not gracefully fail. 304 305 The Load function has the following parameters: 306 307 The first parameter (FreeImageIO *io) is a structure providing 308 function pointers in order to make use of FreeImage's IO redirection. Using 309 FreeImage's file i/o functions instead of standard ones it is garantueed 310 that all bitmap types, both current and future ones, can be loaded from 311 memory, file cabinets, the internet and more. The second parameter (fi_handle handle) 312 is a companion of FreeImageIO and can be best compared with the standard FILE* type, 313 in a generalized form. 314 315 The third parameter (int page) indicates wether we will be loading a certain page 316 in the bitmap or if we will load the default one. This parameter is only used if 317 the plugin supports multi-paged bitmaps, e.g. cabinet bitmaps that contain a series 318 of images or pages. If the plugin does support multi-paging, the page parameter 319 can contain either a number higher or equal to 0 to load a certain page, or -1 to 320 load the default page. If the plugin does not support multi-paging, 321 the page parameter is always -1. 322 323 The fourth parameter (int flags) manipulates the load function to load a bitmap 324 in a certain way. Every plugin has a different flag parameter with different meanings. 325 326 The last parameter (void *data) can contain a special data block used when 327 the file is read multi-paged. Because not every plugin supports multi-paging 328 not every plugin will use the data parameter and it will be set to NULL.However, 329 when the plugin does support multi-paging the parameter contains a pointer to a 330 block of data allocated by the Open function. 331*/ 332 333static FIBITMAP * DLL_CALLCONV 334Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { 335 FIBITMAP *dib = NULL; 336 BYTE *bits; // Pointer to dib data 337 RGBQUAD *pal; // Pointer to dib palette 338 BYTE *line = NULL; // PCX raster line 339 BYTE *ReadBuf = NULL; // buffer; 340 BOOL bIsRLE; // True if the file is run-length encoded 341 342 if(!handle) { 343 return NULL; 344 } 345 346 BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; 347 348 try { 349 // check PCX identifier 350 351 long start_pos = io->tell_proc(handle); 352 BOOL validated = pcx_validate(io, handle); 353 io->seek_proc(handle, start_pos, SEEK_SET); 354 if(!validated) { 355 throw FI_MSG_ERROR_MAGIC_NUMBER; 356 } 357 358 // process the header 359 360 PCXHEADER header; 361 362 if(io->read_proc(&header, sizeof(PCXHEADER), 1, handle) != 1) { 363 throw FI_MSG_ERROR_PARSING; 364 } 365#ifdef FREEIMAGE_BIGENDIAN 366 SwapHeader(&header); 367#endif 368 369 // allocate a new DIB 370 371 unsigned width = header.window[2] - header.window[0] + 1; 372 unsigned height = header.window[3] - header.window[1] + 1; 373 unsigned bitcount = header.bpp * header.planes; 374 375 if (bitcount == 24) { 376 dib = FreeImage_AllocateHeader(header_only, width, height, bitcount, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); 377 } else { 378 dib = FreeImage_AllocateHeader(header_only, width, height, bitcount); 379 } 380 381 // if the dib couldn't be allocated, throw an error 382 383 if (!dib) { 384 throw FI_MSG_ERROR_DIB_MEMORY; 385 } 386 387 // metrics handling code 388 389 FreeImage_SetDotsPerMeterX(dib, (unsigned) (((float)header.hdpi) / 0.0254000 + 0.5)); 390 FreeImage_SetDotsPerMeterY(dib, (unsigned) (((float)header.vdpi) / 0.0254000 + 0.5)); 391 392 // Set up the palette if needed 393 // ---------------------------- 394 395 switch(bitcount) { 396 case 1: 397 { 398 pal = FreeImage_GetPalette(dib); 399 pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0; 400 pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255; 401 break; 402 } 403 404 case 4: 405 { 406 pal = FreeImage_GetPalette(dib); 407 408 BYTE *pColormap = &header.color_map[0]; 409 410 for (int i = 0; i < 16; i++) { 411 pal[i].rgbRed = pColormap[0]; 412 pal[i].rgbGreen = pColormap[1]; 413 pal[i].rgbBlue = pColormap[2]; 414 pColormap += 3; 415 } 416 417 break; 418 } 419 420 case 8: 421 { 422 BYTE palette_id; 423 424 io->seek_proc(handle, -769L, SEEK_END); 425 io->read_proc(&palette_id, 1, 1, handle); 426 427 if (palette_id == 0x0C) { 428 BYTE *cmap = (BYTE*)malloc(768 * sizeof(BYTE)); 429 io->read_proc(cmap, 768, 1, handle); 430 431 pal = FreeImage_GetPalette(dib); 432 BYTE *pColormap = &cmap[0]; 433 434 for(int i = 0; i < 256; i++) { 435 pal[i].rgbRed = pColormap[0]; 436 pal[i].rgbGreen = pColormap[1]; 437 pal[i].rgbBlue = pColormap[2]; 438 pColormap += 3; 439 } 440 441 free(cmap); 442 } 443 444 // wrong palette ID, perhaps a gray scale is needed ? 445 446 else if (header.palette_info == 2) { 447 pal = FreeImage_GetPalette(dib); 448 449 for(int i = 0; i < 256; i++) { 450 pal[i].rgbRed = (BYTE)i; 451 pal[i].rgbGreen = (BYTE)i; 452 pal[i].rgbBlue = (BYTE)i; 453 } 454 } 455 456 io->seek_proc(handle, (long)sizeof(PCXHEADER), SEEK_SET); 457 } 458 break; 459 } 460 461 if(header_only) { 462 // header only mode 463 return dib; 464 } 465 466 // calculate the line length for the PCX and the DIB 467 468 // length of raster line in bytes 469 unsigned linelength = header.bytes_per_line * header.planes; 470 // length of DIB line (rounded to DWORD) in bytes 471 unsigned pitch = FreeImage_GetPitch(dib); 472 473 // run-length encoding ? 474 475 bIsRLE = (header.encoding == 1) ? TRUE : FALSE; 476 477 // load image data 478 // --------------- 479 480 line = (BYTE*)malloc(linelength * sizeof(BYTE)); 481 if(!line) throw FI_MSG_ERROR_MEMORY; 482 483 ReadBuf = (BYTE*)malloc(IO_BUF_SIZE * sizeof(BYTE)); 484 if(!ReadBuf) throw FI_MSG_ERROR_MEMORY; 485 486 bits = FreeImage_GetScanLine(dib, height - 1); 487 488 int ReadPos = IO_BUF_SIZE; 489 490 if ((header.planes == 1) && ((header.bpp == 1) || (header.bpp == 8))) { 491 BYTE skip; 492 unsigned written; 493 494 for (unsigned y = 0; y < height; y++) { 495 written = readline(*io, handle, bits, linelength, bIsRLE, ReadBuf, &ReadPos); 496 497 // skip trailing garbage at the end of the scanline 498 499 for (unsigned count = written; count < linelength; count++) { 500 if (ReadPos < IO_BUF_SIZE) { 501 ReadPos++; 502 } else { 503 io->read_proc(&skip, sizeof(BYTE), 1, handle); 504 } 505 } 506 507 bits -= pitch; 508 } 509 } else if ((header.planes == 4) && (header.bpp == 1)) { 510 BYTE bit, mask, skip; 511 unsigned index; 512 BYTE *buffer; 513 unsigned x, y, written; 514 515 buffer = (BYTE*)malloc(width * sizeof(BYTE)); 516 if(!buffer) throw FI_MSG_ERROR_MEMORY; 517 518 for (y = 0; y < height; y++) { 519 written = readline(*io, handle, line, linelength, bIsRLE, ReadBuf, &ReadPos); 520 521 // build a nibble using the 4 planes 522 523 memset(buffer, 0, width * sizeof(BYTE)); 524 525 for(int plane = 0; plane < 4; plane++) { 526 bit = (BYTE)(1 << plane); 527 528 for (x = 0; x < width; x++) { 529 index = (unsigned)((x / 8) + plane * header.bytes_per_line); 530 mask = (BYTE)(0x80 >> (x & 0x07)); 531 buffer[x] |= (line[index] & mask) ? bit : 0; 532 } 533 } 534 535 // then write the DIB row 536 537 for (x = 0; x < width / 2; x++) { 538 bits[x] = (buffer[2*x] << 4) | buffer[2*x+1]; 539 } 540 541 // skip trailing garbage at the end of the scanline 542 543 for (unsigned count = written; count < linelength; count++) { 544 if (ReadPos < IO_BUF_SIZE) { 545 ReadPos++; 546 } else { 547 io->read_proc(&skip, sizeof(BYTE), 1, handle); 548 } 549 } 550 551 bits -= pitch; 552 } 553 554 free(buffer); 555 556 } else if((header.planes == 3) && (header.bpp == 8)) { 557 BYTE *pline; 558 559 for (unsigned y = 0; y < height; y++) { 560 readline(*io, handle, line, linelength, bIsRLE, ReadBuf, &ReadPos); 561 562 // convert the plane stream to BGR (RRRRGGGGBBBB -> BGRBGRBGRBGR) 563 // well, now with the FI_RGBA_x macros, on BIGENDIAN we convert to RGB 564 565 pline = line; 566 unsigned x; 567 568 for (x = 0; x < width; x++) { 569 bits[x * 3 + FI_RGBA_RED] = pline[x]; 570 } 571 pline += header.bytes_per_line; 572 573 for (x = 0; x < width; x++) { 574 bits[x * 3 + FI_RGBA_GREEN] = pline[x]; 575 } 576 pline += header.bytes_per_line; 577 578 for (x = 0; x < width; x++) { 579 bits[x * 3 + FI_RGBA_BLUE] = pline[x]; 580 } 581 pline += header.bytes_per_line; 582 583 bits -= pitch; 584 } 585 } else { 586 throw FI_MSG_ERROR_UNSUPPORTED_FORMAT; 587 } 588 589 free(line); 590 free(ReadBuf); 591 592 return dib; 593 594 } catch (const char *text) { 595 // free allocated memory 596 597 if (dib != NULL) { 598 FreeImage_Unload(dib); 599 } 600 if (line != NULL) { 601 free(line); 602 } 603 if (ReadBuf != NULL) { 604 free(ReadBuf); 605 } 606 607 FreeImage_OutputMessageProc(s_format_id, text); 608 } 609 610 return NULL; 611} 612 613// ========================================================== 614// Init 615// ========================================================== 616 617/*! 618 Initialises the plugin. The first parameter (Plugin *plugin) 619 contains a pointer to a pre-allocated Plugin structure 620 wherein pointers to the available plugin functions 621 has to be stored. The second parameter (int format_id) is an identification 622 number that the plugin may use to show plugin specific warning messages 623 or other information to the user. The plugin number 624 is generated by FreeImage and can differ everytime the plugin is 625 initialised. 626 627 If you want to create your own plugin you have to take some 628 rules into account. Plugin functions have to be compiled 629 __stdcall using the multithreaded c runtime libraries. Throwing 630 exceptions in plugin functions is allowed, as long as those exceptions 631 are being caught inside the same plugin. It is forbidden for a plugin 632 function to directly call FreeImage functions or to allocate memory 633 and pass it to the main DLL. Exception to this rule is the special file data 634 block that may be allocated the Open function. Allocating a FIBITMAP inside a 635 plugin can be using the function allocate_proc in the FreeImage structure, 636 which will allocate the memory using the DLL's c runtime library. 637*/ 638 639void DLL_CALLCONV 640InitPCX(Plugin *plugin, int format_id) { 641 s_format_id = format_id; 642 643 plugin->format_proc = Format; 644 plugin->description_proc = Description; 645 plugin->extension_proc = Extension; 646 plugin->regexpr_proc = RegExpr; 647 plugin->open_proc = NULL; 648 plugin->close_proc = NULL; 649 plugin->pagecount_proc = NULL; 650 plugin->pagecapability_proc = NULL; 651 plugin->load_proc = Load; 652 plugin->save_proc = NULL; 653 plugin->validate_proc = Validate; 654 plugin->mime_proc = MimeType; 655 plugin->supports_export_bpp_proc = SupportsExportDepth; 656 plugin->supports_export_type_proc = SupportsExportType; 657 plugin->supports_icc_profiles_proc = NULL; 658 plugin->supports_no_pixels_proc = SupportsNoPixels; 659}