3
// Copyright (C) 2001-present, the Celestia Development Team
4
// Original version by Chris Laurel <claurel@shatters.net>
6
// This program is free software; you can redistribute it and/or
7
// modify it under the terms of the GNU General Public License
8
// as published by the Free Software Foundation; either version 2
9
// of the License, or (at your option) any later version.
17
#include <celutil/binaryread.h>
18
#include <celutil/logger.h>
21
namespace celestia::engine
26
// BMP file definitions--can't use windows.h because we might not be
30
unsigned char magic[2];
32
std::uint32_t reserved;
43
std::uint32_t compression;
44
std::uint32_t imageSize;
45
std::int32_t widthPPM;
46
std::int32_t heightPPM;
47
std::uint32_t colorsUsed;
48
std::uint32_t colorsImportant;
52
Image* LoadBMPImage(std::istream& in)
54
BMPFileHeader fileHeader;
55
BMPImageHeader imageHeader;
57
if (!util::readLE<unsigned char>(in, fileHeader.magic[0])
58
|| fileHeader.magic[0] != 'B'
59
|| !util::readLE<unsigned char>(in, fileHeader.magic[1])
60
|| fileHeader.magic[1] != 'M'
61
|| !util::readLE<std::uint32_t>(in, fileHeader.size)
62
|| !util::readLE<std::uint32_t>(in, fileHeader.reserved)
63
|| !util::readLE<std::uint32_t>(in, fileHeader.offset)
64
|| !util::readLE<std::uint32_t>(in, imageHeader.size)
65
|| !util::readLE<std::int32_t>(in, imageHeader.width)
66
|| imageHeader.width <= 0
67
|| !util::readLE<std::int32_t>(in, imageHeader.height)
68
|| imageHeader.height <= 0
69
|| !util::readLE<std::uint16_t>(in, imageHeader.planes)
70
|| !util::readLE<std::uint16_t>(in, imageHeader.bpp)
71
// We don't handle 1-, 2-, or 4-bpp images
72
|| (imageHeader.bpp != 8 && imageHeader.bpp != 24 && imageHeader.bpp != 32)
73
|| !util::readLE<std::uint32_t>(in, imageHeader.compression)
74
// We currently don't support compressed BMPs
75
|| imageHeader.compression != 0
76
|| !util::readLE<std::uint32_t>(in, imageHeader.imageSize)
77
|| !util::readLE<std::int32_t>(in, imageHeader.widthPPM)
78
|| !util::readLE<std::int32_t>(in, imageHeader.heightPPM)
79
|| !util::readLE<std::uint32_t>(in, imageHeader.colorsUsed)
80
|| !util::readLE<std::uint32_t>(in, imageHeader.colorsImportant))
85
std::vector<uint8_t> palette;
86
if (imageHeader.bpp == 8)
88
util::GetLogger()->debug("Reading {} color palette\n", imageHeader.colorsUsed);
89
palette.resize(imageHeader.colorsUsed * 4);
90
if (!in.read(reinterpret_cast<char*>(palette.data()), imageHeader.colorsUsed * 4).good())
96
if (!in.seekg(fileHeader.offset, std::ios::beg)) { return nullptr; }
98
std::size_t bytesPerRow =
99
(imageHeader.width * imageHeader.bpp / 8 + 1) & ~1;
100
std::size_t imageBytes = bytesPerRow * imageHeader.height;
102
// slurp the image data
103
std::vector<std::uint8_t> pixels(imageBytes);
104
if (!in.read(reinterpret_cast<char*>(pixels.data()), imageBytes).good())
109
// check for truncated file
111
auto img = std::make_unique<Image>(PixelFormat::RGB, imageHeader.width, imageHeader.height);
113
// Copy the image and perform any necessary conversions
114
for (std::int32_t y = 0; y < imageHeader.height; y++)
116
const std::uint8_t* src = pixels.data() + y * bytesPerRow;
117
uint8_t* dst = img->getPixelRow(y);
119
switch (imageHeader.bpp)
122
for (std::int32_t x = 0; x < imageHeader.width; x++)
124
const uint8_t* color = palette.data() + (*src << 2);
133
for (std::int32_t x = 0; x < imageHeader.width; x++)
143
for (std::int32_t x = 0; x < imageHeader.width; x++)
155
return img.release();
157
} // anonymous namespace
159
Image* LoadBMPImage(const fs::path& filename)
161
std::ifstream bmpFile(filename.string(), std::ios::in | std::ios::binary);
165
Image* img = LoadBMPImage(bmpFile);
173
} // namespace celestia::engine