diff options
Diffstat (limited to 'plugins/FreeImage/Source/FreeImage/PluginSGI.cpp')
| -rw-r--r-- | plugins/FreeImage/Source/FreeImage/PluginSGI.cpp | 425 | 
1 files changed, 425 insertions, 0 deletions
diff --git a/plugins/FreeImage/Source/FreeImage/PluginSGI.cpp b/plugins/FreeImage/Source/FreeImage/PluginSGI.cpp new file mode 100644 index 0000000000..38ac293b78 --- /dev/null +++ b/plugins/FreeImage/Source/FreeImage/PluginSGI.cpp @@ -0,0 +1,425 @@ +// ==========================================================
 +// SGI Loader
 +//
 +// Design and implementation by
 +// - Sherman Wilcox
 +// - Noam Gat
 +//
 +// References : 
 +// ------------
 +// - The SGI Image File Format, Version 1.0
 +// http://astronomy.swin.edu.au/~pbourke/dataformats/sgirgb/sgiversion.html
 +// - SGI RGB Image Format
 +// http://astronomy.swin.edu.au/~pbourke/dataformats/sgirgb/
 +//
 +//
 +// This file is part of FreeImage 3
 +//
 +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
 +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
 +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
 +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
 +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
 +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
 +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
 +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
 +// THIS DISCLAIMER.
 +//
 +// Use at your own risk!
 +// ==========================================================
 +
 +#include "FreeImage.h"
 +#include "Utilities.h"
 +
 +// ----------------------------------------------------------
 +//   Constants + headers
 +// ----------------------------------------------------------
 +
 +#ifdef _WIN32
 +#pragma pack(push, 1)
 +#else
 +#pragma pack(1)
 +#endif
 +
 +typedef struct tagSGIHeader {
 +	/** IRIS image file magic number. This should be decimal 474. */
 +	WORD magic;
 +	/** Storage format: 0 for uncompressed, 1 for RLE compression. */
 +	BYTE storage;
 +	/** Number of bytes per pixel channel. Legally 1 or 2. */
 +	BYTE bpc;
 +	/**
 +	Number of dimensions. Legally 1, 2, or 3. 
 +	1 means a single row, XSIZE long
 +	2 means a single 2D image
 +	3 means multiple 2D images
 +	*/
 +	WORD dimension;	
 +	/** X size in pixels */
 +	WORD xsize;
 +	/** Y size in pixels */
 +	WORD ysize;
 +	/**
 +	Number of channels. 
 +	1 indicates greyscale
 +	3 indicates RGB
 +	4 indicates RGB and Alpha
 +	*/
 +	WORD zsize;
 +	/** Minimum pixel value. This is the lowest pixel value in the image.*/
 +	LONG pixmin;
 +	/** Maximum pixel value. This is the highest pixel value in the image.*/
 +	LONG pixmax;
 +	/** Ignored. Normally set to 0. */
 +	char dummy[4];
 +	/** Image name. Must be null terminated, therefore at most 79 bytes. */
 +	char imagename[80];
 +	/** 
 +	Colormap ID. 
 +	0 - normal mode
 +	1 - dithered, 3 mits for red and green, 2 for blue, obsolete
 +	2 - index colour, obsolete
 +	3 - not an image but a colourmap
 +	*/
 +	LONG colormap;
 +	/** Ignored. Should be set to 0, makes the header 512 bytes. */
 +	char reserved[404];
 +} SGIHeader;
 +
 +typedef struct tagRLEStatus {
 +  int cnt;
 +  int val;
 +} RLEStatus;
 +
 +#ifdef _WIN32
 +#pragma pack(pop)
 +#else
 +#pragma pack()
 +#endif
 +
 +static const char *SGI_LESS_THAN_HEADER_LENGTH = "Incorrect header size";
 +static const char *SGI_16_BIT_COMPONENTS_NOT_SUPPORTED = "No 16 bit support";
 +static const char *SGI_COLORMAPS_NOT_SUPPORTED = "No colormap support";
 +static const char *SGI_EOF_IN_RLE_INDEX = "EOF in run length encoding";
 +static const char *SGI_EOF_IN_IMAGE_DATA = "EOF in image data";
 +static const char *SGI_INVALID_CHANNEL_COUNT = "Invalid channel count";
 +
 +// ==========================================================
 +// Plugin Interface
 +// ==========================================================
 +
 +static int s_format_id;
 +
 +// ==========================================================
 +// Plugin Implementation
 +// ==========================================================
 +
 +#ifndef FREEIMAGE_BIGENDIAN
 +static void 
 +SwapHeader(SGIHeader *header) {
 +	SwapShort(&header->magic);
 +	SwapShort(&header->dimension);
 +	SwapShort(&header->xsize);
 +	SwapShort(&header->ysize);
 +	SwapShort(&header->zsize);
 +	SwapLong((DWORD*)&header->pixmin);
 +	SwapLong((DWORD*)&header->pixmax);
 +	SwapLong((DWORD*)&header->colormap);
 +}
 +#endif
 +
 +static int 
 +get_rlechar(FreeImageIO *io, fi_handle handle, RLEStatus *pstatus) {
 +	if (!pstatus->cnt) {
 +		int cnt = 0;
 +		while (0 == cnt) {
 +			BYTE packed = 0;
 +			if(io->read_proc(&packed, sizeof(BYTE), 1, handle) < 1) {
 +				return EOF;
 +			}
 +			cnt = packed;
 +		}
 +		if (cnt == EOF) {
 +			return EOF;
 +		}
 +		pstatus->cnt = cnt & 0x7F;
 +		if (cnt & 0x80) {
 +			pstatus->val = -1;
 +		} else {
 +			BYTE packed = 0;
 +			if(io->read_proc(&packed, sizeof(BYTE), 1, handle) < 1) {
 +				return EOF;
 +			}
 +			pstatus->val = packed;
 +		}
 +	}
 +	pstatus->cnt--;
 +	if (pstatus->val == -1) {
 +		BYTE packed = 0;
 +		if(io->read_proc(&packed, sizeof(BYTE), 1, handle) < 1) {
 +			return EOF;
 +		}
 +		return packed;
 +	}
 +	else {
 +		return pstatus->val;
 +	}
 +}
 +
 +static const char * DLL_CALLCONV
 +Format() {
 +  return "SGI";
 +}
 +
 +static const char * DLL_CALLCONV
 +Description() {
 +  return "SGI Image Format";
 +}
 +
 +static const char * DLL_CALLCONV
 +Extension() {
 +  return "sgi";
 +}
 +
 +static const char * DLL_CALLCONV
 +RegExpr() {
 +  return NULL;
 +}
 +
 +static const char * DLL_CALLCONV
 +MimeType() {
 +  return "image/sgi";
 +}
 +
 +static BOOL DLL_CALLCONV
 +Validate(FreeImageIO *io, fi_handle handle) {
 +	BYTE sgi_signature[2] = { 0x01, 0xDA };
 +	BYTE signature[2] = { 0, 0 };
 +
 +	io->read_proc(signature, 1, sizeof(sgi_signature), handle);
 +
 +	return (memcmp(sgi_signature, signature, sizeof(sgi_signature)) == 0);
 +}
 +
 +static BOOL DLL_CALLCONV
 +SupportsExportDepth(int depth) {
 +  return FALSE;
 +}
 +
 +static BOOL DLL_CALLCONV 
 +SupportsExportType(FREE_IMAGE_TYPE type) {
 +  return FALSE;
 +}
 +
 +static FIBITMAP * DLL_CALLCONV
 +Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
 +	int width = 0, height = 0, zsize = 0;
 +	int i, dim;
 +	int bitcount;
 +	SGIHeader sgiHeader;
 +	RLEStatus my_rle_status;
 +	FIBITMAP *dib = NULL;
 +	LONG *pRowIndex = NULL;
 +
 +	try {
 +		// read the header
 +		memset(&sgiHeader, 0, sizeof(SGIHeader));
 +		if(io->read_proc(&sgiHeader, 1, sizeof(SGIHeader), handle) < sizeof(SGIHeader)) {
 +		   throw SGI_LESS_THAN_HEADER_LENGTH;
 +		}
 +#ifndef FREEIMAGE_BIGENDIAN
 +		SwapHeader(&sgiHeader);
 +#endif
 +		if(sgiHeader.magic != 474) {
 +			throw FI_MSG_ERROR_MAGIC_NUMBER;
 +		}
 +		
 +		BOOL bIsRLE = (sgiHeader.storage == 1) ? TRUE : FALSE;
 +	
 +		// check for unsupported image types
 +		if (sgiHeader.bpc != 1) {
 +			// Expected one byte per color component
 +			throw SGI_16_BIT_COMPONENTS_NOT_SUPPORTED; 
 +		}
 +		if (sgiHeader.colormap != 0) {
 +			// Indexed or dithered images not supported
 +			throw SGI_COLORMAPS_NOT_SUPPORTED; 
 +		}
 +
 +		// get the width & height
 +		dim = sgiHeader.dimension;
 +		width = sgiHeader.xsize;
 +		if (dim < 3) {
 +			zsize = 1;
 +		} else {
 +			zsize = sgiHeader.zsize;
 +		}
 +
 +		if (dim < 2) {
 +			height = 1;
 +		} else {
 +			height = sgiHeader.ysize;
 +		}
 +		
 +		if(bIsRLE) {
 +			// read the Offset Tables 
 +			int index_len = height * zsize;
 +			pRowIndex = (LONG*)malloc(index_len * sizeof(LONG));
 +			if(!pRowIndex) {
 +				throw FI_MSG_ERROR_MEMORY;
 +			}
 +			
 +			if ((unsigned)index_len != io->read_proc(pRowIndex, sizeof(LONG), index_len, handle)) {
 +				throw SGI_EOF_IN_RLE_INDEX;
 +			}
 +			
 +#ifndef FREEIMAGE_BIGENDIAN		
 +			// Fix byte order in index
 +			for (i = 0; i < index_len; i++) {
 +				SwapLong((DWORD*)&pRowIndex[i]);
 +			}
 +#endif
 +			// Discard row size index
 +			for (i = 0; i < (int)(index_len * sizeof(LONG)); i++) {
 +				BYTE packed = 0;
 +				if( io->read_proc(&packed, sizeof(BYTE), 1, handle) < 1 ) {
 +					throw SGI_EOF_IN_RLE_INDEX;
 +				}
 +			}
 +		}
 +		
 +		switch(zsize) {
 +			case 1:
 +				bitcount = 8;
 +				break;
 +			case 2:
 +				//Grayscale+Alpha. Need to fake RGBA
 +				bitcount = 32;
 +				break;
 +			case 3:
 +				bitcount = 24;
 +				break;
 +			case 4:
 +				bitcount = 32;
 +				break;
 +			default:
 +				throw SGI_INVALID_CHANNEL_COUNT;
 +		}
 +		
 +		dib = FreeImage_Allocate(width, height, bitcount);
 +		if(!dib) {
 +			throw FI_MSG_ERROR_DIB_MEMORY;
 +		}
 +		
 +		if (bitcount == 8) {
 +			// 8-bit SGI files are grayscale images, so we'll generate
 +			// a grayscale palette.
 +			RGBQUAD *pclrs = FreeImage_GetPalette(dib);
 +			for (i = 0; i < 256; i++) {
 +				pclrs[i].rgbRed = (BYTE)i;
 +				pclrs[i].rgbGreen = (BYTE)i;
 +				pclrs[i].rgbBlue = (BYTE)i;
 +				pclrs[i].rgbReserved = 0;
 +			}
 +		}
 +
 +		// decode the image
 +
 +		memset(&my_rle_status, 0, sizeof(RLEStatus));
 +		
 +		int ns = FreeImage_GetPitch(dib);                                                    
 +		BYTE *pStartRow = FreeImage_GetScanLine(dib, 0);
 +		int offset_table[] = { 2, 1, 0, 3 };
 +		int numChannels = zsize;
 +		if (zsize < 3) {
 +			offset_table[0] = 0;
 +		}
 +		if (zsize == 2)
 +		{
 +			//This is how faked grayscale+alpha works.
 +			//First channel goes into first 
 +			//second channel goes into alpha (4th channel)
 +			//Two channels are left empty and will be copied later
 +			offset_table[1] = 3;
 +			numChannels = 4;
 +		}
 +		
 +		LONG *pri = pRowIndex;
 +		for (i = 0; i < zsize; i++) {
 +			BYTE *pRow = pStartRow + offset_table[i];
 +			for (int j = 0; j < height; j++, pRow += ns, pri++) {
 +				BYTE *p = pRow;
 +				if (bIsRLE) {
 +					my_rle_status.cnt = 0;
 +					io->seek_proc(handle, *pri, SEEK_SET);
 +				}
 +				for (int k = 0; k < width; k++, p += numChannels) {
 +					int ch;
 +					BYTE packed = 0;
 +					if (bIsRLE) {
 +						ch = get_rlechar(io, handle, &my_rle_status);
 +						packed = (BYTE)ch;
 +					}
 +					else {
 +						ch = io->read_proc(&packed, sizeof(BYTE), 1, handle);
 +					}
 +					if (ch == EOF) {
 +						throw SGI_EOF_IN_IMAGE_DATA;
 +					}
 +					*p = packed;
 +				}
 +			}
 +		}
 +		
 +		if (zsize == 2)
 +		{
 +			BYTE *pRow = pStartRow;
 +			//If faking RGBA from grayscale + alpha, copy first channel to second and third
 +			for (int i=0; i<height; i++, pRow += ns)
 +			{
 +				BYTE *pPixel = pRow;
 +				for (int j=0; j<width; j++)
 +				{
 +					pPixel[2] = pPixel[1] = pPixel[0];
 +					pPixel += 4;
 +				}
 +			}
 +		}
 +		if(pRowIndex)
 +			free(pRowIndex);
 +
 +		return dib;
 +
 +	} catch(const char *text) {
 +		if(pRowIndex) free(pRowIndex);
 +		if(dib) FreeImage_Unload(dib);
 +		FreeImage_OutputMessageProc(s_format_id, text);
 +		return NULL;
 +	}
 +}
 +
 +// ==========================================================
 +//   Init
 +// ==========================================================
 +
 +void DLL_CALLCONV 
 +InitSGI(Plugin *plugin, int format_id) {
 +	s_format_id = format_id;
 +	
 +	plugin->format_proc = Format;
 +	plugin->description_proc = Description;
 +	plugin->extension_proc = Extension;
 +	plugin->regexpr_proc = RegExpr;
 +	plugin->open_proc = NULL;
 +	plugin->close_proc = NULL;
 +	plugin->pagecount_proc = NULL;
 +	plugin->pagecapability_proc = NULL;
 +	plugin->load_proc = Load;
 +	plugin->save_proc = NULL;
 +	plugin->validate_proc = Validate;
 +	plugin->mime_proc = MimeType;
 +	plugin->supports_export_bpp_proc = SupportsExportDepth;
 +	plugin->supports_export_type_proc = SupportsExportType;
 +	plugin->supports_icc_profiles_proc = NULL;
 +}
 +
  | 
