1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
//
// Created by ricar on 16.12.2022.
//
#include "bmp.h"
#include "file.h"
#include<string.h>
#define PLANES 1
#define HEADER_SIZE 40
#define TYPE (19778)
#define BIT_COUNT (24)
#pragma pack(push,1)
// bmp_header represents the header for a BMP file
struct bmp_header
{
uint16_t bfType; // type of BMP file (0x4d42 | 0x4349 | 0x5450)
uint32_t bfileSize; // size of the file
uint32_t bfReserved; // reserved field, should be 0
uint32_t bOffBits; // offset to data field
uint32_t biSize; // size of the structure in bytes (40 for BITMAPINFOHEADER, 108 for BITMAPV4HEADER, 124 for BITMAPV5HEADER)
uint32_t biWidth; // width in pixels
uint32_t biHeight; // height in pixels
uint16_t biPlanes; // should always be 1
uint16_t biBitCount; // number of bits per pixel (0, 1, 4, 8, 16, 24, 32)
uint32_t biCompression; // type of compression (BI_RGB, BI_RLE8, BI_RLE4, BI_BITFIELDS, BI_JPEG, BI_PNG) (only BI_RGB is commonly used)
uint32_t biSizeImage; // size of data field in bytes (usually set to 0)
uint32_t biXPelsPerMeter; // horizontal resolution, pixels per inch
uint32_t biYPelsPerMeter; // vertical resolution, pixels per inch
uint32_t biClrUsed; // number of colors used (if there is a color table)
uint32_t biClrImportant; // number of significant colors (can be considered 0)
};
#pragma pack(pop)
// find_padding calculates the number of padding bytes needed at the end of each row of pixels in the BMP file
static uint32_t find_padding(uint64_t a) {
// the number of bytes in each row of pixels must be a multiple of 4
if (a % 4 == 0) {
return 0;
} else {
return (a % 4);
}
}
// Function to check if the BMP header fields are valid
bool check_header_fields(struct bmp_header header) {
return (header.bfileSize != header.biSizeImage + sizeof(struct bmp_header)) ||
(header.bfReserved != 0) ||
(header.biPlanes != 1) ||
((header.biSize != 40) && (header.biSize != 25) && (header.biSize != 124)) ||
(header.bOffBits != 14 + header.biSize) ||
(header.biWidth < 1) || (header.biWidth > 10000) ||
(header.biHeight < 1) || (header.biHeight > 10000) ||
(header.biBitCount != 24) ||
(header.biCompression != 0);
}
// from_bmp reads a BMP file and stores its data in the given image struct
// it returns a read_status enum value indicating the success or failure of the operation
enum read_status read_bmp(FILE *in, struct image const *img) {
// create a variable to store the BMP file header
struct bmp_header header;
// read the BMP file header from the input file
size_t res = fread(&header, 1, sizeof(struct bmp_header), in);
// if the header is not the expected size, return an error status
if (res != sizeof(struct bmp_header)) {
fprintf(stderr, "The header is invalid");
return READ_INVALID_HEADER;
}
// check the signature of the BMP file to ensure it is a valid BMP file
if (header.bfType != 0x4d42 && header.bfType != 0x4349 && header.bfType != 0x5450) {
fprintf(stdout, "The value of bfType is: %d\n", header.bfType);
fprintf(stderr, "The type is invalid");
return READ_INVALID_SIGNATURE;
}
// check the header fields to ensure they are valid
bool invalidHeader = check_header_fields(header);
// if any of the header fields are invalid, return an error status
if (invalidHeader) {
fprintf(stderr, "The header is invalid");
return READ_INVALID_HEADER;
}
// the BMP file header has been read and checked, and the image is a BGR-24 format with known width and height
struct image *img_nonconst = (struct image *)img;
configure_image(img_nonconst, header.biWidth, header.biHeight);
// if the image data could not be allocated, return an error status
if (((*img).data) == NULL) { return READ_INVALID_BITS; }
// read the pixel data from the input file and store it in the image struct
size_t i = 0;
while (i < ((*img).height)) {
// read a row of pixels from the input file
res = fread(&(((*img).data)[i * ((*img).width)]), sizeof(struct pixel), (*img).width, in);
// if the number of pixels read is not equal to the image width, return an error status
if (res != ((*img).width)) return READ_ERROR;
// skip the padding bytes at the end of the row
fseek(in, find_padding((*img).width), SEEK_CUR);
++i;
}
// close the input file and return a status indicating success or failure
return READ_OK;
}
struct bmp_header create_new_header(struct image const* img){
struct bmp_header bmpHeader;
bmpHeader.bfType = TYPE;
//Find the length of the line in the fine
unsigned long width_full = (3* ((*img).width) +3) &(-4);
bmpHeader.biSizeImage = ((*img).height) * width_full;
bmpHeader.bfileSize = sizeof (struct bmp_header) + bmpHeader.biSizeImage;
bmpHeader.bfReserved = 0;
bmpHeader.biPlanes = PLANES;
bmpHeader.biSize = HEADER_SIZE;
bmpHeader.bOffBits = sizeof (struct bmp_header);
bmpHeader.biWidth = (*img).width;
bmpHeader.biHeight = (*img).height;
bmpHeader.biBitCount = BIT_COUNT;
bmpHeader.biCompression = 0;
bmpHeader.biYPelsPerMeter = 0;
bmpHeader.biXPelsPerMeter = 0;
bmpHeader.biClrImportant = 0;
bmpHeader.biClrUsed = 0;
return bmpHeader;
}
// to_bmp writes the data from the given image struct to a BMP file
// it returns a write_status enum value indicating the success or failure of the operation
enum write_status to_bmp(const char* output_name, struct image const* img ){
//create a file pointer and open the output file
FILE *out = NULL;
if(check_file_opened(&out,output_name,WRITE) == false) return WRITE_ERROR;
//create a BMP file header struct and initialize it with the image data
struct bmp_header bmpHeader = create_new_header(img);
//write the BMP file header to the output file
fwrite(&bmpHeader, sizeof(bmpHeader), 1, out);
//write the pixel data to the output file
size_t i = 0;
while (i < ((*img).height)) {
//write a row of pixels to the output file
fwrite(&(((*img).data)[i * ((*img).width)]), sizeof(struct pixel), (*img).width, out);
//write the padding bytes at the end of the row
unsigned char padding[3] = {0, 0, 0};
fwrite(padding, sizeof(unsigned char), find_padding((*img).width), out);
++i;
}
// close the output file and return a status indicating success or failure
if(check_file_closed(&out) == false){return WRITE_ERROR;}
return WRITE_OK;
}