/*++ Copyright (c) Microsoft Corporation Licensed under the MIT license. Module Name: - conattrs.cpp Abstract: - Defines common operations on console attributes, especially in regards to finding the nearest color from a color table. Author(s): - Mike Griese (migrie) 01-Sept-2017 --*/ #include "precomp.h" #include "..\inc\conattrs.hpp" #include struct _HSL { double h, s, l; // constructs an HSL color from a RGB Color. _HSL(const COLORREF rgb) { const double r = (double)GetRValue(rgb); const double g = (double)GetGValue(rgb); const double b = (double)GetBValue(rgb); const auto [min, max] = std::minmax({ r, g, b }); const auto diff = max - min; const auto sum = max + min; // Luminance l = max / 255.0; // Saturation s = (max == 0) ? 0 : diff / max; //Hue double q = (diff == 0) ? 0 : 60.0 / diff; if (max == r) { h = (g < b) ? ((360.0 + q * (g - b)) / 360.0) : ((q * (g - b)) / 360.0); } else if (max == g) { h = (120.0 + q * (b - r)) / 360.0; } else if (max == b) { h = (240.0 + q * (r - g)) / 360.0; } else { h = 0; } } }; //Routine Description: // Finds the "distance" between a given HSL color and an RGB color, using the HSL color space. // This function is designed such that the caller would convert one RGB color to HSL ahead of time, // then compare many RGB colors to that first color. //Arguments: // - phslColorA - a pointer to the first color, as a HSL color. // - rgbColorB - The second color to compare, in RGB color. // Return value: // The "distance" between the two. static double _FindDifference(const _HSL* const phslColorA, const COLORREF rgbColorB) { const _HSL hslColorB = _HSL(rgbColorB); return sqrt(pow((hslColorB.h - phslColorA->h), 2) + pow((hslColorB.s - phslColorA->s), 2) + pow((hslColorB.l - phslColorA->l), 2)); } //Routine Description: // For a given RGB color Color, finds the nearest color from the array ColorTable, and returns the index of that match. //Arguments: // - Color - The RGB color to fine the nearest color to. // - ColorTable - The array of colors to find a nearest color from. // - cColorTable - The number of elements in ColorTable // Return value: // The index in ColorTable of the nearest match to Color. WORD FindNearestTableIndex(const COLORREF Color, _In_reads_(cColorTable) const COLORREF* const ColorTable, const WORD cColorTable) { // Quick check for an exact match in the color table: for (WORD i = 0; i < cColorTable; i++) { if (Color == ColorTable[i]) { return i; } } // Did not find an exact match - do an expensive comparison to the elements // of the table to find the nearest color. const _HSL hslColor = _HSL(Color); WORD closest = 0; double minDiff = _FindDifference(&hslColor, ColorTable[0]); for (WORD i = 1; i < cColorTable; i++) { double diff = _FindDifference(&hslColor, ColorTable[i]); if (diff < minDiff) { minDiff = diff; closest = i; } } return closest; } // Function Description: // - Converts the value of a xterm color table index to the windows color table equivalent. // Arguments: // - xtermTableEntry: the xterm color table index // Return Value: // - The windows color table equivalent. WORD XtermToWindowsIndex(const size_t xtermTableEntry) noexcept { const bool fRed = WI_IsFlagSet(xtermTableEntry, XTERM_RED_ATTR); const bool fGreen = WI_IsFlagSet(xtermTableEntry, XTERM_GREEN_ATTR); const bool fBlue = WI_IsFlagSet(xtermTableEntry, XTERM_BLUE_ATTR); const bool fBright = WI_IsFlagSet(xtermTableEntry, XTERM_BRIGHT_ATTR); return (fRed ? WINDOWS_RED_ATTR : 0x0) + (fGreen ? WINDOWS_GREEN_ATTR : 0x0) + (fBlue ? WINDOWS_BLUE_ATTR : 0x0) + (fBright ? WINDOWS_BRIGHT_ATTR : 0x0); } // Function Description: // - Converts the value of a xterm color table index to the windows color table // equivalent. The range of values is [0, 255], where the lowest 16 are // mapped to the equivalent Windows index, and the rest of the values are // passed through. // Arguments: // - xtermTableEntry: the xterm color table index // Return Value: // - The windows color table equivalent. WORD Xterm256ToWindowsIndex(const size_t xtermTableEntry) noexcept { return xtermTableEntry < 16 ? XtermToWindowsIndex(xtermTableEntry) : static_cast(xtermTableEntry); } // Function Description: // - Converts the value of a pair of xterm color table indices to the legacy attr equivalent. // Arguments: // - xtermForeground: the xterm color table foreground index // - xtermBackground: the xterm color table background index // Return Value: // - The legacy windows attribute equivalent. WORD XtermToLegacy(const size_t xtermForeground, const size_t xtermBackground) { const WORD fgAttr = XtermToWindowsIndex(xtermForeground); const WORD bgAttr = XtermToWindowsIndex(xtermBackground); return (bgAttr << 4) | fgAttr; } //Routine Description: // Returns the exact entry from the color table, if it's in there. //Arguments: // - Color - The RGB color to fine the nearest color to. // - ColorTable - The array of colors to find a nearest color from. // - cColorTable - The number of elements in ColorTable // Return value: // The index in ColorTable of the nearest match to Color. bool FindTableIndex(const COLORREF Color, _In_reads_(cColorTable) const COLORREF* const ColorTable, const WORD cColorTable, _Out_ WORD* const pFoundIndex) { *pFoundIndex = 0; for (WORD i = 0; i < cColorTable; i++) { if (ColorTable[i] == Color) { *pFoundIndex = i; return true; } } return false; } // Method Description: // - Get a COLORREF for the foreground component of the given legacy attributes. // Arguments: // - wLegacyAttrs - The legacy attributes to get the foreground color from. // - ColorTable - The array of colors to get the color from. // - cColorTable - The number of elements in ColorTable // Return Value: // - the COLORREF for the foreground component COLORREF ForegroundColor(const WORD wLegacyAttrs, _In_reads_(cColorTable) const COLORREF* const ColorTable, const size_t cColorTable) { const byte iColorTableIndex = LOBYTE(wLegacyAttrs) & FG_ATTRS; return (iColorTableIndex < cColorTable && iColorTableIndex >= 0) ? ColorTable[iColorTableIndex] : INVALID_COLOR; } // Method Description: // - Get a COLORREF for the background component of the given legacy attributes. // Arguments: // - wLegacyAttrs - The legacy attributes to get the background color from. // - ColorTable - The array of colors to get the color from. // - cColorTable - The number of elements in ColorTable // Return Value: // - the COLORREF for the background component COLORREF BackgroundColor(const WORD wLegacyAttrs, _In_reads_(cColorTable) const COLORREF* const ColorTable, const size_t cColorTable) { const byte iColorTableIndex = (LOBYTE(wLegacyAttrs) & BG_ATTRS) >> 4; return (iColorTableIndex < cColorTable && iColorTableIndex >= 0) ? ColorTable[iColorTableIndex] : INVALID_COLOR; }