Thanks. I mean in hindsight, I agree because I used the stuff I engineered for.
Edit: As far as directx, that's true, except for the format used for the last mile native draw surface. the alpha channel is not used in that situation.
I actually do use variadic templates to do my template definitions.
(background docs if you're interested: https://honeythecodewitch.com/gfx/wiki/pixels.md[^])
Each pixel has one or more channel_traits that are fed into it using variadic templates.
Like so:
template<size_t BitDepth>
using rgb_pixel = pixel<
channel_traits<channel_name::R,(BitDepth/3)>,
channel_traits<channel_name::G,((BitDepth/3)+(BitDepth%3))>,
channel_traits<channel_name::B,(BitDepth/3)>
>;
template<size_t BitDepth>
using rgba_pixel = pixel<
channel_traits<channel_name::R,(BitDepth/4)>,
channel_traits<channel_name::G,((BitDepth/4)+(BitDepth%4))>,
channel_traits<channel_name::B,(BitDepth/4)>,
channel_traits<channel_name::A,(BitDepth/4),0,(1<<(BitDepth/4))-1,(1<<(BitDepth/4))-1>
>;
template<size_t BitDepth>
using gsc_pixel = pixel<
channel_traits<channel_name::L,BitDepth>
>;
template<size_t BitDepth>
using yuv_pixel = pixel<
channel_traits<channel_name::Y,((BitDepth/3)+(BitDepth%3))>,
channel_traits<channel_name::U,(BitDepth/3)>,
channel_traits<channel_name::V,(BitDepth/3)>
>;
template<size_t BitDepth>
using yuva_pixel = pixel<
channel_traits<channel_name::Y,((BitDepth/4)+(BitDepth%4))>,
channel_traits<channel_name::U,(BitDepth/4)>,
channel_traits<channel_name::V,(BitDepth/4)>,
channel_traits<channel_name::A,(BitDepth/4),0,(1<<(BitDepth/4))-1,(1<<(BitDepth/4))-1>
>;
And here's the actual pixel class.
template<typename... ChannelTraits>
struct pixel {
using type = pixel<ChannelTraits...>;
using rgb_conversion_type = pixel<channel_traits<channel_name::R,16>,channel_traits<channel_name::G,16>,channel_traits<channel_name::B,16>,channel_traits<channel_name::A,16>>;
using int_type = bits::uintx<bits::get_word_size(helpers::bit_depth<ChannelTraits...>::value)>;
constexpr static const size_t channels = sizeof...(ChannelTraits);
constexpr static const size_t color_channels = helpers::color_channels_size<ChannelTraits...>::value;
constexpr static const size_t bit_depth = helpers::bit_depth<ChannelTraits...>::value;
constexpr static const size_t packed_size = (bit_depth+7) / 8;
constexpr static const bool byte_aligned = 0==bit_depth/8.0 - (size_t)(bit_depth/8);
constexpr static const size_t total_size_bits = sizeof(int_type)*8;
constexpr static const size_t packed_size_bits = packed_size*8;
constexpr static const size_t pad_right_bits = total_size_bits-bit_depth;
constexpr static const int_type mask = int_type(int_type(~int_type(0))<<(pad_right_bits));
int_type native_value;
constexpr inline pixel() : native_value(0) {
helpers::pixel_init_impl<type,0,ChannelTraits...>::init(*this);
}
constexpr inline pixel(int_type native_value,bool dummy) : native_value(native_value) {
}
constexpr inline pixel(typename ChannelTraits::int_type... values) : native_value(0) {
helpers::pixel_init_impl<type,0,ChannelTraits...>::init(*this,values...);
}
constexpr inline pixel(bool dummy,typename ChannelTraits::real_type... values) : native_value(0) {
helpers::pixel_init_impl<type,0,ChannelTraits...>::initf(*this,values...);
}
constexpr inline int_type value() const {
return helpers::order_guard(native_value);
}
constexpr inline void value(int_type value) {
native_value=helpers::order_guard(value);
}
constexpr inline bool operator==(pixel rhs) {
return rhs.native_value==native_value;
}
constexpr inline bool operator!=(pixel rhs) {
return rhs.native_value!=native_value;
}
template<int Index> using channel_by_index = typename helpers::channel_by_index_impl<type,Index,channels,0,ChannelTraits...>::type;
template<int Index> using channel_by_index_unchecked = typename helpers::channel_by_index_unchecked_impl<type,Index,channels,0,ChannelTraits...>::type;
template<typename Name> using channel_index_by_name = typename helpers::channel_index_by_name_impl<0,Name,ChannelTraits...>;
template<typename Name> using channel_by_name = channel_by_index<helpers::channel_index_by_name_impl<0,Name,ChannelTraits...>::value>;
template<typename Name> using channel_by_name_unchecked = channel_by_index_unchecked<channel_index_by_name<Name>::value>;
template<typename... ChannelNames> using has_channel_names = typename helpers::has_channel_names_impl<type,ChannelNames...>;
template<typename... ChannelNames> using is_color_model = typename helpers::is_color_model_impl<type,ChannelNames...>;
template<typename PixelRhs> using is_subset_of = typename helpers::is_subset_pixel_impl<PixelRhs,ChannelTraits...>;
template<typename PixelRhs> using is_superset_of = typename PixelRhs::template is_subset_of<type>;
template<typename PixelRhs> using unordered_equals = typename helpers::unordered_equals_pixel_impl<type,PixelRhs>;
template<typename PixelRhs> using equals = typename helpers::equals_pixel_impl<PixelRhs,ChannelTraits...>;
template<typename PixelRhs> using equals_exact = typename helpers::is_same<type,PixelRhs>;
template<int Index>
constexpr inline typename channel_by_index_unchecked<Index>::int_type channel_unchecked() const {
return helpers::get_channel_direct_unchecked<type,Index>(native_value);
}
template<int Index>
constexpr inline void channel_unchecked(typename channel_by_index_unchecked<Index>::int_type value) {
helpers::set_channel_direct_unchecked<type,Index>(native_value,value);
}
template<int Index>
constexpr inline typename channel_by_index<Index>::int_type channel() const {
using ch = channel_by_index<Index>;
return typename ch::int_type(typename ch::pixel_type::int_type(native_value & ch::channel_mask)>>ch::total_bits_to_right);
}
template<int Index>
constexpr inline void channel(typename channel_by_index<Index>::int_type value) {
using ch = channel_by_index<Index>;
const typename ch::pixel_type::int_type shval = typename ch::pixel_type::int_type(typename ch::pixel_type::int_type(helpers::clamp(value,ch::min,ch::max))<<ch::total_bits_to_right);
native_value=typename ch::pixel_type::int_type((native_value&typename ch::pixel_type::int_type(~ch::channel_mask))|shval);
}
template<int Index>
constexpr inline typename channel_by_index_unchecked<Index>::real_type channelr() const {
using ch = channel_by_index<Index>;
return channel<Index>()*ch::scaler;
}
template<int Index>
constexpr inline void channelr(typename channel_by_index<Index>::real_type value) {
using ch = channel_by_index<Index>;
channel<Index>(value*ch::scale+.5);
}
template<int Index>
constexpr inline typename channel_by_index_unchecked<Index>::real_type channelr_unchecked() const {
using ch = channel_by_index_unchecked<Index>;
return (typename ch::real_type)channel_unchecked<Index>()*ch::scaler;
}
template<int Index>
constexpr inline void channelr_unchecked(typename channel_by_index<Index>::real_type value) {
using ch = channel_by_index_unchecked<Index>;
channel_unchecked<Index>(value*ch::scale+.5);
}
template<typename Name>
constexpr inline auto channel() const {
const int index = channel_index_by_name<Name>::value;
return channel<index>();
}
template<typename Name>
constexpr inline void channel(typename channel_by_index<channel_index_by_name<Name>::value>::int_type value) {
const int index = channel_index_by_name<Name>::value;
channel<index>(value);
}
template<typename Name>
constexpr inline auto channelr() const {
const int index = channel_index_by_name<Name>::value;
return channelr<index>();
}
template<typename Name>
constexpr inline void channelr(typename channel_by_name<Name>::real_type value) {
const int index = channel_index_by_name<Name>::value;
channelr<index>(value);
}
constexpr double difference(type rhs) const {
return sqrt(helpers::pixel_diff_impl<type,0,ChannelTraits...>::diff_sum(*this,rhs));
}
constexpr gfx_result blend(const type rhs,double ratio,type* out_pixel) const {
if(out_pixel==nullptr) {
return gfx_result::invalid_argument;
}
static_assert(!has_channel_names<channel_name::index>::value,"pixel must not be indexed");
if(ratio==1.0) {
out_pixel->native_value = native_value;
return gfx_result::success;
} else if(ratio==0.0) {
out_pixel->native_value = rhs.native_value;
return gfx_result::success;
}
if(type::template has_channel_names<channel_name::A>::value) {
constexpr const int ai = type::channel_index_by_name<channel_name::A>::value;
float a1 = this->template channelr_unchecked<ai>();
float a2 = rhs.template channelr_unchecked<ai>();
float r2 = a1/a2;
ratio = ratio * r2;
if(ratio>1.0)
ratio = 1.0;
}
helpers::pixel_blend_impl<type,0,ChannelTraits...>::blend_val(*this,rhs,ratio,out_pixel);
return gfx_result::success;
}
constexpr type blend(const type rhs,double ratio) const {
type result;
blend(rhs,ratio,&result);
return result;
}
static_assert(sizeof...(ChannelTraits)>0,"A pixel must have at least one channel trait");
static_assert(bit_depth<=HTCW_MAX_WORD,"Bit depth must be less than or equal to the maximum machine word size");
};
Check out my IoT graphics library here:
https://honeythecodewitch.com/gfx
And my IoT UI/User Experience library here:
https://honeythecodewitch.com/uix
|