potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。

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}
}