Huge graphics overhaul
This commit is contained in:
@@ -0,0 +1,216 @@
|
||||
#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...>> {
|
||||
protected:
|
||||
using Codes = ImageError::Codes;
|
||||
T* _data;
|
||||
Image(unsigned int x, unsigned int y)
|
||||
: width(x), height(y), _data(nullptr) {}
|
||||
|
||||
public:
|
||||
using PixelType = Pixel<T, Channels...>;
|
||||
static constexpr size_t NumberChannels = sizeof...(Channels);
|
||||
const unsigned int width;
|
||||
const unsigned int height;
|
||||
Image(unsigned int x, unsigned int y, const T* data)
|
||||
: Image(x, y) {
|
||||
size_t data_size = width*height*NumberChannels;
|
||||
_data = new T[data_size];
|
||||
std::memcpy(_data, data, data_size*sizeof(T));
|
||||
}
|
||||
Image(const Image&) = delete;
|
||||
Image& operator=(const Image&) = delete;
|
||||
Image& operator=(Image&&) = delete;
|
||||
Image(Image&& mv)
|
||||
: width(mv.width), height(mv.height), _data(mv._data) {
|
||||
mv._data = nullptr;
|
||||
}
|
||||
|
||||
~Image() {
|
||||
if (_data) {
|
||||
delete[] _data;
|
||||
}
|
||||
}
|
||||
|
||||
const T* data() const { return _data; }
|
||||
Image&& copy() const {
|
||||
return Image(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]...
|
||||
);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename... T>
|
||||
class ImageReference;
|
||||
|
||||
template<typename T, typename... Channels>
|
||||
class ImageReference<Pixel<T, Channels...>> : public Image<Pixel<T, Channels...>> {
|
||||
protected:
|
||||
using Base = Image<Pixel<T, Channels...>>;
|
||||
using Base::_data;
|
||||
|
||||
public:
|
||||
using Base::width;
|
||||
using Base::height;
|
||||
using Base::copy;
|
||||
ImageReference(unsigned int x, unsigned int y, T* data)
|
||||
: Base(x, y) {
|
||||
_data = data;
|
||||
}
|
||||
~ImageReference() noexcept {
|
||||
Base::_data = nullptr;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace nb
|
||||
|
||||
#endif // _NB_IMAGE
|
||||
Reference in New Issue
Block a user