コンテンツにスキップ

Color

  • RGBA の色を表すクラスを実装する。
  • 各チャンネルは 8-bit の符号なし整数で表現する。
  • 黒や白などの基本色をわかりやすい名前の定数として提供する。
Color.hpp
#pragma once
#include <iostream> // std::ostream, std::istream
#include <concepts> // std::integral
#include <format> // std::formatter
#include "Common.hpp"

namespace seccamp
{
	/// @brief 色(RGBA)
	struct Color
	{
		/// @brief 赤成分
		uint8 r;

		/// @brief 緑成分
		uint8 g;

		/// @brief 青成分
		uint8 b;

		/// @brief アルファ成分
		uint8 a;

		/// @brief デフォルトコンストラクタ
		[[nodiscard]]
		Color() = default;

		/// @brief 色を作成します。
		/// @param rgb RGB 成分 [0, 255]
		[[nodiscard]]
		explicit constexpr Color(uint8 rgb) noexcept
			: r{ rgb }
			, g{ rgb }
			, b{ rgb }
			, a{ 255 } {}

		/// @brief 色を作成します。
		/// @param rgb RGB 成分 [0, 255]
		[[nodiscard]]
		explicit constexpr Color(std::integral auto rgb) noexcept
			: r{ static_cast<uint8>(rgb) }
			, g{ static_cast<uint8>(rgb) }
			, b{ static_cast<uint8>(rgb) }
			, a{ 255 } {}

		/// @brief 色を作成します。
		/// @param rgb RGB 成分 [0, 255]
		/// @param _a アルファ成分 [0, 255]
		[[nodiscard]]
		constexpr Color(uint8 rgb, uint8 _a) noexcept
			: r{ rgb }
			, g{ rgb }
			, b{ rgb }
			, a{ _a } {}

		/// @brief 色を作成します。
		/// @param rgb RGB 成分 [0, 255]
		/// @param _a アルファ成分 [0, 255]
		[[nodiscard]]
		constexpr Color(std::integral auto rgb, std::integral auto _a) noexcept
			: r{ static_cast<uint8>(rgb) }
			, g{ static_cast<uint8>(rgb) }
			, b{ static_cast<uint8>(rgb) }
			, a{ static_cast<uint8>(_a) } {}

		/// @brief 色を作成します。
		/// @param _r R 成分 [0, 255]
		/// @param _g G 成分 [0, 255]
		/// @param _b B 成分 [0, 255]
		[[nodiscard]]
		constexpr Color(uint8 _r, uint8 _g, uint8 _b) noexcept
			: r{ _r }
			, g{ _g }
			, b{ _b }
			, a{ 255 } {}

		/// @brief 色を作成します。
		/// @param _r R 成分 [0, 255]
		/// @param _g G 成分 [0, 255]
		/// @param _b B 成分 [0, 255]
		[[nodiscard]]
		constexpr Color(std::integral auto _r, std::integral auto _g, std::integral auto _b) noexcept
			: r{ static_cast<uint8>(_r) }
			, g{ static_cast<uint8>(_g) }
			, b{ static_cast<uint8>(_b) }
			, a{ 255 } {}

		/// @brief 色を作成します。
		/// @param _r R 成分 [0, 255]
		/// @param _g G 成分 [0, 255]
		/// @param _b B 成分 [0, 255]
		/// @param _a アルファ成分 [0, 255]
		[[nodiscard]]
		constexpr Color(uint8 _r, uint8 _g, uint8 _b, uint8 _a) noexcept
			: r{ _r }
			, g{ _g }
			, b{ _b }
			, a{ _a } {}

		/// @brief 色を作成します。
		/// @param _r R 成分 [0, 255]
		/// @param _g G 成分 [0, 255]
		/// @param _b B 成分 [0, 255]
		/// @param _a アルファ成分 [0, 255]
		[[nodiscard]]
		constexpr Color(std::integral auto _r, std::integral auto _g, std::integral auto _b, std::integral auto _a) noexcept
			: r{ static_cast<uint8>(_r) }
			, g{ static_cast<uint8>(_g) }
			, b{ static_cast<uint8>(_b) }
			, a{ static_cast<uint8>(_a) } {}

		[[nodiscard]]
		friend constexpr bool operator ==(const Color& lhs, const Color& rhs) noexcept = default;

		/// @brief グレースケール値を返します。
		/// @return グレースケール値
		[[nodiscard]]
		constexpr uint8 grayscaleUint8() const noexcept
		{
			return static_cast<uint8>((0.299 * r) + (0.587 * g) + (0.114 * b));
		}

		/// @brief グレースケール値を返します。
		/// @return グレースケール値
		[[nodiscard]]
		constexpr double grayscale() const noexcept
		{
			return ((0.299 / 255.0 * r) + (0.587 / 255.0 * g) + (0.114 / 255.0 * b));
		}

		/// @brief 出力ストリームに書き込みます。
		/// @param os 出力ストリーム
		/// @param c 書き込む値
		/// @return 出力ストリーム
		friend std::ostream& operator <<(std::ostream& os, const Color& c)
		{
			return os << '(' << static_cast<uint16>(c.r) << ", "
				<< static_cast<uint16>(c.g) << ", "
				<< static_cast<uint16>(c.b) << ", "
				<< static_cast<uint16>(c.a) << ')';
		}

		/// @brief 入力ストリームから読み込みます。
		/// @param is 入力ストリーム
		/// @param c 読み込んだ値の格納先
		/// @return 入力ストリーム
		friend std::istream& operator >>(std::istream& is, Color& c)
		{
			char t;
			uint16 r, g, b, a;
			is >> t >> r >> t >> g >> t >> b >> t;
			
			if (t == ')')
			{
				c.r = static_cast<uint8>(r);
				c.g = static_cast<uint8>(g);
				c.b = static_cast<uint8>(b);
				c.a = 255;
				return is;
			}
			else
			{
				is >> a >> t;
				c.r = static_cast<uint8>(r);
				c.g = static_cast<uint8>(g);
				c.b = static_cast<uint8>(b);
				c.a = static_cast<uint8>(a);
				return is;
			}
		}
	};

	namespace Palette
	{
		/// @brief 黒色
		inline constexpr Color Black{ 0, 0, 0 };

		/// @brief 白色
		inline constexpr Color White{ 255, 255, 255 };
	}
}

/// @brief Color 型を std::format に対応させるための std::formatter 特殊化
template<>
struct std::formatter<seccamp::Color>
{
	template <class ParseContext>
	constexpr auto parse(ParseContext& ctx)
	{
		return ctx.begin();
	}

	template <class FormtContext>
	auto format(const seccamp::Color& c, FormtContext& ctx) const
	{
		return std::format_to(ctx.out(), "({}, {}, {}, {})", c.r, c.g, c.b, c.a);
	}
};

追加のチャレンジ

  • #FF8800#FF8800FF のような 16 進数表記の文字列から Color を作成する。
  • 16 進数表記に変換する。
  • 実装が大きくなる場合は Color.hppColor.cpp に分割するとよい。