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.
Related
I am trying to load a font (arial) from a .ttf file and using the STB TrueType library: https://github.com/nothings/stb/blob/master/stb_truetype.h and display it on the screen.
It is working when displaying the 1st letter in the characters array 'a', but every character after that it looks all distorted and weird, see example below
Comes out as 'a' (correct):
Should be 'b':
I think maybe I am misunderstanding/misapplying how the memory ends up being laid out, or how the file is being read?
Here is the relevant code:
(In win32 platform layer file)
struct character_bitmap
{
unsigned char* memory;
int width;
int height;
};
struct font_buffer
{
character_bitmap* memory;
float size;
};
static font_buffer font_bitmaps;
void test_load_arial(float size)
{
if (font_bitmaps.memory)
{
// VirtualFree(font_bitmaps.memory, 0, MEM_RELEASE);
free(font_bitmaps.memory);
}
unsigned char* ttf_buffer = (unsigned char*)malloc(1 << 25);
stbtt_fontinfo font;
fread(ttf_buffer, 1, 1 << 25, fopen("c:/windows/fonts/arialbd.ttf", "rb"));
stbtt_InitFont(&font, (unsigned char*)ttf_buffer, stbtt_GetFontOffsetForIndex((unsigned char*)ttf_buffer, 0));
// int char_bitmap_width = (int)size;
// int char_bitmap_height = (int)size;
// unsigned int character_memory_size = ((int)size * (int)size) * 4 /*+ 8*/;
// int font_memory_size = character_memory_size * 52;
unsigned int font_memory_size = sizeof(character_bitmap) * 52;
// font_bitmaps.memory = (character_bitmap*)VirtualAlloc(0, font_memory_size, MEM_COMMIT, PAGE_READWRITE);
font_bitmaps.memory = (character_bitmap*)malloc(font_memory_size);
char alphabet[53] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
character_bitmap* current_character = font_bitmaps.memory;
for (int i = 0; i < 52; i++)
{
// current_character->width = char_bitmap_width;
// current_character->height = char_bitmap_height;
current_character->memory = stbtt_GetCodepointBitmap(&font, 0, stbtt_ScaleForPixelHeight(&font, size), alphabet[i],
¤t_character->width, ¤t_character->height, 0, 0);
current_character++;
}
}
(In different file, inside main update and render function which gets called every frame I call this function)
void test_render_text(offscreen_buffer* buffer, font_buffer* font_bitmaps)
{
int width = font_bitmaps->memory[0].width;
int height = font_bitmaps->memory[0].height;
unsigned char* dest_row = (unsigned char*)buffer->memory
unsigned char* source = font_bitmaps->memory[42].memory;
for (int y = 0; y < height; y++)
{
unsigned int* dest_pixel = (unsigned int*)dest_row;
for (int x = 0; x < width; x++)
{
unsigned char alpha = *source;
*dest_pixel = ((alpha << 24)
| (alpha << 16)
| (alpha << 8)
| (alpha << 0));
dest_pixel++;
source++;
}
dest_row += buffer->pitch;
}
}
Also posted on github if you want the full source to review/compile:
https://github.com/jackson-lenhart/loji
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
I am learning C as a hobbyist. As a fun project I decided to code a .hgt file reader. hgt file are earth elevation files.
I found few information on this file format : https://dds.cr.usgs.gov/srtm/version2_1/Documentation/Quickstart.pdf
You can find files for the whole planet here :
http://viewfinderpanoramas.org/Coverage%20map%20viewfinderpanoramas_org3.htm
but it seems to be pretty straight forward : a list of signed two bytes integers they say.
I found that two bytes integers are well represented by "signed short" type, is that right ? In my code , you will see I used int_16t ( I tried that when having issues with signed shorts) I believe they have the same range )
Anyway, I open the file , dump the data in an array, and write it to a bmp file.
At first I thought it worked great, but that was because I was viewing the result of a very low elevation part of the earth. When I tried to render some files corresponding to areas with mountains, the image below shows the issue.
Below is my code so far.
I was pretty sure the issue is at the beginning, when reading the data, but I don't know anymore.
I would love some help.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdint.h>
#include <math.h>
int main(int argc, char *argv[]) {
if(argc < 2) {
printf("I need a hgt file path as a paramater\n");
return 0;
} else {
const int DIM = 1201;
FILE *fp;
int16_t *elevation_buffer;
elevation_buffer = malloc(sizeof(int16_t) * DIM * DIM); // 2 bytes integers
fp = fopen(argv[1], "rb");
/* Seek to the beginning of the file */
fseek(fp, 0, SEEK_SET);
/* read elevation data from HGT file */
fread(elevation_buffer, sizeof(int16_t), DIM*DIM, fp);
fclose(fp);
printf("sizeof signed short int : %d\n", sizeof(signed short int));
printf("sizeof int16_t : %d\n", sizeof(int16_t));
/* creating a bmp file to visualize elevation tile*/
int w = DIM;
int h = DIM;
int x,y,r,g,b;
FILE *f;
unsigned char *img = NULL;
int filesize = 54 + 3 * w * h; //w is your image width, h is image height, both int
img = (unsigned char *)malloc(3 * w * h);
memset(img, 0, 3 * w * h);
for (int i = 0; i < w; i++)
{
for (int j = 0; j < h; j++)
{
x = i;
y = (h - 1) - j;
float elevation = (elevation_buffer[x + y * w] - INT16_MIN) / (float)(INT16_MAX - INT16_MIN);
r = (int)(elevation * 255);
float freq = 100.0f;
if (r > 255) {
r = 255;
} else if(r < 0) {
r = 0;
}
g = r;
b = r;
img[(x + y * w) * 3 + 2] = (unsigned char)(r);
img[(x + y * w) * 3 + 1] = (unsigned char)(g);
img[(x + y * w) * 3 + 0] = (unsigned char)(b);
}
}
unsigned char bmpfileheader[14] = {'B', 'M', 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0};
unsigned char bmpinfoheader[40] = {40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 24, 0};
unsigned char bmppad[3] = {0, 0, 0};
bmpfileheader[2] = (unsigned char)(filesize);
bmpfileheader[3] = (unsigned char)(filesize >> 8);
bmpfileheader[4] = (unsigned char)(filesize >> 16);
bmpfileheader[5] = (unsigned char)(filesize >> 24);
bmpinfoheader[4] = (unsigned char)(w);
bmpinfoheader[5] = (unsigned char)(w >> 8);
bmpinfoheader[6] = (unsigned char)(w >> 16);
bmpinfoheader[7] = (unsigned char)(w >> 24);
bmpinfoheader[8] = (unsigned char)(h);
bmpinfoheader[9] = (unsigned char)(h >> 8);
bmpinfoheader[10] = (unsigned char)(h >> 16);
bmpinfoheader[11] = (unsigned char)(h >> 24);
f = fopen("img.bmp", "wb");
fwrite(bmpfileheader, 1, 14, f);
fwrite(bmpinfoheader, 1, 40, f);
for (int i = 0; i < h; i++)
{
fwrite(img + (w * (h - i - 1) * 3), 3, w, f);
fwrite(bmppad, 1, (4 - (w * 3) % 4) % 4, f);
}
free(img);
free(elevation_buffer);
fclose(f);
return 0;
}
}
You should account for the byte order... your input file format is specified to contain bytes in big endian byte order.
As a simple fix, you could just check your byteorder and reverse the read in data as necessary. The answer https://stackoverflow.com/a/8571139/5265292 explains a way how to detect byte order on your system.
// ...
fread(elevation_buffer, sizeof(int16_t), DIM*DIM, fp);
fclose(fp);
int byteOrderCheck = 1;
if (*(char *)&byteOrderCheck == 1)
{
// need to revert byte order for little endian
for (int i = 0; i < DIM*DIM; ++i)
{
elevation_buffer[i] = (int16_t)reverse_byte_order((uint16_t)elevation_buffer[i]);
}
}
// ...
with function reverse_byte_order as
uint16_t reverse_byte_order(uint16_t num)
{
return ((num & 0xff) << 8) | (num >> 8);
}
Note this is untested, you may need to change some details.
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;
}
#include < stdio.h >
#include < conio.h >
#include < stdlib.h >
#include < process.h >
#include < string.h >
#include < math.h >
int count = 0;
typedef struct bitmap24 {
unsigned char header[54];
unsigned char * pixels;
}BMP;
void readBMP(char * filename) {
int i;
FILE * f = fopen(filename, "rb");
FILE * f1 = fopen("save.bmp", "wb");
FILE * pixelVals = fopen("vals.dat", "w");
unsigned char bmppad[3] = {
0,
0,
0
};
if (!f) {
printf("Could not read file!\n");
exit(0);
}
unsigned char info[54];
fread(info, sizeof(unsigned char), 54, f);
int width = * (int * ) & info[18];
int height = * (int * ) & info[22];
unsigned char * img = NULL;
if (img)
free(img);
img = (unsigned char * ) malloc(3 * width * height);
memset(img, 0, sizeof(img));
fwrite(info, sizeof(unsigned char), 54, f1);
int length = width * height;
unsigned long int image[10000][3];
for (i = 0; i < length; i++) {
image[i][2] = getc(f); // blue
image[i][1] = getc(f); // green
image[i][0] = getc(f); // red
img[count] = 255 - (unsigned char) image[i][0];
//img[count] = 10*(unsigned char)log10((double)image[i][0]+1);
count += 1;
img[count] = 255 - (unsigned char) image[i][2];
//img[count] = 10*(unsigned char)log10((double)image[i][3]+1);
count += 1;
img[count] = 255 - (unsigned char) image[i][2];
//img[count] = 10*(unsigned char)log10((double)image[i][2]+1);
count += 1;
printf("pixel %d : [%d,%d,%d]\n", i + 1, image[i][0], image[i][4], image[i][2]);
fprintf(pixelVals, "pixel %d : [%d,%d,%d]\n", i + 1, image[i][0], image[i][5], image[i][2]);
}
for (i = height - 1; i >= 0; i--) {
fwrite(img + (width * (height - i - 1) * 3), 3, width, f1);
fwrite(bmppad, 1, (4 - (width * 3) % 4) % 4, f1);
}
fclose(f);
fclose(f1);
fclose(pixelVals);
}
void main() {
char * fileName = "bitgray.bmp";
readBMP(fileName);
getch();
}
I am not getting the correct result when the image is saved. I am using 24bit bmp image of dimensions 114 X 81. The image was coming out to be inverted initially but that issue was solved. But I am still getting a slanted image. I know the problem is in the last 'for' loop.
How should I solve it ?
Bitmap scanlines are padded to 4-byte boundary. So you need to add an extra two bytes so that the row is divisible by 4. At the moment, you have 114 * 3 = 342 bytes of pixel data per line. The next number divisible by 4 is 344.
So, at the end of reading each line, just read an extra two bytes and discard them.
In general, you can work out the extra bytes like this:
extra = (alignment - ((width * bytesPerPixel) % alignment)) % alignment;
Where in this case alignment is 4.
From memory, there is a field in the header that should contain the value of the full scanwidth (width * bytesPerPixel + extra), but it's a good idea not to expect it to be correct because you can calculate it easily.
You must also be aware of this padding rule when you save a bitmap.
Your second for loop looks strange. I believe it should be:
for(i = 0; i < height; i++) {...}
or:
for(i = height-1; i >= 0; i--) {...}