Alien-FreeImage

 view release on metacpan or  search on metacpan

src/Source/FreeImage/PluginBMP.cpp  view on Meta::CPAN


					default :
					{
						if(scanline >= abs(height)) {
							return TRUE;
						}

						int count = MIN((int)status_byte, width - bits);

						BYTE *sline = FreeImage_GetScanLine(dib, scanline);

						if(io->read_proc((void *)(sline + bits), sizeof(BYTE) * count, 1, handle) != 1) {
							return FALSE;
						}
						
						// align run length to even number of bytes 

						if ((status_byte & 1) == 1) {
							if(io->read_proc(&second_byte, sizeof(BYTE), 1, handle) != 1) {
								return FALSE;
							}
						}

						bits += status_byte;													

						break;	
					}
				}

				break;

			default :
			{
				if(scanline >= abs(height)) {
					return TRUE;
				}

				int count = MIN((int)status_byte, width - bits);

				BYTE *sline = FreeImage_GetScanLine(dib, scanline);

				if(io->read_proc(&second_byte, sizeof(BYTE), 1, handle) != 1) {
					return FALSE;
				}

				for (int i = 0; i < count; i++) {
					*(sline + bits) = second_byte;

					bits++;					
				}

				break;
			}
		}
	}
}

// --------------------------------------------------------------------------

static FIBITMAP *
LoadWindowsBMP(FreeImageIO *io, fi_handle handle, int flags, unsigned bitmap_bits_offset, int type) {
	FIBITMAP *dib = NULL;

	try {
		BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;

		// load the info header

		BITMAPINFOHEADER bih;

		io->read_proc(&bih, sizeof(BITMAPINFOHEADER), 1, handle);
#ifdef FREEIMAGE_BIGENDIAN
		SwapInfoHeader(&bih);
#endif

		// keep some general information about the bitmap

		unsigned used_colors	= bih.biClrUsed;
		int width				= bih.biWidth;
		int height				= bih.biHeight;		// WARNING: height can be < 0 => check each call using 'height' as a parameter
		unsigned bit_count		= bih.biBitCount;
		unsigned compression	= bih.biCompression;
		unsigned pitch			= CalculatePitch(CalculateLine(width, bit_count));

		switch (bit_count) {
			case 1 :
			case 4 :
			case 8 :
			{
				if ((used_colors == 0) || (used_colors > CalculateUsedPaletteEntries(bit_count))) {
					used_colors = CalculateUsedPaletteEntries(bit_count);
				}
				
				// allocate enough memory to hold the bitmap (header, palette, pixels) and read the palette

				dib = FreeImage_AllocateHeader(header_only, width, height, bit_count);
				if (dib == NULL) {
					throw FI_MSG_ERROR_DIB_MEMORY;
				}

				// set resolution information
				FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
				FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);

				// seek to the end of the header (depending on the BMP header version)
				// type == sizeof(BITMAPVxINFOHEADER)
				switch(type) {
					case 40:	// sizeof(BITMAPINFOHEADER) - all Windows versions since Windows 3.0
						break;
					case 52:	// sizeof(BITMAPV2INFOHEADER) (undocumented)
					case 56:	// sizeof(BITMAPV3INFOHEADER) (undocumented)
					case 108:	// sizeof(BITMAPV4HEADER) - all Windows versions since Windows 95/NT4 (not supported)
					case 124:	// sizeof(BITMAPV5HEADER) - Windows 98/2000 and newer (not supported)
						io->seek_proc(handle, (long)(type - sizeof(BITMAPINFOHEADER)), SEEK_CUR);
						break;
				}
				
				// load the palette

				io->read_proc(FreeImage_GetPalette(dib), used_colors * sizeof(RGBQUAD), 1, handle);
#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
				RGBQUAD *pal = FreeImage_GetPalette(dib);
				for(int i = 0; i < used_colors; i++) {
					INPLACESWAP(pal[i].rgbRed, pal[i].rgbBlue);
				}

src/Source/FreeImage/PluginBMP.cpp  view on Meta::CPAN


 				if (use_bitfields > 0) {
					DWORD bitfields[4];
					io->read_proc(bitfields, use_bitfields * sizeof(DWORD), 1, handle);
					dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, bitfields[0], bitfields[1], bitfields[2]);
				} else {
					if( bit_count == 32 ) {
						dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
					} else {
						dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
					}
				}

				if (dib == NULL) {
					throw FI_MSG_ERROR_DIB_MEMORY;
				}

				// set resolution information
				FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
				FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);

				if(header_only) {
					// header only mode
					return dib;
				}

				// Skip over the optional palette 
				// A 24 or 32 bit DIB may contain a palette for faster color reduction
				// i.e. you can have (FreeImage_GetColorsUsed(dib) > 0)

				// seek to the actual pixel data
				io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);

				// read in the bitmap bits
				// load pixel data and swap as needed if OS is Big Endian
				LoadPixelData(io, handle, dib, height, pitch, bit_count);

				// check if the bitmap contains transparency, if so enable it in the header

				FreeImage_SetTransparent(dib, (FreeImage_GetColorType(dib) == FIC_RGBALPHA));

				return dib;
			}
			break; // 24-, 32-bit
		}
	} catch(const char *message) {
		if(dib) {
			FreeImage_Unload(dib);
		}
		if(message) {
			FreeImage_OutputMessageProc(s_format_id, message);
		}
	}

	return NULL;
}

// --------------------------------------------------------------------------

static FIBITMAP *
LoadOS22XBMP(FreeImageIO *io, fi_handle handle, int flags, unsigned bitmap_bits_offset) {
	FIBITMAP *dib = NULL;

	try {
		BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;

		// load the info header

		BITMAPINFOHEADER bih;

		io->read_proc(&bih, sizeof(BITMAPINFOHEADER), 1, handle);
#ifdef FREEIMAGE_BIGENDIAN
		SwapInfoHeader(&bih);
#endif

		// keep some general information about the bitmap

		unsigned used_colors	= bih.biClrUsed;
		int width				= bih.biWidth;
		int height				= bih.biHeight;		// WARNING: height can be < 0 => check each read_proc using 'height' as a parameter
		unsigned bit_count		= bih.biBitCount;
		unsigned compression	= bih.biCompression;
		unsigned pitch			= CalculatePitch(CalculateLine(width, bit_count));
		
		switch (bit_count) {
			case 1 :
			case 4 :
			case 8 :
			{
				if ((used_colors == 0) || (used_colors > CalculateUsedPaletteEntries(bit_count)))
					used_colors = CalculateUsedPaletteEntries(bit_count);
					
				// allocate enough memory to hold the bitmap (header, palette, pixels) and read the palette

				dib = FreeImage_AllocateHeader(header_only, width, height, bit_count);

				if (dib == NULL) {
					throw FI_MSG_ERROR_DIB_MEMORY;
				}

				// set resolution information
				FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
				FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
				
				// load the palette
				// note that it may contain RGB or RGBA values : we will calculate this
				unsigned pal_size = (bitmap_bits_offset - sizeof(BITMAPFILEHEADER) - bih.biSize) / used_colors; 

				io->seek_proc(handle, sizeof(BITMAPFILEHEADER) + bih.biSize, SEEK_SET);

				RGBQUAD *pal = FreeImage_GetPalette(dib);

				if(pal_size == 4) {
					for (unsigned count = 0; count < used_colors; count++) {
						FILE_BGRA bgra;

						io->read_proc(&bgra, sizeof(FILE_BGRA), 1, handle);
						
						pal[count].rgbRed	= bgra.r;
						pal[count].rgbGreen = bgra.g;
						pal[count].rgbBlue	= bgra.b;
					} 
				} else if(pal_size == 3) {
					for (unsigned count = 0; count < used_colors; count++) {
						FILE_BGR bgr;

src/Source/FreeImage/PluginBMP.cpp  view on Meta::CPAN


				// load pixel data and swap as needed if OS is Big Endian
				LoadPixelData(io, handle, dib, height, pitch, bit_count);

				return dib;
			}

			case 24 :
			case 32 :
			{
				if( bit_count == 32 ) {
					dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
				} else {
					dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
				}

				if (dib == NULL) {
					throw FI_MSG_ERROR_DIB_MEMORY;
				}
				
				// set resolution information
				FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
				FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);

				if(header_only) {
					// header only mode
					return dib;
				}

				// Skip over the optional palette 
				// A 24 or 32 bit DIB may contain a palette for faster color reduction

				if (bitmap_bits_offset > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (used_colors * 3))) {
					io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
				}
				
				// read in the bitmap bits
				// load pixel data and swap as needed if OS is Big Endian
				LoadPixelData(io, handle, dib, height, pitch, bit_count);

				// check if the bitmap contains transparency, if so enable it in the header

				FreeImage_SetTransparent(dib, (FreeImage_GetColorType(dib) == FIC_RGBALPHA));

				return dib;
			}
		}
	} catch(const char *message) {
		if(dib)
			FreeImage_Unload(dib);

		FreeImage_OutputMessageProc(s_format_id, message);
	}

	return NULL;
}

// --------------------------------------------------------------------------

static FIBITMAP *
LoadOS21XBMP(FreeImageIO *io, fi_handle handle, int flags, unsigned bitmap_bits_offset) {
	FIBITMAP *dib = NULL;

	try {
		BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;

		BITMAPINFOOS2_1X_HEADER bios2_1x;

		io->read_proc(&bios2_1x, sizeof(BITMAPINFOOS2_1X_HEADER), 1, handle);
#ifdef FREEIMAGE_BIGENDIAN
		SwapOS21XHeader(&bios2_1x);
#endif
		// keep some general information about the bitmap

		unsigned used_colors = 0;
		unsigned width		= bios2_1x.biWidth;
		unsigned height		= bios2_1x.biHeight;	// WARNING: height can be < 0 => check each read_proc using 'height' as a parameter
		unsigned bit_count	= bios2_1x.biBitCount;
		unsigned pitch		= CalculatePitch(CalculateLine(width, bit_count));
		
		switch (bit_count) {
			case 1 :
			case 4 :
			case 8 :
			{
				used_colors = CalculateUsedPaletteEntries(bit_count);
				
				// allocate enough memory to hold the bitmap (header, palette, pixels) and read the palette

				dib = FreeImage_AllocateHeader(header_only, width, height, bit_count);

				if (dib == NULL) {
					throw FI_MSG_ERROR_DIB_MEMORY;
				}

				// set resolution information to default values (72 dpi in english units)
				FreeImage_SetDotsPerMeterX(dib, 2835);
				FreeImage_SetDotsPerMeterY(dib, 2835);
				
				// load the palette

				RGBQUAD *pal = FreeImage_GetPalette(dib);

				for (unsigned count = 0; count < used_colors; count++) {
					FILE_BGR bgr;

					io->read_proc(&bgr, sizeof(FILE_BGR), 1, handle);
					
					pal[count].rgbRed	= bgr.r;
					pal[count].rgbGreen = bgr.g;
					pal[count].rgbBlue	= bgr.b;
				}
				
				if(header_only) {
					// header only mode
					return dib;
				}

				// Skip over the optional palette 
				// A 24 or 32 bit DIB may contain a palette for faster color reduction

				io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
				
				// read the pixel data

src/Source/FreeImage/PluginBMP.cpp  view on Meta::CPAN

	return "Windows or OS/2 Bitmap";
}

static const char * DLL_CALLCONV
Extension() {
	return "bmp";
}

static const char * DLL_CALLCONV
RegExpr() {
	return "^BM";
}

static const char * DLL_CALLCONV
MimeType() {
	return "image/bmp";
}

static BOOL DLL_CALLCONV
Validate(FreeImageIO *io, fi_handle handle) {
	BYTE bmp_signature1[] = { 0x42, 0x4D };
	BYTE bmp_signature2[] = { 0x42, 0x41 };
	BYTE signature[2] = { 0, 0 };

	io->read_proc(signature, 1, sizeof(bmp_signature1), handle);

	if (memcmp(bmp_signature1, signature, sizeof(bmp_signature1)) == 0)
		return TRUE;

	if (memcmp(bmp_signature2, signature, sizeof(bmp_signature2)) == 0)
		return TRUE;

	return FALSE;
}

static BOOL DLL_CALLCONV
SupportsExportDepth(int depth) {
	return (
			(depth == 1) ||
			(depth == 4) ||
			(depth == 8) ||
			(depth == 16) ||
			(depth == 24) ||
			(depth == 32)
		);
}

static BOOL DLL_CALLCONV 
SupportsExportType(FREE_IMAGE_TYPE type) {
	return (type == FIT_BITMAP) ? TRUE : FALSE;
}

static BOOL DLL_CALLCONV
SupportsNoPixels() {
	return TRUE;
}

// ----------------------------------------------------------

static FIBITMAP * DLL_CALLCONV
Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
	if (handle != NULL) {
		BITMAPFILEHEADER bitmapfileheader;
		DWORD type = 0;

		// we use this offset value to make seemingly absolute seeks relative in the file
		
		long offset_in_file = io->tell_proc(handle);

		// read the fileheader

		io->read_proc(&bitmapfileheader, sizeof(BITMAPFILEHEADER), 1, handle);
#ifdef FREEIMAGE_BIGENDIAN
		SwapFileHeader(&bitmapfileheader);
#endif

		// check the signature

		if((bitmapfileheader.bfType != 0x4D42) && (bitmapfileheader.bfType != 0x4142)) {
			FreeImage_OutputMessageProc(s_format_id, FI_MSG_ERROR_MAGIC_NUMBER);
			return NULL;
		}

		// read the first byte of the infoheader

		io->read_proc(&type, sizeof(DWORD), 1, handle);
		io->seek_proc(handle, 0 - (long)sizeof(DWORD), SEEK_CUR);
#ifdef FREEIMAGE_BIGENDIAN
		SwapLong(&type);
#endif

		// call the appropriate load function for the found bitmap type

		switch(type) {
			case 12:
				// OS/2 and also all Windows versions since Windows 3.0
				return LoadOS21XBMP(io, handle, flags, offset_in_file + bitmapfileheader.bfOffBits);

			case 64:
				// OS/2
				return LoadOS22XBMP(io, handle, flags, offset_in_file + bitmapfileheader.bfOffBits);

			case 40:	// BITMAPINFOHEADER - all Windows versions since Windows 3.0
			case 52:	// BITMAPV2INFOHEADER (undocumented, partially supported)
			case 56:	// BITMAPV3INFOHEADER (undocumented, partially supported)
			case 108:	// BITMAPV4HEADER - all Windows versions since Windows 95/NT4 (partially supported)
			case 124:	// BITMAPV5HEADER - Windows 98/2000 and newer (partially supported)
				return LoadWindowsBMP(io, handle, flags, offset_in_file + bitmapfileheader.bfOffBits, type);

			default:
				break;
		}

		FreeImage_OutputMessageProc(s_format_id, "unknown bmp subtype with id %d", type);
	}

	return NULL;
}

// ----------------------------------------------------------

/**
Encode a 8-bit source buffer into a 8-bit target buffer using a RLE compression algorithm. 
The size of the target buffer must be equal to the size of the source buffer. 
On return, the function will return the real size of the target buffer, which should be less that or equal to the source buffer size. 
@param target 8-bit Target buffer
@param source 8-bit Source buffer
@param size Source/Target input buffer size
@return Returns the target buffer size
*/
static int
RLEEncodeLine(BYTE *target, BYTE *source, int size) {
	BYTE buffer[256];
	int buffer_size = 0;
	int target_pos = 0;

	for (int i = 0; i < size; ++i) {
		if ((i < size - 1) && (source[i] == source[i + 1])) {
			// find a solid block of same bytes

			int j = i + 1;
			int jmax = 254 + i;

			while ((j < size - 1) && (j < jmax) && (source[j] == source[j + 1]))
				++j;

			// if the block is larger than 3 bytes, use it
			// else put the data into the larger pool

			if (((j - i) + 1) > 3) {
				// don't forget to write what we already have in the buffer

				switch(buffer_size) {
					case 0 :
						break;

					case RLE_DELTA :
						target[target_pos++] = 1;
						target[target_pos++] = buffer[0];
						target[target_pos++] = 1;
						target[target_pos++] = buffer[1];
						break;

					case RLE_ENDOFBITMAP :
						target[target_pos++] = (BYTE)buffer_size;
						target[target_pos++] = buffer[0];
						break;

src/Source/FreeImage/PluginBMP.cpp  view on Meta::CPAN

		}

		// write the buffer if it's full

		if (buffer_size == 254) {
			target[target_pos++] = RLE_COMMAND;
			target[target_pos++] = (BYTE)buffer_size;
			memcpy(target + target_pos, buffer, buffer_size);

			// prepare for next run

			target_pos += buffer_size;
			buffer_size = 0;
		}
	}

	// write the last bytes

	switch(buffer_size) {
		case 0 :
			break;

		case RLE_DELTA :
			target[target_pos++] = 1;
			target[target_pos++] = buffer[0];
			target[target_pos++] = 1;
			target[target_pos++] = buffer[1];
			break;

		case RLE_ENDOFBITMAP :
			target[target_pos++] = (BYTE)buffer_size;
			target[target_pos++] = buffer[0];
			break;

		default :
			target[target_pos++] = RLE_COMMAND;
			target[target_pos++] = (BYTE)buffer_size;
			memcpy(target + target_pos, buffer, buffer_size);

			// prepare for next run
			
			target_pos += buffer_size;

			if ((buffer_size & 1) == 1)
				target_pos++;

			break;			
	}

	// write the END_OF_LINE marker

	target[target_pos++] = RLE_COMMAND;
	target[target_pos++] = RLE_ENDOFLINE;

	// return the written size

	return target_pos;
}

static BOOL DLL_CALLCONV
Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
	if ((dib != NULL) && (handle != NULL)) {
		// write the file header

		BITMAPFILEHEADER bitmapfileheader;
		bitmapfileheader.bfType = 0x4D42;
		bitmapfileheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + FreeImage_GetColorsUsed(dib) * sizeof(RGBQUAD);
		bitmapfileheader.bfSize = bitmapfileheader.bfOffBits + FreeImage_GetHeight(dib) * FreeImage_GetPitch(dib);
		bitmapfileheader.bfReserved1 = 0;
		bitmapfileheader.bfReserved2 = 0;

		// take care of the bit fields data of any

		bool bit_fields = (FreeImage_GetBPP(dib) == 16);

		if (bit_fields) {
			bitmapfileheader.bfSize += 3 * sizeof(DWORD);
			bitmapfileheader.bfOffBits += 3 * sizeof(DWORD);
		}

#ifdef FREEIMAGE_BIGENDIAN
		SwapFileHeader(&bitmapfileheader);
#endif
		if (io->write_proc(&bitmapfileheader, sizeof(BITMAPFILEHEADER), 1, handle) != 1)
			return FALSE;		

		// update the bitmap info header

		BITMAPINFOHEADER bih;
		memcpy(&bih, FreeImage_GetInfoHeader(dib), sizeof(BITMAPINFOHEADER));

		if (bit_fields)
			bih.biCompression = BI_BITFIELDS;
		else if ((bih.biBitCount == 8) && (flags & BMP_SAVE_RLE))
			bih.biCompression = BI_RLE8;
		else
			bih.biCompression = BI_RGB;

		// write the bitmap info header

#ifdef FREEIMAGE_BIGENDIAN
		SwapInfoHeader(&bih);
#endif
		if (io->write_proc(&bih, sizeof(BITMAPINFOHEADER), 1, handle) != 1)
			return FALSE;

		// write the bit fields when we are dealing with a 16 bit BMP

		if (bit_fields) {
			DWORD d;

			d = FreeImage_GetRedMask(dib);

			if (io->write_proc(&d, sizeof(DWORD), 1, handle) != 1)
				return FALSE;

			d = FreeImage_GetGreenMask(dib);

			if (io->write_proc(&d, sizeof(DWORD), 1, handle) != 1)
				return FALSE;

			d = FreeImage_GetBlueMask(dib);

			if (io->write_proc(&d, sizeof(DWORD), 1, handle) != 1)
				return FALSE;
		}

		// write the palette

		if (FreeImage_GetPalette(dib) != NULL) {
			RGBQUAD *pal = FreeImage_GetPalette(dib);
			FILE_BGRA bgra;
			for(unsigned i = 0; i < FreeImage_GetColorsUsed(dib); i++ ) {
				bgra.b = pal[i].rgbBlue;
				bgra.g = pal[i].rgbGreen;
				bgra.r = pal[i].rgbRed;
				bgra.a = pal[i].rgbReserved;
				if (io->write_proc(&bgra, sizeof(FILE_BGRA), 1, handle) != 1)
					return FALSE;
			}
		}

		// write the bitmap data... if RLE compression is enable, use it

		unsigned bpp = FreeImage_GetBPP(dib);
		if ((bpp == 8) && (flags & BMP_SAVE_RLE)) {
			BYTE *buffer = (BYTE*)malloc(FreeImage_GetPitch(dib) * 2 * sizeof(BYTE));

			for (DWORD i = 0; i < FreeImage_GetHeight(dib); ++i) {
				int size = RLEEncodeLine(buffer, FreeImage_GetScanLine(dib, i), FreeImage_GetLine(dib));

				if (io->write_proc(buffer, size, 1, handle) != 1) {
					free(buffer);
					return FALSE;
				}
			}

			buffer[0] = RLE_COMMAND;
			buffer[1] = RLE_ENDOFBITMAP;

			if (io->write_proc(buffer, 2, 1, handle) != 1) {
				free(buffer);
				return FALSE;
			}

			free(buffer);
#ifdef FREEIMAGE_BIGENDIAN
		} else if (bpp == 16) {
			int padding = FreeImage_GetPitch(dib) - FreeImage_GetWidth(dib) * sizeof(WORD);
			WORD pad = 0;
			WORD pixel;
			for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
				BYTE *line = FreeImage_GetScanLine(dib, y);
				for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
					pixel = ((WORD *)line)[x];
					SwapShort(&pixel);
					if (io->write_proc(&pixel, sizeof(WORD), 1, handle) != 1)
						return FALSE;
				}
				if(padding != 0) {
					if(io->write_proc(&pad, padding, 1, handle) != 1) {
						return FALSE;				
					}
				}
			}
#endif
#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
		} else if (bpp == 24) {
			int padding = FreeImage_GetPitch(dib) - FreeImage_GetWidth(dib) * sizeof(FILE_BGR);
			DWORD pad = 0;
			FILE_BGR bgr;
			for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
				BYTE *line = FreeImage_GetScanLine(dib, y);
				for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
					RGBTRIPLE *triple = ((RGBTRIPLE *)line)+x;
					bgr.b = triple->rgbtBlue;
					bgr.g = triple->rgbtGreen;
					bgr.r = triple->rgbtRed;
					if (io->write_proc(&bgr, sizeof(FILE_BGR), 1, handle) != 1)
						return FALSE;
				}
				if(padding != 0) {
					if(io->write_proc(&pad, padding, 1, handle) != 1) {
						return FALSE;					
					}
				}



( run in 1.402 second using v1.01-cache-2.11-cpan-56fb94df46f )