//----------------------------------------------------------------------------
// Borland WinSys Library
// Copyright (c) 1992, 1996 by Borland International, All Rights Reserved
//
/// \file
/// Implementation of color classes
//----------------------------------------------------------------------------
 
#include <owl/pch.h>
#include <owl/color.h>
#include <owl/except.h> // TXOwl
#include <owl/system.h>
#include <unordered_map>
#include <algorithm>
#include <iomanip> // std::hex
#include <cmath> // fmod
 
using namespace std;
 
namespace owl {
 
//
/// Convert a bit count into a color count for color table use, verifying that
/// the bit count is one that is supported by Windows, ie 1, 4, 8, 16, 24, 32.
///
/// \returns For \p bitCount less than 16, 2 to the power of \p bitCount is returned. For
/// \p bitCount greater or equal to 16, 0 is returned. If the bit count is not supported, -1 is
/// returned.
//
_OWLFUNC(auto) NColors(int bitCount) -> int
{
  if (bitCount == 1 || bitCount == 4 || bitCount == 8)
    return 1 << bitCount;
  if (bitCount == 16 || bitCount == 24 || bitCount == 32)
    return 0;
  return -1;
}
 
//
/// Returns the number of bits required to represent a given number of colors.
//
_OWLFUNC(auto) NBits(int colorCount) -> int
{
  if (colorCount <= 2)
    return 1;
  if (colorCount <= 16)
    return 4;
  if (colorCount <= 256)
    return 8;
  if (colorCount <= 65536)
    return 16;
  if (colorCount <= 16777216)
    return 24;
  return 32;
}
 
//
// The basic predefined colors.
//
const TColor TColor::Black{0, 0, 0};
const TColor TColor::LtGray{192, 192, 192};
const TColor TColor::Gray{128, 128, 128};
const TColor TColor::LtRed{255, 0, 0};
const TColor TColor::LtGreen{0, 255, 0};
const TColor TColor::LtYellow{255, 255, 0};
const TColor TColor::LtBlue{0, 0, 255};
const TColor TColor::LtMagenta{255, 0, 255};
const TColor TColor::LtCyan{0, 255, 255};
const TColor TColor::White{255, 255, 255};
 
//
// Additional colors defined by the WWW Consortium.
// https://www.w3schools.com/colors/colors_names.asp
//
// Copied and adapted from Ales Krajnc's "Naming Commmon Colors" at CodeProject.
// https://www.codeproject.com/Articles/1276/Naming-Common-Colors
//
// Note: Colors Blue, Green and Red in the WWW color list have been renamed to XBlue, XGreen and
// XRed, due to name clash with existing TColor members. Also note that Cyan, Magenta and Yellow
// have identical definitions to LtCyan, LtMagenta and LtYellow already defined. Finally, note that
// the list omits Black, Gray and White, with no renamed variants, since these were already defined
// with the same values.
//
const TColor TColor::AliceBlue{240, 248, 255};
const TColor TColor::AntiqueWhite{250, 235, 215};
const TColor TColor::Aqua{0, 255, 255};
const TColor TColor::Aquamarine{127, 255, 212};
const TColor TColor::Azure{240, 255, 255};
const TColor TColor::Beige{245, 245, 220};
const TColor TColor::Bisque{255, 228, 196};
const TColor TColor::BlanchedAlmond{255, 235, 205};
const TColor TColor::BlueViolet{138, 43, 226};
const TColor TColor::Brown{165, 42, 42};
const TColor TColor::Burlywood{222, 184, 135};
const TColor TColor::CadetBlue{95, 158, 160};
const TColor TColor::Chartreuse{127, 255, 0};
const TColor TColor::Chocolate{210, 105, 30};
const TColor TColor::Coral{255, 127, 80};
const TColor TColor::CornflowerBlue{100, 149, 237};
const TColor TColor::Cornsilk{255, 248, 220};
const TColor TColor::Crimson{220, 20, 60};
const TColor TColor::Cyan{0, 255, 255};
const TColor TColor::DarkBlue{0, 0, 139};
const TColor TColor::DarkCyan{0, 139, 139};
const TColor TColor::DarkGoldenRod{184, 134, 11};
const TColor TColor::DarkGray{169, 169, 169};
const TColor TColor::DarkGreen{0, 100, 0};
const TColor TColor::DarkKhaki{189, 183, 107};
const TColor TColor::DarkMagenta{139, 0, 139};
const TColor TColor::DarkOliveGreen{85, 107, 47};
const TColor TColor::DarkOrange{255, 140, 0};
const TColor TColor::DarkOrchid{153, 50, 204};
const TColor TColor::DarkRed{139, 0, 0};
const TColor TColor::DarkSalmon{233, 150, 122};
const TColor TColor::DarkSeaGreen{143, 188, 143};
const TColor TColor::DarkSlateBlue{72, 61, 139};
const TColor TColor::DarkSlateGray{47, 79, 79};
const TColor TColor::DarkTurquoise{0, 206, 209};
const TColor TColor::DarkViolet{148, 0, 211};
const TColor TColor::DeepPink{255, 20, 147};
const TColor TColor::DeepSkyBlue{0, 191, 255};
const TColor TColor::DimGray{105, 105, 105};
const TColor TColor::DodgerBlue{30, 144, 255};
const TColor TColor::FireBrick{178, 34, 34};
const TColor TColor::FloralWhite{255, 250, 240};
const TColor TColor::ForestGreen{34, 139, 34};
const TColor TColor::Fuchsia{255, 0, 255};
const TColor TColor::Gainsboro{220, 220, 220};
const TColor TColor::GhostWhite{248, 248, 255};
const TColor TColor::Gold{255, 215, 0};
const TColor TColor::GoldenRod{218, 165, 32};
const TColor TColor::GreenYellow{173, 255, 47};
const TColor TColor::HoneyDew{240, 255, 240};
const TColor TColor::HotPink{255, 105, 180};
const TColor TColor::IndianRed{205, 92, 92};
const TColor TColor::Indigo{75, 0, 130};
const TColor TColor::Ivory{255, 255, 240};
const TColor TColor::Khaki{240, 230, 140};
const TColor TColor::Lavender{230, 230, 250};
const TColor TColor::LavenderBlush{255, 240, 245};
const TColor TColor::Lawngreen{124, 252, 0};
const TColor TColor::LemonChiffon{255, 250, 205};
const TColor TColor::LightBlue{173, 216, 230};
const TColor TColor::LightCoral{240, 128, 128};
const TColor TColor::LightCyan{224, 255, 255};
const TColor TColor::LightGoldenRodYellow{250, 250, 210};
const TColor TColor::LightGreen{144, 238, 144};
const TColor TColor::LightGrey{211, 211, 211};
const TColor TColor::LightPink{255, 182, 193};
const TColor TColor::LightSalmon{255, 160, 122};
const TColor TColor::LightSeaGreen{32, 178, 170};
const TColor TColor::LightSkyBlue{135, 206, 250};
const TColor TColor::LightSlateGray{119, 136, 153};
const TColor TColor::LightSteelBlue{176, 196, 222};
const TColor TColor::LightYellow{255, 255, 224};
const TColor TColor::Lime{0, 255, 0};
const TColor TColor::LimeGreen{50, 205, 50};
const TColor TColor::Linen{250, 240, 230};
const TColor TColor::Magenta{255, 0, 255};
const TColor TColor::Maroon{128, 0, 0};
const TColor TColor::MediumAquamarine{102, 205, 170};
const TColor TColor::MediumBlue{0, 0, 205};
const TColor TColor::MediumOrchid{186, 85, 211};
const TColor TColor::MediumPurple{147, 112, 219};
const TColor TColor::MediumSeaGreen{60, 179, 113};
const TColor TColor::MediumSlateBlue{123, 104, 238};
const TColor TColor::MediumSpringGreen{0, 250, 154};
const TColor TColor::MediumTurquoise{72, 209, 204};
const TColor TColor::MediumVioletRed{199, 21, 133};
const TColor TColor::MidnightBlue{25, 25, 112};
const TColor TColor::MintCream{245, 255, 250};
const TColor TColor::MistyRose{255, 228, 225};
const TColor TColor::Moccasin{255, 228, 181};
const TColor TColor::NavajoWhite{255, 222, 173};
const TColor TColor::Navy{0, 0, 128};
const TColor TColor::Navyblue{159, 175, 223};
const TColor TColor::OldLace{253, 245, 230};
const TColor TColor::Olive{128, 128, 0};
const TColor TColor::OliveDrab{107, 142, 35};
const TColor TColor::Orange{255, 165, 0};
const TColor TColor::OrangeRed{255, 69, 0};
const TColor TColor::Orchid{218, 112, 214};
const TColor TColor::PaleGoldenRod{238, 232, 170};
const TColor TColor::PaleGreen{152, 251, 152};
const TColor TColor::PaleTurquoise{175, 238, 238};
const TColor TColor::PaleVioletRed{219, 112, 147};
const TColor TColor::PapayaWhip{255, 239, 213};
const TColor TColor::PeachPuff{255, 218, 185};
const TColor TColor::Peru{205, 133, 63};
const TColor TColor::Pink{255, 192, 203};
const TColor TColor::Plum{221, 160, 221};
const TColor TColor::PowderBlue{176, 224, 230};
const TColor TColor::Purple{128, 0, 128};
const TColor TColor::RosyBrown{188, 143, 143};
const TColor TColor::RoyalBlue{65, 105, 225};
const TColor TColor::SaddleBrown{139, 69, 19};
const TColor TColor::Salmon{250, 128, 114};
const TColor TColor::SandyBrown{244, 164, 96};
const TColor TColor::SeaGreen{46, 139, 87};
const TColor TColor::SeaShell{255, 245, 238};
const TColor TColor::Sienna{160, 82, 45};
const TColor TColor::Silver{192, 192, 192};
const TColor TColor::SkyBlue{135, 206, 235};
const TColor TColor::SlateBlue{106, 90, 205};
const TColor TColor::SlateGray{112, 128, 144};
const TColor TColor::Snow{255, 250, 250};
const TColor TColor::SpringGreen{0, 255, 127};
const TColor TColor::SteelBlue{70, 130, 180};
const TColor TColor::Tan{210, 180, 140};
const TColor TColor::Teal{0, 128, 128};
const TColor TColor::Thistle{216, 191, 216};
const TColor TColor::Tomato{255, 99, 71};
const TColor TColor::Turquoise{64, 224, 208};
const TColor TColor::Violet{238, 130, 238};
const TColor TColor::Wheat{245, 222, 179};
const TColor TColor::WhiteSmoke{245, 245, 245};
const TColor TColor::Yellow{255, 255, 0};
const TColor TColor::YellowGreen{154, 205, 50};
const TColor TColor::XBlue{0, 0, 255};
const TColor TColor::XGreen{0, 128, 0};
const TColor TColor::XRed{255, 0, 0};
 
//
// Special marker colors using flag bit pattern. Value never really used.
// Value must not change for streaming compatibility w/ OWL's TWindow
//
const TColor TColor::None{COLORREF{0xFF000000UL}};
const TColor TColor::Transparent{COLORREF{0xFE000000UL}};
 
namespace {
 
//
// Symbolic system colors looked up on evaluation or conversion.
//
const auto Symbolic_ = COLORREF{0x80000000UL};
 
} // namespace
 
//
/// The symbolic system color value for what is usually the gray area of scrollbars.
/// This is the region that the scrollbar slider slides upon.
//
const TColor TColor::SysScrollbar{Symbolic_ | COLOR_SCROLLBAR};
 
//
/// The symbolic system color value for the desktop.
//
const TColor TColor::SysDesktop{Symbolic_ | COLOR_BACKGROUND};
 
//
/// The symbolic system color value for the caption of the active window.
//
const TColor TColor::SysActiveCaption{Symbolic_ | COLOR_ACTIVECAPTION};
 
//
/// The symbolic system color value for the caption background of every inactive window.
//
const TColor TColor::SysInactiveCaption{Symbolic_ | COLOR_INACTIVECAPTION};
 
//
/// The symbolic system color value for the background of menus.
//
const TColor TColor::SysMenu{Symbolic_ | COLOR_MENU};
 
//
/// The symbolic system color value for the background of each window.
//
const TColor TColor::SysWindow{Symbolic_ | COLOR_WINDOW};
 
//
/// The symbolic system color value for the frame around each window.
/// The frame is not the same as the border.
//
const TColor TColor::SysWindowFrame{Symbolic_ | COLOR_WINDOWFRAME};
 
//
/// The symbolic system color value for the text shown on menus.
//
const TColor TColor::SysMenuText{Symbolic_ | COLOR_MENUTEXT};
 
//
/// The symbolic system color value for text in every window.
//
const TColor TColor::SysWindowText{Symbolic_ | COLOR_WINDOWTEXT};
 
//
/// The symbolic system color value for text in captions and size boxes, and for the arrow boxes on
/// scroll bars.
//
const TColor TColor::SysCaptionText{Symbolic_ | COLOR_CAPTIONTEXT};
 
//
/// The symbolic system color value for the borders of the active window.
//
const TColor TColor::SysActiveBorder{Symbolic_ | COLOR_ACTIVEBORDER};
 
//
/// The symbolic system color value for the borders of every inactive window.
//
const TColor TColor::SysInactiveBorder{Symbolic_ | COLOR_INACTIVEBORDER};
 
//
/// The symbolic system color value for the background of multiple document interface (MDI)
/// applications.
//
const TColor TColor::SysAppWorkspace{Symbolic_ | COLOR_APPWORKSPACE};
 
//
/// The symbolic system color value for items selected in a control.
//
const TColor TColor::SysHighlight{Symbolic_ | COLOR_HIGHLIGHT};
 
//
/// The symbolic system color value for text selected in a control.
//
const TColor TColor::SysHighlightText{Symbolic_ | COLOR_HIGHLIGHTTEXT};
 
//
/// The symbolic system color value for the face color of 3-dimensional display elements.
//
const TColor TColor::Sys3dFace{Symbolic_ | COLOR_BTNFACE};
 
//
/// The symbolic system color value for the shadow regions of 3-dimensional display elements (for
/// edges facing away from the light source).
//
const TColor TColor::Sys3dShadow{Symbolic_ | COLOR_BTNSHADOW};
 
//
/// The symbolic system color value for grayed (disabled) text.
///
/// This color is set to 0 if the current display driver does not support a solid gray color.
//
const TColor TColor::SysGrayText{Symbolic_ | COLOR_GRAYTEXT};
 
//
/// The symbolic system color value for the text on buttons.
//
const TColor TColor::SysBtnText{Symbolic_ | COLOR_BTNTEXT};
 
//
/// The symbolic system color value for the caption text of every inactive window.
//
const TColor TColor::SysInactiveCaptionText{Symbolic_ | COLOR_INACTIVECAPTIONTEXT};
 
//
/// The symbolic system color value for highlighted 3-dimensional display elements (for edges
/// facing the light source).
//
const TColor TColor::Sys3dHilight{Symbolic_ | COLOR_BTNHIGHLIGHT};
 
//
/// The symbolic system color value for dark shadow regions of 3-dimensional display elements.
//
const TColor TColor::Sys3dDkShadow{Symbolic_ | COLOR_3DDKSHADOW};
 
//
/// The symbolic system color value for the light color for 3-dimensional display elements (for
/// edges facing the light source).
//
const TColor TColor::Sys3dLight{Symbolic_ | COLOR_3DLIGHT};
 
//
/// The symbolic system color value for text shown on tooltip controls.
//
const TColor TColor::SysInfoText{Symbolic_ | COLOR_INFOTEXT};
 
//
/// The symbolic system color value for the background of tooltip controls.
//
const TColor TColor::SysInfoBk{Symbolic_ | COLOR_INFOBK};
 
namespace {
 
using TColorTable_ = std::unordered_multimap<TColor, tstring>;
 
auto GetColorTable_() -> const TColorTable_&
{
  using namespace std::string_literals;
  static const auto table = TColorTable_
  {
    // HTML/CSS colors
 
    {TColor::AliceBlue, _T("Alice Blue"s)},
    {TColor::AntiqueWhite, _T("AntiqueWhite"s)},
    {TColor::Aqua, _T("Aqua"s)},
    {TColor::Aquamarine, _T("Aquamarine"s)},
    {TColor::Azure, _T("Azure"s)},
    {TColor::Beige, _T("Beige"s)},
    {TColor::Bisque, _T("Bisque"s)},
    {TColor::BlanchedAlmond, _T("BlanchedAlmond"s)},
    {TColor::BlueViolet, _T("BlueViolet"s)},
    {TColor::Brown, _T("Brown"s)},
    {TColor::Burlywood, _T("Burlywood"s)},
    {TColor::CadetBlue, _T("CadetBlue"s)},
    {TColor::Chartreuse, _T("Chartreuse"s)},
    {TColor::Chocolate, _T("Chocolate"s)},
    {TColor::Coral, _T("Coral"s)},
    {TColor::CornflowerBlue, _T("CornflowerBlue"s)},
    {TColor::Cornsilk, _T("Cornsilk"s)},
    {TColor::Crimson, _T("Crimson"s)},
    {TColor::Cyan, _T("Cyan"s)},
    {TColor::DarkBlue, _T("DarkBlue"s)},
    {TColor::DarkCyan, _T("DarkCyan"s)},
    {TColor::DarkGoldenRod, _T("DarkGoldenRod"s)},
    {TColor::DarkGray, _T("DarkGray"s)},
    {TColor::DarkGreen, _T("DarkGreen"s)},
    {TColor::DarkKhaki, _T("DarkKhaki"s)},
    {TColor::DarkMagenta, _T("DarkMagenta"s)},
    {TColor::DarkOliveGreen, _T("DarkOliveGreen"s)},
    {TColor::DarkOrange, _T("DarkOrange"s)},
    {TColor::DarkOrchid, _T("DarkOrchid"s)},
    {TColor::DarkRed, _T("DarkRed"s)},
    {TColor::DarkSalmon, _T("DarkSalmon"s)},
    {TColor::DarkSeaGreen, _T("DarkSeaGreen"s)},
    {TColor::DarkSlateBlue, _T("DarkSlateBlue"s)},
    {TColor::DarkSlateGray, _T("DarkSlateGray"s)},
    {TColor::DarkTurquoise, _T("DarkTurquoise"s)},
    {TColor::DarkViolet, _T("DarkViolet"s)},
    {TColor::DeepPink, _T("DeepPink"s)},
    {TColor::DeepSkyBlue, _T("DeepSkyBlue"s)},
    {TColor::DimGray, _T("DimGray"s)},
    {TColor::DodgerBlue, _T("DodgerBlue"s)},
    {TColor::FireBrick, _T("FireBrick"s)},
    {TColor::FloralWhite, _T("FloralWhite"s)},
    {TColor::ForestGreen, _T("ForestGreen"s)},
    {TColor::Fuchsia, _T("Fuchsia"s)},
    {TColor::Gainsboro, _T("Gainsboro"s)},
    {TColor::GhostWhite, _T("GhostWhite"s)},
    {TColor::Gold, _T("Gold"s)},
    {TColor::GoldenRod, _T("GoldenRod"s)},
    {TColor::GreenYellow, _T("GreenYellow"s)},
    {TColor::HoneyDew, _T("HoneyDew"s)},
    {TColor::HotPink, _T("HotPink"s)},
    {TColor::IndianRed, _T("IndianRed"s)},
    {TColor::Indigo, _T("Indigo"s)},
    {TColor::Ivory, _T("Ivory"s)},
    {TColor::Khaki, _T("Khaki"s)},
    {TColor::Lavender, _T("Lavender"s)},
    {TColor::LavenderBlush, _T("LavenderBlush"s)},
    {TColor::Lawngreen, _T("Lawngreen"s)},
    {TColor::LemonChiffon, _T("LemonChiffon"s)},
    {TColor::LightBlue, _T("LightBlue"s)},
    {TColor::LightCoral, _T("LightCoral"s)},
    {TColor::LightCyan, _T("LightCyan"s)},
    {TColor::LightGoldenRodYellow, _T("LightGoldenRodYellow"s)},
    {TColor::LightGreen, _T("LightGreen"s)},
    {TColor::LightGrey, _T("LightGrey"s)},
    {TColor::LightPink, _T("LightPink"s)},
    {TColor::LightSalmon, _T("LightSalmon"s)},
    {TColor::LightSeaGreen, _T("LightSeaGreen"s)},
    {TColor::LightSkyBlue, _T("LightSkyBlue"s)},
    {TColor::LightSlateGray, _T("LightSlateGray"s)},
    {TColor::LightSteelBlue, _T("LightSteelBlue"s)},
    {TColor::LightYellow, _T("LightYellow"s)},
    {TColor::Lime, _T("Lime"s)},
    {TColor::LimeGreen, _T("LimeGreen"s)},
    {TColor::Linen, _T("Linen"s)},
    {TColor::Magenta, _T("Magenta"s)},
    {TColor::Maroon, _T("Maroon"s)},
    {TColor::MediumAquamarine, _T("MediumAquamarine"s)},
    {TColor::MediumBlue, _T("MediumBlue"s)},
    {TColor::MediumOrchid, _T("MediumOrchid"s)},
    {TColor::MediumPurple, _T("MediumPurple"s)},
    {TColor::MediumSeaGreen, _T("MediumSeaGreen"s)},
    {TColor::MediumSlateBlue, _T("MediumSlateBlue"s)},
    {TColor::MediumSpringGreen, _T("MediumSpringGreen"s)},
    {TColor::MediumTurquoise, _T("MediumTurquoise"s)},
    {TColor::MediumVioletRed, _T("MediumVioletRed"s)},
    {TColor::MidnightBlue, _T("MidnightBlue"s)},
    {TColor::MintCream, _T("MintCream"s)},
    {TColor::MistyRose, _T("MistyRose"s)},
    {TColor::Moccasin, _T("Moccasin"s)},
    {TColor::NavajoWhite, _T("NavajoWhite"s)},
    {TColor::Navy, _T("Navy"s)},
    {TColor::Navyblue, _T("Navyblue"s)},
    {TColor::OldLace, _T("OldLace"s)},
    {TColor::Olive, _T("Olive"s)},
    {TColor::OliveDrab, _T("OliveDrab"s)},
    {TColor::Orange, _T("Orange"s)},
    {TColor::OrangeRed, _T("OrangeRed"s)},
    {TColor::Orchid, _T("Orchid"s)},
    {TColor::PaleGoldenRod, _T("PaleGoldenRod"s)},
    {TColor::PaleGreen, _T("PaleGreen"s)},
    {TColor::PaleTurquoise, _T("PaleTurquoise"s)},
    {TColor::PaleVioletRed, _T("PaleVioletRed"s)},
    {TColor::PapayaWhip, _T("PapayaWhip"s)},
    {TColor::PeachPuff, _T("PeachPuff"s)},
    {TColor::Peru, _T("Peru"s)},
    {TColor::Pink, _T("Pink"s)},
    {TColor::Plum, _T("Plum"s)},
    {TColor::PowderBlue, _T("PowderBlue"s)},
    {TColor::Purple, _T("Purple"s)},
    {TColor::RosyBrown, _T("RosyBrown"s)},
    {TColor::RoyalBlue, _T("RoyalBlue"s)},
    {TColor::SaddleBrown, _T("SaddleBrown"s)},
    {TColor::Salmon, _T("Salmon"s)},
    {TColor::SandyBrown, _T("SandyBrown"s)},
    {TColor::SeaGreen, _T("SeaGreen"s)},
    {TColor::SeaShell, _T("SeaShell"s)},
    {TColor::Sienna, _T("Sienna"s)},
    {TColor::Silver, _T("Silver"s)},
    {TColor::SkyBlue, _T("SkyBlue"s)},
    {TColor::SlateBlue, _T("SlateBlue"s)},
    {TColor::SlateGray, _T("SlateGray"s)},
    {TColor::Snow, _T("Snow"s)},
    {TColor::SpringGreen, _T("SpringGreen"s)},
    {TColor::SteelBlue, _T("SteelBlue"s)},
    {TColor::Tan, _T("Tan"s)},
    {TColor::Teal, _T("Teal"s)},
    {TColor::Thistle, _T("Thistle"s)},
    {TColor::Tomato, _T("Tomato"s)},
    {TColor::Turquoise, _T("Turquoise"s)},
    {TColor::Violet, _T("Violet"s)},
    {TColor::Wheat, _T("Wheat"s)},
    {TColor::WhiteSmoke, _T("WhiteSmoke"s)},
    {TColor::Yellow, _T("Yellow"s)},
    {TColor::YellowGreen, _T("YellowGreen"s)},
    {TColor::XBlue, _T("Blue"s)},
    {TColor::XGreen, _T("Green"s)},
    {TColor::XRed, _T("Red"s)},
 
    // Special colors
 
    {TColor::None, _T("None"s)},
    {TColor::Transparent, _T("Transparent"s)},
 
    // Basic predefind colors
 
    {TColor::Black, _T("Black"s)},
    {TColor::Gray, _T("Gray"s)},
    {TColor::LtBlue, _T("LtBlue"s)},
    {TColor::LtCyan, _T("LtCyan"s)},
    {TColor::LtGray, _T("LtGray"s)},
    {TColor::LtGreen, _T("LtGreen"s)},
    {TColor::LtMagenta, _T("LtMagenta"s)},
    {TColor::LtRed, _T("LtRed"s)},
    {TColor::LtYellow, _T("LtYellow"s)},
    {TColor::White, _T("White"s)},
 
    // Symbolic colors
 
    {TColor::Sys3dDkShadow, _T("Sys3dDkShadow"s)},
    {TColor::Sys3dFace, _T("Sys3dFace"s)},
    {TColor::Sys3dHilight, _T("Sys3dHilight"s)},
    {TColor::Sys3dLight, _T("Sys3dLight"s)},
    {TColor::Sys3dShadow, _T("Sys3dShadow"s)},
    {TColor::SysActiveBorder, _T("SysActiveBorder"s)},
    {TColor::SysActiveCaption, _T("SysActiveCaption"s)},
    {TColor::SysAppWorkspace, _T("SysAppWorkspace"s)},
    {TColor::SysBtnText, _T("SysBtnText"s)},
    {TColor::SysCaptionText, _T("SysCaptionText"s)},
    {TColor::SysDesktop, _T("SysDesktop"s)},
    {TColor::SysGrayText, _T("SysGrayText"s)},
    {TColor::SysHighlight, _T("SysHighlight"s)},
    {TColor::SysHighlightText, _T("SysHighlightText"s)},
    {TColor::SysInactiveBorder, _T("SysInactiveBorder"s)},
    {TColor::SysInactiveCaption, _T("SysInactiveCaption"s)},
    {TColor::SysInactiveCaptionText, _T("SysInactiveCaptionText"s)},
    {TColor::SysInfoBk, _T("SysInfoBk"s)},
    {TColor::SysInfoText, _T("SysInfoText"s)},
    {TColor::SysMenu, _T("SysMenu"s)},
    {TColor::SysScrollbar, _T("SysScrollbar"s)},
    {TColor::SysMenuText, _T("SysMenuText"s)},
    {TColor::SysWindow, _T("SysWindow"s)},
    {TColor::SysWindowFrame, _T("SysWindowFrame"s)},
    {TColor::SysWindowText, _T("SysWindowText"s)}
  };
  return table;
}
 
} // namespace
 
//
/// Overload; see TColor::CreateFromHsl(const THsl&).
//
inline auto TColor::CreateFromHsl(float hue, float saturation, float lightness) -> TColor
{
  PRECONDITION(hue >= 0.0f && hue <= 360.0f); // degrees
  PRECONDITION(saturation >= 0.0f && saturation <= 100.0f); // percent
  PRECONDITION(lightness >= 0.0f && lightness <= 100.0f); // percent
 
  const auto i = lightness / 100.0f;
  const auto s = saturation / 100.0f;
 
  const auto calcGrayscale = [&]
  {
    const auto v = static_cast<int>(round(i * 255.0f));
    return TColor{v, v, v};
  };
 
  const auto calcColor = [&]
  {
    const auto calcComponent = [](float a, float b, float hue) -> int
    {
      const auto h =
        (hue > 360.0f) ? hue - 360.0f :
        (hue < 0.0f) ? hue + 360.0f :
        hue;
      const auto v =
        (h < 60.0f) ? a + (b - a) * h / 60.0f :
        (h < 180.0f) ? b :
        (h < 240.0f) ? a + (b - a) * (240.0f - h) / 60.0f :
        a;
      return min(255, static_cast<int>(round(v * 255.0f)));
    };
 
    const auto m = 0.5f; // 128.0f / 255.0f; // Bias the midpoint for RGB; 128 is half.
    const auto b = (i <= m) ? i + i * s : i + s - i * s;
    const auto a = 2.0f * i - b;
    const auto red = calcComponent(a, b, hue + 120.0f);
    const auto green = calcComponent(a, b, hue);
    const auto blue = calcComponent(a, b, hue - 120.0f);
    return TColor{red, green, blue};
  };
 
  return (s == 0.0f) ? calcGrayscale() : calcColor();
}
 
//
/// Constructs a color from the given color name, if recognized.
///
/// The function recognizes the following color names:
///
/// - HTML/CSS color names defined by the WWW Consortium (note that Aqua is the same color as Cyan,
/// and Fuchsia is the same color as Magenta).
///
/// - Basic predefined colors: Black, Gray, LtBlue, LtCyan (Aqua), LtGray (Silver), LtGreen (Lime),
/// LtMagenta (Fuchsia), LtRed (Red), LtYellow (Yellow) and White.
///
/// - Symbolic colors: Sys3dDkShadow, Sys3dFace, Sys3dHilight, Sys3dLight, Sys3dShadow,
/// SysActiveBorder, SysActiveCaption, SysAppWorkspace, SysBtnText, SysCaptionText, SysDesktop,
/// SysGrayText, SysHighlight, SysHighlightText, SysInactiveBorder, SysInactiveCaption,
/// SysInactiveCaptionText, SysInfoBk, SysInfoText, SysMenu, SysScrollbar, SysMenuText, SysWindow,
/// SysWindowFrame and SysWindowText.
///
/// - Special color values: None and Transparent.
///
/// \exception TXOwl is thrown, if the color name is not recognized.
///
/// \sa <a href="https://www.w3schools.com/colors/colors_names.asp">
/// HTML Color Names</a> at W3Schools.
//
auto TColor::CreateFromName(const tstring_view& name) -> TColor
{
  const auto& t = GetColorTable_();
  const auto i = find_if(t.cbegin(), t.cend(), [&name](const auto& v) { return v.second == name; });
  if (i == t.end()) throw TXOwl{_T("TColor::CreateFromName: Unknown color name")};
  return i->first;
}
 
//
/// Creates a color from the given HTML/CSS hexadecimal value (HEX).
///
/// The given string must not contain any whitespace or content other than the value.
//
/// \exception TXOwl is thrown on failure to parse the string.
///
/// \sa <a href="https://www.w3schools.com/colors/colors_hex.asp">
/// HTML Color Values</a> at W3Schools.
//
 
auto TColor::CreateFromHex(const tstring& hexRgb) -> TColor
{
  auto is = tistringstream(hexRgb);
  auto prefix = tchar{};
  auto value = int{};
  is >> prefix >> hex >> value;
  if (prefix != _T('#') || is.fail() || !is.eof()) throw TXOwl{_T("TColor::CreateFromHex: String parse failed")};
  return TColor{(value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF};
}
 
//
/// Retrieves the 32bit COLORREF type from this color object.
///
/// Performs a ::%GetSysColor lookup if the object represents a symbolic system color index.
///
/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsyscolor">
/// GetSysColor</a> in the Windows API.
//
COLORREF TColor::GetValue() const
{
  return IsSysColor() ? ::GetSysColor(Index()) : Value;
}
 
//
/// Increases the intensity of each of the color components by the given absolute amount.
/// To prevent overflow, each of the color components will saturate at the maximum intensity.
///
/// \deprecated Use TColor::Tinted instead.
///
/// \sa TColor::Tinted (non-mutating alternative)
//
void TColor::Lighten(int amount)
{
  const auto d = abs(amount);
  const auto r = Red();
  const auto g = Green();
  const auto b = Blue();
  Value = MkRGB(
    (r + d > 255) ? 255 : r + d,
    (g + d > 255) ? 255 : g + d,
    (b + d > 255) ? 255 : b + d);
}
 
//
/// Creates a new color based on this one, but brightened by the given percentage.
///
/// Each color component is increased linearly by the given percentage of the remaining available
/// intensity.
///
/// \note The given percentage must be in the range [0, 100], inclusive.
///
/// \sa TColor::Lighten (mutating alternative)
/// \sa <a href="https://en.wikipedia.org/wiki/Tints_and_shades">
/// Tints and shades</a> at Wikipedia.
//
auto TColor::Tinted(float percentage) const -> TColor
{
  PRECONDITION(percentage >= 0 && percentage <= 100);
  const auto r = Red();
  const auto g = Green();
  const auto b = Blue();
  return
  {
    static_cast<int>(round(r + (255.0f - r) * percentage / 100.0f)),
    static_cast<int>(round(g + (255.0f - g) * percentage / 100.0f)),
    static_cast<int>(round(b + (255.0f - b) * percentage / 100.0f))
  };
}
 
//
/// Decreases the intensity of each of the color components by the given absolute amount.
/// To prevent underflow, each of the color components will saturate at 0 intensity.
///
/// \deprecated Use TColor::Shaded instead.
///
/// \sa TColor::Shaded (non-mutating alternative)
//
void TColor::Darken(int amount)
{
  const auto d = abs(amount);
  const auto r = Red();
  const auto g = Green();
  const auto b = Blue();
  Value = MkRGB(
    (r < d) ? 0 : r - d,
    (g < d) ? 0 : g - d,
    (b < d) ? 0 : b - d);
}
 
//
/// Creates a new color based on this one, but darkened by the given percentage.
/// Each color component is decreased linearly by the given percentage of the current intensity.
///
/// \note The given percentage must be in the range [0, 100], inclusive.
///
/// \sa TColor::Darken (mutating alternative)
/// \sa <a href="https://en.wikipedia.org/wiki/Tints_and_shades">
/// Tints and shades</a> at Wikipedia.
//
auto TColor::Shaded(float percentage) const -> TColor
{
  PRECONDITION(percentage >= 0 && percentage <= 100);
  return
  {
    static_cast<int>(round(Red() * (100.0f - percentage) / 100.0f)),
    static_cast<int>(round(Green() * (100.0f - percentage) / 100.0f)),
    static_cast<int>(round(Blue() * (100.0f - percentage) / 100.0f))
  };
}
 
//
/// Changes this color to a 50/50 percent mix between this and the given color.
/// Each color component is averaged with the other color's corresponding color component.
///
/// \deprecated Use TColor::Mixed instead.
///
/// \sa TColor::Mixed (non-mutating alternative)
//
void TColor::Merge(const TColor& other)
{
  Value = MkRGB(
    (Red() + other.Red()) / 2,
    (Green() + other.Green()) / 2,
    (Blue() + other.Blue()) / 2);
}
 
//
/// Creates a new color based on this one, but blended with the given color by the given percentage.
///
/// Each color component is adjusted linearly by the given percentage of the difference from the
/// corresponding color component in the given color. A percentage of 0 will result in the current
/// color, and 100 will result in the given color. A percentage of 50 (the default) will result in a
/// color with components midway between the components of this color and the other color.
///
/// \note The given percentage must be in the range [0, 100], inclusive.
///
/// \sa TColor::Merge (mutating alternative, using a fixed 50 percent blend).
/// \sa <a href="https://en.wikipedia.org/wiki/Color_theory">
/// Color theory</a> at Wikipedia.
//
auto TColor::Mixed(const TColor& other, float percentage) const -> TColor
{
  PRECONDITION(percentage >= 0 && percentage <= 100);
  const auto r = Red();
  const auto g = Green();
  const auto b = Blue();
  return
  {
    static_cast<int>(round(r + (other.Red() - r) * percentage / 100)),
    static_cast<int>(round(g + (other.Green() - g) * percentage / 100)),
    static_cast<int>(round(b + (other.Blue() - b) * percentage / 100))
  };
}
 
//
/// Creates a new color based on this one, but with the hue changed by the given amount.
///
/// A HSL/HSV color wheel is assumed, where red, green and blue are each 120 degrees apart. Both
/// positive and negative rotations are allowed, and the result is allowed to wrap above 360 or
/// below 0 degrees. For, example, to transform red into green, or red into blue:
///
/// \code
/// const auto green = TColor::XRed.Rotated(120);
/// const auto blue = TColor::XRed.Rotated(-120);
/// \endcode
///
/// \sa <a href="https://en.wikipedia.org/wiki/Color_wheel">
/// Color wheel</a> at Wikipedia.
// 
auto TColor::Rotated(float degrees) const -> TColor
{
  const auto hsl = GetHsl();
  const auto newHue = fmod(hsl.Hue + degrees, 360.0f);
  const auto newHueNormalized = newHue >= 0 ? newHue : 360.0f + newHue;
  return CreateFromHsl(newHueNormalized, hsl.Saturation, hsl.Lightness);
}
 
//
/// Creates a new color based on this one, but with the saturation decreased by the given amount.
/// The given \p percentage must be in the inclusive range [0, 100].
///
/// The color is transformed to HSL format, then the saturation component is decreased by the given
/// percentage. 0 creates no change, while 100 reduces the saturation component to 0.
///
/// \sa <a href="https://en.wikipedia.org/wiki/HSL_and_HSV">
/// HSL and HSV</a> at Wikipedia.
//
auto TColor::Dried(float percentage) const -> TColor
{
  PRECONDITION(percentage >= 0.0f && percentage <= 100.0f);
  const auto hsl = GetHsl();
  const auto newSaturation = hsl.Saturation - hsl.Saturation * percentage / 100.0f;
  return CreateFromHsl(hsl.Hue, newSaturation, hsl.Lightness);
}
 
//
/// Creates a new color based on this one, but with the saturation increased by the given amount.
/// The given \p percentage must be in the inclusive range [0, 100].
///
/// The color is transformed to HSL format, then the saturation component is increased by the given
/// percentage of the remaining available saturation. 0 creates no change, while 100 sets the
/// saturation component to the maximum value.
///
/// \sa <a href="https://en.wikipedia.org/wiki/HSL_and_HSV">
/// HSL and HSV</a> at Wikipedia.
//
auto TColor::Wetted(float percentage) const -> TColor
{
  PRECONDITION(percentage >= 0.0f && percentage <= 100.0f);
  const auto hsl = GetHsl();
  const auto newSaturation = hsl.Saturation + (100.0f - hsl.Saturation) * percentage / 100.0f;
  return CreateFromHsl(hsl.Hue, newSaturation, hsl.Lightness);
}
 
//
/// Creates a new color based on this one, but with the saturation component set to the given
/// absolute value.
///
/// The given \p percentage must be in the inclusive range [0, 100].
///
/// The color is transformed to HSL format, then the saturation component is set to the given
/// percentage. 0 creates gray scale, while 100 sets the saturation component to the maximum value.
///
/// \sa <a href="https://en.wikipedia.org/wiki/HSL_and_HSV">
/// HSL and HSV</a> at Wikipedia.
/// \sa TColor::Dried and TColor::Wetted changes the saturation relative to the current value.
//
auto TColor::Saturated(float percentage) const -> TColor
{
  PRECONDITION(percentage >= 0.0f && percentage <= 100.0f);
  const auto hsl = GetHsl();
  return CreateFromHsl(hsl.Hue, percentage, hsl.Lightness);
}
 
//
/// Retrieves the name of this color, if any.
///
/// If there are many known names for the same color, the HTML/CSS names are prioritized (of the
/// duplicates among these --- Fuchsia/Magenta and Cyan/Aqua --- it is arbitrary which one is
/// returned). If the color is a symbolic system color, e.g. TColor::SysMenu, or the special color
/// values TColor::None and TColor::Transparent, the symbolic name is returned.
///
/// \returns If the color is known, its name is returned. Otherwise, an empty string is returned.
///
/// \sa TColor::CreateFromName documents the known color names.
//
auto TColor::GetName() const -> tstring
{
  const auto& t = GetColorTable_();
  const auto i = t.find(*this);
  return i != t.end() ? i->second : tstring{};
}
 
//
/// Returns the HTML/CSS hexadecimal color value (HEX value) of this color.
/// \sa <a href="https://www.w3schools.com/colors/colors_hex.asp">
/// HTML Color Values</a> at W3Schools.
//
auto TColor::GetHex() const -> tstring
{
  auto s = tostringstream{};
  s << _T('#') << std::setfill(_T('0')) << std::hex
    << std::setw(2) << Red() << std::setw(2) << Green() << std::setw(2) << Blue();
  return s.str();
}
 
auto TColor::GetHsl() const -> THsl
{
  const auto r = Red();
  const auto g = Green();
  const auto b = Blue();
  const auto minv = min(r, min(g, b));
  const auto maxv = max(r, max(g, b));
  const auto mdif = static_cast<float>(maxv - minv);
  const auto rnrm = [&] { return (maxv - r) / mdif; };
  const auto gnrm = [&] { return (maxv - g) / mdif; };
  const auto bnrm = [&] { return (maxv - b) / mdif; };
 
  const auto h = (maxv == minv) ? 0.0f :
    (r == maxv) ? 60.0f * (6.0f + bnrm() - gnrm()) :
    (g == maxv) ? 60.0f * (2.0f + rnrm() - bnrm()) :
    60.0f * (4.0f + gnrm() - rnrm()); // b == maxv
  const auto hue = (h > 360.0f) ? h - 360.0f : (h < 0.0f) ? h + 360.0f : h;
 
  const auto msum = static_cast<float>(maxv + minv);
 
  const auto lightness = msum / 510.0f;
  const auto saturation = (maxv == minv) ? 0.0f :
    (lightness <= 0.5f) ? (mdif / msum) :
    (mdif / (510.0f - msum));
 
  CHECK(hue >= 0.0f && hue <= 360.0f);
  WARN(!(saturation >= 0.0f && saturation <= 1.0f), _T("TColor::GetHsl: saturation out of range: ") << saturation);
  WARN(!(lightness >= 0.0f && lightness <= 1.0f), _T("TColor::GetHsl: lightness out of range: ") << lightness);
  return 
  {
    hue, 
    max(0.0f, min(1.0f, saturation)) * 100.0f, 
    max(0.0f, min(1.0f, lightness)) * 100.0f
  };
}
 
////////////////////////////////////////////////////////////////////////////////
 
#if defined(BI_COMP_MSC)
#pragma warning(push)
#pragma warning(disable: 4996) // Disable "deprecated" warning.
#endif
 
auto TColor::Rgb2Hls() const -> HLSCOLOR
{
  auto minval = Red();
  auto maxval = Red();
 
  if (minval > Green())
    minval = Green();
  if (maxval < Green())
    maxval = Green();
 
  if (minval > Blue())
    minval = Blue();
  if (maxval < Blue())
    maxval = Blue();
 
  float mdiff = float(maxval) - float(minval);
  float msum = float(maxval) + float(minval);
 
  float luminance = msum / 510.0f;
  float saturation = 0.0f;
  float hue = 0.0f;
 
  if (maxval != minval)
  {
    float rnorm = (maxval - Red()) / mdiff;
    float gnorm = (maxval - Green()) / mdiff;
    float bnorm = (maxval - Blue()) / mdiff;
 
    saturation = (luminance <= 0.5f) ? (mdiff / msum) : (mdiff / (510.0f - msum));
 
    if (Red() == maxval)
      hue = 60.0f * (6.0f + bnorm - gnorm);
 
    if (Green() == maxval)
      hue = 60.0f * (2.0f + rnorm - bnorm);
 
    if (Blue() == maxval)
      hue = 60.0f * (4.0f + gnorm - rnorm);
 
    if (hue > 360.0f)
      hue = hue - 360.0f;
  }
 
  return HLS((hue * 255) / 360, luminance * 255, saturation * 255);
}
 
void TColor::Hls2Rgb(HLSCOLOR hls)
{
  const auto toRgb = [](float rm1, float rm2, float rh) -> int
  {
    if (rh > 360.0f)
      rh -= 360.0f;
    else if (rh < 0.0f)
      rh += 360.0f;
 
    if (rh < 60.0f)
      rm1 = rm1 + (rm2 - rm1) * rh / 60.0f;
    else if (rh < 180.0f)
      rm1 = rm2;
    else if (rh < 240.0f)
      rm1 = rm1 + (rm2 - rm1) * (240.0f - rh) / 60.0f;
 
    return static_cast<int>(rm1 * 255);
  };
 
  float hue = (static_cast<int>(HLS_H(hls)) * 360) / 255.0f;
  float luminance = HLS_L(hls) / 255.0f;
  float saturation = HLS_S(hls) / 255.0f;
 
  if (saturation == 0.0f)
  {
    SetValue(RGB(HLS_L(hls), HLS_L(hls), HLS_L(hls)));
    return;
  }
 
  float rm1, rm2;
 
  if (luminance <= 0.5f)
    rm2 = luminance + luminance * saturation;
  else
    rm2 = luminance + saturation - luminance * saturation;
 
  rm1 = 2.0f * luminance - rm2;
  const auto red = toRgb(rm1, rm2, hue + 120.0f);
  const auto green = toRgb(rm1, rm2, hue);
  const auto blue = toRgb(rm1, rm2, hue - 120.0f);
 
  SetValue(RGB(red, green, blue));
}
 
void TColor::HlsTransform(int percent_L, int percent_S)
{
  HLSCOLOR hls = Rgb2Hls();
  BYTE h = HLS_H(hls);
  BYTE l = HLS_L(hls);
  BYTE s = HLS_S(hls);
 
  if (percent_L > 0)
  {
    l = BYTE(l + ((255 - l) * percent_L) / 100);
  }
  else if (percent_L < 0)
  {
    l = BYTE((l * (100 + percent_L)) / 100);
  }
 
  if (percent_S > 0)
  {
    s = BYTE(s + ((255 - s) * percent_S) / 100);
  }
  else if (percent_S < 0)
  {
    s = BYTE((s * (100 + percent_S)) / 100);
  }
 
  Hls2Rgb(HLS(h, l, s));
}
 
#if defined(BI_COMP_MSC)
#pragma warning(pop)
#endif
 
} // OWL namespace

V550 An odd precise comparison: s == 0.0f. It's probably better to use a comparison with defined precision: fabs(A - B) < Epsilon.

V550 An odd precise comparison: saturation == 0.0f. It's probably better to use a comparison with defined precision: fabs(A - B) < Epsilon.

V636 The 'static_cast (((BYTE)(hls))) * 360' expression was implicitly cast from 'int' type to 'float' type. Consider utilizing an explicit type cast to avoid overflow. An example: double A = (double)(X) * Y;.

V656 Variables 'minval', 'maxval' are initialized through the call to the same function. It's probably an error or un-optimized code. Consider inspecting the 'Red()' expression. Check lines: 974, 975.