Create simple bitmap in C (without external libraries) - c

For learning purposes, I want to create a single 2x2 bitmap image using C programming language with no external libraries (such as SDL).
I've read that I need to create a header for the bitmap file but failed to understand how to implement it in code and why does the bmp header have those parameters. I can't find any clear tutorial on how I could do it using C.
Could you provide me some guidance on how to implement this by a simple documented example? I'm using Linux.

why does the bmp header have those parameters
The pixel data in bitmap is essentially a one-dimensional array of bytes. It's impossible to interpret this data without header information which reveals width, height, bit-count and other information about the bitmap.
There are different types of bitmaps, including different palette format and non-palette 16, 24, or 32 bits, and different types within some of these groups.
The header also includes other information which are there for historical reasons. This has little value for learning C. You just have to accept it.
The header information is in two structures BITMAPFILEHEADER and BITMAPINFO It's a little more complicated because the structures are expected to be packed.
The pixel data depends on the bitmap format. There is not a single bitmap format as mentioned earlier. The example below shows a 24-bit bitmap.
Other oddities is that the width of each row should always be multiple of 4 bytes. This called padding. Also the bitmap rows start at the bottom, not at the top. This is explained in other resources
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
int main(void)
{
//width, height, and bitcount are the key factors:
int32_t width = 2;
int32_t height = 2;
uint16_t bitcount = 24;//<- 24-bit bitmap
//take padding in to account
int width_in_bytes = ((width * bitcount + 31) / 32) * 4;
//total image size in bytes, not including header
uint32_t imagesize = width_in_bytes * height;
//this value is always 40, it's the sizeof(BITMAPINFOHEADER)
const uint32_t biSize = 40;
//bitmap bits start after headerfile,
//this is sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)
const uint32_t bfOffBits = 54;
//total file size:
uint32_t filesize = 54 + imagesize;
//number of planes is usually 1
const uint16_t biPlanes = 1;
//create header:
//copy to buffer instead of BITMAPFILEHEADER and BITMAPINFOHEADER
//to avoid problems with structure packing
unsigned char header[54] = { 0 };
memcpy(header, "BM", 2);
memcpy(header + 2 , &filesize, 4);
memcpy(header + 10, &bfOffBits, 4);
memcpy(header + 14, &biSize, 4);
memcpy(header + 18, &width, 4);
memcpy(header + 22, &height, 4);
memcpy(header + 26, &biPlanes, 2);
memcpy(header + 28, &bitcount, 2);
memcpy(header + 34, &imagesize, 4);
//prepare pixel data:
unsigned char* buf = malloc(imagesize);
for(int row = height - 1; row >= 0; row--)
{
for(int col = 0; col < width; col++)
{
buf[row * width_in_bytes + col * 3 + 0] = 255;//blue
buf[row * width_in_bytes + col * 3 + 1] = 0;//green
buf[row * width_in_bytes + col * 3 + 2] = 0;//red
}
}
FILE *fout = fopen("test.bmp", "wb");
fwrite(header, 1, 54, fout);
fwrite((char*)buf, 1, imagesize, fout);
fclose(fout);
free(buf);
return 0;
}
You can rewrite this code using structures. In this code structure is packed with #pragma pack(push, 1) which is compiler dependent. Use this version if #pragma directive works.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#pragma pack(push, 1)
struct my_BITMAPFILEHEADER {
uint16_t bfType;
uint32_t bfSize;
uint16_t bfReserved1;
uint16_t bfReserved2;
uint32_t bfOffBits;
};
struct my_BITMAPINFOHEADER {
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 main(void)
{
if(sizeof(struct my_BITMAPFILEHEADER) != 14 &&
sizeof(struct my_BITMAPINFOHEADER) != 40)
{
printf("bitmap structures not packed properly\n");
return 0;
}
//only width and height can be changed in this code:
int width = 2;
int height = 2;
int bitcount = 24;//<- 24-bit bitmap
int width_in_bytes = ((width * bitcount + 31) / 32) * 4; //for padding
uint32_t imagesize = width_in_bytes * height; //total image size
struct my_BITMAPFILEHEADER filehdr = { 0 };
struct my_BITMAPINFOHEADER infohdr = { 0 };
memcpy(&filehdr, "BM", 2);//bitmap signature
filehdr.bfSize = 54 + imagesize;//total file size
filehdr.bfOffBits = 54; //sizeof(filehdr) + sizeof(infohdr)
infohdr.biSize = 40; //sizeof(infohdr)
infohdr.biPlanes = 1; //number of planes is usually 1
infohdr.biWidth = width;
infohdr.biHeight = height;
infohdr.biBitCount = bitcount;
infohdr.biSizeImage = imagesize;
//prepare pixel data:
unsigned char* buf = malloc(imagesize);
for(int row = height - 1; row >= 0; row--)
{
for(int col = 0; col < width; col++)
{
buf[row * width_in_bytes + col * 3 + 0] = 255;//blue
buf[row * width_in_bytes + col * 3 + 1] = 0;//red
buf[row * width_in_bytes + col * 3 + 2] = 0;//green
}
}
FILE *fout = fopen("test.bmp", "wb");
fwrite(&filehdr, sizeof(filehdr), 1, fout);
fwrite(&infohdr, sizeof(infohdr), 1, fout);
fwrite((char*)buf, 1, imagesize, fout);
fclose(fout);
free(buf);
return 0;
}
The first version used unsigned char header[54] to achieve the same thing. See the size of each data and its relative position in memory. The structure is 14 bytes long. bfType starts at 0, and is 2 bytes long (16 bits). bfSize starts at position 2, and is 4 bytes long...
struct my_BITMAPFILEHEADER {
uint16_t bfType; //starts at 0, 2 bytes long
uint32_t bfSize; //starts at 2, 4 bytes long
uint16_t bfReserved1; //starts at 6, 2 bytes long
uint16_t bfReserved2; //starts at 8, 2 bytes long
uint32_t bfOffBits; //starts at 10, 4 bytes long
};
Then you have the next structure which is 40 bytes long:
struct my_BITMAPINFOHEADER {
uint32_t biSize;//starts at 14
int32_t biWidth;//starts at 18
int32_t biHeight;//starts at 22
uint16_t biPlanes;//starts at 26
uint16_t biBitCount;//starts at 28
uint32_t biCompression;//starts at 30
uint32_t biSizeImage;//starts at 34
int32_t biXPelsPerMeter;
int32_t biYPelsPerMeter;
uint32_t biClrUsed;
uint32_t biClrImportant;
};
biSize starts at 0. But the previous structure was 14 bytes. So biSzie starts at 14. This should reveal the mystery of the numbers used in previous version to copy integers to header

#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
#include <stdint.h>
long asciiToBinary(int n);
long asciiToBinary(int n) {
int remainder;
long binary = 0, i = 1;
while(n != 0) {
remainder = n%2;
n = n/2;
binary= binary + (remainder*i);
i = i*10;
}
return binary;
}
typedef struct
{ //BITMAPFILEHEADER
char bfType[2];
int bfSize;
short bfReserved1;
short bfReserved2;
int bfOffBits;
//BITMAPINFOHEADER
int biSize;
int biWidth;
int biHeight;
short biPlanes;
short biBitCount;
int biCompression;
int biSizeImage;
int biXPelsPerMeter;
int biYPelsPerMeter;
int biClrUsed;
int biClrImportant;
} BITMAPINFOHEADER;
typedef struct {
unsigned char blue;
unsigned char green;
unsigned char red;
} PIXEL;
int imageInfo(BITMAPINFOHEADER *img)
{
int offset = 0;
printf("------------------------------------------------------------- BITMAPFILEHEADER\n");
printf("0x%08X (%08d) bfType %8c%c (0x%04X)\n", offset, offset, img->bfType[0], img->bfType[1], *(short *)img->bfType);
offset += sizeof img->bfType;
printf("0x%08X (%08d) bfSize %9d (0x%08X)\n", offset, offset, img->bfSize, img->bfSize);
offset += sizeof img->bfSize;
printf("0x%08X (%08d) bfReserved1 %9hd (0x%04hX)\n", offset, offset, img->bfReserved1, img->bfReserved1);
offset += sizeof img->bfReserved1;
printf("0x%08X (%08d) bfReserved2 %9hd (0x%04hX)\n", offset, offset, img->bfReserved2, img->bfReserved2);
offset += sizeof img->bfReserved2;
printf("0x%08X (%08d) bfOffBits %9d (0x%08X)\n", offset, offset, img->bfOffBits, img->bfOffBits);
printf("------------------------------------------------------------- BITMAPINFOHEADER\n");
offset += sizeof img->bfOffBits;
printf("0x%08X (%08d) biSize %9d (0x%08X)\n", offset, offset, img->biSize, img->biSize);
offset += sizeof img->biSize;
printf("0x%08X (%08d) biWidth %9d (0x%08X)\n", offset, offset, img->biWidth, img->biWidth);
offset += sizeof img->biWidth;
printf("0x%08X (%08d) biHeight %9d (0x%08X)\n", offset, offset, img->biHeight, img->biHeight);
offset += sizeof img->biHeight;
printf("0x%08X (%08d) biPlanes %9d (0x%04hX)\n", offset, offset, img->biPlanes, img->biPlanes);
offset += sizeof img->biPlanes;
printf("0x%08X (%08d) biBitCount %9d (0x%04hX)\n", offset, offset, img->biBitCount, img->biBitCount);
offset += sizeof img->biBitCount;
printf("0x%08X (%08d) biCompression %9d (0x%08X)\n", offset, offset, img->biCompression, img->biCompression);
offset += sizeof img->biCompression;
printf("0x%08X (%08d) biSizeImage %9d (0x%08X)\n", offset, offset, img->biSizeImage, img->biSizeImage);
offset += sizeof img->biSizeImage;
printf("0x%08X (%08d) biXPelsPerMeter %9d (0x%08X)\n", offset, offset, img->biXPelsPerMeter, img->biXPelsPerMeter);
offset += sizeof img->biXPelsPerMeter;
printf("0x%08X (%08d) biYPelsPerMeter %9d (0x%08X)\n", offset, offset, img->biYPelsPerMeter, img->biYPelsPerMeter);
offset += sizeof img->biYPelsPerMeter;
printf("0x%08X (%08d) biClrUsed %9d (0x%08X)\n", offset, offset, img->biClrUsed, img->biClrUsed);
offset += sizeof img->biClrUsed;
printf("0x%08X (%08d) biClrImportant %9d (0x%08X)\n", offset, offset, img->biClrImportant, img->biClrImportant);
return offset;
}
int main(){
uint32_t RGB565ColorTable[] = {
0x7E00000, 0xF8000000, 0x001F0000, 0
};
uint16_t myData[64];
for(int i = 0; i<64; i++)
{
myData[i] = 0x241B; //Random Color
}
FILE *image;
const char filename[] = "full.bmp";
FILE *fichier = fopen(filename, "rb");
FILE *fptr = fopen("tth.bmp","w+");
if(!fichier)
printf ("Erreur a l'ouverture du fichier [%s]\n", filename);
else
{
BITMAPINFOHEADER *img = malloc(sizeof *img);
char fpath[1000],mydata[100];
BITMAPINFOHEADER bih;
int i=0,b[8],g[8],r[8];
double asciiTobinary;
image=fopen("full.bmp","rb");
fseek(image,2,SEEK_SET); //reading the height and width
fread(&bih.biSize,4,1,image);
printf("\n \n Size of the image=%d\n",bih.biSize);
fseek(image,18,SEEK_SET);
fread(&bih.biWidth,4,1,image);
fseek(image,22,SEEK_SET);
fread(&bih.biHeight,4,1,image);
printf("\n \n Width of the image =%d \n Height of the image =%d \n pixel = b | g | r \n \n",bih.biWidth,bih.biHeight);
PIXEL pic[bih.biWidth*bih.biHeight*2],p;
while(!feof(image)){ //reading the pixels rgb values
fread(&p.blue,sizeof(p.blue),1,image);
fread(&p.green,sizeof(p.green),1,image);
fread(&p.red,sizeof(p.red),1,image);
pic[i]=p;
printf(" %d= %u | %u | %u ",i+54,pic[i].blue,pic[i].green,pic[i].red);
i++; }
fclose(image);
if(img)
{
int i, tmp, offset;
printf("\n[%s]\n", filename);
fread(img, sizeof(BITMAPINFOHEADER), 1, fichier);
if(img->bfType[0] == 'B' && img->bfType[1] == 'M')
{
offset = imageInfo(img);
if(img->biBitCount == 1)
{
printf("---------------------------------------------------------------------- RGBQUAD\n");
fread(&tmp, sizeof tmp, 1, fichier);
offset += sizeof img->biClrImportant;
printf("0x%08X (%08d) %9d (0x%08X)\n", offset, offset, tmp, tmp);
fread(&tmp, sizeof tmp, 1, fichier);
offset += sizeof tmp;
printf("0x%08X (%08d) %9d (0x%08X)\n", offset, offset, tmp, tmp);
printf("------------------------------------------------------------------------- BITS\n");
for(i = 0; i < img->biSizeImage / (int)sizeof tmp; i++)
{
char bin[33];
fread(&tmp, sizeof tmp, 1, fichier);
offset += sizeof tmp;
printf("0x%08X (%08d) %8d (0x%08X) (%s)\n", offset, offset, tmp, tmp, itoa(tmp, bin, 2));
}
}
}
free(img);
}
fclose(fichier);
}
if(fptr == NULL) {
printf("Error!");
exit(1);
}
fwrite(&filename, sizeof(filename), 1, fptr);
//fwrite(&myInfoHeader, sizeof(myInfoHeader), 1, fptr);
fwrite(&RGB565ColorTable, sizeof(RGB565ColorTable), 1, fptr);
fwrite(&myData, sizeof(myData), 1, fptr);
fclose(fptr);
return 0;
}

Related

Convert bitmap to bytes array C - How to get a smoother result?

I'm trying to convert 480x280 24-bit bitmap file to a C array (1-bit per pixel). Here is my code which is based on this answer:
int main(void)
{
int i, j;
int avg = 0;
unsigned int line = 0;
unsigned char byte = 0;
unsigned char *ptr;
FILE* f = fopen("test.bmp", "rb");
unsigned char info[54];
// read the 54-byte header
fread(info, sizeof(unsigned char), 54, f);
// extract image height and width from header
int width = *(int*)&info[18];
int height = *(int*)&info[22];
// allocate 3 bytes per pixel
int size = 3 * 280 * 480;
unsigned char* data = (unsigned char*)malloc(size);
// read the rest of the data at once
fread(data, sizeof(unsigned char), size, f);
fclose(f);
/* convert 24-bit to 1-bit -> each 3 bytes (r,g,b) to 1 bit (black,white) */
for(i = 0; i < size; i += 24)
{
ptr = &data[i];
/* take 24 bytes in each iteration (3 colors per pixel x 8 pixels) */
for(j = 0; j < 24; j += 3)
{
/* convert 3 bytes to 1 byte */
avg = (ptr[j] + ptr[j+1] + ptr[j+2]) / 3;
/* convert 1 byte to white or black bit */
if(avg > 128)
byte |= (1 << (j / 3)); /* black */
else
byte |= (0 << (j / 3)); /* white */
}
printf("0x%02X,", byte);
byte = 0;
avg = 0;
if(++line == 16)
{
printf("\n");
line = 0;
}
}
}
This does the job but the result array produces edgy image. Check these original and converted images:
What am I missing to get a smooth result image?
the image You Convert Is RGB888 may be your Runnning Controller accepct ARGB888.
Convert RGB Image With Alpha ARGB888.

.BMP files in C - DIB header returns image size 0 even though the BMP header returns the file size

I'm trying to flip a BMP image in C.
I ran into a problem where when I try to read the BMP header everything is alright and the values are correct, but when I try to read the DIB header I get all of the values right except for the image size (raw bitmap data). I get a zero, which is really weird considering I got the file size in the BMP header. I tried debugging the code and looking up the problem online but that didn't help much.
This is my code:
#include <stdlib.h>
typedef unsigned int int32;
typedef unsigned short int int16;
typedef unsigned char char8;
struct BITMAP_header {
char name[2]; // BM
int32 size;
int garbage; // ?
int32 image_offset; //offset from where the image starts in the file
};
struct DIB_header {
int32 header_size;
int32 width;
int32 height;
int16 colorplanes;
int16 bitsperpixel;
int32 compression;
int32 image_size;
int32 temp[4];
};
struct RGB
{
char8 blue;
char8 green;
char8 red;
};
struct Image {
struct RGB** rgb;
int height;
int width;
};
struct Image readImage(FILE *fp, int height, int width) {
struct Image pic;
pic.rgb = (struct RGB**)malloc(height * sizeof(void*)); // pointer to a row of rgb data (pixels)
pic.height = height;
pic.width = width;
for (int i = height-1; i >=0 ; i--)
{
pic.rgb[i] = (struct RGB*)malloc(width * sizeof(struct RGB)); // allocating a row of pixels
fread(pic.rgb[i], width, sizeof(struct RGB), fp);
}
return pic;
}
void freeImage(struct Image pic) {
for (int i = pic.height -1; i>= 0; i--)
{
free(pic.rgb[i]);
}
free(pic.rgb);
}
void createImage(struct BITMAP_header header, struct DIB_header dibheader, struct Image pic) {
FILE* fpw = fopen("new.bmp", "w");
if (fpw == NULL) {
return 1;
}
fwrite(header.name, 2, 1, fpw);
fwrite(&header.size, 3 * sizeof(int), 1, fpw);
fwrite(&dibheader, sizeof(struct DIB_header), 1, fpw);
int count = 0;
for (int i = pic.height - 1; i >= 0; i--) {
fwrite(pic.rgb[count], pic.width, sizeof(struct RGB), fpw);
count++;
}
fclose(fpw);
}
int openbmpfile() {
FILE* fp = fopen("C:\\Users\\User\\Downloads\\MARBLES.BMP", "rb"); // read binary
if (fp == NULL) {
return 1;
}
struct BITMAP_header header;
struct DIB_header dibheader;
fread(header.name, 2, 1, fp); //BM
fread(&header.size, 3 * sizeof(int), 1, fp); // 12 bytes
printf("First two characters: %c%c\n", header.name[0], header.name[1]);
if ((header.name[0] != 'B') || (header.name[1] != 'M')) {
fclose(fp);
return 1;
}
printf("Size: %d\n", header.size);
printf("Offset: %d\n", header.image_offset);
fread(&dibheader.header_size, sizeof(struct DIB_header) , 1, fp);
printf("Header size: %d\nWidth: %d\nHeight: %d\nColor planes: %d\nBits per pixel: %d\nCompression: %d\nImage size: %d\n",
dibheader.header_size, dibheader.width, dibheader.height, dibheader.colorplanes, dibheader.bitsperpixel,
dibheader.compression, dibheader.image_size);
if ((dibheader.header_size != 40) || (dibheader.compression != 0) || (dibheader.bitsperpixel != 24)) {
fclose(fp);
return 1;
}
fseek(fp, header.image_offset, SEEK_SET);
struct Image image = readImage(fp, dibheader.height, dibheader.width);
createImage(header, dibheader, image);
fclose(fp);
freeImage(image);
return 0;
}
int main() {
openbmpfile();
}
I also tried writing the size manually by decreasing the header size by the file size but it didn't work.
thanks in advance!
The format expect the structure size to be 14 and 40 for BITMAP_header and DIB_header. But the compiler aligns the structure and the size will be different. You have to use compiler specific flags to disable structure alignment (it's unclear which compiler you use). It's the same problem with RGB structure.
There is also endian-ness dependence, and the fact that bitmap doesn't store pixels in RGB format, it flips the bytes.
typedef unsigned int int32;
This will hide unsigned as signed. Some of the values in the structure are supposed to be signed. For example, height could be negative, indicating that it's supposed to be flipped.
You can read the integers one by one, using
void readint(FILE *f, int *val, int size)
{
unsigned char buf[4];
fread(buf, size, 1, f);
*val = 0;
for (int i = size - 1; i >= 0; i--)
*val += (buf[i] << (8 * i));
}
void writeint(FILE* f, int val, int size)
{
unsigned char buf[4];
for (int i = size - 1; i >= 0; i--)
buf[i] = (val >> 8 * i) & 0xff;
fwrite(buf, size, 1, f);
}
Then you read a 24-bit bitmap as follows:
fread(header.name, 2, 1, fp); //BM
readint(fp, &header.size, 4);
readint(fp, &header.garbage, 4);
readint(fp, &header.image_offset, 4);
readint(fp, &dibheader.header_size, 4);
readint(fp, &dibheader.width, 4);
readint(fp, &dibheader.height, 4);
readint(fp, &dibheader.colorplanes, 2);
readint(fp, &dibheader.bitsperpixel, 2);
readint(fp, &dibheader.compression, 4);
readint(fp, &dibheader.image_size, 4);
readint(fp, &dibheader.temp[0], 4);
readint(fp, &dibheader.temp[1], 4);
readint(fp, &dibheader.temp[2], 4);
readint(fp, &dibheader.temp[3], 4);
char *buf = malloc(dibheader.image_size);
if (!buf)
return 0;
fread(buf, 1, dibheader.image_size, fp);
There are other issues you have to consider, for example width alignment of bitmap, etc. You want to start with a 24-bit bitmap whose width is multiple of 4.
To write the bitmap, use the binary flag, write in the same order
FILE* fout = fopen("new.bmp", "wb");
if (fout == NULL) return 0;
fwrite(header.name, 1, 2, fout);
writeint(fout, header.size, 4);
writeint(fout, header.reserved, 4);
writeint(fout, header.image_offset, 4);
writeint(fout, info.header_size, 4);
writeint(fout, info.width, 4);
writeint(fout, info.height, 4);
writeint(fout, info.colorplanes, 2);
writeint(fout, info.bitsperpixel, 2);
writeint(fout, info.compression, 4);
writeint(fout, info.image_size, 4);
writeint(fout, info.temp[0], 4);
writeint(fout, info.temp[1], 4);
writeint(fout, info.temp[2], 4);
writeint(fout, info.temp[3], 4);
fwrite(buf, 1, info.image_size, fout);

converting 24bpp bmp colour image to 8bpp grayscale bmp image in c

I am trying to convert 24bpp BMP color image to 8bpp grayscale BMP image. I have done something but i am not getting 100% correct output.
I have converted 24bpp to 8bpp. but in result image colour table is also being considered as pixel data. I have tried setting offset byte in header but problem still persists.
#include <stdio.h>
int main()
{
FILE* fIn = fopen("lena_colored_256.bmp","rb");
FILE* fOut = fopen("lena_gray.bmp", "w+");
int i, j, y;
unsigned char byte[54];
unsigned char colorTable[1024];
if (fIn == NULL)// check if the input file has not been opened
{
printf("File does not exist.\n");
}
for (i = 0; i < 54; i++)//read the 54 byte header from fIn
{
byte[i] = getc(fIn);
}
byte[28] = (unsigned char)8; // write code to modify bitDepth to 8
byte[11] = (unsigned char)04;
//write code to add colorTable
for (i = 0; i < 256; i++)
{
colorTable[i * 4 + 0] = i;
colorTable[i * 4 + 1] = i;
colorTable[i * 4 + 2] = i;
colorTable[i * 4 + 3] = i;
}
for (i = 0; i < 54; i++)
{
putc(byte[i], fOut);
}
for (i = 0; i < 1024; i++)
{
putc(colorTable[i], fOut);
}
// extract image height, width and bitDepth from imageHeader
int *height = (int)& byte[18];
int *width = (int)& byte[22];
int *bitDepth = (int)& byte[28];
printf("bitDepth : %d\n", *bitDepth);
printf("width: %d\n", *width);
printf("height: %d\n", *height);
int size = (*height) * (*width)*3; //calculate image size
unsigned char* buffer;
buffer = (unsigned char*)malloc(sizeof(int) * size);
for (i = 0; i < size; i=i+3) //RGB to gray
{
buffer[i+2] = getc(fIn);//blue
buffer[i+1] = getc(fIn);//green
buffer[i+0] = getc(fIn);//red
y = (buffer[i+0] * 0.33) + (buffer[i+1] * 0.33) + (buffer[i+2] * 0.33);
putc(y, fOut);
}
fclose(fOut);
fclose(fIn);
return 0;
}
Colour table data is also being considered as pixel data by image.
i have checked my colour table data entering into BMP file. I have printed out file pointer location, after entering at 94th byte it is increasing by 2 byte instead of 1 byte, this is happening total 4 times and other 1020 time file pointer is increasing by 1 byte. Any explanation regarding this?
Changing 24-bit to 8-bit bitmap is not as simple as changing the bitcount in header file. 24-bit bitmap doesn't have a color table. You have to build a color table for the 8-bit. Fortunately this is relatively easy for gray scale images.
Several values in bitmap header file need to be modified.
Then change the 24-bit to gray scale, and change to 8-bit bitmap. See bitmap file format for additional information. Also read about "padding" where the width of bitmap in bytes should always be a multiple of 4.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#pragma pack(push, 2)
typedef struct {
int16_t bfType;
int32_t bfSize;
int16_t bfReserved1;
int16_t bfReserved2;
int32_t bfOffBits;
} BITMAPFILEHEADER;
typedef struct {
int32_t biSize;
int32_t biWidth;
int32_t biHeight;
int16_t biPlanes;
int16_t biBitCount;
int32_t biCompression;
int32_t biSizeImage;
int32_t biXPelsPerMeter;
int32_t biYPelsPerMeter;
int32_t biClrUsed;
int32_t biClrImportant;
} BITMAPINFOHEADER;
#pragma pack(pop)
int main()
{
FILE* fin = fopen("fin.bmp", "rb");
FILE* fout = fopen("fout.bmp", "wb");
if(!fin) { printf("fin error\n"); goto error; }
if(!fout) { printf("fout error\n"); goto error; }
BITMAPFILEHEADER bf;
BITMAPINFOHEADER bi;
fread(&bf, sizeof bf, 1, fin);
fread(&bi, sizeof bi, 1, fin);
if(sizeof bf != 14) { printf("Wrong pragma pack\n"); goto error; }
if(sizeof bi != 40) { printf("Wrong pragma pack\n"); goto error; }
if(bf.bfType != 0x4D42) { printf("not bmp, or not LE system\n"); goto error; }
if(bi.biSize != 40) { printf("Can't handle this bitmap format\n"); goto error; }
if(bi.biBitCount != 24) { printf("not 24-bit\n"); goto error; }
int height = bi.biHeight;
if(height < 0) height = -height;
//width in bytes:
int src_wb = ((bi.biWidth * 24 + 31) / 32) * 4;
int dst_wb = ((bi.biWidth * 8 + 31) / 32) * 4;
int src_size = src_wb * height;
int dst_size = dst_wb * height;
//allocate for source and destination
uint8_t *src = malloc(src_size);
uint8_t *dst = malloc(dst_size);
//read pixels
fread(src, 1, src_size, fin);
//make gray scale color-table
uint8_t clr[1024] = { 0 };
for(int i = 0; i < 256; i++)
clr[i * 4 + 0] = clr[i * 4 + 1] = clr[i * 4 + 2] = (uint8_t)i;
for(int y = height - 1; y >= 0; y--)
{
for(int x = 0; x < bi.biWidth; x++)
{
uint8_t blu = src[y * src_wb + x * 3 + 0];
uint8_t grn = src[y * src_wb + x * 3 + 1];
uint8_t red = src[y * src_wb + x * 3 + 2];
uint8_t gry = (uint8_t)(.33 * red + .34 * grn + .33 * blu);
dst[y * dst_wb + x] = gry; //this will match the index in color-table
}
}
//modify bitmap headers
bf.bfSize = 54 + 1024 + dst_size;
bi.biBitCount = 8;
bi.biSizeImage = dst_size;
fwrite(&bf, sizeof bf, 1, fout);
fwrite(&bi, sizeof bi, 1, fout);
fwrite(clr, 1, 1024, fout);
fwrite(dst, 1, dst_size, fout);
free(src);
free(dst);
error:
fclose(fout);
fclose(fin);
return 0;
}
FILE* fOut = fopen("lena_gray.bmp", "w+");//ERROR
FILE* fOut = fopen("lena_gray.bmp", "wb");//TRUE

Navigation/Scrolling in between the bmp Image in C

I have able to read Bmp Header of Image as per below properties.
Image Bit-depth : 1
Image Attributes : 1
I want to scroll/Navigate in between Black and White Image (Bit Depth 1). Without Closing and reopening it (bmp image).
1) Like if input == f or F , Pixel Data goes one by one Forward Direction
Y0,Y1,Y2,Y3,Y4,....Yn
2) Like if input == r or R, Pixel Data goes one by one Backward Direction
Y123,Y122,Y121,Y120,Y119, ......Y0
3) Also I don't want to read pixels from starting (On Every Read) .
I want to read in between (Vertical Only).
Below is my code .
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct BitMap {
short Type;
long Size;
short Reserve1;
short Reserve2;
long OffBits;
long biSize;
long biWidth;
long biHeight;
short biPlanes;
short biBitCount;
long biCompression;
long biSizeImage;
long biXPelsPerMeter;
long biYPelsPerMeter;
long biClrUsed;
long biClrImportant; } Header;
unsigned char DummyValueRead ; unsigned char pixelValue =0 ;
int Horizontal= 0 ,Vertical = 0 ;
long myPalette[2]; long HeaderBytes;
unsigned char bPadding ; long wBytesPerRow ; unsigned char
bAdditionalBitsPerRow ; FILE *BMPFile; void SendPixelValues();
unsigned char input ;
int main( void ) {
BMPFile = fopen ("mul.bmp", "r");
if (BMPFile == NULL)
{
printf("Image Not Found !!!!!!");
return -1;
}
fread(&Header.Type, sizeof(Header.Type), 1, BMPFile);
fread(&Header.Size, sizeof(Header.Size), 1, BMPFile);
fread(&Header.Reserve1, sizeof(Header.Reserve1) , 1, BMPFile);
fread(&Header.Reserve2, sizeof(Header.Reserve2) , 1, BMPFile);
fread(&Header.OffBits, sizeof(Header.OffBits), 1, BMPFile);
fread(&Header.biSize, sizeof(Header.biSize), 1, BMPFile);
fread(&Header.biWidth, sizeof(Header.biWidth), 1, BMPFile);
fread(&Header.biHeight, sizeof(Header.biHeight) , 1, BMPFile);
fread(&Header.biPlanes, sizeof(Header.biClrUsed), 1, BMPFile);
fread(&Header.biBitCount, sizeof(Header.biBitCount), 1, BMPFile);
fread(&Header.biCompression, sizeof(Header.biCompression), 1, BMPFile);
fread(&Header.biSizeImage, sizeof(Header.biSizeImage), 1, BMPFile);
fread(&Header.biXPelsPerMeter, sizeof(Header.biXPelsPerMeter), 1, BMPFile);
fread(&Header.biYPelsPerMeter, sizeof(Header.biYPelsPerMeter), 1, BMPFile);
fread(&Header.biClrUsed, sizeof(Header.biClrUsed), 1, BMPFile);
fread(&Header.biClrImportant, sizeof(Header.biClrImportant), 1, BMPFile);
fseek(BMPFile,Header.OffBits,SEEK_SET) ;
printf("\nType:%hd and Type in %x\n", Header.Type,Header.Type);
printf("Size:%ld\n", Header.Size);
printf("Reserve1:%hd\n", Header.Reserve1);
printf("Reserve2:%hd\n", Header.Reserve2);
printf("OffBits:%ld\n", Header.OffBits);
printf("biSize:%ld\n", Header.biSize);
printf("Width: %ld\n", Header.biWidth);
printf("Height: %ld\n", Header.biHeight);
printf("biPlanes:%hd\n", Header.biPlanes);
printf("biBitCount:%hd\n", Header.biBitCount);
printf("biCompression:%ld\n", Header.biCompression);
printf("biSizeImage:%ld\n", Header.biSizeImage);
printf("biXPelsPerMeter:%ld\n", Header.biXPelsPerMeter);
printf("biYPelsPerMeter:%ld\n", Header.biYPelsPerMeter);
printf("biClrUsed:%ld\n", Header.biClrUsed);
printf("biClrImportant:%ld\n\n", Header.biClrImportant);
wBytesPerRow =Header.biWidth/8;
bAdditionalBitsPerRow = Header.biWidth % 8;
bPadding = (4 - ((wBytesPerRow + (bAdditionalBitsPerRow?1:0) ) % 4 ) ) %4;
HeaderBytes = Header.biWidth/8 ;
for(Vertical = 0 ; Vertical < Header.biHeight ; Vertical ++)
{
printf("Sr. No. %d \n",Vertical) ;
scanf("%c",&input) ;
if(input =='r' || input =='R' ) // Reverse Direction
{
// fseek(BMPFile,((4*968) + 3), SEEK_SET );
SendPixelValues() ;
}
if(input =='f' || input =='F' ) // Forward Direction
SendPixelValues() ;
printf("\n") ;
}
fclose(BMPFile);
return 0;
}
unsigned int bAdditionalBitsPerRowCount =
0,bPaddingCount=0; void SendPixelValues() {
for(Horizontal = 0 ; Horizontal < HeaderBytes ; Horizontal++)
{
fread(&pixelValue, sizeof(pixelValue), 1, BMPFile);
printf("0x%x ",pixelValue) ;
}
if(bAdditionalBitsPerRow > 0)
{
fread(&DummyValueRead , sizeof(DummyValueRead ), 1, BMPFile);
bAdditionalBitsPerRow++;
printf("bAdditionalBitsPerRowCount %d",bAdditionalBitsPerRowCount);
}
for(Horizontal = 0 ; Horizontal < bPadding; Horizontal++)
{
fread(&DummyValueRead , sizeof(DummyValueRead ), 1, BMPFile);
bPaddingCount++;
}
printf("bPaddingCount = %d",bPaddingCount) ;
}
Thanks
Karan
1-bit bitmap format has 8 bytes for its palette, you have to read those 8 bytes before reading the pixels.
There are 8 pixels packed in to one byte. You have to separate the bits and write them accordingly. Bitmap can have width padding, you have to calculate the padding for the input file as well as output file.
Make sure to read/write the file in binary (it matters in Windows).
Example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#pragma pack(push, 1)
struct BITMAPFILEHEADER {
short bfType;
int bfSize;
short bfReserved1;
short bfReserved2;
int bfOffBits;
};
struct BITMAPINFOHEADER {
int biSize;
int biWidth;
int biHeight;
short biPlanes;
short biBitCount;
int biCompression;
int biSizeImage;
int biXPelsPerMeter;
int biYPelsPerMeter;
int biClrUsed;
int biClrImportant;
};
#pragma pack(pop)
int main(void)
{
//set X and Y offset:
int x0 = 10;
int y0 = 10;
if(sizeof(short) != 2 ||
sizeof(int) != 4 ||
sizeof(struct BITMAPFILEHEADER) != 14 ||
sizeof(struct BITMAPINFOHEADER) != 40)
{
printf("Error, wrong structure size...");
return -1;
}
FILE *fin = fopen("mul.bmp", "rb");
FILE *fout = fopen("output.bmp", "wb");
if(fin == NULL)
{
printf("Image Not Found !!!!!!");
return -1;
}
struct BITMAPFILEHEADER header;
struct BITMAPINFOHEADER info;
fread(&header, sizeof(header), 1, fin);
fread(&info, sizeof(info), 1, fin);
if (info.biBitCount != 1)
{
printf("Error, not 1-bit bitmap\n");
return -1;
}
int old_width = info.biWidth;
int old_height = info.biHeight;
int bitcount = info.biBitCount;
int width_in_bytes = ((old_width * bitcount + 31) / 32) * 4;
char palette[8];
fread(palette, 1, sizeof(palette), fin);
int new_width = old_width - x0;
int new_height = old_height - y0;
//convert to 24-bit bitmap for output file
info.biBitCount = 24;
info.biWidth = new_width;
info.biHeight = new_height;
info.biSizeImage = ((info.biHeight * bitcount + 31) / 32) * info.biWidth;
header.bfSize = 54 + info.biSizeImage;
header.bfOffBits = 54;
fwrite(&header, 1, sizeof(header), fout);
fwrite(&info, 1, sizeof(info), fout);
char *bytes_read = malloc(width_in_bytes);
char black[3] = { 0 };
char white[3] = { 255,255,255 };
for(int y = 0; y < old_height; y++)
{
fread(bytes_read, 1, width_in_bytes, fin);
int width_index = 0;
for(int i = 0; i < width_in_bytes; i++)
{
//8 pixels are packed in to 1 byte
//separate the bits, write them as bytes in 24-bit format
for(int j = 0; j < 8; j++)
{
width_index = i * 8 + j;
if(width_index < x0) continue;
if(width_index - x0 >= new_width) break;
int pixel = (bytes_read[i] & (1 << (8 - j)));
if(pixel)
fwrite(white, 1, 3, fout);
else
fwrite(black, 1, 3, fout);
}
}
//add padding to output file
int m = width_index % 4;
if(m)
fwrite(black, 1, m, fout);
}
fclose(fin);
fclose(fout);
return 0;
}

Read color bytes (RGB) from a bitmap

I'm having a problem with a code that I made that should display the bytes of the RGB color values of each pixel of an image in bmp (bitmap) format.
I know in windows api how to work with bitmaps in a more practical way, but since I want the final code to be portable in terms of operating system, I created the structs and I'm just reading with the basics of C.
Here's the code:
#include <stdio.h>
#include <stdlib.h>
unsigned char *readBMP(char *filename, int *size) {
int width, height;
unsigned char *data;
unsigned char info[54];
FILE *file = fopen(filename, "rb");
if (file == NULL)
return 0;
fread(info, sizeof(unsigned char), 54, file); // read the 54-byte header
// extract image height and width from header
width = *(int *) &info[18];
height = *(int *) &info[22];
*size = 3 * width * height;
data = (unsigned char *) malloc(*size * sizeof(unsigned char)); // allocate 3 bytes per pixel
fread(data, sizeof(unsigned char), (size_t) *size, file); // read the rest of the data at once
for (int i = 0; i < *size; i += 3) {
unsigned char tmp = data[i];
data[i] = data[i + 2];
data[i + 2] = tmp;
}
fclose(file);
return data;
}
int main() {
int size = 0;
char filename[] = "output.bmp";
unsigned char *data = readBMP(filename, &size);
for (int i = 0; i < size; i++) {
printf("%d. %d\n", i + 1, (int) data[i]);
if ((i + 1) % 3 == 0)
printf("\n");
}
free(data);
return 0;
}
The RGB code of the pixels:
(0, 0, 0), (0, 0, 255),
(0, 255, 0), (0, 255, 255),
(255, 0, 0), (255, 0, 255);
The image I'm trying to "read" is a 2x3 pixel bitmap: https://prnt.sc/gnygch
And the output I have is:
1. 255
2. 0
3. 0
4. 255
5. 0
6. 255
7. 0
8. 0
9. 0
10. 255
11. 0
12. 255
13. 0
14. 0
15. 255
16. 0
17. 0
18. 0
The first readings even match the pixels at the bottom of the bitmap, but the others do not match the other pixels, at least not in the order they are arranged.
Can anyone see what I'm doing wrong?
The size of the bitmap is not width * height * 3. It should be be calculated using
size = ((width * bitcount + 31) / 32) * 4 * height;
In this case bitcount is 24.
The rows in 24-bit bitmaps have to be padded. You also need to make sure you are reading a 24-bit bitmaps. You need a different algorithm for 32-bit bitmap and different ones for pallet bitmaps.
The rows are read from bottom to top.
Below is an example for 24-bit.
You may still run in to other problems with this code. It's better to use a library for these functions. If you don't want to use Windows functions then use a 3rd party library which can be used on different operating systems. There are many such libraries out there.
int main()
{
int width, height, padding, bitcount, size;
unsigned char *data = 0;
unsigned char info[54] = { 0 };
FILE *file = fopen("output.bmp", "rb");
if(!file)
return 0;
fread(info, 1, 54, file);
width = *(int*)(info + 18);
height = *(int*)(info + 22);
bitcount = *(int*)(info + 28);
size = ((width * bitcount + 31) / 32) * 4 * height;
padding = width % 4;
if(bitcount != 24) //this code works for 24-bit bitmap only
goto error;
data = malloc(size);
fread(data, 1, size, file);
for(int row = height - 1; row >= 0; row--)
{
for(int col = 0; col < width; col++)
{
int p = (row * width + col) * 3 + row * padding;
printf("%02X%02X%02X ", data[p + 0], data[p + 1], data[p + 2]);
}
printf("\n");
}
error:
if(file) fclose(file);
if(data) free(data);
return 0;
}

Resources