Click here to Skip to main content
13,252,195 members (63,989 online)
Rate this:
Please Sign up or sign in to vote.
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 29-Jul-12 4:11am
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?
ki19880210 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?
ki19880210 29-Jul-12 17:54pm
I just want to save in to new file with a part of image,Not trouble with using gimp.
Mohibur Rashid 30-Jul-12 2:09am
More question, do you want to cut or just you want copy part of file and save it? I can give you php solution, otherwise you will have type format.

You can also look at OpenCV it might have some solution.
sum_and_sum 30-Jul-12 18:15pm
Thank you for much,But I don't know any knowledge of php,So I probable can't
understand you code.

1 solution

Rate this: bad
Please Sign up or sign in to vote.

Solution 1

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.

#ifdef _WIN32
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;
# include <stdint.h>
#ifndef BI_RGB
# define BI_RGB 0
# define BI_BITFIELDS 3
#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:
#pragma pack(push, 1)
typedef struct tagBITMAPFILEHEADER {
	uint16_t	bfType;
	uint32_t	bfSize;
	uint16_t	bfReserved1;
	uint16_t	bfReserved2;
	uint32_t	bfOffBits;
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;
#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;
#define GET_INT16_LE(var) \
#define SET_INT16_LE(var, value) \
	var = value
#define GET_INT32_LE(var) \
#define SET_INT32_LE(var, value) \
	var = value
#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))
// 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 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;
	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
	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))
		return 0;
	data = (char*)malloc(filesize);
	if (!data)
		return 0;
	bytes_read = fread(data, 1, (size_t)filesize, f);
	if (bytes_read != (size_t)filesize)
		return 0;
	new_data = NULL;
	new_size = 0;
	success = CropBmpData(data, filesize, crop_x0, crop_y0, crop_x1, crop_y1, &new_data, &new_size);
	if (success)
		f = fopen(output_file, "wb");
		if (f)
			bytes_written = fwrite(new_data, 1, (size_t)new_size, f);
			success = bytes_written == (size_t)new_size;
			success = 0;
	if (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;
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)

  Print Answers RSS
Top Experts
Last 24hrsThis month

Advertise | Privacy |
Web02 | 2.8.171114.1 | Last Updated 29 Jul 2012
Copyright © CodeProject, 1999-2017
All Rights Reserved. Terms of Service
Layout: fixed | fluid

CodeProject, 503-250 Ferrand Drive Toronto Ontario, M3C 3G8 Canada +1 416-849-8900 x 100