bwidgets/inc/basic_widgets/core/type/color.hpp

151 lines
5.0 KiB
C++
Raw Permalink Normal View History

2021-08-06 14:22:56 +02:00
#ifndef BWIDGETS_COLOR_HPP
#define BWIDGETS_COLOR_HPP
#include <cmath>
2021-08-06 14:22:56 +02:00
#include <functional>
#include <stdexcept>
2021-08-06 14:22:56 +02:00
#include <SDL_pixels.h>
2021-08-06 14:22:56 +02:00
#include <basic_widgets/core/type/concepts.hpp>
namespace bwidgets
{
2021-08-23 23:50:14 +02:00
// Wrap SDL_Color to provide operator overloads.
2021-08-06 14:22:56 +02:00
class Color final
{
public:
SDL_Color sdl_type;
2021-08-14 09:25:28 +02:00
Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a, bool op_on_alpha = false,
bool op_on_colors = true) noexcept
2021-08-15 16:13:05 +02:00
: sdl_type {r, g, b, a},
_operate_on_alpha {op_on_alpha},
_operate_on_colors {op_on_colors}
2021-08-06 14:22:56 +02:00
{}
2021-08-15 16:13:05 +02:00
Color(SDL_Color c = {}, bool op_on_alpha = false,
bool op_on_colors = true) noexcept
2021-08-15 16:13:05 +02:00
: sdl_type(c), _operate_on_alpha(op_on_alpha), _operate_on_colors(op_on_colors)
2021-08-06 14:22:56 +02:00
{}
Color(const Color&) noexcept = default;
Color(Color&&) noexcept = default;
2021-08-14 09:25:28 +02:00
~Color() = default;
2021-08-06 14:22:56 +02:00
2021-08-23 23:50:14 +02:00
// Enable/disable operations on alpha channel. Disabled by default on
// construction.
[[nodiscard]] auto alpha(const bool state = true) const noexcept -> Color
2021-08-06 14:22:56 +02:00
{
2021-08-23 23:50:14 +02:00
return {sdl_type, _operate_on_colors, state};
2021-08-06 14:22:56 +02:00
}
2021-08-23 23:50:14 +02:00
// Enable/disable operations on color channels.Enabled by default on
// construction.
[[nodiscard]] auto colors(const bool state = true) const noexcept -> Color
2021-08-06 14:22:56 +02:00
{
2021-08-23 23:50:14 +02:00
return {sdl_type, state, _operate_on_alpha};
2021-08-06 14:22:56 +02:00
}
2021-08-23 23:50:14 +02:00
// Get reference to the wrapped SDL native object.
2021-08-15 16:13:05 +02:00
[[nodiscard]] auto& operator()() noexcept
{
return sdl_type;
}
[[nodiscard]] const auto& operator()() const noexcept
{
return sdl_type;
}
2021-08-23 23:50:14 +02:00
// Add operand to enabled channels.
2021-08-06 14:22:56 +02:00
template<Numeric N>
[[nodiscard]] auto operator+(const N operand) const noexcept
2021-08-06 14:22:56 +02:00
{
2021-08-14 16:37:02 +02:00
return _operate(operand, [](N a, N b) { return a + b; });
2021-08-06 14:22:56 +02:00
}
2021-08-23 23:50:14 +02:00
// Substract operand from enabled channels.
2021-08-06 14:22:56 +02:00
template<Numeric N>
[[nodiscard]] auto operator-(const N operand) const noexcept
2021-08-06 14:22:56 +02:00
{
2021-08-14 16:37:02 +02:00
return _operate(operand, [](N a, N b) { return a - b; });
2021-08-06 14:22:56 +02:00
}
2021-08-23 23:50:14 +02:00
// Multiply enabled channel values by opererand.
2021-08-06 14:22:56 +02:00
template<Numeric N>
[[nodiscard]] auto operator*(const N operand) const noexcept
2021-08-06 14:22:56 +02:00
{
2021-08-14 16:37:02 +02:00
return _operate(operand, [](N a, N b) { return a * b; });
2021-08-06 14:22:56 +02:00
}
2021-08-23 23:50:14 +02:00
// Divide enabled channel values by operand.
2021-08-06 14:22:56 +02:00
template<Numeric N>
[[nodiscard]] auto operator/(const N operand) const
2021-08-06 14:22:56 +02:00
{
if (operand == 0) throw std::domain_error("division by zero.");
2021-08-14 16:37:02 +02:00
return _operate<N>(operand, [](N a, N b) { return a / b; });
2021-08-06 14:22:56 +02:00
}
auto operator=(const Color& c) noexcept -> Color& = default;
auto operator=(Color&&) noexcept -> Color& = default;
2021-08-06 14:22:56 +02:00
2021-08-14 16:37:02 +02:00
auto& operator=(SDL_Color c) noexcept
2021-08-06 14:22:56 +02:00
{
sdl_type = c;
return *this;
}
2021-08-23 23:50:14 +02:00
// Compare for equality of the enabled channel values between two Color
// instances.
[[nodiscard]] auto operator==(const Color other) const noexcept
2021-08-06 14:22:56 +02:00
{
return (_operate_on_colors && sdl_type.r == other().r
&& sdl_type.g == other().g && sdl_type.b == other().b)
|| (_operate_on_alpha && sdl_type.a == other().a);
}
2021-08-23 23:50:14 +02:00
// Compare for inequality of the enabled channel values between two Color
// instances.
[[nodiscard]] auto operator!=(const Color other) const noexcept
2021-08-06 14:22:56 +02:00
{
return (sdl_type.r != other().r || sdl_type.g != other().g
|| sdl_type.b != other().b)
&& ((_operate_on_alpha && sdl_type.a != other().a)
|| !_operate_on_alpha);
}
2021-08-15 16:13:05 +02:00
private:
bool _operate_on_alpha;
bool _operate_on_colors;
template<Numeric N>
auto _operate(const N operand, const std::function<N(N, N)>& operator_) const
2021-08-15 16:13:05 +02:00
{
const auto overunderflow_check = [](auto x) {
if (x > (uint8_t)x) throw std::overflow_error("uint8_t overflow.");
if (x < (uint8_t)x) throw std::underflow_error("uint8_t underflow.");
};
2021-08-15 16:13:05 +02:00
Color c(sdl_type, _operate_on_alpha, _operate_on_colors);
if (_operate_on_alpha) {
const auto a = std::round(operator_(c().a, operand));
overunderflow_check(a);
2021-08-15 16:13:05 +02:00
c().a = (uint8_t)a;
}
if (_operate_on_colors) {
const auto r = std::round(operator_(c().r, operand));
const auto g = std::round(operator_(c().g, operand));
const auto b = std::round(operator_(c().b, operand));
for (const auto x : {r, g, b}) overunderflow_check(x);
2021-08-15 16:13:05 +02:00
c().r = (uint8_t)r;
c().g = (uint8_t)g;
c().b = (uint8_t)b;
}
return c;
}
2021-08-06 14:22:56 +02:00
};
}
#endif