potisanのプログラミングメモ

プログラミング素人です。昔の自分を育ててくれたネット情報に少しでも貢献できるよう、情報を貯めていこうと思っています。Windows環境のC++やC#がメインです。

C++20 Win32 APIを使用したHLS色空間クラス

Win32 API、具体的にはShell lightweight utility functions(Microsoft Docs)ColorHLSToRGB関数、ColotRGBToHLS関数を使用したHLS色空間クラスです。

std::clampを使っているので動作環境をC++20としていますが、適当に書き換えればC++17以前にも対応できます。

ソースコード

// win32_hls_color.hpp
// 動作確認環境:C++ 20

#include <algorithm>
#include <stdexcept>
#include <tuple>

#include <shlwapi.h>

class win32_hls_color
{
private:
    uint16_t _h;
    uint16_t _l;
    uint16_t _s;
public:
    static const uint16_t h_min = 0;
    static const uint16_t h_max = 240;
    static const uint16_t l_min = 0;
    static const uint16_t l_max = 240;
    static const uint16_t s_min = 0;
    static const uint16_t s_max = 240;

    static constexpr win32_hls_color min() noexcept { return from_hls_nochecked(h_min, l_min, s_min); }
    static constexpr win32_hls_color max() noexcept { return from_hls_nochecked(h_max, l_max, s_max); }

    constexpr win32_hls_color() noexcept
        : _h(), _l(), _s() {}
    constexpr win32_hls_color(const win32_hls_color& source) noexcept
        : _h(source._h), _l(source._l), _s(source._s) {}
    constexpr win32_hls_color(win32_hls_color&& source) noexcept
        : _h(source._h), _l(source._l), _s(source._s) {}

    constexpr uint16_t get_h() const noexcept { return _h; }
    constexpr uint16_t get_l() const noexcept { return _l; }
    constexpr uint16_t get_s() const noexcept { return _s; }
    constexpr void get_hsl(uint16_t& h, uint16_t l, uint16_t& s) const noexcept
    {
        h = _h;
        l = _l;
        s = _s;
    }
    constexpr std::tuple<uint16_t, uint16_t, uint16_t> get_hls_tuple() const noexcept
    {
        return { _h, _l, _s };
    }

    constexpr void set_h(uint16_t h)
    {
        if (!(h_min <= h && h <= h_max)) {
            throw std::range_error(nullptr);
        }
        _h = h;
    }
    constexpr void set_l(uint16_t l)
    {
        if (!(l_min <= l && l <= l_max)) {
            throw std::range_error(nullptr);
        }
        _l = l;
    }
    constexpr void set_s(uint16_t s)
    {
        if (!(s_min <= s && s <= s_max)) {
            throw std::range_error(nullptr);
        }
        _s = s;
    }
    constexpr void set_hls(uint16_t h, uint16_t s, uint16_t l)
    {
        set_h(h);
        set_l(l);
        set_s(s);
    }
    constexpr void set_hls_tuple(std::tuple<uint16_t, uint16_t, uint16_t> tuple)
    {
        set_h(std::get<0>(tuple));
        set_l(std::get<1>(tuple));
        set_s(std::get<2>(tuple));
    }
    constexpr void set_h_clamped(uint16_t h) noexcept
    {
        _h = std::clamp(h, h_min, h_max);
    }
    constexpr void set_l_clamped(uint16_t l) noexcept
    {
        _l = std::clamp(l, l_min, l_max);
    }
    constexpr void set_s_clamped(uint16_t s) noexcept
    {
        _s = std::clamp(s, s_min, s_max);
    }
    constexpr void set_hls_clamped(uint16_t h, uint16_t s, uint16_t l) noexcept
    {
        set_h_clamped(h);
        set_l_clamped(l);
        set_s_clamped(s);
    }
    constexpr void set_hls_tuple_clamped(std::tuple<uint16_t, uint16_t, uint16_t> tuple) noexcept
    {
        set_h_clamped(std::get<0>(tuple));
        set_l_clamped(std::get<1>(tuple));
        set_s_clamped(std::get<2>(tuple));
    }

    uint32_t to_rgb() noexcept
    {
        return ColorHLSToRGB(_h, _l, _s);
    }

    static win32_hls_color from_rgb(uint32_t rgb) noexcept
    {
        uint16_t h, l, s;
        ColorRGBToHLS(rgb, &h, &l, &s);
        return from_hls_nochecked(h, l, s);
    }
    static win32_hls_color from_rgb(uint8_t r, uint8_t g, uint8_t b) noexcept
    {
        return from_rgb(RGB(r, g, b));
    }

    static constexpr win32_hls_color from_hls(uint16_t h, uint16_t l, uint16_t s)
    {
        win32_hls_color color;
        color.set_h(h);
        color.set_l(l);
        color.set_s(s);
        return color;
    }
    static constexpr win32_hls_color from_hls_clamped(uint16_t h, uint16_t l, uint16_t s) noexcept
    {
        win32_hls_color color;
        color.set_h_clamped(h);
        color.set_l_clamped(l);
        color.set_s_clamped(s);
        return color;
    }
    static constexpr win32_hls_color from_hls(std::initializer_list<uint16_t> hls)
    {
        auto i = hls.begin();
        win32_hls_color color;
        if (i != hls.end()) {
            color.set_h(*i++);
            if (i != hls.end()) {
                color.set_l(*i++);
                if (i != hls.end()) {
                    color.set_s(*i);
                }
            }
        }
        return color;
    }
    static constexpr win32_hls_color from_hls_clamped(std::initializer_list<uint16_t> hls) noexcept
    {
        auto i = hls.begin();
        win32_hls_color color;
        if (i != hls.end()) {
            color.set_h_clamped(*i++);
            if (i != hls.end()) {
                color.set_l_clamped(*i++);
                if (i != hls.end()) {
                    color.set_s_clamped(*i);
                }
            }
        }
        return color;
    }
private:
    static constexpr win32_hls_color from_hls_nochecked(uint16_t h, uint16_t l, uint16_t s) noexcept
    {
        win32_hls_color color;
        color._h = h;
        color._l = l;
        color._s = s;
        return color;
    }
};

サンプルコード

#pragma comment(lib, "shlwapi.lib")
#define STRICT
#define NOMINMAX
#include <Windows.h>
#include <Shlwapi.h>

#include "win32_hls_color.hpp"

int main()
{
    auto red = win32_hls_color::from_rgb(255, 0, 0);
    auto green = win32_hls_color::from_rgb(0, 255, 0);
    auto blue = win32_hls_color::from_rgb(0, 0, 255);
    auto yellow = win32_hls_color::from_rgb(255, 255, 0);
    auto magenta = win32_hls_color::from_rgb(255, 0, 255);
    auto cyan = win32_hls_color::from_rgb(0, 255, 255);
    auto black = win32_hls_color::from_rgb(0, 0, 0);
    auto white = win32_hls_color::from_rgb(255, 255, 255);
    // name    { H ,  S ,  L }
    // red     {  0, 120, 240}
    // green   { 80, 120, 240}
    // blue    {160, 120, 240}
    // yellow  { 40, 120, 240}
    // magenta {200, 120, 240}
    // cyan    {120, 120, 240}
    // black   {160,   0,   0}
    // white   {160, 240,   0}
}