257 lines
6.4 KiB
C++
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
|