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
// Copyright (c) 2012-2017 VideoStitch SAS
// Copyright (c) 2018 stitchEm
#ifndef BMPR_HPP_
#define BMPR_HPP_
#include "io.hpp"
#include <cassert>
#include <fstream>
#include <ostream>
#include <vector>
typedef struct {
char* filename;
void* buffer;
} cache_t;
#define MAX_CACHED_FILES (8)
static cache_t filesCache[MAX_CACHED_FILES] = {
{nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr},
{nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr},
};
void* cachedFile(const char* filename) {
cache_t* free_entry = nullptr;
for (int i = 0; i < MAX_CACHED_FILES; i++) {
if (filesCache[i].filename) {
if (strcmp(filesCache[i].filename, filename) == 0) {
return (filesCache[i].buffer);
}
} else if (free_entry == nullptr) {
free_entry = filesCache + i;
}
}
assert(free_entry);
free_entry->filename = (char*)malloc(strlen(filename) + 1);
strcpy(free_entry->filename, filename);
FILE* fd = VideoStitch::Io::openFile(filename, "rb");
assert(fd);
fseek(fd, 0, SEEK_END);
size_t len = ftell(fd);
fseek(fd, 0, SEEK_SET);
free_entry->buffer = malloc(len);
assert(free_entry->buffer);
// we don't care about the return of read
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-result"
fread(free_entry->buffer, sizeof(char), len, fd);
#pragma GCC diagnostic pop
fclose(fd);
return (free_entry->buffer);
}
namespace VideoStitch {
namespace Input {
class BMPReader {
public:
BMPReader(const char* filename, std::ostream*) : width(0), height(0), cury(0), bottomup(0), pitch(0) {
buffer = (char*)cachedFile(filename);
assert(buffer);
(void)readHeader();
}
~BMPReader() {}
unsigned getWidth() const { return width; }
unsigned getHeight() const { return height; }
bool ok() const { return true; }
/**
* Fill in the given buffer with the next row (RGBRGBRGB).
* @data must be large enough to hold one row.
*/
bool getNextRow(unsigned char* data) {
char* src = buffer + bmf.bfOffBits;
if (cury < height) {
if (!bottomup) {
src += (height - (cury + 1)) * pitch;
} else {
src += cury * pitch;
}
memcpy(data, src, width * 3);
cury++;
return true;
}
return false;
}
private:
int readHeader() {
memcpy(&bmf, buffer, sizeof(struct bmpfileheader));
memcpy(&bmi, buffer + sizeof(struct bmpfileheader), sizeof(struct bmpinfoheader));
if ((bmf.bfType != 0x4D42) || (bmf.bfReserved1) || (bmf.bfReserved2) ||
(bmi.biSize != sizeof(struct bmpinfoheader)) || (bmi.biPlanes != 1) || (bmi.biBitCount != 24) ||
(bmi.biCompression)) {
return (-1);
}
width = bmi.biWidth;
bottomup = (int)(bmi.biHeight >> 31);
height = (bmi.biHeight ^ bottomup) - bottomup;
pitch = (size_t)(unsigned int)((((width * bmi.biPlanes * bmi.biBitCount) + 31) >> 3) & -4);
cury = 0;
return (0);
}
private:
char* buffer;
unsigned int width;
unsigned int height;
unsigned int cury;
int bottomup;
size_t pitch;
struct __attribute__((__packed__)) bmpfileheader {
uint16_t bfType;
uint32_t bfSize;
uint16_t bfReserved1;
uint16_t bfReserved2;
uint32_t bfOffBits;
} bmf;
struct __attribute__((__packed__)) bmpinfoheader {
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;
} bmi;
};
} // namespace Input
} // namespace VideoStitch
#endif