円柱モデルのHSV色を扱うクラスとそれを利用したHSV四角形(V固定)の描画サンプルです。RGBとHSVの変換はWikipediaのコードを使用しています。
HSVをH:[0, 360)、SとV:[0, 1]で扱うとRGBも[0, 1]で扱う方が簡単です。そのため、[0, 1]のRGBと[0, 255]のRGB、それらの相互変換も定義しています。
HSV色クラス
// cylindrical_hsv_color.hpp #include <stdexcept> #include <cmath> #include "rgb_color.hpp" /// <summary> /// 円柱モデルのHSV色(H:[0, 360)、S:[0, 1]、V:[0, 1]) /// </summary> class cylindrical_hsv_color_f { private: float h, s, v; public: constexpr cylindrical_hsv_color_f() = default; constexpr cylindrical_hsv_color_f(float h, float s, float v) { set_hsv(h, s, v); } constexpr float get_h() const noexcept { return h; } constexpr float get_s() const noexcept { return s; } constexpr float get_v() const noexcept { return v; } constexpr void set_h(float value) { if (!(0 <= value && value < 360)) throw_out_of_range(); h = value; } constexpr void set_s(float value) { if (!(0 <= value && value <= 1)) throw_out_of_range(); s = value; } constexpr void set_v(float value) { if (!(0 <= value && value <= 1)) throw_out_of_range(); v = value; } constexpr void set_hsv(float h, float s, float v) { set_h(h); set_s(s); set_v(v); } constexpr void get_hsv(float& h, float& s, float& v) const noexcept { h = this->h; s = this->s; v = this->v; } constexpr bool operator== (const cylindrical_hsv_color_f& other) const noexcept { return h == other.h && s == other.s && v == other.v; } constexpr bool operator!= (const cylindrical_hsv_color_f& other) const noexcept { return !(*this == other); } private: static void throw_out_of_range() { throw std::out_of_range(""); } }; rgb_color1f to_rgb_color1f(const cylindrical_hsv_color_f& hsv) noexcept { auto c = hsv.get_v() * hsv.get_s(); auto hd = hsv.get_h() / 60.0f; auto x = c * (1 - std::abs(std::fmodf(hd, 2) - 1)); float r1, g1, b1; switch (static_cast<int>(hd)) { case 0: r1 = c; g1 = x; b1 = 0; break; case 1: r1 = x; g1 = c; b1 = 0; break; case 2: r1 = 0; g1 = c; b1 = x; break; case 3: r1 = 0; g1 = x; b1 = c; break; case 4: r1 = x; g1 = 0; b1 = c; break; default: r1 = c; g1 = 0; b1 = x; break; } auto m = hsv.get_v() - c; rgb_color1f rgb; rgb.set_rgb_nocheck(r1 + m, g1 + m, b1 + m); return rgb; } constexpr cylindrical_hsv_color_f to_cyrindrical_hsv_color_f(const rgb_color1f& rgb) noexcept { auto r = rgb.get_r(); auto g = rgb.get_g(); auto b = rgb.get_b(); auto max = std::max(r, std::max(g, b)); auto min = std::min(r, std::min(g, b)); auto diff = max - min; cylindrical_hsv_color_f hsv; if (min == b) hsv.set_h(60 * (g - r) / diff + 60); else if (min = r) hsv.set_h(60 * (b - g) / diff + 180); else hsv.set_h(60 * (r - b) / diff + 300); hsv.set_s(diff / max); hsv.set_v(max); return hsv; }
RGB色クラス
// rgb_color.hpp #include <cstdint> #include <stdexcept> class rgb_color255 { public: constexpr rgb_color255() = default; constexpr rgb_color255(std::uint8_t r, std::uint8_t g, std::uint8_t b) { set_rgb(r, g, b); } constexpr std::uint8_t get_r() const noexcept { return r; } constexpr std::uint8_t get_g() const noexcept { return g; } constexpr std::uint8_t get_b() const noexcept { return b; } constexpr void set_r(std::uint8_t value) { r = value; } constexpr void set_g(std::uint8_t value) { g = value; } constexpr void set_b(std::uint8_t value) { b = value; } constexpr void get_rgb(std::uint8_t& r, std::uint8_t& g, std::uint8_t& b) const noexcept { r = this->r; g = this->g; b = this->b; } constexpr void set_rgb(std::uint8_t r, std::uint8_t g, std::uint8_t b) { set_r(r); set_g(g); set_b(b); } constexpr bool operator==(const rgb_color255& other) { return r == other.r && g == other.g && b == other.b; } constexpr bool operator!=(const rgb_color255& other) { return !(*this == other); } private: std::uint8_t r; std::uint8_t g; std::uint8_t b; }; class rgb_color1f { public: constexpr rgb_color1f() = default; rgb_color1f(float r, float g, float b) { set_rgb(r, g, b); } constexpr float get_r() const noexcept { return r; } constexpr float get_g() const noexcept { return g; } constexpr float get_b() const noexcept { return b; } void set_r(float value) { if (!(0 <= value && value <= 1)) throw_out_of_range(); r = value; } void set_g(float value) { if (!(0 <= value && value <= 1)) throw_out_of_range(); g = value; } void set_b(float value) { if (!(0 <= value && value <= 1)) throw_out_of_range(); b = value; } constexpr void set_r_nocheck(float value) noexcept { r = value; } constexpr void set_g_nocheck(float value) noexcept { g = value; } constexpr void set_b_nocheck(float value) noexcept { b = value; } constexpr void get_rgb(float& r, float& g, float& b) const noexcept { r = this->r; g = this->g; b = this->b; } void set_rgb(float r, float g, float b) { set_r(r); set_g(g); set_b(b); } constexpr void set_rgb_nocheck(float r, float g, float b) noexcept { set_r_nocheck(r); set_g_nocheck(g); set_b_nocheck(b); } constexpr bool operator==(const rgb_color1f& other) { return r == other.r && g == other.g && b == other.b; } constexpr bool operator!=(const rgb_color1f& other) { return !(*this == other); } private: float r; float g; float b; static void throw_out_of_range() { throw std::out_of_range(""); } }; constexpr rgb_color1f to_rgb_color1f(const rgb_color255& source) noexcept { rgb_color1f rgb; rgb.set_rgb_nocheck( source.get_r() / 255.0f, source.get_g() / 255.0f, source.get_b() / 255.0f); return rgb; } constexpr rgb_color255 to_rgb_color255(const rgb_color1f& source) noexcept { return rgb_color255( static_cast<std::uint8_t>(source.get_r() * 255), static_cast<std::uint8_t>(source.get_g() * 255), static_cast<std::uint8_t>(source.get_b() * 255)); }
背景をHSV四角形で塗りつぶしたウィンドウを表示するコード
#include <bit> #include <string> #include "cylindrical_hsv_color.hpp" #define STRICT #define WIN32_LEAN_AND_MEAN #define NOMINMAX #include <Windows.h> const std::wstring_view MAINWINDOW_CLASSNAME{ L"MainWindow" }; const std::wstring_view MAINWINDOW_WINDOWNAME{ L"hsv_color_square" }; LRESULT CALLBACK MainWindow_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); void MainWindow_OnClose(HWND hwnd); void MainWindow_OnPaint(HWND hwnd); HBITMAP CreateHSVSquareDIBSectionWithV(int w, int h, float v); inline constexpr long GetWidth(const RECT& rc) noexcept { return rc.right - rc.left; } inline constexpr long GetHeight(const RECT& rc) noexcept { return rc.bottom - rc.top; } HWND g_hwndMain = nullptr; int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int cmdShow) { WNDCLASSEXW wcex{ sizeof(wcex) }; wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.hCursor = LoadCursorW(nullptr, IDC_ARROW); wcex.hIcon = LoadIconW(nullptr, IDI_APPLICATION); wcex.lpfnWndProc = MainWindow_WndProc; wcex.lpszClassName = MAINWINDOW_CLASSNAME.data(); if (!RegisterClassExW(&wcex)) return -1; auto hwnd = CreateWindowExW( WS_EX_OVERLAPPEDWINDOW, MAINWINDOW_CLASSNAME.data(), MAINWINDOW_WINDOWNAME.data(), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, nullptr, nullptr); ShowWindow(hwnd, cmdShow); UpdateWindow(hwnd); MSG msg; int ret; while ((ret = GetMessageW(&msg, nullptr, 0, 0)) != 0) { if (ret == -1) break; TranslateMessage(&msg); DispatchMessageW(&msg); } return ret; } LRESULT CALLBACK MainWindow_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_CLOSE: MainWindow_OnClose(hwnd); return 0; case WM_PAINT: MainWindow_OnPaint(hwnd); return 0; default: return DefWindowProcW(hwnd, msg, wParam, lParam); } } void MainWindow_OnClose(HWND hwnd) { PostQuitMessage(0); } void MainWindow_OnPaint(HWND hwnd) { PAINTSTRUCT ps; BeginPaint(hwnd, &ps); RECT rcClient; GetClientRect(hwnd, &rcClient); auto w = GetWidth(rcClient); auto h = GetHeight(rcClient); auto hbmp = CreateHSVSquareDIBSectionWithV(w, h, 1); auto hdcBmp = CreateCompatibleDC(ps.hdc); SelectObject(hdcBmp, hbmp); BitBlt(ps.hdc, 0, 0, w, h, hdcBmp, 0, 0, SRCCOPY); DeleteDC(hdcBmp); DeleteObject(hbmp); EndPaint(hwnd, &ps); } HBITMAP CreateHSVSquareDIBSectionWithV(int w, int h, float v) { BITMAPINFOHEADER bih{ sizeof(bih), w, h, 1, 32 }; LPBYTE pixels = nullptr; auto hbmp = CreateDIBSection( nullptr, std::bit_cast<LPBITMAPINFO>(&bih), DIB_RGB_COLORS, std::bit_cast<void**>(&pixels), nullptr, 0); auto x0 = 0; auto dx = 360.0f / w; auto dy = 1.0f / h; for (int y = 0; y < h; ++y) { auto ydy = y * dy; for (int x = 0; x < w; ++x) { cylindrical_hsv_color_f hsv(x * dx, ydy, v); auto rgb = to_rgb_color255(to_rgb_color1f(hsv)); pixels[x0] = rgb.get_b(); // B pixels[++x0] = rgb.get_g(); // G pixels[++x0] = rgb.get_r(); // R x0 += 2; } } return hbmp; }