Added Read method
This commit is contained in:
parent
33932906c4
commit
808738ac2e
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,6 +1,12 @@
|
|||||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# All images
|
||||||
|
*.bmp
|
||||||
|
*.jpg
|
||||||
|
*.jpeg
|
||||||
|
*.png
|
||||||
|
|
||||||
# Vim swap files
|
# Vim swap files
|
||||||
*.swp
|
*.swp
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ project(BmpPP_exec)
|
|||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
ADD_COMPILE_DEFINITIONS(_EULE_NO_INTRINSICS_)
|
ADD_COMPILE_DEFINITIONS(_EULE_NO_INTRINSICS_)
|
||||||
|
ADD_COMPILE_DEFINITIONS(_BMPLIB_DEBUG_OUTPUT_)
|
||||||
|
|
||||||
INCLUDE_DIRECTORIES(../Src/Eule/)
|
INCLUDE_DIRECTORIES(../Src/Eule/)
|
||||||
INCLUDE_DIRECTORIES(../Src/)
|
INCLUDE_DIRECTORIES(../Src/)
|
||||||
|
@ -6,6 +6,7 @@ using namespace Leonetienne::BmpPP;
|
|||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
|
|
||||||
|
/*
|
||||||
BMP bmp({800, 600}, Colormode::RGB);
|
BMP bmp({800, 600}, Colormode::RGB);
|
||||||
|
|
||||||
for (int x = 0; x < 800; x++)
|
for (int x = 0; x < 800; x++)
|
||||||
@ -19,8 +20,19 @@ int main() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bmp.Write("test.bmp"))
|
*/
|
||||||
std::cerr << "What the hell" << std::endl;
|
|
||||||
|
BMP bmp;
|
||||||
|
|
||||||
|
if (!bmp.Read("test.bmp"))
|
||||||
|
std::cerr << "Failed to read" << std::endl;
|
||||||
|
|
||||||
|
std::cout << (bmp.GetColormode() == Colormode::RGB) << std::endl;
|
||||||
|
|
||||||
|
if (!bmp.Write("testwrite.bmp"))
|
||||||
|
std::cerr << "What the hell" << std::endl;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
23
Src/BMP.cpp
23
Src/BMP.cpp
@ -1,8 +1,7 @@
|
|||||||
#include "BMP.h"
|
#include "BMP.h"
|
||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include "BmpWriter.h"
|
#include "BmpWriter.h"
|
||||||
|
#include "BmpReader.h"
|
||||||
|
|
||||||
#define CHECK_IF_INITIALIZED if (!isInitialized) throw std::runtime_error("Image not initialized!");
|
#define CHECK_IF_INITIALIZED if (!isInitialized) throw std::runtime_error("Image not initialized!");
|
||||||
|
|
||||||
@ -13,6 +12,14 @@ namespace Leonetienne::BmpPP {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BMP::BMP(const std::string &filename) {
|
||||||
|
if(!Read(filename))
|
||||||
|
throw std::runtime_error("Unable to read bmp image!");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
BMP::BMP(const Eule::Vector2i &size, const Colormode& colormode)
|
BMP::BMP(const Eule::Vector2i &size, const Colormode& colormode)
|
||||||
:
|
:
|
||||||
size { size },
|
size { size },
|
||||||
@ -57,17 +64,7 @@ namespace Leonetienne::BmpPP {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool BMP::Read(const std::string &filename) {
|
bool BMP::Read(const std::string &filename) {
|
||||||
|
return BmpReader::Read(*this, filename);
|
||||||
std::ifstream ifs(filename, std::ifstream::binary);
|
|
||||||
if (!ifs.good())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// When reading the image, we don't care about most of the header.
|
|
||||||
// All we want is the images resolutions, bits-per-pixel, and pixelbuffer address...
|
|
||||||
// This is because we do not support ALL of bmp, but just the most common formats...
|
|
||||||
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::uint8_t *BMP::GetPixel(const Eule::Vector2i &position) {
|
std::uint8_t *BMP::GetPixel(const Eule::Vector2i &position) {
|
||||||
|
@ -9,12 +9,19 @@
|
|||||||
namespace Leonetienne::BmpPP {
|
namespace Leonetienne::BmpPP {
|
||||||
|
|
||||||
class BmpWriter;
|
class BmpWriter;
|
||||||
|
class BmpReader;
|
||||||
|
|
||||||
class BMP {
|
class BMP {
|
||||||
public:
|
public:
|
||||||
|
// Will create an uninitialized image
|
||||||
BMP();
|
BMP();
|
||||||
|
|
||||||
|
//! Will create an image with the entire pixel buffer set to 0
|
||||||
explicit BMP(const Eule::Vector2i& size, const Colormode& colormode = Colormode::RGBA);
|
explicit BMP(const Eule::Vector2i& size, const Colormode& colormode = Colormode::RGBA);
|
||||||
|
|
||||||
|
//! Will create a image and read it from a bmp file
|
||||||
|
explicit BMP(const std::string& filename);
|
||||||
|
|
||||||
//! Will return a pointer to the first byte of a pixel at a given position
|
//! Will return a pointer to the first byte of a pixel at a given position
|
||||||
std::uint8_t* GetPixel(const Eule::Vector2i& position);
|
std::uint8_t* GetPixel(const Eule::Vector2i& position);
|
||||||
|
|
||||||
@ -66,6 +73,7 @@ namespace Leonetienne::BmpPP {
|
|||||||
bool isInitialized = false;
|
bool isInitialized = false;
|
||||||
|
|
||||||
friend class BmpWriter;
|
friend class BmpWriter;
|
||||||
|
friend class BmpReader;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
199
Src/BmpReader.cpp
Normal file
199
Src/BmpReader.cpp
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
#include "BmpReader.h"
|
||||||
|
#include "Colormodes.h"
|
||||||
|
#include "BMP.h"
|
||||||
|
#include <fstream>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#ifdef _BMPLIB_DEBUG_OUTPUT_
|
||||||
|
#include <iostream>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Leonetienne::BmpPP {
|
||||||
|
|
||||||
|
bool BmpReader::Read(BMP &image, const std::string &filename) {
|
||||||
|
// Open file
|
||||||
|
std::ifstream ifs(filename, std::ifstream::binary);
|
||||||
|
if (!ifs.good())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// When reading the image, we don't care about most of the header.
|
||||||
|
// All we want is the images resolution, bits-per-pixel, and the pixelbuffer address...
|
||||||
|
// This is because we do not support ALL of bmp, but just the most common formats...
|
||||||
|
|
||||||
|
// Fetch bmp signature
|
||||||
|
std::uint16_t signature;
|
||||||
|
ReadBytes(ifs, signature);
|
||||||
|
|
||||||
|
// Does the signature match?
|
||||||
|
if (signature != 0x4D42) {
|
||||||
|
#ifdef _BMPLIB_DEBUG_OUTPUT_
|
||||||
|
std::cerr << "Bmp signature doesn't match" << std::endl;
|
||||||
|
#endif
|
||||||
|
ifs.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip eight useless bytes (filesize + reserved0 + reserved1)
|
||||||
|
ifs.ignore(8);
|
||||||
|
|
||||||
|
// Read the address of the pixel buffer
|
||||||
|
std::uint32_t pixelBufferAddress;
|
||||||
|
ReadBytes(ifs, pixelBufferAddress);
|
||||||
|
|
||||||
|
// Read the size of the dib header
|
||||||
|
std::uint32_t dibHeaderSize;
|
||||||
|
ReadBytes(ifs, dibHeaderSize);
|
||||||
|
|
||||||
|
std::uint32_t width;
|
||||||
|
std::uint32_t height;
|
||||||
|
std::uint16_t bitsPerPixel;
|
||||||
|
|
||||||
|
// Check if what kind of dib header we're dealing with
|
||||||
|
// These are the two we're supporting (to read, at least).
|
||||||
|
if (dibHeaderSize == 12) {
|
||||||
|
// BITMAPCOREHEADER it is
|
||||||
|
|
||||||
|
// Read the width and the height (both 2-byte ints)
|
||||||
|
std::uint16_t width_2byte;
|
||||||
|
std::uint16_t height_2byte;
|
||||||
|
|
||||||
|
ifs >> width_2byte;
|
||||||
|
ifs >> height_2byte;
|
||||||
|
ReadBytes(ifs, width_2byte);
|
||||||
|
ReadBytes(ifs, height_2byte);
|
||||||
|
|
||||||
|
width = width_2byte;
|
||||||
|
height = height_2byte;
|
||||||
|
|
||||||
|
// Read the bits per pixel
|
||||||
|
ReadBytes(ifs, bitsPerPixel);
|
||||||
|
}
|
||||||
|
else if (dibHeaderSize == 40) {
|
||||||
|
// BITMAPINFOHEADER it is
|
||||||
|
|
||||||
|
// Read the width and height (both 4-byte signed ints)
|
||||||
|
std::int32_t width_4byte_signed;
|
||||||
|
std::int32_t height_4byte_signed;
|
||||||
|
|
||||||
|
ReadBytes(ifs, width_4byte_signed);
|
||||||
|
ReadBytes(ifs, height_4byte_signed);
|
||||||
|
|
||||||
|
width = width_4byte_signed;
|
||||||
|
height = height_4byte_signed;
|
||||||
|
|
||||||
|
// Skip two useless bytes
|
||||||
|
ifs.ignore(2);
|
||||||
|
|
||||||
|
// Read the bits per pixel value
|
||||||
|
ReadBytes(ifs, bitsPerPixel);
|
||||||
|
}
|
||||||
|
else if (dibHeaderSize == 124) {
|
||||||
|
// BITMAPV5HEADER it is
|
||||||
|
|
||||||
|
// Read the width and height
|
||||||
|
ReadBytes(ifs, width);
|
||||||
|
ReadBytes(ifs, height);
|
||||||
|
|
||||||
|
// Skip two useless bytes
|
||||||
|
ifs.ignore(2);
|
||||||
|
|
||||||
|
// Read the bits per pixel value
|
||||||
|
ReadBytes(ifs, bitsPerPixel);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
#ifdef _BMPLIB_DEBUG_OUTPUT_
|
||||||
|
std::cerr << "Unsupported dib header found. Dib header size: " << dibHeaderSize << " bytes." << std::endl;
|
||||||
|
#endif
|
||||||
|
ifs.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert bits per pixel to color mode
|
||||||
|
Colormode colormode;
|
||||||
|
switch (bitsPerPixel) {
|
||||||
|
case 32:
|
||||||
|
colormode = Colormode::RGBA;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 24:
|
||||||
|
colormode = Colormode::RGB;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
#ifdef _BMPLIB_DEBUG_OUTPUT_
|
||||||
|
std::cerr << "Unsupported pixel format found (bits per pixel). Bits per pixel: " << bitsPerPixel << std::endl;
|
||||||
|
#endif
|
||||||
|
ifs.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now read the pixel array
|
||||||
|
// Pre-calculate the channel count
|
||||||
|
const std::size_t numChannels = bitsPerPixel / 8;
|
||||||
|
|
||||||
|
// Calculate how much padding there should be between each row
|
||||||
|
const std::size_t paddingBytesPerRow = (4 - ((width * numChannels) % 4)) % 4;
|
||||||
|
|
||||||
|
// Calculate how long bytes we should care about per row (so, excluding padding)
|
||||||
|
const std::size_t rowWidth = width * numChannels;
|
||||||
|
|
||||||
|
// Create the pixel buffer
|
||||||
|
std::vector<std::uint8_t> pixelArray;
|
||||||
|
pixelArray.resize(width * height * numChannels);
|
||||||
|
|
||||||
|
// Set the reading-head to the address of the pixel array
|
||||||
|
ifs.seekg(pixelBufferAddress);
|
||||||
|
|
||||||
|
// Iterate over all rows, and skip padding after each row
|
||||||
|
for (std::int64_t y = height-1; y >= 0; y--) {
|
||||||
|
const std::size_t rowIndex = y * width * numChannels;
|
||||||
|
|
||||||
|
for (std::size_t x = 0; x < width; x++) {
|
||||||
|
const std::size_t pixelIndex = rowIndex + x * numChannels;
|
||||||
|
|
||||||
|
switch (colormode) {
|
||||||
|
case Colormode::RGBA: {
|
||||||
|
std::uint8_t r, g, b, a;
|
||||||
|
ReadBytes(ifs, b);
|
||||||
|
ReadBytes(ifs, g);
|
||||||
|
ReadBytes(ifs, r);
|
||||||
|
ReadBytes(ifs, a);
|
||||||
|
|
||||||
|
pixelArray[pixelIndex + 0] = r;
|
||||||
|
pixelArray[pixelIndex + 1] = g;
|
||||||
|
pixelArray[pixelIndex + 2] = b;
|
||||||
|
pixelArray[pixelIndex + 3] = a;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Colormode::RGB: {
|
||||||
|
std::uint8_t r, g, b;
|
||||||
|
ReadBytes(ifs, b);
|
||||||
|
ReadBytes(ifs, g);
|
||||||
|
ReadBytes(ifs, r);
|
||||||
|
|
||||||
|
pixelArray[pixelIndex + 0] = r;
|
||||||
|
pixelArray[pixelIndex + 1] = g;
|
||||||
|
pixelArray[pixelIndex + 2] = b;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (paddingBytesPerRow > 0)
|
||||||
|
ifs.ignore(paddingBytesPerRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are done with the file
|
||||||
|
ifs.close();
|
||||||
|
|
||||||
|
// Now construct our image
|
||||||
|
image.ReInitialize(Eule::Vector2i(width, height), colormode);
|
||||||
|
|
||||||
|
// We're a friend-class, so we're going to MOVE (not copy) the pixels over manually. Much faster.
|
||||||
|
image.pixelBuffer.swap(pixelArray);
|
||||||
|
|
||||||
|
// Done.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
36
Src/BmpReader.h
Normal file
36
Src/BmpReader.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#ifndef BMPPP_BMPREADER_H
|
||||||
|
#define BMPPP_BMPREADER_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
namespace Leonetienne::BmpPP {
|
||||||
|
|
||||||
|
class BMP;
|
||||||
|
|
||||||
|
class BmpReader {
|
||||||
|
public:
|
||||||
|
static bool Read(BMP& image, const std::string& filename);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Will read sizeof(T) bytes of is into buffer
|
||||||
|
template <typename T>
|
||||||
|
static std::ifstream& ReadBytes(std::ifstream& is, T& buffer) {
|
||||||
|
const std::size_t sizeofT = sizeof(T);
|
||||||
|
buffer = 0x0;
|
||||||
|
std::uint8_t buf;
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < sizeofT; i++)
|
||||||
|
{
|
||||||
|
is.read((char*)&buf, 1);
|
||||||
|
T bbuf = buf << (i * 8);
|
||||||
|
buffer |= bbuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
return is;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //BMPP_BMPREADER_H
|
@ -33,10 +33,10 @@ namespace Leonetienne::BmpPP {
|
|||||||
const std::size_t numChannels = image.GetNumColorChannels();
|
const std::size_t numChannels = image.GetNumColorChannels();
|
||||||
|
|
||||||
// Calculate how many padding bytes to add per row
|
// Calculate how many padding bytes to add per row
|
||||||
std::size_t paddingBytesPerRow = (4 - ((image.size.x * numChannels) % 4)) % 4;
|
const std::size_t paddingBytesPerRow = (4 - ((image.size.x * numChannels) % 4)) % 4;
|
||||||
|
|
||||||
// Iterate over all pixel rows
|
// Iterate over all pixel rows
|
||||||
for (std::size_t y = 0; y < image.size.y; y++) {
|
for (std::int64_t y = image.size.y-1; y >= 0; y--) {
|
||||||
const std::size_t rowIndex = y * image.size.x * numChannels;
|
const std::size_t rowIndex = y * image.size.x * numChannels;
|
||||||
|
|
||||||
for (std::size_t x = 0; x < image.size.x; x++) {
|
for (std::size_t x = 0; x < image.size.x; x++) {
|
||||||
|
@ -12,5 +12,6 @@ add_library(BmpPP
|
|||||||
|
|
||||||
BMP.cpp
|
BMP.cpp
|
||||||
BmpWriter.cpp
|
BmpWriter.cpp
|
||||||
|
BmpReader.cpp
|
||||||
BmpHeader.cpp
|
BmpHeader.cpp
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user