コンテンツにスキップ

Image

  • Color 型を要素とし、二次元配列のように画像データを扱える Image クラスを実装する。
Image.hpp
#pragma once
#include <concepts> // std::integral
#include <vector> // std::vector
#include "Common.hpp"
#include "Color.hpp"
#include "Point.hpp"

namespace seccamp
{
	/// @brief 画像
	class Image
	{
	public:

		/// @brief 使用する配列型
		using container_type			= std::vector<Color>;
		
		/// @brief 要素を指すイテレータ型
		using iterator					= container_type::iterator;

		/// @brief 要素を指す const イテレータ型
		using const_iterator			= container_type::const_iterator;

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

		/// @brief 画像を作成します。
		/// @param width 画像の幅(ピクセル)
		/// @param height 画像の高さ(ピクセル)
		/// @param color 画像の初期色
		[[nodiscard]]
		Image(int32 width, int32 height, const Color& color = Palette::White)
			: m_pixels{ (static_cast<size_t>(width) * static_cast<size_t>(height)), color }
			, m_size{ width, height } {}

		/// @brief 画像を作成します。
		/// @param width 画像の幅(ピクセル)
		/// @param height 画像の高さ(ピクセル)
		/// @param color 画像の初期色
		[[nodiscard]]
		Image(std::integral auto width, std::integral auto height, const Color& color = Palette::White)
			: Image{ static_cast<int32>(width), static_cast<int32>(height), color } {}

		/// @brief 画像を作成します。
		/// @param size 画像の幅と高さ(ピクセル)
		/// @param color 画像の初期色
		[[nodiscard]]
		Image(const Size& size, const Color& color = Palette::White)
			: Image{ size.x, size.y, color } {}

		/// @brief ファイルからデータを読み込んで画像を作成します。
		/// @param path 画像ファイルのパス
		[[nodiscard]]
		explicit Image(std::string_view path);

		/// @brief 2 つの画像が等しいかを返します。
		/// @param lhs 一方の画像データ
		/// @param rhs もう一方の画像データ
		/// @return 2 つの画像が等しい場合 true, それ以外の場合は false
		[[nodiscard]]
		friend bool operator ==(const Image& lhs, const Image& rhs) noexcept
		{
			// 画像の中身を比較する前に、画像のサイズを比較する
			return ((lhs.m_size == rhs.m_size) && (lhs.m_pixels == rhs.m_pixels));
		}

		/// @brief 画像の幅(ピクセル)を返します。
		/// @return 画像の幅(ピクセル)
		[[nodiscard]]
		int32 width() const noexcept
		{
			return m_size.x;
		}

		/// @brief 画像の高さ(ピクセル)を返します。
		/// @return 画像の高さ(ピクセル)
		[[nodiscard]]
		int32 height() const noexcept
		{
			return m_size.y;
		}

		/// @brief 画像の幅と高さ(ピクセル)を返します。
		/// @return 画像の幅と高さ(ピクセル)
		[[nodiscard]]
		Size size() const noexcept
		{
			return m_size;
		}

		/// @brief 画像の総ピクセル数を返します。
		/// @return 画像の総ピクセル数
		[[nodiscard]]
		size_t numPixels() const noexcept
		{
			return m_pixels.size();
		}

		/// @brief 画像が空であるかを返します。
		/// @return 画像が空である場合 true, それ以外の場合は false
		[[nodiscard]]
		bool isEmpty() const noexcept
		{
			return m_pixels.empty();
		}

		/// @brief 画像が空でないかを返します。
		/// @return 画像が空でない場合 true, それ以外の場合は false
		[[nodiscard]]
		explicit operator bool() const noexcept
		{
			return (!m_pixels.empty());
		}

		/// @brief 指定した行の先頭ポインタを返します。
		/// @param y 位置(行)
		/// @remark image[y][x] で指定したピクセルにアクセスします。
		/// @return 指定した行の先頭ポインタ
		[[nodiscard]]
		Color* operator [](size_t y) noexcept
		{
			return (m_pixels.data() + (y * m_size.x));
		}

		/// @brief 指定した行の先頭ポインタを返します。
		/// @param y 位置(行)
		/// @remark image[y][x] で指定したピクセルにアクセスします。
		/// @return 指定した行の先頭ポインタ
		[[nodiscard]]
		const Color* operator [](size_t y) const noexcept
		{
			return (m_pixels.data() + (y * m_size.x));
		}

		/// @brief 画像データの先頭ポインタを返します。
		/// @return 画像データの先頭ポインタ
		[[nodiscard]]
		Color* data() noexcept
		{
			return m_pixels.data();
		}

		/// @brief 画像データの先頭ポインタを返します。
		/// @return 画像データの先頭ポインタ
		[[nodiscard]]
		const Color* data() const noexcept
		{
			return m_pixels.data();
		}

		/// @brief 画像を空にします。
		void clear() noexcept
		{
			m_pixels.clear();
			m_size.clear();
		}

		/// @brief 画像をリサイズします。
		/// @param width 新しい画像の幅(ピクセル)
		/// @param height 新しい画像の高さ(ピクセル)
		/// @param color 新しい画像の初期色
		void resize(int32 width, int32 height, const Color& color = Palette::White)
		{
			m_pixels.assign(width * height, color);
			m_size.set(width, height);
		}

		/// @brief 画像をリサイズします。
		/// @param size 新しい画像の幅と高さ(ピクセル)
		/// @param color 新しい画像の初期色
		void resize(const Size& size, const Color& color = Palette::White)
		{
			resize(size.x, size.y, color);
		}

		/// @brief 画像を指定した色で塗りつぶします。
		/// @param color 塗りつぶしの色
		void fill(const Color& color) noexcept;

		/// @brief 2 つの画像をスワップします。
		/// @param other もう一方の画像
		void swap(Image& other) noexcept
		{
			m_pixels.swap(other.m_pixels);
			std::ranges::swap(m_size, other.m_size);
		}

		/// @brief ピクセル配列の先頭位置を指すイテレータを返します。
		/// @return ピクセル配列の先頭位置を指すイテレータ
		[[nodiscard]]
		iterator begin() noexcept
		{
			return m_pixels.begin();
		}

		/// @brief ピクセル配列の終端位置を指すイテレータを返します。
		/// @return ピクセル配列の終端位置を指すイテレータ
		[[nodiscard]]
		iterator end() noexcept
		{
			return m_pixels.end();
		}

		/// @brief ピクセル配列の先頭位置を指すイテレータを返します。
		/// @return ピクセル配列の先頭位置を指すイテレータ
		[[nodiscard]]
		const_iterator begin() const noexcept
		{
			return m_pixels.begin();
		}

		/// @brief ピクセル配列の終端位置を指すイテレータを返します。
		/// @return ピクセル配列の終端位置を指すイテレータ
		[[nodiscard]]
		const_iterator end() const noexcept
		{
			return m_pixels.end();
		}

		/// @brief 画像をファイルに保存します。
		/// @param path 保存するファイルのパス
		/// @return 保存に成功した場合 true, それ以外の場合は false
		bool save(std::string_view path) const;

		/// @brief 2 つの画像をスワップします。
		/// @param lhs 一方の画像
		/// @param rhs もう一方の画像
		friend void swap(Image& lhs, Image& rhs) noexcept
		{
			lhs.swap(rhs);
		}

	private:

		container_type m_pixels;

		Size m_size{ 0, 0 };
	};
}
Image.cpp
#include <algorithm> // std::ranges::fill
#include "Image.hpp"
#include "BMP.hpp"

namespace seccamp
{
	Image::Image(const std::string_view path)
	{
		*this = LoadBMP(path);
	}

	void Image::fill(const Color& color) noexcept
	{
		std::ranges::fill(m_pixels, color);
	}

	bool Image::save(const std::string_view path) const
	{
		return SaveBMP(*this, path);
	}
}

追加のチャレンジ

  • 左右反転、上下反転、グレースケール化などの各種画像処理を実装する。