PageRenderTime 86ms CodeModel.GetById 15ms app.highlight 65ms RepoModel.GetById 2ms app.codeStats 0ms

/src/FreeImage/Source/FreeImage/PluginPCX.cpp

https://bitbucket.org/cabalistic/ogredeps/
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}