Click here to Skip to main content
15,896,912 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I know how to copy a picture ,but I don't know how to cut a part of a picture like gimp? Of couse I know the type of image is differnet.So i just know how to handle a type of .bmp.
Posted
Comments
pasztorpisti 29-Jul-12 9:37am    
It can be a bit complicated with bit twiddling if you want to handle bit depths below 8bit/pixel. Otherwise its no big deal. Are you OK with just 8, 16, 24, 32 bits?
sum_and_sum 29-Jul-12 18:00pm    
OK, Thank you very much! I need the library. I'm begginer in computer and English, I am sorry I can't express myself with accurate word.
pasztorpisti 29-Jul-12 9:44am    
If you need a library for that I can't help, but if all you need is platform/library dependent sample code I can write it for you for 8, 16, 24, 32 bit bmps. Just dont want to bother with 1, and 4 bits because I am lazy. :P
pasztorpisti 29-Jul-12 13:15pm    
BTW, why C and not C++?
Mohibur Rashid 29-Jul-12 13:25pm    
The question does not make sense. in tag you wrote c, in the description you talked about gimp. what exactly do you want? do you want to remove a part of any picture and save in to new file? or you are having trouble with using gimp?

1 solution

Here you go! (crop_x0, crop_y0) is the topleft coordinate and (crop_x1, crop_y1) is the bottomright coordinate of the cut on the original picture. Note that the row addressed by crop_y1 and the column addressed by crop_x1 aren't included in the resulting picture.
Works with 8, 16, 24, and 32 bit bmp images without RLE compression. Handles original V4 and V5 headers as well. You might want to comment out the LITTLE_ENDIAN define if your processor is big endian.

C++
#define LITTLE_ENDIAN

#ifdef _WIN32
# define _CRT_SECURE_NO_WARNINGS
typedef __int8 int8_t;
typedef __int16 int16_t;
typedef __int32 int32_t;
typedef __int64 int64_t;
typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int64 uint64_t;
#else
# include <stdint.h>
#endif

#ifndef BI_RGB
# define BI_RGB 0
#endif
#ifndef BI_BITFIELDS
# define BI_BITFIELDS 3
#endif

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>


// Setting struct alignment to 1 byte, you might have to use something else on linux to do this...
// Newer gcc version support pragma pack. see: http://gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html
#pragma pack(push, 1)
typedef struct tagBITMAPFILEHEADER {
	uint16_t	bfType;
	uint32_t	bfSize;
	uint16_t	bfReserved1;
	uint16_t	bfReserved2;
	uint32_t	bfOffBits;
} BITMAPFILEHEADER;

typedef struct tagBITMAPINFOHEADER{
	uint32_t	biSize;
	int32_t		biWidth;
	int32_t		biHeight;
	uint16_t	biPlanes;
	uint16_t	biBitCount;
	uint32_t	biCompression;
	uint32_t	biSizeImage;
	int32_t		biXPelsPerMeter;
	int32_t		biYPelsPerMeter;
	uint32_t	biClrUsed;
	uint32_t	biClrImportant;
} BITMAPINFOHEADER;
#pragma pack(pop)


int Max(int a, int b)
{
	return a>b ? a : b;
}
int Min(int a, int b)
{
	return a<b ? a : b;
}

#ifdef LITTLE_ENDIAN

#define GET_INT16_LE(var) \
	var
#define SET_INT16_LE(var, value) \
	var = value
#define GET_INT32_LE(var) \
	var
#define SET_INT32_LE(var, value) \
	var = value

#else

#define GET_INT16_LE(var) \
	((var >> 8) & 0xFF) | ((var << 8) & 0xFF00)
#define SET_INT16_LE(var, value) \
	var = ((value >> 8) & 0xFF) | ((value << 8) & 0xFF00)
#define GET_INT32_LE(var) \
	(((var >> 24) & 0xFF) | ((var >> 8) & 0xFF00) | ((var << 8) & 0xFF0000) | ((var << 24) & 0xFF000000))
#define SET_INT32_LE(var, value) \
	var = (((value >> 24) & 0xFF) | ((value >> 8) & 0xFF00) | ((value << 8) & 0xFF0000) | ((value << 24) & 0xFF000000))

#endif

// returns zero on error
int CropBmpData(const char* data, int data_size, int crop_x0, int crop_y0, int crop_x1, int crop_y1, char** out_data, int* out_size)
{
	const BITMAPFILEHEADER* header = (BITMAPFILEHEADER*)data;
	const BITMAPINFOHEADER* bmih = (BITMAPINFOHEADER*)(header + 1);
	int width, height, top_down, bpp, stride, bytes_per_pixel, bytes_to_copy_per_row;
	int width2, height2, stride2, head_size;
	int y, padding_bytes;
	BITMAPFILEHEADER* header2;
	BITMAPINFOHEADER* bmih2;
	const char* src;
	char* dest;
	uint16_t magic = GET_INT16_LE(header->bfType);

	if (magic != 0x4D42)
		return 0;
	// We use the fact that the first fields we use are the same in
	// BITMAPINFOHEADER and BITMAPV4HEADER and BITMAPV5HEADER.
	if (GET_INT32_LE(bmih->biSize)<sizeof(BITMAPINFOHEADER))
		return 0;

	// bits per pixel
	bpp = GET_INT16_LE(bmih->biBitCount);
	if (bpp != 8 && bpp != 16 && bpp != 24 && bpp != 32)
		return 0;

	// We don't handle RLE compressed BMP files.
	if (GET_INT32_LE(bmih->biCompression) != BI_RGB)
	{
		// We allow BI_BITFIELDS only for 16 bit bmp files.
		if (bpp != 16 || GET_INT32_LE(bmih->biCompression) != BI_BITFIELDS)
			return 0;
	}
	// Never seen a bmp with other value here...
	if (GET_INT16_LE(bmih->biPlanes) != 1)
		return 0;

	width = GET_INT32_LE(bmih->biWidth);
	height = GET_INT32_LE(bmih->biHeight);

	if (width <= 0 || height == 0)
		return 0;

	top_down = height < 0;
	if (top_down)
		height = -height;

	crop_x0 = Max(crop_x0, 0);
	crop_y0 = Max(crop_y0, 0);
	crop_x1 = Min(crop_x1, width);
	crop_y1 = Min(crop_y1, height);

	// If the resulting picture has zero width or height its a fatal error
	// because zero is not a valid size in .bmp files!
	if (crop_x0 >= crop_x1)
		return 0;
	if (crop_y0 >= crop_y1)
		return 0;

	// stride (data bytes per row of pixels) is always a multiple of 4 in .bmp files.
	stride = ((bpp * width + 31) / 8) & ~3;

	head_size = GET_INT32_LE(header->bfOffBits);

	// According to the bitmap header and bmp dimensions the file size should be bigger...
	if (data_size < head_size+stride*height)
		return 0;

	src = data + head_size;
	if (!top_down)
	{
		src += (height-1) * stride;
		stride = -stride;
	}
	bytes_per_pixel = bpp / 8;
	src += crop_x0 * bytes_per_pixel + crop_y0 * stride;

	width2 = crop_x1 - crop_x0;
	height2 = crop_y1 - crop_y0;
	stride2 = ((bpp * width2 + 31) / 8) & ~3;

	*out_size = head_size + stride2*height2;
	*out_data = (char*)malloc(*out_size);
	if (!*out_data)
		return 0;
	memcpy(*out_data, data, head_size);
	header2 = (BITMAPFILEHEADER*)*out_data;
	bmih2 = (BITMAPINFOHEADER*)(header2+1);
	SET_INT32_LE(bmih2->biWidth, width2);
	SET_INT32_LE(bmih2->biHeight, top_down ? -height2 : height2);
	SET_INT32_LE(bmih2->biSizeImage, 0);

	dest = *out_data + head_size;
	if (!top_down)
	{
		dest += (height2-1) * stride2;
		stride2 = -stride2;
	}

	bytes_to_copy_per_row = width2 * bytes_per_pixel;
	padding_bytes = stride2 - bytes_to_copy_per_row;

	for (y=0; y<height2; y++)
	{
		memcpy(dest, src, bytes_to_copy_per_row);
		if (padding_bytes > 0)
			memset(dest+bytes_to_copy_per_row, 0, padding_bytes);
		src += stride;
		dest += stride2;
	}

	return 1;
}

// returns zero on error
int CropBmpFile(const char* input_file, const char* output_file, int crop_x0, int crop_y0, int crop_x1, int crop_y1)
{
	FILE* f;
	long filesize;
	char* data;
	size_t bytes_read, bytes_written;
	char* new_data;
	int new_size;
	int success;

	f = fopen(input_file, "rb");
	if (!f)
		return 0;
	fseek(f, 0, SEEK_END);
	filesize = ftell(f);
	if (filesize <= sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER))
	{
		fclose(f);
		return 0;
	}

	data = (char*)malloc(filesize);
	if (!data)
	{
		fclose(f);
		return 0;
	}

	rewind(f);
	bytes_read = fread(data, 1, (size_t)filesize, f);
	fclose(f);
	if (bytes_read != (size_t)filesize)
	{
		free(data);
		return 0;
	}

	new_data = NULL;
	new_size = 0;
	success = CropBmpData(data, filesize, crop_x0, crop_y0, crop_x1, crop_y1, &new_data, &new_size);
	free(data);
	if (success)
	{
		f = fopen(output_file, "wb");
		if (f)
		{
			bytes_written = fwrite(new_data, 1, (size_t)new_size, f);
			fclose(f);
			success = bytes_written == (size_t)new_size;
		}
		else
		{
			success = 0;
		}
	}

	if (new_data)
		free(new_data);
	return success;
}


int main(int argc, char* argv[])
{
	printf("result: %d\n", CropBmpFile("e:\\w-mode-1_03.bmp", "e:\\w-mode-1_03_cropped.bmp", 5, 10, 50, 20));
	printf("result: %d\n", CropBmpFile("e:\\w-mode-1_03_8.bmp", "e:\\w-mode-1_03_8_cropped.bmp", 5, 10, 50, 20));
	printf("result: %d\n", CropBmpFile("e:\\w-mode-1_03_16.bmp", "e:\\w-mode-1_03_16_cropped.bmp", 5, 10, 50, 20));
	return 0;
}
 
Share this answer
 
v4
Comments
enhzflep 30-Jul-12 0:40am    
Brilliant effort and excellent answer. Following the comments, this is clearly a solution crafted in response to the answer - i.e, not just old code that's been hacked together. If I could vote more than 5, I would! Extra credit for ability to handle big and little endian machines.
pasztorpisti 30-Jul-12 5:35am    
Thank you! This is not old, but new code hacked togehter. :-) Coded C ten years ago last time. It was particularly annoying to use int instead of bool, and to declare all variables at the beginning of the function body. Not to mention file and memory allocator classes on the stack that would close my file/free my memory regardless of the reason for the return of my function. HUH, so coding this was an old nightmare reborn. :-) I hope I haven't put in too many bugs and leaks...

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900