Example #1
0
BOOL DLL_CALLCONV 
FreeImage_JPEGCrop(const char *src_file, const char *dst_file, int left, int top, int right, int bottom) {
	char crop[64];

	try {
		// check the src file format
		if(FreeImage_GetFileType(src_file) != FIF_JPEG) {
			throw FI_MSG_ERROR_MAGIC_NUMBER;
		}
		
		// normalize the rectangle
		if(right < left) {
			INPLACESWAP(left, right);
		}
		if(bottom < top) {
			INPLACESWAP(top, bottom);
		}

		// build the crop option
		sprintf(crop, "%dx%d+%d+%d", right - left, bottom - top, left, top);

		// setup IO
		FilenameIO filenameIO;
		memset(&filenameIO, 0, sizeof(FilenameIO));
		filenameIO.src_file = src_file;
		filenameIO.dst_file = dst_file;

		// perform the transformation
		return LosslessTransform(&filenameIO, FIJPEG_OP_NONE, crop, FALSE);

	} catch(const char *text) {
		FreeImage_OutputMessageProc(FIF_JPEG, text);
		return FALSE;
	}
}
void ImageLoader::
compatible()
{
	// BITMAP数据是从下往上的, 需要翻转
	FreeImage_FlipVertical(mDIB);

	// FreeImage是BGR顺序, 与NVTT要求的一致, 而PVRT需要RGB顺序, 两者都需要32bits数据
	if (config.imageCompressionType != CT_DXTC)
	{
		// 将BGR改为RGB, 交换R channel和B channel
		const unsigned bytesperpixel = FreeImage_GetBPP(mDIB) / 8;
		const unsigned height = FreeImage_GetHeight(mDIB);
		const unsigned pitch = FreeImage_GetPitch(mDIB);
		const unsigned lineSize = FreeImage_GetLine(mDIB);

		BYTE* line = FreeImage_GetBits(mDIB);
		for (unsigned y = 0; y < height; ++y, line += pitch) {
			for (BYTE* pixel = line; pixel < line + lineSize; pixel += bytesperpixel) {
				INPLACESWAP(pixel[0], pixel[2]);
			}
		}
	}

	if (FreeImage_GetBPP(mDIB) != 32)
	{
		FIBITMAP* convertDIB = FreeImage_ConvertTo32Bits(mDIB);
		FreeImage_Unload(mDIB);
		mDIB = convertDIB;
	}
}
Example #3
0
/**
Build a crop string. 

@param crop Output crop string
@param left Specifies the left position of the cropped rectangle
@param top Specifies the top position of the cropped rectangle
@param right Specifies the right position of the cropped rectangle
@param bottom Specifies the bottom position of the cropped rectangle
@param width Image width
@param height Image height
@return Returns TRUE if successful, returns FALSE otherwise
*/
static BOOL
getCropString(char* crop, int* left, int* top, int* right, int* bottom, int width, int height) {
	if(!left || !top || !right || !bottom) {
		return FALSE;
	}

	*left = CLAMP(*left, 0, width);
	*top = CLAMP(*top, 0, height);

	// negative/zero right and bottom count from the edges inwards

	if(*right <= 0) {
		*right = width + *right;
	}
	if(*bottom <= 0) {
		*bottom = height + *bottom;
	}

	*right = CLAMP(*right, 0, width);
	*bottom = CLAMP(*bottom, 0, height);

	// test for empty rect

	if(((*left - *right) == 0) || ((*top - *bottom) == 0)) {
		return FALSE;
	}

	// normalize the rectangle

	if(*right < *left) {
		INPLACESWAP(*left, *right);
	}
	if(*bottom < *top) {
		INPLACESWAP(*top, *bottom);
	}

	// test for "noop" rect

	if(*left == 0 && *right == width && *top == 0 && *bottom == height) {
		return FALSE;
	}

	// build the crop option
	sprintf(crop, "%dx%d+%d+%d", *right - *left, *bottom - *top, *left, *top);

	return TRUE;
}
Example #4
0
/**
Load uncompressed image pixels for 1-, 4-, 8-, 16-, 24- and 32-bit dib
@param io FreeImage IO
@param handle FreeImage IO handle
@param dib Image to be loaded 
@param height Image height
@param pitch Image pitch
@param bit_count Image bit-depth (1-, 4-, 8-, 16-, 24- or 32-bit)
@return Returns TRUE if successful, returns FALSE otherwise
*/
static BOOL 
LoadPixelData(FreeImageIO *io, fi_handle handle, FIBITMAP *dib, int height, unsigned pitch, unsigned bit_count) {
	unsigned count = 0;

	// Load pixel data
	// NB: height can be < 0 for BMP data
	if (height > 0) {
		count = io->read_proc((void *)FreeImage_GetBits(dib), height * pitch, 1, handle);
		if(count != 1) {
			return FALSE;
		}
	} else {
		int positiveHeight = abs(height);
		for (int c = 0; c < positiveHeight; ++c) {
			count = io->read_proc((void *)FreeImage_GetScanLine(dib, positiveHeight - c - 1), pitch, 1, handle);
			if(count != 1) {
				return FALSE;
			}
		}
	}

	// swap as needed
#ifdef FREEIMAGE_BIGENDIAN
	if (bit_count == 16) {
		for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
			WORD *pixel = (WORD *)FreeImage_GetScanLine(dib, y);
			for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
				SwapShort(pixel);
				pixel++;
			}
		}
	}
#endif
#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
	if (bit_count == 24 || bit_count == 32) {
		for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
			BYTE *pixel = FreeImage_GetScanLine(dib, y);
			for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
				INPLACESWAP(pixel[0], pixel[2]);
				pixel += (bit_count >> 3);
			}
		}
	}
#endif

	return TRUE;
}
Example #5
0
static FIBITMAP *
LoadOS21XBMP(FreeImageIO *io, fi_handle handle, int flags, unsigned bitmap_bits_offset) {
	FIBITMAP *dib = NULL;

	try {
		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

		int used_colors = 0;
		int width       = bios2_1x.biWidth;
		int height      = bios2_1x.biHeight;
		int bit_count   = bios2_1x.biBitCount;
		int 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_Allocate(width, height, bit_count);

				if (dib == NULL)
					throw "DIB allocation failed";						

				BITMAPINFOHEADER *pInfoHeader = FreeImage_GetInfoHeader(dib);
				pInfoHeader->biXPelsPerMeter = 0;
				pInfoHeader->biYPelsPerMeter = 0;
				
				// load the palette

				RGBQUAD *pal = FreeImage_GetPalette(dib);

				for (int 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;
				}

				// 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

				if (height > 0) {
					io->read_proc((void *)FreeImage_GetBits(dib), height * pitch, 1, handle);
				} else {
					for (int c = 0; c < abs(height); ++c) {
						io->read_proc((void *)FreeImage_GetScanLine(dib, height - c - 1), pitch, 1, handle);								
					}
				}
						
				return dib;
			}

			case 16 :
			{
				dib = FreeImage_Allocate(width, height, bit_count, FI16_555_RED_MASK, FI16_555_GREEN_MASK, FI16_555_BLUE_MASK);

				if (dib == NULL)
					throw "DIB allocation failed";						

				BITMAPINFOHEADER *pInfoHeader = FreeImage_GetInfoHeader(dib);
				pInfoHeader->biXPelsPerMeter = 0;
				pInfoHeader->biYPelsPerMeter = 0;

				io->read_proc(FreeImage_GetBits(dib), height * pitch, 1, handle);
#ifdef FREEIMAGE_BIGENDIAN
				for(int y = 0; y < FreeImage_GetHeight(dib); y++) {
					WORD *pixel = (WORD *)FreeImage_GetScanLine(dib, y);
					for(int x = 0; x < FreeImage_GetWidth(dib); x++) {
						SwapShort(pixel);
						pixel++;
					}
				}
#endif

				return dib;
			}

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

				if (dib == NULL)
					throw "DIB allocation failed";						

				BITMAPINFOHEADER *pInfoHeader = FreeImage_GetInfoHeader(dib);
				pInfoHeader->biXPelsPerMeter = 0;
				pInfoHeader->biYPelsPerMeter = 0;

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

				io->read_proc(FreeImage_GetBits(dib), height * pitch, 1, handle);
#ifdef FREEIMAGE_BIGENDIAN
				for(int y = 0; y < FreeImage_GetHeight(dib); y++) {
					BYTE *pixel = FreeImage_GetScanLine(dib, y);
					for(int x = 0; x < FreeImage_GetWidth(dib); x++) {
						INPLACESWAP(pixel[0], pixel[2]);
						pixel += (bit_count>>3);
					}
				}
#endif

				// 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;
}
Example #6
0
static FIBITMAP *
LoadOS22XBMP(FreeImageIO *io, fi_handle handle, int flags, unsigned bitmap_bits_offset) {
	FIBITMAP *dib = NULL;

	try {
		// 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

		int used_colors = bih.biClrUsed;
		int width       = bih.biWidth;
		int height      = bih.biHeight;
		int bit_count   = bih.biBitCount;
		int compression = bih.biCompression;
		int 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_Allocate(width, height, bit_count);

				if (dib == NULL)
					throw "DIB allocation failed";

				BITMAPINFOHEADER *pInfoHeader = FreeImage_GetInfoHeader(dib);
				pInfoHeader->biXPelsPerMeter = bih.biXPelsPerMeter;
				pInfoHeader->biYPelsPerMeter = bih.biYPelsPerMeter;
				
				// load the palette

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

				RGBQUAD *pal = FreeImage_GetPalette(dib);

				for (int 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;
				}

				// seek to the actual pixel data.
				// this is needed because sometimes the palette is larger than the entries it contains predicts

				if (bitmap_bits_offset > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (used_colors * 3)))
					io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);

				// read the pixel data

				switch (compression) {
					case BI_RGB :
						if (height > 0) {
							io->read_proc((void *)FreeImage_GetBits(dib), height * pitch, 1, handle);
						} else {
							for (int c = 0; c < abs(height); ++c) {
								io->read_proc((void *)FreeImage_GetScanLine(dib, height - c - 1), pitch, 1, handle);								
							}
						}
						
						return dib;

					case BI_RLE4 :
					{
						BYTE status_byte = 0;
						BYTE second_byte = 0;
						int scanline = 0;
						int bits = 0;
						BOOL low_nibble = FALSE;

						for (;;) {
							io->read_proc(&status_byte, sizeof(BYTE), 1, handle);

							switch (status_byte) {
								case RLE_COMMAND :
									io->read_proc(&status_byte, sizeof(BYTE), 1, handle);

									switch (status_byte) {
										case RLE_ENDOFLINE :
											bits = 0;
											scanline++;
											low_nibble = FALSE;
											break;

										case RLE_ENDOFBITMAP :
											return (FIBITMAP *)dib;

										case RLE_DELTA :
										{
											// read the delta values

											BYTE delta_x;
											BYTE delta_y;

											io->read_proc(&delta_x, sizeof(BYTE), 1, handle);
											io->read_proc(&delta_y, sizeof(BYTE), 1, handle);

											// apply them

											bits       += delta_x / 2;
											scanline   += delta_y;
											break;
										}

										default :
											io->read_proc(&second_byte, sizeof(BYTE), 1, handle);

											BYTE *sline = FreeImage_GetScanLine(dib, scanline);

											for (int i = 0; i < status_byte; i++) {
												if (low_nibble) {
													*(sline + bits) |= LOWNIBBLE(second_byte);

													if (i != status_byte - 1)
														io->read_proc(&second_byte, sizeof(BYTE), 1, handle);

													bits++;
												} else {
													*(sline + bits) |= HINIBBLE(second_byte);
												}
												
												low_nibble = !low_nibble;
											}

											if (((status_byte / 2) & 1 ) == 1)
												io->read_proc(&second_byte, sizeof(BYTE), 1, handle);												

											break;
									};

									break;

								default :
								{
									BYTE *sline = FreeImage_GetScanLine(dib, scanline);

									io->read_proc(&second_byte, sizeof(BYTE), 1, handle);

									for (unsigned i = 0; i < status_byte; i++) {
										if (low_nibble) {
											*(sline + bits) |= LOWNIBBLE(second_byte);

											bits++;
										} else {
											*(sline + bits) |= HINIBBLE(second_byte);
										}				
										
										low_nibble = !low_nibble;
									}
								}

								break;
							};
						}

						break;
					}

					case BI_RLE8 :
					{
						BYTE status_byte = 0;
						BYTE second_byte = 0;
						int scanline = 0;
						int bits = 0;

						for (;;) {
							io->read_proc(&status_byte, sizeof(BYTE), 1, handle);

							switch (status_byte) {
								case RLE_COMMAND :
									io->read_proc(&status_byte, sizeof(BYTE), 1, handle);

									switch (status_byte) {
										case RLE_ENDOFLINE :
											bits = 0;
											scanline++;
											break;

										case RLE_ENDOFBITMAP :
											return (FIBITMAP *)dib;

										case RLE_DELTA :
										{
											// read the delta values

											BYTE delta_x;
											BYTE delta_y;

											io->read_proc(&delta_x, sizeof(BYTE), 1, handle);
											io->read_proc(&delta_y, sizeof(BYTE), 1, handle);

											// apply them

											bits     += delta_x;
											scanline += delta_y;
											break;
										}

										default :
											io->read_proc((void *)(FreeImage_GetScanLine(dib, scanline) + bits), sizeof(BYTE) * status_byte, 1, handle);
											
											// align run length to even number of bytes 

											if ((status_byte & 1) == 1)
												io->read_proc(&second_byte, sizeof(BYTE), 1, handle);												

											bits += status_byte;

											break;								
									};

									break;

								default :
									BYTE *sline = FreeImage_GetScanLine(dib, scanline);

									io->read_proc(&second_byte, sizeof(BYTE), 1, handle);

									for (unsigned i = 0; i < status_byte; i++) {
										*(sline + bits) = second_byte;

										bits++;
									}

									break;
							};
						}

						break;
					}

					default :		
						throw "compression type not supported";
				}						

				break;
			}

			case 16 :
			{
				if (bih.biCompression == 3) {
					DWORD bitfields[3];

					io->read_proc(bitfields, 3 * sizeof(DWORD), 1, handle);

					dib = FreeImage_Allocate(width, height, bit_count, bitfields[0], bitfields[1], bitfields[2]);
				} else {
					dib = FreeImage_Allocate(width, height, bit_count, FI16_555_RED_MASK, FI16_555_GREEN_MASK, FI16_555_BLUE_MASK);
				}

				if (dib == NULL)
					throw "DIB allocation failed";						

				BITMAPINFOHEADER *pInfoHeader = FreeImage_GetInfoHeader(dib);
				pInfoHeader->biXPelsPerMeter = bih.biXPelsPerMeter;
				pInfoHeader->biYPelsPerMeter = bih.biYPelsPerMeter;

				if (bitmap_bits_offset > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (used_colors * 3)))
					io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);

				io->read_proc(FreeImage_GetBits(dib), height * pitch, 1, handle);
#ifdef FREEIMAGE_BIGENDIAN
				for(int y = 0; y < FreeImage_GetHeight(dib); y++) {
					WORD *pixel = (WORD *)FreeImage_GetScanLine(dib, y);
					for(int x = 0; x < FreeImage_GetWidth(dib); x++) {
						SwapShort(pixel);
						pixel++;
					}
				}
#endif
				return dib;
			}

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

				if (dib == NULL)
					throw "DIB allocation failed";
				
				BITMAPINFOHEADER *pInfoHeader = FreeImage_GetInfoHeader(dib);
				pInfoHeader->biXPelsPerMeter = bih.biXPelsPerMeter;
				pInfoHeader->biYPelsPerMeter = bih.biYPelsPerMeter;

				// 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

				io->read_proc(FreeImage_GetBits(dib), height * pitch, 1, handle);
#ifdef FREEIMAGE_BIGENDIAN
				for(int y = 0; y < FreeImage_GetHeight(dib); y++) {
					BYTE *pixel = FreeImage_GetScanLine(dib, y);
					for(int x = 0; x < FreeImage_GetWidth(dib); x++) {
						INPLACESWAP(pixel[0], pixel[2]);
						pixel += (bit_count>>3);
					}
				}
#endif

				// 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 HBITMAP 
GetDibSection(FIBITMAP *src, HDC hdc, int left, int top, int right, int bottom)
{
	if(!src) 
		return NULL;

	unsigned bpp = FreeImage_GetBPP(src);

	if(bpp <=4)
		return NULL;

	// normalize the rectangle
	if(right < left)
		INPLACESWAP(left, right);

	if(bottom < top)
		INPLACESWAP(top, bottom);

	// check the size of the sub image
	int src_width  = FreeImage_GetWidth(src);
	int src_height = FreeImage_GetHeight(src);
	int src_pitch = FreeImage_GetPitch(src);

	if((left < 0) || (right > src_width) || (top < 0) || (bottom > src_height))
		return NULL;

	// allocate the sub image
	int dst_width = (right - left);
	int dst_height = (bottom - top);

	BITMAPINFO *info = (BITMAPINFO *) malloc(sizeof(BITMAPINFO) + (FreeImage_GetColorsUsed(src) * sizeof(RGBQUAD)));
	BITMAPINFOHEADER *bih = (BITMAPINFOHEADER *)info;

	bih->biSize = sizeof(BITMAPINFOHEADER);
	bih->biWidth = dst_width;
	bih->biHeight = dst_height;
	bih->biPlanes = 1;
	bih->biBitCount = bpp;
	bih->biCompression = BI_RGB;
	bih->biSizeImage = 0;
    bih->biXPelsPerMeter = 0;
    bih->biYPelsPerMeter = 0;
   	bih->biClrUsed = 0;           // Always use the whole palette.
    bih->biClrImportant = 0;

	// copy the palette
	if(bpp < 16)
		memcpy(info->bmiColors, FreeImage_GetPalette(src), FreeImage_GetColorsUsed(src) * sizeof(RGBQUAD));

	BYTE *dst_bits;

	HBITMAP hbitmap = CreateDIBSection(hdc, info, DIB_RGB_COLORS, (void **) &dst_bits, NULL, 0);

	free(info);

	if(hbitmap == NULL || dst_bits == NULL)
		return NULL;

	// get the pointers to the bits and such
	BYTE *src_bits = FreeImage_GetScanLine(src, src_height - top - dst_height);

	// calculate the number of bytes per pixel
	unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
	
	// point to x = left
	src_bits += (left * bytespp);	

	int dst_line = (dst_width * bpp + 7) / 8;
	int dst_pitch = (dst_line + 3) & ~3;

	for(int y = 0; y < dst_height; y++)
		memcpy(dst_bits + (y * dst_pitch), src_bits + (y * src_pitch), dst_line);

	return hbitmap;
}
Example #8
0
FIBITMAP * DLL_CALLCONV
FreeImage_RescaleRect(FIBITMAP *src, int dst_width, int dst_height, int src_left, int src_top, int src_right, int src_bottom, FREE_IMAGE_FILTER filter, unsigned flags) {
	FIBITMAP *dst = NULL;

	const int src_width = FreeImage_GetWidth(src);
	const int src_height = FreeImage_GetHeight(src);

	if (!FreeImage_HasPixels(src) || (dst_width <= 0) || (dst_height <= 0) || (src_width <= 0) || (src_height <= 0)) {
		return NULL;
	}

	// normalize the rectangle
	if (src_right < src_left) {
		INPLACESWAP(src_left, src_right);
	}
	if (src_bottom < src_top) {
		INPLACESWAP(src_top, src_bottom);
	}

	// check the size of the sub image
	if((src_left < 0) || (src_right > src_width) || (src_top < 0) || (src_bottom > src_height)) {
		return NULL;
	}

	// select the filter
	CGenericFilter *pFilter = NULL;
	switch (filter) {
		case FILTER_BOX:
			pFilter = new(std::nothrow) CBoxFilter();
			break;
		case FILTER_BICUBIC:
			pFilter = new(std::nothrow) CBicubicFilter();
			break;
		case FILTER_BILINEAR:
			pFilter = new(std::nothrow) CBilinearFilter();
			break;
		case FILTER_BSPLINE:
			pFilter = new(std::nothrow) CBSplineFilter();
			break;
		case FILTER_CATMULLROM:
			pFilter = new(std::nothrow) CCatmullRomFilter();
			break;
		case FILTER_LANCZOS3:
			pFilter = new(std::nothrow) CLanczos3Filter();
			break;
	}

	if (!pFilter) {
		return NULL;
	}

	CResizeEngine Engine(pFilter);

	dst = Engine.scale(src, dst_width, dst_height, src_left, src_top,
			src_right - src_left, src_bottom - src_top, flags);

	delete pFilter;

	if ((flags & FI_RESCALE_OMIT_METADATA) != FI_RESCALE_OMIT_METADATA) {
		// copy metadata from src to dst
		FreeImage_CloneMetadata(dst, src);
	}

	return dst;
}
Example #9
0
static FIBITMAP * DLL_CALLCONV
Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
	if (handle) {
		FIBITMAP *dib = NULL;

		try {
			// set up the jpeglib structures

			struct jpeg_decompress_struct cinfo;
			struct jpeg_error_mgr jerr;

			// step 1: allocate and initialize JPEG decompression object

			cinfo.err = jpeg_std_error(&jerr);

			jerr.error_exit     = jpeg_error_exit;
			jerr.output_message = jpeg_output_message;

			jpeg_create_decompress(&cinfo);

			// step 2a: specify data source (eg, a handle)

			jpeg_freeimage_src(&cinfo, handle, io);

			// step 2b: save special markers for later reading
			
			jpeg_save_markers(&cinfo, JPEG_COM, 0xFFFF);
			for(int m = 0; m < 16; m++) {
				jpeg_save_markers(&cinfo, JPEG_APP0 + m, 0xFFFF);
			}

			// step 3: read handle parameters with jpeg_read_header()

			jpeg_read_header(&cinfo, TRUE);

			// step 4: set parameters for decompression

			unsigned int scale_denom = 1;		// fraction by which to scale image
			int	requested_size = flags >> 16;	// requested user size in pixels
			if(requested_size > 0) {
				// the JPEG codec can perform x2, x4 or x8 scaling on loading
				// try to find the more appropriate scaling according to user's need
				double scale = MAX((double)cinfo.image_width, (double)cinfo.image_height) / (double)requested_size;
				if(scale >= 8) {
					scale_denom = 8;
				} else if(scale >= 4) {
					scale_denom = 4;
				} else if(scale >= 2) {
					scale_denom = 2;
				}
			}
			cinfo.scale_num = 1;
			cinfo.scale_denom = scale_denom;

			if ((flags & JPEG_ACCURATE) != JPEG_ACCURATE) {
				cinfo.dct_method          = JDCT_IFAST;
				cinfo.do_fancy_upsampling = FALSE;
			}

			// step 5a: start decompressor and calculate output width and height

			jpeg_start_decompress(&cinfo);

			// step 5b: allocate dib and init header

			if((cinfo.num_components == 4) && (cinfo.out_color_space == JCS_CMYK)) {
				// CMYK image
				if((flags & JPEG_CMYK) == JPEG_CMYK) {
					// load as CMYK
					dib = FreeImage_Allocate(cinfo.output_width, cinfo.output_height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
					if(!dib) return NULL;
					FreeImage_GetICCProfile(dib)->flags |= FIICC_COLOR_IS_CMYK;
				} else {
					// load as CMYK and convert to RGB
					dib = FreeImage_Allocate(cinfo.output_width, cinfo.output_height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
					if(!dib) return NULL;
				}
			} else {
				// RGB or greyscale image
				dib = FreeImage_Allocate(cinfo.output_width, cinfo.output_height, 8 * cinfo.num_components, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
				if(!dib) return NULL;

				if (cinfo.num_components == 1) {
					// build a greyscale palette
					RGBQUAD *colors = FreeImage_GetPalette(dib);

					for (int i = 0; i < 256; i++) {
						colors[i].rgbRed   = (BYTE)i;
						colors[i].rgbGreen = (BYTE)i;
						colors[i].rgbBlue  = (BYTE)i;
					}
				}
			}
			if(scale_denom != 1) {
				// store original size info if a scaling was requested
				store_size_info(dib, cinfo.image_width, cinfo.image_height);
			}

			// step 5c: handle metrices

			if (cinfo.density_unit == 1) {
				// dots/inch
				FreeImage_SetDotsPerMeterX(dib, (unsigned) (((float)cinfo.X_density) / 0.0254000 + 0.5));
				FreeImage_SetDotsPerMeterY(dib, (unsigned) (((float)cinfo.Y_density) / 0.0254000 + 0.5));
			} else if (cinfo.density_unit == 2) {
				// dots/cm
				FreeImage_SetDotsPerMeterX(dib, (unsigned) (cinfo.X_density * 100));
				FreeImage_SetDotsPerMeterY(dib, (unsigned) (cinfo.Y_density * 100));
			}

			// step 6a: while (scan lines remain to be read) jpeg_read_scanlines(...);

			if((cinfo.out_color_space == JCS_CMYK) && ((flags & JPEG_CMYK) != JPEG_CMYK)) {
				// convert from CMYK to RGB

				JSAMPARRAY buffer;		// output row buffer
				unsigned row_stride;	// physical row width in output buffer

				// JSAMPLEs per row in output buffer
				row_stride = cinfo.output_width * cinfo.output_components;
				// make a one-row-high sample array that will go away when done with image
				buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);

				while (cinfo.output_scanline < cinfo.output_height) {
					JSAMPROW src = buffer[0];
					JSAMPROW dst = FreeImage_GetScanLine(dib, cinfo.output_height - cinfo.output_scanline - 1);

					jpeg_read_scanlines(&cinfo, buffer, 1);

					for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
						WORD K = (WORD)src[3];
						dst[FI_RGBA_RED]   = (BYTE)((K * src[0]) / 255);
						dst[FI_RGBA_GREEN] = (BYTE)((K * src[1]) / 255);
						dst[FI_RGBA_BLUE]  = (BYTE)((K * src[2]) / 255);
						src += 4;
						dst += 3;
					}
				}
			} else {
				// normal case (RGB or greyscale image)

				while (cinfo.output_scanline < cinfo.output_height) {
					JSAMPROW dst = FreeImage_GetScanLine(dib, cinfo.output_height - cinfo.output_scanline - 1);

					jpeg_read_scanlines(&cinfo, &dst, 1);
				}

				// step 6b: swap red and blue components (see LibJPEG/jmorecfg.h: #define RGB_RED, ...)
				// The default behavior of the JPEG library is kept "as is" because LibTIFF uses 
				// LibJPEG "as is".

#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
				if(cinfo.num_components == 3) {
					for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
						BYTE *target = FreeImage_GetScanLine(dib, y);
						for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
							INPLACESWAP(target[0], target[2]);
							target += 3;
						}
					}
				}
#endif
			}

			// step 7: read special markers

			read_markers(&cinfo, dib);

			// step 8: finish decompression

			jpeg_finish_decompress(&cinfo);

			// step 9: release JPEG decompression object

			jpeg_destroy_decompress(&cinfo);

			// check for automatic Exif rotation
			if((flags & JPEG_EXIFROTATE) == JPEG_EXIFROTATE) {
				rotate_exif(&dib);
			}

			// everything went well. return the loaded dib

			return (FIBITMAP *)dib;
		} catch (...) {
			if(NULL != dib) {
				FreeImage_Unload(dib);
			}
		}
	}

	return NULL;
}
Example #10
0
static BOOL DLL_CALLCONV
Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
	if ((dib) && (handle)) {
		try {
			// Check dib format

			const char *sError = "only 24-bit highcolor or 8-bit greyscale/palette bitmaps can be saved as JPEG";

			FREE_IMAGE_COLOR_TYPE color_type = FreeImage_GetColorType(dib);
			WORD bpp = (WORD)FreeImage_GetBPP(dib);

			if ((bpp != 24) && (bpp != 8))
				throw sError;

			if(bpp == 8) {
				// allow grey, reverse grey and palette 
				if ((color_type != FIC_MINISBLACK) && (color_type != FIC_MINISWHITE) && (color_type != FIC_PALETTE))
					throw sError;
			}


			struct jpeg_compress_struct cinfo;
			struct jpeg_error_mgr jerr;

			// Step 1: allocate and initialize JPEG compression object

			cinfo.err = jpeg_std_error(&jerr);

			jerr.error_exit     = jpeg_error_exit;
			jerr.output_message = jpeg_output_message;

			// Now we can initialize the JPEG compression object

			jpeg_create_compress(&cinfo);

			// Step 2: specify data destination (eg, a file)

			jpeg_freeimage_dst(&cinfo, handle, io);

			// Step 3: set parameters for compression 

			cinfo.image_width = FreeImage_GetWidth(dib);
			cinfo.image_height = FreeImage_GetHeight(dib);

			switch(color_type) {
				case FIC_MINISBLACK :
				case FIC_MINISWHITE :
					cinfo.in_color_space = JCS_GRAYSCALE;
					cinfo.input_components = 1;
					break;

				default :
					cinfo.in_color_space = JCS_RGB;
					cinfo.input_components = 3;
					break;
			}

			jpeg_set_defaults(&cinfo);

		    // progressive-JPEG support
			if((flags & JPEG_PROGRESSIVE) == JPEG_PROGRESSIVE) {
				jpeg_simple_progression(&cinfo);
			}

			// Set JFIF density parameters from the DIB data

			cinfo.X_density = (UINT16) (0.5 + 0.0254 * FreeImage_GetDotsPerMeterX(dib));
			cinfo.Y_density = (UINT16) (0.5 + 0.0254 * FreeImage_GetDotsPerMeterY(dib));
			cinfo.density_unit = 1;	// dots / inch

			// set subsampling options if required

			if(cinfo.in_color_space == JCS_RGB) {
				if((flags & JPEG_SUBSAMPLING_411) == JPEG_SUBSAMPLING_411) { 
					// 4:1:1 (4x1 1x1 1x1) - CrH 25% - CbH 25% - CrV 100% - CbV 100%
					// the horizontal color resolution is quartered
					cinfo.comp_info[0].h_samp_factor = 4;	// Y 
					cinfo.comp_info[0].v_samp_factor = 1; 
					cinfo.comp_info[1].h_samp_factor = 1;	// Cb 
					cinfo.comp_info[1].v_samp_factor = 1; 
					cinfo.comp_info[2].h_samp_factor = 1;	// Cr 
					cinfo.comp_info[2].v_samp_factor = 1; 
				} else if((flags & JPEG_SUBSAMPLING_420) == JPEG_SUBSAMPLING_420) {
					// 4:2:0 (2x2 1x1 1x1) - CrH 50% - CbH 50% - CrV 50% - CbV 50%
					// the chrominance resolution in both the horizontal and vertical directions is cut in half
					cinfo.comp_info[0].h_samp_factor = 2;	// Y
					cinfo.comp_info[0].v_samp_factor = 2; 
					cinfo.comp_info[1].h_samp_factor = 1;	// Cb
					cinfo.comp_info[1].v_samp_factor = 1; 
					cinfo.comp_info[2].h_samp_factor = 1;	// Cr
					cinfo.comp_info[2].v_samp_factor = 1; 
				} else if((flags & JPEG_SUBSAMPLING_422) == JPEG_SUBSAMPLING_422){ //2x1 (low) 
					// 4:2:2 (2x1 1x1 1x1) - CrH 50% - CbH 50% - CrV 100% - CbV 100%
					// half of the horizontal resolution in the chrominance is dropped (Cb & Cr), 
					// while the full resolution is retained in the vertical direction, with respect to the luminance
					cinfo.comp_info[0].h_samp_factor = 2;	// Y 
					cinfo.comp_info[0].v_samp_factor = 1; 
					cinfo.comp_info[1].h_samp_factor = 1;	// Cb 
					cinfo.comp_info[1].v_samp_factor = 1; 
					cinfo.comp_info[2].h_samp_factor = 1;	// Cr 
					cinfo.comp_info[2].v_samp_factor = 1; 
				} 
				else if((flags & JPEG_SUBSAMPLING_444) == JPEG_SUBSAMPLING_444){ //1x1 (no subsampling) 
					// 4:4:4 (1x1 1x1 1x1) - CrH 100% - CbH 100% - CrV 100% - CbV 100%
					// the resolution of chrominance information (Cb & Cr) is preserved 
					// at the same rate as the luminance (Y) information
					cinfo.comp_info[0].h_samp_factor = 1;	// Y 
					cinfo.comp_info[0].v_samp_factor = 1; 
					cinfo.comp_info[1].h_samp_factor = 1;	// Cb 
					cinfo.comp_info[1].v_samp_factor = 1; 
					cinfo.comp_info[2].h_samp_factor = 1;	// Cr 
					cinfo.comp_info[2].v_samp_factor = 1;  
				} 
			}

			// Step 4: set quality
			// the first 7 bits are reserved for low level quality settings
			// the other bits are high level (i.e. enum-ish)

			int quality;

			if ((flags & JPEG_QUALITYBAD) == JPEG_QUALITYBAD) {
				quality = 10;
			} else if ((flags & JPEG_QUALITYAVERAGE) == JPEG_QUALITYAVERAGE) {
				quality = 25;
			} else if ((flags & JPEG_QUALITYNORMAL) == JPEG_QUALITYNORMAL) {
				quality = 50;
			} else if ((flags & JPEG_QUALITYGOOD) == JPEG_QUALITYGOOD) {
				quality = 75;
			} else 	if ((flags & JPEG_QUALITYSUPERB) == JPEG_QUALITYSUPERB) {
				quality = 100;
			} else {
				if ((flags & 0x7F) == 0) {
					quality = 75;
				} else {
					quality = flags & 0x7F;
				}
			}

			jpeg_set_quality(&cinfo, quality, TRUE); /* limit to baseline-JPEG values */

			// Step 5: Start compressor 

			jpeg_start_compress(&cinfo, TRUE);

			// Step 6: Write special markers

			write_markers(&cinfo, dib);

			// Step 7: while (scan lines remain to be written) 

			if(color_type == FIC_RGB) {
				// 24-bit RGB image : need to swap red and blue channels
				unsigned pitch = FreeImage_GetPitch(dib);
				BYTE *target = (BYTE*)malloc(pitch * sizeof(BYTE));
				if (target == NULL) {
					throw FI_MSG_ERROR_MEMORY;
				}

				while (cinfo.next_scanline < cinfo.image_height) {
					// get a copy of the scanline
					memcpy(target, FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1), pitch);
#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
					// swap R and B channels
					BYTE *target_p = target;
					for(unsigned x = 0; x < cinfo.image_width; x++) {
						INPLACESWAP(target_p[0], target_p[2]);
						target_p += 3;
					}
#endif
					// write the scanline
					jpeg_write_scanlines(&cinfo, &target, 1);
				}
				free(target);
			}
			else if(color_type == FIC_MINISBLACK) {
				// 8-bit standard greyscale images
				while (cinfo.next_scanline < cinfo.image_height) {
					JSAMPROW b = FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1);

					jpeg_write_scanlines(&cinfo, &b, 1);
				}
			}
			else if(color_type == FIC_PALETTE) {
				// 8-bit palettized images are converted to 24-bit images
				RGBQUAD *palette = FreeImage_GetPalette(dib);
				BYTE *target = (BYTE*)malloc(cinfo.image_width * 3);
				if (target == NULL) {
					throw FI_MSG_ERROR_MEMORY;
				}

				while (cinfo.next_scanline < cinfo.image_height) {
					BYTE *source = FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1);
					FreeImage_ConvertLine8To24(target, source, cinfo.image_width, palette);

#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
					// swap R and B channels
					BYTE *target_p = target;
					for(unsigned x = 0; x < cinfo.image_width; x++) {
						INPLACESWAP(target_p[0], target_p[2]);
						target_p += 3;
					}
#endif


					jpeg_write_scanlines(&cinfo, &target, 1);
				}

				free(target);
			}
			else if(color_type == FIC_MINISWHITE) {
				// reverse 8-bit greyscale image, so reverse grey value on the fly
				unsigned i;
				BYTE reverse[256];
				BYTE *target = (BYTE *)malloc(cinfo.image_width);
				if (target == NULL) {
					throw FI_MSG_ERROR_MEMORY;
				}

				for(i = 0; i < 256; i++) {
					reverse[i] = (BYTE)(255 - i);
				}

				while(cinfo.next_scanline < cinfo.image_height) {
					BYTE *source = FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1);
					for(i = 0; i < cinfo.image_width; i++) {
						target[i] = reverse[ source[i] ];
					}
					jpeg_write_scanlines(&cinfo, &target, 1);
				}

				free(target);
			}

			// Step 8: Finish compression 

			jpeg_finish_compress(&cinfo);

			// Step 9: release JPEG compression object 

			jpeg_destroy_compress(&cinfo);

			return TRUE;

		} catch (const char *text) {
			FreeImage_OutputMessageProc(s_format_id, text);
			return FALSE;
		} catch (FREE_IMAGE_FORMAT) {
			return FALSE;
		}
	}

	return FALSE;
}
Example #11
0
static FIBITMAP * DLL_CALLCONV
Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
	if (handle != NULL) {
		FIBITMAP *dib = NULL;

		DWORD type, size;

		io->read_proc(&type, 4, 1, handle);
#ifndef FREEIMAGE_BIGENDIAN
		SwapLong(&type);
#endif

		if(type != ID_FORM)
			return NULL;

		io->read_proc(&size, 4, 1, handle);
#ifndef FREEIMAGE_BIGENDIAN
		SwapLong(&size);
#endif

		io->read_proc(&type, 4, 1, handle);
#ifndef FREEIMAGE_BIGENDIAN
		SwapLong(&type);
#endif

		if((type != ID_ILBM) && (type != ID_PBM))
			return NULL;

		size -= 4;

		unsigned width = 0, height = 0, planes = 0, depth = 0, comp = 0;

		while (size) {
			DWORD ch_type,ch_size;

			io->read_proc(&ch_type, 4, 1, handle);
#ifndef FREEIMAGE_BIGENDIAN
			SwapLong(&ch_type);
#endif

			io->read_proc(&ch_size,4,1,handle );
#ifndef FREEIMAGE_BIGENDIAN
			SwapLong(&ch_size);
#endif

			unsigned ch_end = io->tell_proc(handle) + ch_size;

			if (ch_type == ID_BMHD) {			// Bitmap Header
				if (dib)
					FreeImage_Unload(dib);

				BMHD bmhd;

				io->read_proc(&bmhd, sizeof(bmhd), 1, handle);
#ifndef FREEIMAGE_BIGENDIAN
				SwapHeader(&bmhd);
#endif

				width = bmhd.w;
				height = bmhd.h;
				planes = bmhd.nPlanes;
				comp = bmhd.compression;

				if(bmhd.masking & 1)
					planes++;	// there is a mask ( 'stencil' )

				if (planes > 8 && planes != 24)
					return NULL;

				depth = planes > 8 ? 24 : 8;

				if( depth == 24 ) {
					dib = FreeImage_Allocate(width, height, depth, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
				} else {
					dib = FreeImage_Allocate(width, height, depth);
				}
			} else if (ch_type == ID_CMAP) {	// Palette (Color Map)
				if (!dib)
					return NULL;

				RGBQUAD *pal = FreeImage_GetPalette(dib);

				for (unsigned k = 0; k < ch_size / 3; k++) {					
					io->read_proc(&pal[k].rgbRed, 1, 1, handle );
					io->read_proc(&pal[k].rgbGreen, 1, 1, handle );
					io->read_proc(&pal[k].rgbBlue, 1, 1, handle );
				}
			} else if (ch_type == ID_BODY) {
				if (!dib)
					return NULL;

				if (type == ID_PBM) {
					// NON INTERLACED (LBM)

					unsigned line = FreeImage_GetLine(dib) + 1 & ~1;
					
					for (unsigned i = 0; i < FreeImage_GetHeight(dib); i++) {
						BYTE *bits = FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - i - 1);

						if (comp == 1) {
							// use RLE compression

							DWORD number_of_bytes_written = 0;
							BYTE rle_count;
							BYTE byte;

							while (number_of_bytes_written < line) {
								io->read_proc(&rle_count, 1, 1, handle);

								if (rle_count < 128) {
									for (int k = 0; k < rle_count + 1; k++) {
										io->read_proc(&byte, 1, 1, handle);

										bits[number_of_bytes_written++] += byte;
									}
								} else if (rle_count > 128) {
									io->read_proc(&byte, 1, 1, handle);

									for (int k = 0; k < 257 - rle_count; k++) {
										bits[number_of_bytes_written++] += byte;
									}
								}
							}
						} else {
							// don't use compression

							io->read_proc(bits, line, 1, handle);
						}
					}

					return dib;
				} else {
					// INTERLACED (ILBM)

					unsigned pixel_size = depth/8;
					unsigned n_width=(width+15)&~15;
					unsigned plane_size = n_width/8;
					unsigned src_size = plane_size * planes;
					BYTE *src = (BYTE*)malloc(src_size);
					BYTE *dest = FreeImage_GetBits(dib);

					dest += FreeImage_GetPitch(dib) * height;

					for (unsigned y = 0; y < height; y++) {
						dest -= FreeImage_GetPitch(dib);

						// read all planes in one hit,
						// 'coz PSP compresses across planes...

						if (comp) {
							// unpacker algorithm

							for(unsigned x = 0; x < src_size;) {
								// read the next source byte into t
								signed char t = 0;
								io->read_proc(&t, 1, 1, handle);
								
								if (t >= 0) {
									// t = [0..127] => copy the next t+1 bytes literally
									unsigned size_to_read = t + 1;

									if((size_to_read + x) > src_size) {
										// sanity check for buffer overruns 
										size_to_read = src_size - x;
										io->read_proc(src + x, size_to_read, 1, handle);
										x += (t + 1);
									} else {
										io->read_proc(src + x, size_to_read, 1, handle);
										x += size_to_read;
									}
								} else if (t != -128) {
									// t = [-1..-127]  => replicate the next byte -t+1 times
									BYTE b = 0;
									io->read_proc(&b, 1, 1, handle);
									unsigned size_to_copy = (unsigned)(-(int)t + 1);

									if((size_to_copy + x) > src_size) {
										// sanity check for buffer overruns 
										size_to_copy = src_size - x;
										memset(src + x, b, size_to_copy);
										x += (unsigned)(-(int)t + 1);
									} else {
										memset(src + x, b, size_to_copy);
										x += size_to_copy;
									}
								}
								// t = -128 => noop
							}
						} else {
							io->read_proc(src, src_size, 1, handle);
						}

						// lazy planar->chunky...

						for (unsigned x = 0; x < width; x++) {
							for (unsigned n = 0; n < planes; n++) {
								BYTE bit = (BYTE)( src[n * plane_size + (x / 8)] >> ((x^7) & 7) );

								dest[x * pixel_size + (n / 8)] |= (bit & 1) << (n & 7);
							}
						}

#ifndef FREEIMAGE_BIGENDIAN
						if (depth == 24) {
							for (unsigned x = 0; x < width; ++x){
								INPLACESWAP(dest[x * 3], dest[x * 3 + 2]);
							}
						}
#endif
					}

					free(src);

					return dib;
				}
			}

			// Every odd-length chunk is followed by a 0 pad byte.  This pad
			//  byte is not counted in ch_size.
			if (ch_size & 1) {
				ch_size++;
				ch_end++;
			}

			io->seek_proc(handle, ch_end - io->tell_proc(handle), SEEK_CUR);

			size -= ch_size + 8;
		}

		if (dib)
			FreeImage_Unload(dib);
	}