BmpPP/Src/BMP.cpp

299 lines
7.7 KiB
C++
Raw Normal View History

2022-03-05 16:15:00 +01:00
#include "BMP.h"
#include <stdexcept>
#include <algorithm>
2022-03-05 20:40:49 +01:00
#include "BmpWriter.h"
2022-03-05 22:00:57 +01:00
#include "BmpReader.h"
2022-03-05 16:15:00 +01:00
#define CHECK_IF_INITIALIZED if (!isInitialized) throw std::runtime_error("Image not initialized!");
2022-03-05 16:15:00 +01:00
namespace Leonetienne::BmpPP {
BMP::BMP() {
// Do nothing
return;
}
2022-03-05 22:00:57 +01:00
BMP::BMP(const std::string &filename) {
if(!Read(filename))
throw std::runtime_error("Unable to read bmp image!");
return;
}
2022-03-05 16:15:00 +01:00
BMP::BMP(const Eule::Vector2i &size, const Colormode& colormode)
:
size { size },
colormode { colormode }
2022-03-05 16:15:00 +01:00
{
2022-03-05 19:51:57 +01:00
ReInitialize(size, colormode);
2022-03-05 16:15:00 +01:00
2022-03-05 19:51:57 +01:00
return;
}
void BMP::ReInitialize(const Eule::Vector2i &size) {
isInitialized = true;
2022-03-05 19:51:57 +01:00
// Carry over new attributes
this->size = size;
// Re-initialize the pixelbuffer
pixelBuffer.clear();
pixelBuffer.resize(size.x * size.y * GetNumChannels());
// If we're initializing with an alpha channel, set it to 255
if (colormode == Colormode::RGBA)
FillChannel(3, 255);
2022-03-05 19:51:57 +01:00
return;
}
void BMP::ReInitialize(const Eule::Vector2i &size, const Colormode &colormode) {
isInitialized = true;
2022-03-05 19:51:57 +01:00
// Carry over new attributes
this->size = size;
this->colormode = colormode;
// Re-initialize the pixelbuffer
2022-03-05 16:15:00 +01:00
pixelBuffer.clear();
pixelBuffer.resize(size.x * size.y * GetNumChannels());
// If we're initializing with an alpha channel, set it to 255
if (colormode == Colormode::RGBA)
FillChannel(3, 255);
2022-03-05 16:15:00 +01:00
return;
}
bool BMP::Write(const std::string &filename) const {
CHECK_IF_INITIALIZED
2022-03-05 20:40:49 +01:00
return BmpWriter::Write(*this, filename);
}
2022-03-05 20:40:49 +01:00
bool BMP::Read(const std::string &filename) {
2022-03-05 22:00:57 +01:00
return BmpReader::Read(*this, filename);
}
std::uint8_t *BMP::GetPixel(const Eule::Vector2i &position) {
CHECK_IF_INITIALIZED
const std::size_t pixelIndex =
(position.y * size.x + position.x) * GetNumChannels();
if (pixelIndex >= pixelBuffer.size())
throw std::runtime_error("Pixel index out of range!");
return pixelBuffer.data() + pixelIndex;
}
const std::uint8_t *BMP::GetPixel(const Eule::Vector2i &position) const {
CHECK_IF_INITIALIZED
const std::size_t pixelIndex =
(position.y * size.x + position.x) * GetNumChannels();
if (pixelIndex >= pixelBuffer.size())
throw std::runtime_error("Pixel index out of range!");
return pixelBuffer.data() + pixelIndex;
}
void BMP::SetPixel(const Eule::Vector2i &position,
const std::uint8_t r,
const std::uint8_t g,
const std::uint8_t b,
const std::uint8_t a)
{
CHECK_IF_INITIALIZED
std::uint8_t* pixel = GetPixel(position);
switch (colormode) {
case Colormode::RGBA:
pixel[0] = r;
pixel[1] = g;
pixel[2] = b;
pixel[3] = a;
break;
case Colormode::RGB:
pixel[0] = r;
pixel[1] = g;
pixel[2] = b;
break;
}
return;
2022-03-05 16:15:00 +01:00
}
2022-03-05 19:37:02 +01:00
std::uint8_t *BMP::data() {
CHECK_IF_INITIALIZED
2022-03-05 19:37:02 +01:00
return pixelBuffer.data();
}
const std::uint8_t *BMP::data() const {
CHECK_IF_INITIALIZED
2022-03-05 19:37:02 +01:00
return pixelBuffer.data();
}
const Eule::Vector2i &BMP::GetDimensions() const {
CHECK_IF_INITIALIZED
2022-03-05 19:37:02 +01:00
return size;
}
const Colormode &BMP::GetColormode() const {
CHECK_IF_INITIALIZED
2022-03-05 19:37:02 +01:00
return colormode;
}
std::size_t BMP::GetNumChannels() const {
CHECK_IF_INITIALIZED
2022-03-05 19:37:02 +01:00
switch (colormode) {
case Colormode::RGB:
return 3;
case Colormode::RGBA:
return 4;
}
// Unreachable
return -1;
}
std::size_t BMP::GetPixelbufferSize() const {
CHECK_IF_INITIALIZED
2022-03-05 19:37:02 +01:00
return pixelBuffer.size();
}
bool BMP::IsInitialized() const {
return isInitialized;
}
std::vector<std::uint8_t> &BMP::GetPixelbuffer() {
CHECK_IF_INITIALIZED
return pixelBuffer;
}
const std::vector<std::uint8_t> &BMP::GetPixelbuffer() const {
CHECK_IF_INITIALIZED
return pixelBuffer;
}
bool BMP::operator==(const BMP &other) const {
// Check initialization status
if (isInitialized != other.isInitialized)
return false;
// Check metadata
if (colormode != other.colormode)
return false;
if (size != other.size)
return false;
// Check pixel values
if (pixelBuffer != other.pixelBuffer)
return false;
return true;
}
bool BMP::operator!=(const BMP &other) const {
return !operator==(other);
}
void BMP::FillChannel(const size_t &channel, const std::uint8_t value) {
CHECK_IF_INITIALIZED
if (GetNumChannels() <= channel)
throw std::runtime_error("Channel index out of range!");
const std::size_t numChannels = GetNumChannels();
for (std::size_t i = 0; i < pixelBuffer.size(); i += numChannels)
pixelBuffer[i + channel] = value;
return;
}
void BMP::SwapChannels(const size_t &channel1, const size_t &channel2) {
CHECK_IF_INITIALIZED
2022-03-06 13:00:58 +01:00
if ((GetNumChannels() <= channel1) || (GetNumChannels() <= channel2))
throw std::runtime_error("Channel index out of range!");
const std::size_t numChannels = GetNumChannels();
for (std::size_t i = 0; i < pixelBuffer.size(); i += numChannels)
std::swap(pixelBuffer[i + channel1], pixelBuffer[i + channel2]);
return;
2022-03-06 13:00:58 +01:00
}
BMP BMP::MirrorHorizontally() const {
CHECK_IF_INITIALIZED
// Create a new image matching this's metadata
BMP bmp(size, colormode);
// Now copy over the pixels, mirroring it horizontally
const std::size_t numChannels = GetNumChannels();
for (std::size_t y = 0; y < size.y; y++) {
const std::size_t rowIndex = y * size.x * numChannels;
for (std::size_t x = 0; x < size.x; x++) {
const std::size_t pixelIndex = rowIndex + x * numChannels;
const std::size_t flippedPixelIndex = rowIndex + (size.x - 1 - x) * numChannels;
// Copy over the whole pixel
std::copy(
pixelBuffer.cbegin() + flippedPixelIndex,
pixelBuffer.cbegin() + flippedPixelIndex + numChannels,
bmp.pixelBuffer.begin() + pixelIndex
);
}
}
// return it
return bmp;
}
BMP BMP::MirrorVertically() const {
CHECK_IF_INITIALIZED
// Create a new image matching this's metadata
BMP bmp(size, colormode);
const std::size_t numChannels = GetNumChannels();
const std::size_t rowLength = size.x * numChannels;
// Now iterate over all rows, and copy them over, mirroring it vertically
for (std::size_t y = 0; y < size.y; y++) {
const std::size_t rowIndex = y * rowLength;
const std::size_t flippedRowIndex = (size.y - 1 - y) * rowLength;
// Copy over the whole row
std::copy(
pixelBuffer.cbegin() + flippedRowIndex,
pixelBuffer.cbegin() + flippedRowIndex + rowLength,
bmp.pixelBuffer.begin() + rowIndex
);
}
// return it
return bmp;
}
2022-03-05 16:15:00 +01:00
}
#undef CHECK_IF_INITIALIZED