-
Notifications
You must be signed in to change notification settings - Fork 25
/
libbmp.c
223 lines (185 loc) · 4.89 KB
/
libbmp.c
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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
/* Copyright 2016 - 2017 Marc Volker Dickmann
* Project: LibBMP
*/
#include <stdio.h>
#include <stdlib.h>
#include "libbmp.h"
// BMP_HEADER
void
bmp_header_init_df (bmp_header *header,
const int width,
const int height)
{
header->bfSize = (sizeof (bmp_pixel) * width + BMP_GET_PADDING (width))
* abs (height);
header->bfReserved = 0;
header->bfOffBits = 54;
header->biSize = 40;
header->biWidth = width;
header->biHeight = height;
header->biPlanes = 1;
header->biBitCount = 24;
header->biCompression = 0;
header->biSizeImage = 0;
header->biXPelsPerMeter = 0;
header->biYPelsPerMeter = 0;
header->biClrUsed = 0;
header->biClrImportant = 0;
}
enum bmp_error
bmp_header_write (const bmp_header *header,
FILE *img_file)
{
if (header == NULL)
{
return BMP_HEADER_NOT_INITIALIZED;
}
else if (img_file == NULL)
{
return BMP_FILE_NOT_OPENED;
}
// Since an adress must be passed to fwrite, create a variable!
const unsigned short magic = BMP_MAGIC;
fwrite (&magic, sizeof (magic), 1, img_file);
// Use the type instead of the variable because its a pointer!
fwrite (header, sizeof (bmp_header), 1, img_file);
return BMP_OK;
}
enum bmp_error
bmp_header_read (bmp_header *header,
FILE *img_file)
{
if (img_file == NULL)
{
return BMP_FILE_NOT_OPENED;
}
// Since an adress must be passed to fread, create a variable!
unsigned short magic;
// Check if its an bmp file by comparing the magic nbr:
if (fread (&magic, sizeof (magic), 1, img_file) != 1 ||
magic != BMP_MAGIC)
{
return BMP_INVALID_FILE;
}
if (fread (header, sizeof (bmp_header), 1, img_file) != 1)
{
return BMP_ERROR;
}
return BMP_OK;
}
// BMP_PIXEL
void
bmp_pixel_init (bmp_pixel *pxl,
const unsigned char red,
const unsigned char green,
const unsigned char blue)
{
pxl->red = red;
pxl->green = green;
pxl->blue = blue;
}
// BMP_IMG
void
bmp_img_alloc (bmp_img *img)
{
const size_t h = abs (img->img_header.biHeight);
// Allocate the required memory for the pixels:
img->img_pixels = malloc (sizeof (bmp_pixel*) * h);
for (size_t y = 0; y < h; y++)
{
img->img_pixels[y] = malloc (sizeof (bmp_pixel) * img->img_header.biWidth);
}
}
void
bmp_img_init_df (bmp_img *img,
const int width,
const int height)
{
// INIT the header with default values:
bmp_header_init_df (&img->img_header, width, height);
bmp_img_alloc (img);
}
void
bmp_img_free (bmp_img *img)
{
const size_t h = abs (img->img_header.biHeight);
for (size_t y = 0; y < h; y++)
{
free (img->img_pixels[y]);
}
free (img->img_pixels);
}
enum bmp_error
bmp_img_write (const bmp_img *img,
const char *filename)
{
FILE *img_file = fopen (filename, "wb");
if (img_file == NULL)
{
return BMP_FILE_NOT_OPENED;
}
// NOTE: This way the correct error code could be returned.
const enum bmp_error err = bmp_header_write (&img->img_header, img_file);
if (err != BMP_OK)
{
// ERROR: Could'nt write the header!
fclose (img_file);
return err;
}
// Select the mode (bottom-up or top-down):
const size_t h = abs (img->img_header.biHeight);
const size_t offset = (img->img_header.biHeight > 0 ? h - 1 : 0);
// Create the padding:
const unsigned char padding[3] = {'\0', '\0', '\0'};
// Write the content:
for (size_t y = 0; y < h; y++)
{
// Write a whole row of pixels to the file:
fwrite (img->img_pixels[abs (offset - y)], sizeof (bmp_pixel), img->img_header.biWidth, img_file);
// Write the padding for the row!
fwrite (padding, sizeof (unsigned char), BMP_GET_PADDING (img->img_header.biWidth), img_file);
}
// NOTE: All good!
fclose (img_file);
return BMP_OK;
}
enum bmp_error
bmp_img_read (bmp_img *img,
const char *filename)
{
FILE *img_file = fopen (filename, "rb");
if (img_file == NULL)
{
return BMP_FILE_NOT_OPENED;
}
// NOTE: This way the correct error code can be returned.
const enum bmp_error err = bmp_header_read (&img->img_header, img_file);
if (err != BMP_OK)
{
// ERROR: Could'nt read the image header!
fclose (img_file);
return err;
}
bmp_img_alloc (img);
// Select the mode (bottom-up or top-down):
const size_t h = abs (img->img_header.biHeight);
const size_t offset = (img->img_header.biHeight > 0 ? h - 1 : 0);
const size_t padding = BMP_GET_PADDING (img->img_header.biWidth);
// Needed to compare the return value of fread
const size_t items = img->img_header.biWidth;
// Read the content:
for (size_t y = 0; y < h; y++)
{
// Read a whole row of pixels from the file:
if (fread (img->img_pixels[abs (offset - y)], sizeof (bmp_pixel), items, img_file) != items)
{
fclose (img_file);
return BMP_ERROR;
}
// Skip the padding:
fseek (img_file, padding, SEEK_CUR);
}
// NOTE: All good!
fclose (img_file);
return BMP_OK;
}