Files
NBEngine/engine/NBGraphics/include/NBGraphics/Image.hpp
T

257 lines
6.4 KiB
C++

#pragma once
#ifndef _NB_IMAGE
#define _NB_IMAGE
#include <cstring>
#include <NBCore/Errors.hpp>
namespace nb {
template<typename T>
struct Color {};
struct Red : Color<Red> {};
struct Green : Color<Green> {};
struct Blue : Color<Blue> {};
struct Alpha : Color<Alpha> {};
struct Depth;
struct Stencil;
template<typename... T>
struct PixelChannel;
template<typename T>
struct PixelChannel<T, Red> {
using type=T;
T value;
T& r=value;
};
template<typename T>
struct PixelChannel<T, Green> {
using type=T;
T value;
T& g=value;
};
template<typename T>
struct PixelChannel<T, Blue> {
using type=T;
T value;
T& b=value;
};
template<typename T>
struct PixelChannel<T, Alpha> {
using type=T;
T value;
T& a=value;
};
template<typename T>
struct PixelChannel<T, Depth> {
using type=T;
T value;
T& z=value;
};
template<>
struct PixelChannel<uint8_t, Stencil> {
using type=uint8_t;
uint8_t value;
uint8_t& u=value;
};
template<typename T, typename... Channels>
struct Pixel : public PixelChannel<T, Channels>... {
Pixel() = default;
Pixel& operator=(const Pixel& rhs) {
((
[&](const typename PixelChannel<T, Channels>::type& val){
this->PixelChannel<T, Channels>::value = val;
}(rhs.PixelChannel<T, Channels>::value)
), ...);
return *this;
}
Pixel(const Pixel& cpy)
: Pixel(cpy.PixelChannel<T, Channels>::value...) {}
Pixel(typename PixelChannel<T, Channels>::type... vals)
: PixelChannel<T, Channels>{vals}... {}
};
template<typename Ref>
struct PixelReference;
template<typename T, typename... Channels>
struct PixelReference<Pixel<T, Channels...>>
: public Pixel<T&, Channels...> {
using PixelType = Pixel<T, Channels...>;
using Base = Pixel<T&, Channels...>;
using Base::Base;
PixelReference(PixelType& pixel)
: Base(pixel.PixelChannel<T, Channels>::value...) {}
operator PixelType() {
return PixelType(this->PixelChannel<T&, Channels>::value...);
}
PixelReference& operator=(const PixelType& pixel) {
((
[&](const typename PixelChannel<T, Channels>::type& val){
this->PixelChannel<T&, Channels>::value = val;
}(pixel.PixelChannel<T, Channels>::value)
), ...);
return *this;
}
};
template <typename T>
using RGBA = Pixel<T, Red, Green, Blue, Alpha>;
template <typename T>
using RGB = Pixel<T, Red, Green, Blue>;
class ImageError : public Error<ImageError> {
protected:
using Base = Error<ImageError>;
using Base::Base;
public:
enum Codes : unsigned int {
UNDEFINED, OUT_OF_BOUNDS
};
static const std::string type;
static const ErrorCodeMap ErrorMessages;
};
template<typename T>
class Image;
template<typename T, typename... Channels>
class Image<Pixel<T, Channels...>> {
private:
Error<> sizeCompare(const Image& rhs) {
const std::string errmsg = "Cannot assign images of different size";
if (width != rhs.width) {
return Error<>(
Error<>::VALUE_ERROR,
errmsg+"[ Width: "+std::to_string(width)+\
" != "+std::to_string(rhs.width)+" ]"
);
}
if (height != rhs.height) {
return Error<>(
Error<>::VALUE_ERROR,
errmsg+"[ Width: "+std::to_string(width)+\
" != "+std::to_string(rhs.width)+" ]"
);
}
return Error<>("NoError");
}
protected:
using Codes = ImageError::Codes;
bool _data_managed;
T* _data;
Image(unsigned int x, unsigned int y, T* data, bool managed)
: width(x), height(y), _data(data), _data_managed(managed) {}
void clear() {
if (_data_managed) {
delete[] _data;
_data_managed = false;
}
_data = nullptr;
}
public:
using PixelType = Pixel<T, Channels...>;
static constexpr size_t NumberChannels = sizeof...(Channels);
const unsigned int width;
const unsigned int height;
static Image CreateImageStorage(
unsigned int width,
unsigned int height,
T* data=nullptr
) {
unsigned int data_size = NumberChannels*width*height*sizeof(T);
T* data_ptr = new T[data_size];
Image ret(width, height, data_ptr, true);
if (data) {
std::memcpy(data_ptr, data, data_size);
} else {
std::memset(data_ptr, 0x00, data_size);
}
ret._data_managed = true;
return ret;
}
Image(unsigned int x, unsigned int y, T* data=nullptr)
: width(x), height(y), _data(data), _data_managed(false) {}
Image(const Image& cpy)
: Image(cpy.width, cpy.height) {
*this = cpy;
}
Image(Image&& mv)
: Image(mv.width, mv.height) {
*this = std::move(mv);
}
Image& operator=(const Image& cpy) {
if (_data_managed) {
THROW(Error<>(Error<>::OVERWRITE_ERROR));
}
const Error<> sizeErr = sizeCompare(cpy);
if (sizeErr.code!=1) { THROW(sizeErr); }
if (!cpy._data_managed) {
_data = cpy._data;
_data_managed = false;
} else {
*this = cpy.copy();
}
return *this;
}
Image& operator=(Image&& mv) {
if (_data_managed) {
THROW(Error<>(Error<>::OVERWRITE_ERROR));
}
const Error<> sizeErr = sizeCompare(mv);
if (sizeErr.code!=1) { THROW(sizeErr); }
_data = mv._data;
_data_managed = mv._data_managed;
mv._data_managed = false;
return *this;
}
virtual ~Image() {
clear();
}
const T* data() const { return _data; }
Image<PixelType> copy() const {
return CreateImageStorage(width, height, _data);
}
PixelReference<PixelType> at(unsigned int x, unsigned int y) {
if (!(x<width) || !(y<height)) {
std::string note = "w/ ("+std::to_string(x);
note += ", "+std::to_string(y) + ") >= (";
note += std::to_string(width)+", "+std::to_string(height);
note += ")";
THROW(ImageError(
Codes::OUT_OF_BOUNDS,
note
));
}
auto coord = y*height+x;
return PixelReference<PixelType>(
_data[coord*NumberChannels+Find<Channels, Channels...>::value-1]...
);
}
};
} // namespace nb
#endif // _NB_IMAGE