Ejemplo n.º 1
0
FIBITMAP* TargaThumbnail::toFIBITMAP() {
	if(isNull() || _depth == 0) {
		return NULL;
	}
		
	const unsigned line_size = _depth * _w / 8;
	FIBITMAP* dib = FreeImage_Allocate(_w, _h, _depth);
	if(!dib) {
		return NULL;
	}

	const BYTE* line = _data;
	const BYTE height = _h;
	for (BYTE h = 0; h < height; ++h, line += line_size) {
		BYTE* dst_line = FreeImage_GetScanLine(dib, height - 1 - h);
		memcpy(dst_line, line, line_size);
	}

#ifdef FREEIMAGE_BIGENDIAN
	swapShortPixels(dib);
#endif
	
#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
	SwapRedBlue32(dib);
#endif

	return dib;
}
Ejemplo n.º 2
0
	byte* LoadImage(const char* filename, uint* width, uint* height, uint* bits, bool flipY)
	{
		FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
		FIBITMAP* dib = nullptr;
		fif = FreeImage_GetFileType(filename, 0);
		if (fif == FIF_UNKNOWN)
			fif = FreeImage_GetFIFFromFilename(filename);
		if (fif == FIF_UNKNOWN)
			return nullptr;

		if (FreeImage_FIFSupportsReading(fif))
			dib = FreeImage_Load(fif, filename);

		SP_ASSERT(dib, "Could not load image '", filename, "'!");

		FIBITMAP* bitmap = FreeImage_ConvertTo32Bits(dib);
		FreeImage_Unload(dib);

		byte* pixels = FreeImage_GetBits(bitmap);
		uint w = FreeImage_GetWidth(bitmap);
		uint h = FreeImage_GetHeight(bitmap);
		uint b = FreeImage_GetBPP(bitmap);

		if (flipY)
			FreeImage_FlipVertical(bitmap);

		if (FreeImage_GetRedMask(bitmap) == 0xff0000)
			SwapRedBlue32(bitmap);

		if (width)
			*width = w;
		if (height)
			*height = h;
		if (bits)
			*bits = b;

		int32 size = w * h * (b / 8);
		byte* result = spnew byte[size];
		memcpy(result, pixels, size);
		FreeImage_Unload(bitmap);
		return result;
	}
Ejemplo n.º 3
0
FIBITMAP* psdParser::ReadImageData(FreeImageIO *io, fi_handle handle) {
	if(handle == NULL) 
		return NULL;
	
	bool header_only = (_fi_flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
	
	WORD nCompression = 0;
	io->read_proc(&nCompression, sizeof(nCompression), 1, handle);
	
#ifndef FREEIMAGE_BIGENDIAN
	SwapShort(&nCompression);
#endif
	
	if((nCompression != PSDP_COMPRESSION_NONE && nCompression != PSDP_COMPRESSION_RLE))	{
		FreeImage_OutputMessageProc(_fi_format_id, "Unsupported compression %d", nCompression);
		return NULL;
	}
	
	const unsigned nWidth = _headerInfo._Width;
	const unsigned nHeight = _headerInfo._Height;
	const unsigned nChannels = _headerInfo._Channels;
	const unsigned depth = _headerInfo._BitsPerChannel;
	const unsigned bytes = (depth == 1) ? 1 : depth / 8;
		
	// channel(plane) line (BYTE aligned)
	const unsigned lineSize = (_headerInfo._BitsPerChannel == 1) ? (nWidth + 7) / 8 : nWidth * bytes;
	
	if(nCompression == PSDP_COMPRESSION_RLE && depth > 16) {
		FreeImage_OutputMessageProc(_fi_format_id, "Unsupported RLE with depth %d", depth);
		return NULL;
	}
	
	// build output buffer
	
	FIBITMAP* bitmap = NULL;
	unsigned dstCh = 0;
	
	short mode = _headerInfo._ColourMode;
	
	if(mode == PSDP_MULTICHANNEL && nChannels < 3) {
		// CM 
		mode = PSDP_GRAYSCALE; // C as gray, M as extra channel
	}
		
	bool needPalette = false;
	switch (mode) {
		case PSDP_BITMAP:
		case PSDP_DUOTONE:	
		case PSDP_INDEXED:
		case PSDP_GRAYSCALE:
			dstCh = 1;
			switch(depth) {
				case 16:
				bitmap = FreeImage_AllocateHeaderT(header_only, FIT_UINT16, nWidth, nHeight, depth*dstCh);
				break;
				case 32:
				bitmap = FreeImage_AllocateHeaderT(header_only, FIT_FLOAT, nWidth, nHeight, depth*dstCh);
				break;
				default: // 1-, 8-
				needPalette = true;
				bitmap = FreeImage_AllocateHeader(header_only, nWidth, nHeight, depth*dstCh);
				break;
			}
			break;
		case PSDP_RGB:	
		case PSDP_LAB:		
		case PSDP_CMYK	:
		case PSDP_MULTICHANNEL	:
			// force PSDP_MULTICHANNEL CMY as CMYK
			dstCh = (mode == PSDP_MULTICHANNEL && !header_only) ? 4 : MIN<unsigned>(nChannels, 4);
			if(dstCh < 3) {
				throw "Invalid number of channels";
			}

			switch(depth) {
				case 16:
				bitmap = FreeImage_AllocateHeaderT(header_only, dstCh < 4 ? FIT_RGB16 : FIT_RGBA16, nWidth, nHeight, depth*dstCh);
				break;
				case 32:
				bitmap = FreeImage_AllocateHeaderT(header_only, dstCh < 4 ? FIT_RGBF : FIT_RGBAF, nWidth, nHeight, depth*dstCh);
				break;
				default:
				bitmap = FreeImage_AllocateHeader(header_only, nWidth, nHeight, depth*dstCh);
				break;
			}
			break;
		default:
			throw "Unsupported color mode";
			break;
	}
	if(!bitmap) {
		throw FI_MSG_ERROR_DIB_MEMORY;
	}

	// write thumbnail
	FreeImage_SetThumbnail(bitmap, _thumbnail.getDib());
		
	// @todo Add some metadata model
		
	if(header_only) {
		return bitmap;
	}
	
	// Load pixels data

	const unsigned dstChannels = dstCh;
	
	const unsigned dstBpp =  (depth == 1) ? 1 : FreeImage_GetBPP(bitmap)/8;
	const unsigned dstLineSize = FreeImage_GetPitch(bitmap);	
	BYTE* const dst_first_line = FreeImage_GetScanLine(bitmap, nHeight - 1);//<*** flipped
	
	BYTE* line_start = new BYTE[lineSize]; //< fileline cache

	switch ( nCompression ) {
		case PSDP_COMPRESSION_NONE: // raw data	
		{			
			for(unsigned c = 0; c < nChannels; c++) {
				if(c >= dstChannels) {
					// @todo write extra channels
					break; 
				}
					
				const unsigned channelOffset = c * bytes;
				
				BYTE* dst_line_start = dst_first_line;
				for(unsigned h = 0; h < nHeight; ++h, dst_line_start -= dstLineSize) {//<*** flipped

					io->read_proc(line_start, lineSize, 1, handle);
					
					for (BYTE *line = line_start, *dst_line = dst_line_start; line < line_start + lineSize; 
						line += bytes, dst_line += dstBpp) {
#ifdef FREEIMAGE_BIGENDIAN
							memcpy(dst_line + channelOffset, line, bytes);
#else
						// reverse copy bytes
						for (unsigned b = 0; b < bytes; ++b) {
							dst_line[channelOffset + b] = line[(bytes-1) - b];
						}
#endif // FREEIMAGE_BIGENDIAN
					}
				} //< h
			}//< ch
			
			SAFE_DELETE_ARRAY(line_start);
					
		}
		break;
		
		case PSDP_COMPRESSION_RLE: // RLE compression	
		{			
									
			// The RLE-compressed data is preceeded by a 2-byte line size for each row in the data,
			// store an array of these

			// later use this array as WORD rleLineSizeList[nChannels][nHeight];
			WORD *rleLineSizeList = new (std::nothrow) WORD[nChannels*nHeight];

			if(!rleLineSizeList) {
				FreeImage_Unload(bitmap);
				SAFE_DELETE_ARRAY(line_start);
				throw std::bad_alloc();
			}	
			
			io->read_proc(rleLineSizeList, 2, nChannels * nHeight, handle);
			
			WORD largestRLELine = 0;
			for(unsigned ch = 0; ch < nChannels; ++ch) {
				for(unsigned h = 0; h < nHeight; ++h) {
					const unsigned index = ch * nHeight + h;

#ifndef FREEIMAGE_BIGENDIAN 
					SwapShort(&rleLineSizeList[index]);
#endif
					if(largestRLELine < rleLineSizeList[index]) {
						largestRLELine = rleLineSizeList[index];
					}
				}
			}

			BYTE* rle_line_start = new (std::nothrow) BYTE[largestRLELine];
			if(!rle_line_start) {
				FreeImage_Unload(bitmap);
				SAFE_DELETE_ARRAY(line_start);
				SAFE_DELETE_ARRAY(rleLineSizeList);
				throw std::bad_alloc();
			}
			
			// Read the RLE data (assume 8-bit)
			
			const BYTE* const line_end = line_start + lineSize;

			for (unsigned ch = 0; ch < nChannels; ch++) {
				const unsigned channelOffset = ch * bytes;
				
				BYTE* dst_line_start = dst_first_line;
				for(unsigned h = 0; h < nHeight; ++h, dst_line_start -= dstLineSize) {//<*** flipped
					const unsigned index = ch * nHeight + h;
					
					// - read and uncompress line -
					
					const WORD rleLineSize = rleLineSizeList[index];
					
					io->read_proc(rle_line_start, rleLineSize, 1, handle);
					
					for (BYTE* rle_line = rle_line_start, *line = line_start; 
						rle_line < rle_line_start + rleLineSize, line < line_end;) {

						int len = *rle_line++;
						
						// NOTE len is signed byte in PackBits RLE
						
						if ( len < 128 ) { //<- MSB is not set
							// uncompressed packet
							
							// (len + 1) bytes of data are copied
							
							++len;
							
							// assert we don't write beyound eol
							memcpy(line, rle_line, line + len > line_end ? line_end - line : len);
							line += len;
							rle_line += len;
						}
						else if ( len > 128 ) { //< MSB is set
						
							// RLE compressed packet
							
							// One byte of data is repeated (–len + 1) times
							
							len ^= 0xFF; // same as (-len + 1) & 0xFF 
							len += 2;    //

							// assert we don't write beyound eol
							memset(line, *rle_line++, line + len > line_end ? line_end - line : len);							
							line += len;

						}
						else if ( 128 == len ) {
							// Do nothing
						}
					}//< rle_line
					
					// - write line to destination -
					
					if(ch >= dstChannels) {
						// @todo write to extra channels
						break; 
					}
						
					// byte by byte copy a single channel to pixel
					for (BYTE *line = line_start, *dst_line = dst_line_start; line < line_start + lineSize; 
						line += bytes, dst_line += dstBpp) {

#ifdef FREEIMAGE_BIGENDIAN
							memcpy(dst_line + channelOffset, line, bytes);
#else
							// reverse copy bytes
							for (unsigned b = 0; b < bytes; ++b) {
								dst_line[channelOffset + b] = line[(bytes-1) - b];							
							}
#endif // FREEIMAGE_BIGENDIAN
					}	
				}//< h
			}//< ch
			
			SAFE_DELETE_ARRAY(line_start);
			SAFE_DELETE_ARRAY(rleLineSizeList);
			SAFE_DELETE_ARRAY(rle_line_start);
		}
		break;
		
		case 2: // ZIP without prediction, no specification
			break;
			
		case 3: // ZIP with prediction, no specification
			break;
			
		default: // Unknown format
			break;
		
	}
	
	// --- Further process the bitmap ---
	
	if((mode == PSDP_CMYK || mode == PSDP_MULTICHANNEL)) {	
		// CMYK values are "inverted", invert them back		

		if(mode == PSDP_MULTICHANNEL) {
			invertColor(bitmap);
		} else {
			FreeImage_Invert(bitmap);
		}

		if((_fi_flags & PSD_CMYK) == PSD_CMYK) {
			// keep as CMYK

			if(mode == PSDP_MULTICHANNEL) {
				//### we force CMY to be CMYK, but CMY has no ICC. 
				// Create empty profile and add the flag.
				FreeImage_CreateICCProfile(bitmap, NULL, 0);
				FreeImage_GetICCProfile(bitmap)->flags |= FIICC_COLOR_IS_CMYK;
			}
		}
		else { 
			// convert to RGB
			
			ConvertCMYKtoRGBA(bitmap);
			
			// The ICC Profile is no longer valid
			_iccProfile.clear();
			
			// remove the pending A if not present in source 
			if(nChannels == 4 || nChannels == 3 ) {
				FIBITMAP* t = RemoveAlphaChannel(bitmap);
				if(t) {
					FreeImage_Unload(bitmap);
					bitmap = t;
				} // else: silently fail
			}
		}
	}
	else if ( mode == PSDP_LAB && !((_fi_flags & PSD_LAB) == PSD_LAB)) {
		ConvertLABtoRGB(bitmap);
	}
	else {
		if (needPalette && FreeImage_GetPalette(bitmap)) {
			
			if(mode == PSDP_BITMAP) {
				CREATE_GREYSCALE_PALETTE_REVERSE(FreeImage_GetPalette(bitmap), 2);
			}
			else if(mode == PSDP_INDEXED) {
				if(!_colourModeData._plColourData || _colourModeData._Length != 768 || _ColourCount < 0) {
					FreeImage_OutputMessageProc(_fi_format_id, "Indexed image has no palette. Using the default grayscale one.");
				} else {
					_colourModeData.FillPalette(bitmap);
				}
			}
			// GRAYSCALE, DUOTONE - use default grayscale palette
		}
		
#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
		if(FreeImage_GetImageType(bitmap) == FIT_BITMAP) {
			SwapRedBlue32(bitmap);
		}
#endif
	}
	
	return bitmap;
} 
Ejemplo n.º 4
0
int psdThumbnail::Read(FreeImageIO *io, fi_handle handle, int iResourceSize, bool isBGR) {
	BYTE ShortValue[2], IntValue[4];
	int nBytes=0, n;

	// remove the header size (28 bytes) from the total data size
	int iTotalData = iResourceSize - 28;

	const long block_end = io->tell_proc(handle) + iTotalData;	
	
	n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle);
	nBytes += n * sizeof(IntValue);
	_Format = psdGetValue(IntValue, sizeof(_Format) );
	
	n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle);
	nBytes += n * sizeof(IntValue);
	_Width = psdGetValue(IntValue, sizeof(_Width) );
	
	n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle);
	nBytes += n * sizeof(IntValue);
	_Height = psdGetValue(IntValue, sizeof(_Height) );
	
	n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle);
	nBytes += n * sizeof(IntValue);
	_WidthBytes = psdGetValue(IntValue, sizeof(_WidthBytes) );

	n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle);
	nBytes += n * sizeof(IntValue);
	_Size = psdGetValue(IntValue, sizeof(_Size) );

	n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle);
	nBytes += n * sizeof(IntValue);
	_CompressedSize = psdGetValue(IntValue, sizeof(_CompressedSize) );

	n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
	nBytes += n * sizeof(ShortValue);
	_BitPerPixel = (short)psdGetValue(ShortValue, sizeof(_BitPerPixel) );

	n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
	nBytes += n * sizeof(ShortValue);
	_Planes = (short)psdGetValue(ShortValue, sizeof(_Planes) );

	const long JFIF_startpos = io->tell_proc(handle);

	if(_dib) {
		FreeImage_Unload(_dib);
	}

	if(_Format == 1) {
		// kJpegRGB thumbnail image
		_dib = FreeImage_LoadFromHandle(FIF_JPEG, io, handle);
		if(isBGR) {
			SwapRedBlue32(_dib);
		}			
		// HACK: manually go to end of thumbnail, because (for some reason) LoadFromHandle consumes more bytes then available! 
		io->seek_proc(handle, block_end, SEEK_SET);
	}
	else {
		// kRawRGB thumbnail image		
		// ### Unimplemented (should be trivial)

		// skip the thumbnail part
		io->seek_proc(handle, iTotalData, SEEK_CUR);
		return iResourceSize;
	}
	
	nBytes += (block_end - JFIF_startpos); 

	return nBytes;
}
Ejemplo n.º 5
0
bool M_loadImage(const char * filename, void * data, void * arg)
{
	if(! filename)
		return false;
		
	if(! data)
		return false;

	// freeimage io
	FreeImageIO io;
	io.read_proc  = (FI_ReadProc)readProc;
	io.write_proc = (FI_WriteProc)writeProc;
	io.tell_proc  = (FI_TellProc)tellProc;
	io.seek_proc  = (FI_SeekProc)seekProc;
	
	
	// read file buffer
	MFile * fp = M_fopen(filename, "rb");
	if(! fp)
	{
		printf("Error : can't read file %s\n", filename);
		return false;
	}
	
	
	// read freeimage
	FREE_IMAGE_FORMAT format = FreeImage_GetFileTypeFromHandle(&io, (fi_handle)fp, 0);
	if(format == FIF_UNKNOWN)
	{
		printf("Error : unknow format %s\n", filename);
		M_fclose(fp);
		return false;
	}
	
	FIBITMAP * dib = FreeImage_LoadFromHandle(format, &io, (fi_handle)fp);
	if(! dib)
	{
		printf("Error : unknow dib %s\n", filename);
		M_fclose(fp);
		return false;
	}
	
	BYTE * bits = FreeImage_GetBits(dib);
	unsigned int width = FreeImage_GetWidth(dib);
	unsigned int height = FreeImage_GetHeight(dib);
	unsigned int bpp = FreeImage_GetBPP(dib);
	FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
	
	if((bits == 0) || (width == 0) || (height == 0))
	{
		FreeImage_Unload(dib);
		M_fclose(fp);
		return false;
	}
	
	// flip
	FreeImage_FlipVertical(dib);
	
	// create image
	MImage * image = (MImage *)data;
	
	switch(image_type)
	{
	case FIT_BITMAP:
		switch(bpp)
		{
		case 8:
			image->create(M_UBYTE, width, height, 1);
			for(unsigned int y=0; y<height; y++)
			{
				unsigned char * dest = (unsigned char *)image->getData() + width*y;
				bits = FreeImage_GetScanLine(dib, y);
				memcpy(dest, bits, width*sizeof(char));
			}
			break;

		case 24:
			SwapRedBlue32(dib);
			image->create(M_UBYTE, width, height, 3);
			for(unsigned int y=0; y<height; y++)
			{
				unsigned char * dest = (unsigned char *)image->getData() + width*y*3;
				bits = FreeImage_GetScanLine(dib, y);
				memcpy(dest, bits, width*3*sizeof(char));
			}
			break;
	
		case 32:
			SwapRedBlue32(dib);
			image->create(M_UBYTE, width, height, 4);
			for(unsigned int y=0; y<height; y++)
			{
				unsigned char * dest = (unsigned char *)image->getData() + width*y*4;
				bits = FreeImage_GetScanLine(dib, y);
				memcpy(dest, bits, width*4*sizeof(char));
			}
			break;
			
		default:
			break;
		}
		break;
		
	case FIT_RGB16:
		image->create(M_USHORT, width, height, 3);
		for(unsigned int y=0; y<height; y++)
		{
			unsigned short * dest = (unsigned short *)image->getData() + width*y*3;
			bits = FreeImage_GetScanLine(dib, y);
			memcpy(dest, bits, width*3*sizeof(short));
		}
		break;
	
	case FIT_RGBA16:
		image->create(M_USHORT, width, height, 4);
		for(unsigned int y=0; y<height; y++)
		{
			unsigned short * dest = (unsigned short *)image->getData() + width*y*4;
			bits = FreeImage_GetScanLine(dib, y);
			memcpy(dest, bits, width*4*sizeof(short));
		}
		break;

	case FIT_FLOAT:
		image->create(M_FLOAT, width, height, 1);
		for(unsigned int y=0; y<height; y++)
		{
			float * dest = (float *)image->getData() + width*y;
			bits = FreeImage_GetScanLine(dib, y);
			memcpy(dest, bits, width*sizeof(float));
		}
		break;
		
	case FIT_RGBF:
		image->create(M_FLOAT, width, height, 3);
		for(unsigned int y=0; y<height; y++)
		{
			float * dest = (float *)image->getData() + width*y*3;
			bits = FreeImage_GetScanLine(dib, y);
			memcpy(dest, bits, width*3*sizeof(float));
		}
		break;
		
	case FIT_RGBAF:
		image->create(M_FLOAT, width, height, 4);
		for(unsigned int y=0; y<height; y++)
		{
			float * dest = (float *)image->getData() + width*y*4;
			bits = FreeImage_GetScanLine(dib, y);
			memcpy(dest, bits, width*4*sizeof(float));
		}
		break;
		
	default:
		break;
	}
	
	
	// clean
	FreeImage_Unload(dib);
	M_fclose(fp);
	return true;
}
Ejemplo n.º 6
0
bool M_saveImage(const char * filename, void * data)
{
	if(! filename)
		return false;
		
	if(! data)
		return false;
		
		
	// format
	FREE_IMAGE_FORMAT format = FreeImage_GetFIFFromFilename(filename);
	if(format == FIF_UNKNOWN)
	{
		printf("Error freeImage: unknow image format %s\n", filename);
		return false;
	}
		
		
	// image
	MImage * image = (MImage *)data;
	if(! image->getData())
		return false;
	
	unsigned int width = image->getWidth();
	unsigned int height = image->getHeight();
	unsigned int components = image->getComponents();
	unsigned int yStep = width*components;
	
	FIBITMAP * dib = NULL;
	if(image->getDataType() == M_UBYTE)
	{
		dib = FreeImage_AllocateT(FIT_BITMAP, width, height, components*8);
		if(dib)
		{
			unsigned char * srcBits = (unsigned char *)image->getData();
			for(unsigned int y=0; y<height; y++)
			{
				BYTE * bits = FreeImage_GetScanLine(dib, y);
				memcpy(bits, srcBits, yStep*sizeof(char));
				srcBits += yStep;
			}
			
			if(components == 3 || components == 4)
				SwapRedBlue32(dib);
		}
	}
	else if(image->getDataType() == M_FLOAT)
	{
		switch(image->getComponents())
		{
		case 1:
			dib = FreeImage_AllocateT(FIT_FLOAT, image->getWidth(), image->getHeight(), 32);
			break;
		case 3:
			dib = FreeImage_AllocateT(FIT_RGBF, image->getWidth(), image->getHeight(), 3*32);
			break;
		case 4:
			dib = FreeImage_AllocateT(FIT_RGBAF, image->getWidth(), image->getHeight(), 4*32);
			break;
		}
		
		if(dib)
		{
			float * srcBits = (float *)image->getData();
			for(unsigned int y=0; y<height; y++)
			{
				BYTE * bits = FreeImage_GetScanLine(dib, y);
				memcpy(bits, srcBits, yStep*sizeof(float));
				srcBits += yStep;
			}
		}
	}
	
	
	if(dib)
	{
		// flip
		FreeImage_FlipVertical(dib);

		// save
		if(FreeImage_Save(format, dib, filename, PNG_Z_BEST_SPEED))
		{
			FreeImage_Unload(dib);
			return true;
		}
	}
	
		
	return false;
}
Ejemplo n.º 7
0
/**
Copy or convert & copy decoded pixels into the dib
@param pDecoder Decoder handle
@param out_guid_format Target guid format
@param dib Output dib
@param width Image width
@param height Image height
@return Returns 0 if successful, returns ERR otherwise
*/
static ERR
CopyPixels(PKImageDecode *pDecoder, PKPixelFormatGUID out_guid_format, FIBITMAP *dib, int width, int height) {
	PKFormatConverter *pConverter = NULL;	// pixel format converter
	ERR error_code = 0;	// error code as returned by the interface
	BYTE *pb = NULL;	// local buffer used for pixel format conversion
	
	// image dimensions
	const PKRect rect = {0, 0, width, height};

	try {
		// get input file pixel format ...
		PKPixelFormatGUID in_guid_format;
		error_code = pDecoder->GetPixelFormat(pDecoder, &in_guid_format);
		JXR_CHECK(error_code);
		
		// is a format conversion needed ?

		if(IsEqualGUID(out_guid_format, in_guid_format)) {
			// no conversion, load bytes "as is" ...

			// get a pointer to dst pixel data
			BYTE *dib_bits = FreeImage_GetBits(dib);

			// get dst pitch (count of BYTE for stride)
			const unsigned cbStride = FreeImage_GetPitch(dib);			

			// decode and copy bits to dst array
			error_code = pDecoder->Copy(pDecoder, &rect, dib_bits, cbStride);
			JXR_CHECK(error_code);		
		}
		else {
			// we need to use the conversion API ...
			
			// allocate the pixel format converter
			error_code = PKCodecFactory_CreateFormatConverter(&pConverter);
			JXR_CHECK(error_code);
			
			// set the conversion function
			error_code = pConverter->Initialize(pConverter, pDecoder, NULL, out_guid_format);
			JXR_CHECK(error_code);
			
			// get the maximum stride
			unsigned cbStride = 0;
			{
				PKPixelInfo pPIFrom;
				PKPixelInfo pPITo;
				
				pPIFrom.pGUIDPixFmt = &in_guid_format;
				error_code = PixelFormatLookup(&pPIFrom, LOOKUP_FORWARD);
				JXR_CHECK(error_code);

				pPITo.pGUIDPixFmt = &out_guid_format;
				error_code = PixelFormatLookup(&pPITo, LOOKUP_FORWARD);
				JXR_CHECK(error_code);

				unsigned cbStrideFrom = ((pPIFrom.cbitUnit + 7) >> 3) * width;
				unsigned cbStrideTo = ((pPITo.cbitUnit + 7) >> 3) * width;
				cbStride = MAX(cbStrideFrom, cbStrideTo);
			}

			// allocate a local decoder / encoder buffer
			error_code = PKAllocAligned((void **) &pb, cbStride * height, 128);
			JXR_CHECK(error_code);

			// copy / convert pixels
			error_code = pConverter->Copy(pConverter, &rect, pb, cbStride);
			JXR_CHECK(error_code);

			// now copy pixels into the dib
			const size_t line_size = FreeImage_GetLine(dib);
			for(int y = 0; y < height; y++) {
				BYTE *src_bits = (BYTE*)(pb + y * cbStride);
				BYTE *dst_bits = (BYTE*)FreeImage_GetScanLine(dib, y);
				memcpy(dst_bits, src_bits, line_size);
			}
			
			// free the local buffer
			PKFreeAligned((void **) &pb);

			// free the pixel format converter
			PKFormatConverter_Release(&pConverter);
		}

		// FreeImage DIB are upside-down relative to usual graphic conventions
		FreeImage_FlipVertical(dib);

		// post-processing ...
		// -------------------

		// swap RGB as needed

#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
		if(IsEqualGUID(out_guid_format, GUID_PKPixelFormat24bppRGB) || IsEqualGUID(out_guid_format, GUID_PKPixelFormat32bppRGB)) {
			SwapRedBlue32(dib);
		}
#elif FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
		if(IsEqualGUID(out_guid_format, GUID_PKPixelFormat24bppBGR) || IsEqualGUID(out_guid_format, GUID_PKPixelFormat32bppBGR)) {
			SwapRedBlue32(dib);
		}
#endif
		
		return WMP_errSuccess;

	} catch(...) {
		// free the local buffer
		PKFreeAligned((void **) &pb);
		// free the pixel format converter
		PKFormatConverter_Release(&pConverter);

		return error_code;
	}
}