Beatmup
Beatmup::BmpFile Class Reference

Toolset to load and store images in BMP format. More...

#include <bmp_file.h>

Public Member Functions

 BmpFile (const char *filename)
 
void load (void *pixels, const uint32_t pixelsSizeInBytes)
 Loads the content of the file into memory. More...
 
uint8_t getBitsPerPixel () const
 
int32_t getWidth () const
 
int32_t getHeight () const
 

Static Public Member Functions

static void save (const void *pixels, int32_t width, int32_t height, uint8_t bpp, const char *filename)
 Stores an image into a BMP file. More...
 

Private Member Functions

struct __attribute__ ((packed))
 

Private Attributes

 Header
 
Header header
 
std::ifstream in
 

Static Private Attributes

static const Header BMP_HEADER_REFERENCE
 

Detailed Description

Toolset to load and store images in BMP format.

Definition at line 28 of file bmp_file.h.

Constructor & Destructor Documentation

◆ BmpFile()

BmpFile::BmpFile ( const char *  filename)

Definition at line 82 of file bmp_file.cpp.

82  {
83  // open file
84  in.open(filename, std::fstream::in | std::fstream::binary);
85  if (!in.good())
86  throw IOError(filename, "Cannot read.");
87 
88  // grab header & check some fields
89  in.read((char*)&header, sizeof(header));
90  if (header.magic[0] != BMP_HEADER_REFERENCE.magic[0]
91  || header.magic[1] != BMP_HEADER_REFERENCE.magic[1])
92  throw IOError(filename, "Likely not a bmp file. Bad magic.");
93  if (header.headerSize < BMP_HEADER_REFERENCE.headerSize
94  || header.numColorPlanes != BMP_HEADER_REFERENCE.numColorPlanes)
95  throw IOError(filename, "Likely not a bmp file. Invalid header.");
96  if (header.bpp == 32 && header.compression != BMP_32BIT_COMPRESSION)
97  throw IOError(filename, "Unsupported 32-bit bitmap compression.");
98  else if (header.bpp != 32 && header.compression != BMP_HEADER_REFERENCE.compression)
99  throw IOError(filename, "Compressed bitmaps are not supported.");
100 }
static const uint32_t BMP_32BIT_COMPRESSION
Definition: bmp_file.cpp:79
std::ifstream in
Definition: bmp_file.h:58
Header header
Definition: bmp_file.h:57
static const Header BMP_HEADER_REFERENCE
Definition: bmp_file.h:55
JNIEnv jlong jstring filename

Member Function Documentation

◆ __attribute__()

struct Beatmup::BmpFile::__attribute__ ( (packed)  )
inlineprivate

Definition at line 1 of file bmp_file.h.

37  {
38  uint8_t magic[2];
39  uint32_t size;
40  uint16_t reserved[2];
41  uint32_t offset;
42  uint32_t headerSize;
43  int32_t width, height;
44  uint16_t numColorPlanes;
45  uint16_t bpp;
46  uint32_t compression;
47  uint32_t imageSize;
48  int32_t hdpi, vdpi;
49  uint32_t numImportantColors;
50  } Header;
jlong jint width
jlong jint jint height
jlong jobject size

◆ load()

void BmpFile::load ( void *  pixels,
const uint32_t  pixelsSizeInBytes 
)

Loads the content of the file into memory.

Parameters
[out]pixelsThe memory buffer to fill with pixel data.
[in]pixelsSizeInBytesThe memory buffer size in bytes

Definition at line 103 of file bmp_file.cpp.

103  {
104  const msize
105  rowSize = ceili(header.width * header.bpp, 8),
106  rowAlign = ceili(rowSize, 4) * 4 - rowSize;
107  RuntimeError::check(pixelsSizeInBytes >= rowSize * header.height,
108  "Cannot fit BMP file to a pixel buffer");
109  char pad[3];
110  in.seekg(header.offset, std::ios_base::beg);
111  for (int y = header.height - 1; y >= 0 && !in.eof(); --y) {
112  char* ptr = (char*)pixels + y * rowSize;
113  if (header.bpp == 32)
114  for (int x = 0; x < header.width && !in.eof(); ++x, ptr+=4) {
115  in.read(ptr + CHANNELS_4.A, 1);
116  in.read(ptr + CHANNELS_4.B, 1);
117  in.read(ptr + CHANNELS_4.G, 1);
118  in.read(ptr + CHANNELS_4.R, 1);
119  }
120  else if (header.bpp == 24)
121  for (int x = 0; x < header.width && !in.eof(); ++x, ptr+=3) {
122  in.read(ptr + CHANNELS_3.B, 1);
123  in.read(ptr + CHANNELS_3.G, 1);
124  in.read(ptr + CHANNELS_3.R, 1);
125  }
126  else if (header.bpp == 4)
127  for (msize i = 0; i < rowSize && !in.eof(); ++i, ptr++) {
128  in.read(ptr, 1);
129  *ptr = (char)LSN_MSN_REVERSE_LOOKUP[(uint8_t)*ptr];
130  }
131  else if (header.bpp == 1)
132  for (msize i = 0; i < rowSize && !in.eof(); ++i, ptr++) {
133  in.read(ptr, 1);
134  *ptr = (char)LSB_MSB_REVERSE_LOOKUP[(uint8_t)*ptr];
135  }
136  else {
137  in.read(ptr, rowSize);
138  ptr += rowSize;
139  }
140  in.read(pad, rowAlign);
141  }
142 }
static const uint8_t LSB_MSB_REVERSE_LOOKUP[]
Definition: bmp_file.cpp:27
static const uint8_t LSN_MSN_REVERSE_LOOKUP[]
Definition: bmp_file.cpp:47
static void check(const bool condition, const std::string &message)
Definition: exception.h:64
uint32_t msize
memory size
Definition: basic_types.h:30
static const struct Beatmup::@1 CHANNELS_4
static const struct Beatmup::@2 CHANNELS_3
#define ceili(x, y)
integer division x/y with ceiling
Definition: utils.hpp:21
jobject jlong jint jint y
jobject jlong jint x

◆ save()

void BmpFile::save ( const void *  pixels,
int32_t  width,
int32_t  height,
uint8_t  bpp,
const char *  filename 
)
static

Stores an image into a BMP file.

Parameters
[in]pixelsMemory buffer containing the pixel data to save
[in]widthImage width in pixels
[in]heightImage height in pixels
[in]bppNumber of bits per pixel
[in]filenameName/path of the file to write the data to.

Definition at line 145 of file bmp_file.cpp.

151  {
152  // setup header
154  switch (bpp) {
155  case 1:
156  case 4:
157  case 24:
158  header.bpp = bpp;
159  break;
160 
161  case 8:
162  header.bpp = 8;
163  header.numImportantColors = 256;
164  break;
165 
166  case 32:
167  header.bpp = 32;
168  header.compression = BMP_32BIT_COMPRESSION;
169  break;
170 
171  default:
172  throw IOError(filename, "Unsupported number of bits per pixel when saving a BMP file.");
173  }
174  const msize
175  rowSize = ceili(width * bpp, 8),
176  rowAlign = ceili(rowSize, 4) * 4 - rowSize;
177  header.width = width;
178  header.height = height;
179  header.offset = sizeof(header);
180  header.size = ceili(rowSize, 4) * 4 * height; // offset added later
181 
182  // setup output stream
183  std::fstream out(filename, std::ios::out | std::ios::binary);
184  if (!out.good())
185  throw IOError(filename, "Cannot write to file.");
186 
187  static const uint32_t
188  BMP_1BIT_COLOR_SPACE_HEADER[] = { 2 },
189  BMP_4BIT_COLOR_SPACE_HEADER[] = { 16 },
190  BMP_8BIT_COLOR_SPACE_HEADER[] = { 256 },
191  BMP_24BIT_COLOR_SPACE_HEADER[] = { 0 },
192  BMP_32BIT_COLOR_SPACE_HEADER[] = { 0, 0xff000000, 0xff0000, 0xff00, 0xff },
193  BMP_COLOR_SPACE_INFO[] = { 0x73524742, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2, 0, 0, 0 },
194  BMP_8BIT_COLOR_TABLE[] = { 0, 0x10101, 0x20202, 0x30303, 0x40404, 0x50505, 0x60606, 0x70707, 0x80808, 0x90909, 0xa0a0a, 0xb0b0b, 0xc0c0c, 0xd0d0d, 0xe0e0e, 0xf0f0f, 0x101010, 0x111111, 0x121212, 0x131313, 0x141414, 0x151515, 0x161616, 0x171717, 0x181818, 0x191919, 0x1a1a1a, 0x1b1b1b, 0x1c1c1c, 0x1d1d1d, 0x1e1e1e, 0x1f1f1f, 0x202020, 0x212121, 0x222222, 0x232323, 0x242424, 0x252525, 0x262626, 0x272727, 0x282828, 0x292929, 0x2a2a2a, 0x2b2b2b, 0x2c2c2c, 0x2d2d2d, 0x2e2e2e, 0x2f2f2f, 0x303030, 0x313131, 0x323232, 0x333333, 0x343434, 0x353535, 0x363636, 0x373737, 0x383838, 0x393939, 0x3a3a3a, 0x3b3b3b, 0x3c3c3c, 0x3d3d3d, 0x3e3e3e, 0x3f3f3f, 0x404040, 0x414141, 0x424242, 0x434343, 0x444444, 0x454545, 0x464646, 0x474747, 0x484848, 0x494949, 0x4a4a4a, 0x4b4b4b, 0x4c4c4c, 0x4d4d4d, 0x4e4e4e, 0x4f4f4f, 0x505050, 0x515151, 0x525252, 0x535353, 0x545454, 0x555555, 0x565656, 0x575757, 0x585858, 0x595959, 0x5a5a5a, 0x5b5b5b, 0x5c5c5c, 0x5d5d5d, 0x5e5e5e, 0x5f5f5f, 0x606060, 0x616161, 0x626262, 0x636363, 0x646464, 0x656565, 0x666666, 0x676767, 0x686868, 0x696969, 0x6a6a6a, 0x6b6b6b, 0x6c6c6c, 0x6d6d6d, 0x6e6e6e, 0x6f6f6f, 0x707070, 0x717171, 0x727272, 0x737373, 0x747474, 0x757575, 0x767676, 0x777777, 0x787878, 0x797979, 0x7a7a7a, 0x7b7b7b, 0x7c7c7c, 0x7d7d7d, 0x7e7e7e, 0x7f7f7f, 0x808080, 0x818181, 0x828282, 0x838383, 0x848484, 0x858585, 0x868686, 0x878787, 0x888888, 0x898989, 0x8a8a8a, 0x8b8b8b, 0x8c8c8c, 0x8d8d8d, 0x8e8e8e, 0x8f8f8f, 0x909090, 0x919191, 0x929292, 0x939393, 0x949494, 0x959595, 0x969696, 0x979797, 0x989898, 0x999999, 0x9a9a9a, 0x9b9b9b, 0x9c9c9c, 0x9d9d9d, 0x9e9e9e, 0x9f9f9f, 0xa0a0a0, 0xa1a1a1, 0xa2a2a2, 0xa3a3a3, 0xa4a4a4, 0xa5a5a5, 0xa6a6a6, 0xa7a7a7, 0xa8a8a8, 0xa9a9a9, 0xaaaaaa, 0xababab, 0xacacac, 0xadadad, 0xaeaeae, 0xafafaf, 0xb0b0b0, 0xb1b1b1, 0xb2b2b2, 0xb3b3b3, 0xb4b4b4, 0xb5b5b5, 0xb6b6b6, 0xb7b7b7, 0xb8b8b8, 0xb9b9b9, 0xbababa, 0xbbbbbb, 0xbcbcbc, 0xbdbdbd, 0xbebebe, 0xbfbfbf, 0xc0c0c0, 0xc1c1c1, 0xc2c2c2, 0xc3c3c3, 0xc4c4c4, 0xc5c5c5, 0xc6c6c6, 0xc7c7c7, 0xc8c8c8, 0xc9c9c9, 0xcacaca, 0xcbcbcb, 0xcccccc, 0xcdcdcd, 0xcecece, 0xcfcfcf, 0xd0d0d0, 0xd1d1d1, 0xd2d2d2, 0xd3d3d3, 0xd4d4d4, 0xd5d5d5, 0xd6d6d6, 0xd7d7d7, 0xd8d8d8, 0xd9d9d9, 0xdadada, 0xdbdbdb, 0xdcdcdc, 0xdddddd, 0xdedede, 0xdfdfdf, 0xe0e0e0, 0xe1e1e1, 0xe2e2e2, 0xe3e3e3, 0xe4e4e4, 0xe5e5e5, 0xe6e6e6, 0xe7e7e7, 0xe8e8e8, 0xe9e9e9, 0xeaeaea, 0xebebeb, 0xececec, 0xededed, 0xeeeeee, 0xefefef, 0xf0f0f0, 0xf1f1f1, 0xf2f2f2, 0xf3f3f3, 0xf4f4f4, 0xf5f5f5, 0xf6f6f6, 0xf7f7f7, 0xf8f8f8, 0xf9f9f9, 0xfafafa, 0xfbfbfb, 0xfcfcfc, 0xfdfdfd, 0xfefefe, 0xffffff },
195  BMP_4BIT_COLOR_TABLE[] = { 0, 0x111111, 0x222222, 0x333333, 0x444444, 0x555555, 0x666666, 0x777777, 0x888888, 0x999999, 0xaaaaaa, 0xbbbbbb, 0xcccccc, 0xdddddd, 0xeeeeee, 0xffffff },
196  BMP_1BIT_COLOR_TABLE[] = { 0, 0xffffff };
197 
198  // write out header & color space info
199  if (header.bpp == 1) {
200  uint32_t size = sizeof(BMP_1BIT_COLOR_SPACE_HEADER) + sizeof(BMP_COLOR_SPACE_INFO);
201  header.offset += size + sizeof(BMP_1BIT_COLOR_TABLE);
202  header.size += header.offset;
203  header.headerSize += size;
204  out.write((const char*)&header, sizeof(header));
205  out.write((const char*)BMP_1BIT_COLOR_SPACE_HEADER, sizeof(BMP_1BIT_COLOR_SPACE_HEADER));
206  out.write((const char*)BMP_COLOR_SPACE_INFO, sizeof(BMP_COLOR_SPACE_INFO));
207  out.write((const char*)BMP_1BIT_COLOR_TABLE, sizeof(BMP_1BIT_COLOR_TABLE));
208  }
209  else if (header.bpp == 4) {
210  uint32_t size = sizeof(BMP_4BIT_COLOR_SPACE_HEADER) + sizeof(BMP_COLOR_SPACE_INFO);
211  header.offset += size + sizeof(BMP_4BIT_COLOR_TABLE);
212  header.size += header.offset;
213  header.headerSize += size;
214  out.write((const char*)&header, sizeof(header));
215  out.write((const char*)BMP_4BIT_COLOR_SPACE_HEADER, sizeof(BMP_4BIT_COLOR_SPACE_HEADER));
216  out.write((const char*)BMP_COLOR_SPACE_INFO, sizeof(BMP_COLOR_SPACE_INFO));
217  out.write((const char*)BMP_4BIT_COLOR_TABLE, sizeof(BMP_4BIT_COLOR_TABLE));
218  }
219  else if (header.bpp == 8) {
220  uint32_t size = sizeof(BMP_8BIT_COLOR_SPACE_HEADER) + sizeof(BMP_COLOR_SPACE_INFO);
221  header.offset += size + sizeof(BMP_8BIT_COLOR_TABLE);
222  header.size += header.offset;
223  header.headerSize += size;
224  out.write((const char*)&header, sizeof(header));
225  out.write((const char*)BMP_8BIT_COLOR_SPACE_HEADER, sizeof(BMP_8BIT_COLOR_SPACE_HEADER));
226  out.write((const char*)BMP_COLOR_SPACE_INFO, sizeof(BMP_COLOR_SPACE_INFO));
227  out.write((const char*)BMP_8BIT_COLOR_TABLE, sizeof(BMP_8BIT_COLOR_TABLE));
228  }
229  else if (header.bpp == 24) {
230  uint32_t size = sizeof(BMP_24BIT_COLOR_SPACE_HEADER) + sizeof(BMP_COLOR_SPACE_INFO);
231  header.offset += size;
232  header.size += header.offset;
233  header.headerSize += size;
234  out.write((const char*)&header, sizeof(header));
235  out.write((const char*)BMP_24BIT_COLOR_SPACE_HEADER, sizeof(BMP_24BIT_COLOR_SPACE_HEADER));
236  out.write((const char*)BMP_COLOR_SPACE_INFO, sizeof(BMP_COLOR_SPACE_INFO));
237  }
238  else if (header.bpp == 32) {
239  uint32_t size = sizeof(BMP_32BIT_COLOR_SPACE_HEADER) + sizeof(BMP_COLOR_SPACE_INFO);
240  header.compression = BMP_32BIT_COMPRESSION;
241  header.offset += size;
242  header.size += header.offset;
243  header.headerSize += size;
244  out.write((const char*)&header, sizeof(header));
245  out.write((const char*)BMP_32BIT_COLOR_SPACE_HEADER, sizeof(BMP_32BIT_COLOR_SPACE_HEADER));
246  out.write((const char*)BMP_COLOR_SPACE_INFO, sizeof(BMP_COLOR_SPACE_INFO));
247  }
248 
249  // writing cycle
250  char pad[3] = {0, 0, 0};
251  for (int y = height - 1; y >= 0; --y) {
252  pixbyte const* ptr = (pixbyte const*)pixels + y * rowSize;
253  if (bpp == 32)
254  for (int x = 0; x < width; ++x, ptr+=4)
255  out << ptr[CHANNELS_4.A] << ptr[CHANNELS_4.B] << ptr[CHANNELS_4.G] << ptr[CHANNELS_4.R];
256  else if (bpp == 24)
257  for (int x = 0; x < width; ++x, ptr+=3)
258  out << ptr[CHANNELS_3.B] << ptr[CHANNELS_3.G] << ptr[CHANNELS_3.R];
259  else if (bpp == 4)
260  for (int i = 0; i < rowSize; ++i, ptr++)
261  out << LSN_MSN_REVERSE_LOOKUP[*ptr];
262  else if (bpp == 1)
263  for (int i = 0; i < rowSize; ++i, ptr++)
264  out << LSB_MSB_REVERSE_LOOKUP[*ptr];
265  else {
266  out.write((const char*)ptr, rowSize);
267  }
268  out.write(pad, rowAlign);
269  }
270 }
uint8_t pixbyte
Definition: basic_types.h:34
if(nativeObj)
JNIEnv jlong jint out

◆ getBitsPerPixel()

uint8_t Beatmup::BmpFile::getBitsPerPixel ( ) const
inline

Definition at line 86 of file bmp_file.h.

86 { return header.bpp; }

◆ getWidth()

int32_t Beatmup::BmpFile::getWidth ( ) const
inline

Definition at line 87 of file bmp_file.h.

87 { return header.width; }

◆ getHeight()

int32_t Beatmup::BmpFile::getHeight ( ) const
inline

Definition at line 88 of file bmp_file.h.

88 { return header.height; }

Member Data Documentation

◆ Header

Beatmup::BmpFile::Header
private

Definition at line 50 of file bmp_file.h.

◆ BMP_HEADER_REFERENCE

const BmpFile::Header BmpFile::BMP_HEADER_REFERENCE
staticprivate
Initial value:
= {
{ 'B', 'M' },
0, { 0, 0 }, 0,
36,
0, 0,
1,
0,
0,
0, 0, 0, 0
}

Definition at line 55 of file bmp_file.h.

◆ header

Header Beatmup::BmpFile::header
private

Definition at line 57 of file bmp_file.h.

◆ in

std::ifstream Beatmup::BmpFile::in
private

Definition at line 58 of file bmp_file.h.


The documentation for this class was generated from the following files: