/* SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include #include "BLI_math_color.h" namespace blender { /** * CPP based color structures. * * Strongly typed color storage structures with space and alpha association. * Will increase readability and visibility of typical mistakes when * working with colors. * * The storage structs can hold 4 channels (r, g, b and a). * * Usage: * * Convert a theme byte color to a linearrgb premultiplied. * \code{.cc} * ColorTheme4b theme_color; * ColorSceneLinear4f linearrgb_color = * BLI_color_convert_to_scene_linear(theme_color).premultiply_alpha(); * \endcode * * The API is structured to make most use of inlining. Most notable are space * conversions done via `BLI_color_convert_to*` functions. * * - Conversions between spaces (theme <=> scene linear) should always be done by * invoking the `BLI_color_convert_to*` methods. * - Encoding colors (compressing to store colors inside a less precision storage) * should be done by invoking the `encode` and `decode` methods. * - Changing alpha association should be done by invoking `premultiply_alpha` or * `unpremultiply_alpha` methods. * * # Encoding. * * Color encoding is used to store colors with less precision as in using `uint8_t` in * stead of `float`. This encoding is supported for `eSpace::SceneLinear`. * To make this clear to the developer the `eSpace::SceneLinearByteEncoded` * space is added. * * # Precision * * Colors can be stored using `uint8_t` or `float` colors. The conversion * between the two precisions are available as methods. (`to_4b` and * `to_4f`). * * # Alpha conversion * * Alpha conversion is only supported in SceneLinear space. * * Extending this file: * - This file can be extended with `ColorHex/Hsl/Hsv` for different representations * of rgb based colors. `ColorHsl4f` * - Add non RGB spaces/storages ColorXyz. */ /** Enumeration containing the different alpha modes. */ enum class eAlpha { /** Color and alpha are unassociated. */ Straight, /** Color and alpha are associated. */ Premultiplied, }; std::ostream &operator<<(std::ostream &stream, const eAlpha &space); /** Enumeration containing internal spaces. */ enum class eSpace { /** Blender theme color space (sRGB). */ Theme, /** Blender internal scene linear color space (maps to scene_linear role in OCIO). */ SceneLinear, /** Blender internal scene linear color space compressed to be stored in 4 uint8_t. */ SceneLinearByteEncoded, }; std::ostream &operator<<(std::ostream &stream, const eSpace &space); /** Template class to store RGBA values with different precision, space, and alpha association. */ template class ColorRGBA { public: ChannelStorageType r, g, b, a; constexpr ColorRGBA() = default; constexpr ColorRGBA(const ChannelStorageType rgba[4]) : r(rgba[0]), g(rgba[1]), b(rgba[2]), a(rgba[3]) { } constexpr ColorRGBA(const ChannelStorageType r, const ChannelStorageType g, const ChannelStorageType b, const ChannelStorageType a) : r(r), g(g), b(b), a(a) { } operator ChannelStorageType *() { return &r; } operator const ChannelStorageType *() const { return &r; } friend std::ostream &operator<<(std::ostream &stream, const ColorRGBA &c) { stream << Space << Alpha << "(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")"; return stream; } friend bool operator==(const ColorRGBA &a, const ColorRGBA &b) { return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a; } friend bool operator!=(const ColorRGBA &a, const ColorRGBA &b) { return !(a == b); } uint64_t hash() const { uint64_t x1 = *reinterpret_cast(&r); uint64_t x2 = *reinterpret_cast(&g); uint64_t x3 = *reinterpret_cast(&b); uint64_t x4 = *reinterpret_cast(&a); return (x1 * 1283591) ^ (x2 * 850177) ^ (x3 * 735391) ^ (x4 * 442319); } }; /* Forward declarations of concrete color classes. */ template class ColorSceneLinear4f; template class ColorSceneLinearByteEncoded4b; template class ColorTheme4; /* Forward declaration of precision conversion methods. */ BLI_INLINE ColorTheme4 BLI_color_convert_to_theme4f(const ColorTheme4 &srgb4b); BLI_INLINE ColorTheme4 BLI_color_convert_to_theme4b(const ColorTheme4 &srgb4f); template class ColorSceneLinear4f final : public ColorRGBA { public: constexpr ColorSceneLinear4f() : ColorRGBA() { } constexpr ColorSceneLinear4f(const float *rgba) : ColorRGBA(rgba) { } constexpr ColorSceneLinear4f(float r, float g, float b, float a) : ColorRGBA(r, g, b, a) { } /** * Convert to its byte encoded counterpart. */ ColorSceneLinearByteEncoded4b encode() const { ColorSceneLinearByteEncoded4b encoded; linearrgb_to_srgb_uchar4(encoded, *this); return encoded; } /** * Convert color and alpha association to premultiplied alpha. * * Does nothing when color already has a premultiplied alpha. */ ColorSceneLinear4f premultiply_alpha() const { if constexpr (Alpha == eAlpha::Straight) { ColorSceneLinear4f premultiplied; straight_to_premul_v4_v4(premultiplied, *this); return premultiplied; } else { return *this; } } /** * Convert color and alpha association to straight alpha. * * Does nothing when color has straight alpha. */ ColorSceneLinear4f unpremultiply_alpha() const { if constexpr (Alpha == eAlpha::Premultiplied) { ColorSceneLinear4f straighten; premul_to_straight_v4_v4(straighten, *this); return straighten; } else { return *this; } } }; template class ColorSceneLinearByteEncoded4b final : public ColorRGBA { public: constexpr ColorSceneLinearByteEncoded4b() = default; constexpr ColorSceneLinearByteEncoded4b(const uint8_t *rgba) : ColorRGBA(rgba) { } constexpr ColorSceneLinearByteEncoded4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) : ColorRGBA(r, g, b, a) { } /** * Convert to a float color. */ ColorSceneLinear4f decode() const { ColorSceneLinear4f decoded; srgb_to_linearrgb_uchar4(decoded, *this); return decoded; } }; /** * Theme color template class. * * Don't use directly, but use `ColorTheme4b/ColorTheme4b`. * * This has been implemented as a template to improve inlining. When implemented as concrete * classes (ColorTheme4b/f) the functions would be hidden in a compile unit what wouldn't be * inlined. */ template class ColorTheme4 final : public ColorRGBA { public: constexpr ColorTheme4() : ColorRGBA(){}; constexpr ColorTheme4(const ChannelStorageType *rgba) : ColorRGBA(rgba) { } constexpr ColorTheme4(ChannelStorageType r, ChannelStorageType g, ChannelStorageType b, ChannelStorageType a) : ColorRGBA(r, g, b, a) { } /** * Change precision of color to float. */ ColorTheme4 to_4f() const { if constexpr ((std::is_same_v)) { return BLI_color_convert_to_theme4f(*this); } else { return *this; } } /** * Change precision of color to uint8_t. */ ColorTheme4 to_4b() const { if constexpr ((std::is_same_v)) { return BLI_color_convert_to_theme4b(*this); } else { return *this; } } }; using ColorTheme4b = ColorTheme4; using ColorTheme4f = ColorTheme4; BLI_INLINE ColorTheme4b BLI_color_convert_to_theme4b(const ColorTheme4f &theme4f) { ColorTheme4b theme4b; rgba_float_to_uchar(theme4b, theme4f); return theme4b; } BLI_INLINE ColorTheme4f BLI_color_convert_to_theme4f(const ColorTheme4b &theme4b) { ColorTheme4f theme4f; rgba_uchar_to_float(theme4f, theme4b); return theme4f; } BLI_INLINE ColorSceneLinear4f BLI_color_convert_to_scene_linear( const ColorTheme4f &theme4f) { ColorSceneLinear4f scene_linear; srgb_to_linearrgb_v4(scene_linear, theme4f); return scene_linear; } BLI_INLINE ColorSceneLinear4f BLI_color_convert_to_scene_linear( const ColorTheme4b &theme4b) { ColorSceneLinear4f scene_linear; srgb_to_linearrgb_uchar4(scene_linear, theme4b); return scene_linear; } BLI_INLINE ColorTheme4f BLI_color_convert_to_theme4f(const ColorSceneLinear4f &scene_linear) { ColorTheme4f theme4f; linearrgb_to_srgb_v4(theme4f, scene_linear); return theme4f; } BLI_INLINE ColorTheme4b BLI_color_convert_to_theme4b(const ColorSceneLinear4f &scene_linear) { ColorTheme4b theme4b; linearrgb_to_srgb_uchar4(theme4b, scene_linear); return theme4b; } /* Internal roles. For convenience to shorten the type names and hide complexity. */ using ColorGeometry4f = ColorSceneLinear4f; using ColorGeometry4b = ColorSceneLinearByteEncoded4b; using ColorPaint4f = ColorSceneLinear4f; using ColorPaint4b = ColorSceneLinearByteEncoded4b; } // namespace blender