0030623: Draw Harness - support hex color codes within ViewerTest::ParseColor()
[occt.git] / src / Quantity / Quantity_ColorRGBA.cxx
diff --git a/src/Quantity/Quantity_ColorRGBA.cxx b/src/Quantity/Quantity_ColorRGBA.cxx
new file mode 100644 (file)
index 0000000..b7ade56
--- /dev/null
@@ -0,0 +1,200 @@
+// Created on: 2019-03-22
+// Created by: Timur Izmaylov
+// Copyright (c) 2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <Quantity_ColorRGBA.hxx>
+
+#include <Graphic3d_Vec4.hxx>
+
+#include <algorithm>
+
+namespace
+{
+
+  //! The integer type used to represent some color or color component
+  typedef unsigned int ColorInteger;
+
+  //! Defines all possible lengths of strings representing color in hex format
+  enum HexColorLength
+  {
+    HexColorLength_ShortRGB  = 3, //!< short RGB hex color format
+    HexColorLength_ShortRGBA = 4, //!< short RGBA hex color format
+    HexColorLength_RGB       = 6, //!< RGB hex color format
+    HexColorLength_RGBA      = 8  //!< RGBA hex color format
+  };
+
+  //! Takes next color component from the integer representing a color (it is a step in a process of a conversion
+  //! implemented by the function ConvertIntegerToColorRGBA)
+  //! @param theColorInteger the integer representing a color
+  //! @param theColorComponentBase the base of the numeral system used to represent a color
+  //! @return a color component taken from the integer
+  static Standard_ShortReal takeColorComponentFromInteger (ColorInteger&      theColorInteger,
+                                                           const ColorInteger theColorComponentBase)
+  {
+    Standard_ASSERT_RETURN (theColorComponentBase >= 2,
+                            __FUNCTION__ ": 'theColorComponentBase' must be greater than 1.",
+                            0.0f);
+    const ColorInteger       aColorComponentMaxValue  = theColorComponentBase - 1;
+    const ColorInteger       aColorComponentAsInteger = theColorInteger % theColorComponentBase;
+    const Standard_ShortReal aColorComponent          = aColorComponentAsInteger * 1.0f / aColorComponentMaxValue;
+    theColorInteger /= theColorComponentBase;
+    return aColorComponent;
+  }
+
+  //! Converts the integer representing a color to a RGBA color object
+  //! @param theColorInteger the integer representing a color (using the numerical system based
+  //! on theColorComponentBase value, where color components represent digits:
+  //! an alpha component is a low number and a red component is a high number)
+  //! @param theColorComponentBase the base of the numeral system used to represent a color
+  //! @param hasAlphaComponent true if the integer to be converted contains an alpha component value
+  //! @param theColor a color that is a result of a conversion
+  //! @return true if a conversion was successful, or false otherwise
+  static bool convertIntegerToColorRGBA (ColorInteger        theColorInteger,
+                                         const ColorInteger  theColorComponentBase,
+                                         const bool          hasAlphaComponent,
+                                         Quantity_ColorRGBA& theColor)
+  {
+    Standard_ASSERT_RETURN (theColorComponentBase >= 2,
+                            __FUNCTION__ ": 'theColorComponentBase' must be greater than 1.",
+                            0.0f);
+    Graphic3d_Vec4 aColor (1.0f);
+    if (hasAlphaComponent)
+    {
+      const Standard_ShortReal anAlphaComponent = takeColorComponentFromInteger (theColorInteger,
+                                                                                 theColorComponentBase);
+      aColor.a()                                = anAlphaComponent;
+    }
+    for (Standard_Integer aColorComponentIndex = 2; aColorComponentIndex >= 0; --aColorComponentIndex)
+    {
+      const Standard_ShortReal aColorComponent = takeColorComponentFromInteger (theColorInteger, theColorComponentBase);
+      aColor[aColorComponentIndex]             = aColorComponent;
+    }
+    if (theColorInteger != 0)
+    {
+      return false;
+    }
+    theColor = Quantity_ColorRGBA (aColor);
+    return true;
+  }
+
+  //! Converts the string to an integer number using the number base
+  //! @tparam TheNumber the type of a resulting number
+  //! @param theString the string to be converted
+  //! @param theNumber a number that is the result of the conversion
+  //! @param theBase the base of a numeral system used to represent a number in a string form
+  //! @return true if a conversion was successful, or false otherwise
+  template <typename TheNumber>
+  static bool convertStringToInteger (const char* const theString, TheNumber& theNumber, const TheNumber theBase = 10)
+  {
+    std::stringstream aConversionStringStream;
+    aConversionStringStream << std::setbase (theBase) << theString;
+    if (aConversionStringStream.fail())
+    {
+      return false;
+    }
+    aConversionStringStream >> theNumber;
+    if (aConversionStringStream.fail())
+    {
+      return false;
+    }
+    return true;
+  }
+
+  //! Checks if the character is a hexadecimal digit (0 .. 9, a .. f, A .. F)
+  //! @param theCharacter the character to be checked
+  //! @return true if the checking character is a hexadecimal digit, or false otherwise
+  static bool isHexDigit (const char theCharacter)
+  {
+    return std::isxdigit (static_cast<unsigned char> (theCharacter)) != 0;
+  }
+
+  //! Checks if the string consists only of hexadecimal digits (0 .. 9, a .. f, A .. F)
+  //! @param theString the string to be checked
+  //! @param theLength the length of the checked string
+  //! @return true if the checking string consists only of hexadecimal digits, or false otherwise
+  //! an empty string is not interpreted as a hex string
+  static bool isHexString (const char* const theString, const std::size_t theLength)
+  {
+    if (theLength == 0)
+    {
+      return false;
+    }
+    // std::all_of is not used due to VS2008 compilability limitation
+    return std::count_if (theString, theString + theLength, isHexDigit) == static_cast<std::ptrdiff_t> (theLength);
+  }
+
+} // namespace
+
+//=======================================================================
+// function : ColorFromHex
+// purpose  :
+//=======================================================================
+bool Quantity_ColorRGBA::ColorFromHex (const char* const   theHexColorString,
+                                       Quantity_ColorRGBA& theColor,
+                                       const bool          theAlphaComponentIsOff)
+{
+  std::size_t aHexColorStringLength = std::strlen (theHexColorString);
+  if (aHexColorStringLength == 0)
+  {
+    return false;
+  }
+
+  const bool        hasPrefix       = (theHexColorString[0] == '#');
+  const std::size_t aPrefixLength   = hasPrefix ? 1 : 0;
+  const char* const aHexColorString = theHexColorString + aPrefixLength;
+  aHexColorStringLength -= aPrefixLength;
+  if (!isHexString (aHexColorString, aHexColorStringLength))
+  {
+    return false;
+  }
+
+  ColorInteger aHexColorInteger;
+  if (!convertStringToInteger (aHexColorString, aHexColorInteger, 16u))
+  {
+    return false;
+  }
+
+  bool hasAlphaComponent = false;
+  bool isShort           = false;
+  switch (static_cast<HexColorLength> (aHexColorStringLength))
+  {
+    case HexColorLength_ShortRGBA:
+      hasAlphaComponent = true;
+      Standard_FALLTHROUGH
+    case HexColorLength_ShortRGB:
+      isShort = true;
+      break;
+    case HexColorLength_RGBA:
+      hasAlphaComponent = true;
+      break;
+    case HexColorLength_RGB:
+      break;
+    default:
+      return false;
+  }
+  if (theAlphaComponentIsOff && hasAlphaComponent)
+  {
+    return false;
+  }
+  // to distinguish with a usual integer color component value
+  if (isShort && !hasAlphaComponent && !hasPrefix)
+  {
+    return false;
+  }
+
+  const ColorInteger THE_HEX_COLOR_COMPONENT_BASE       = 1 << 8;
+  const ColorInteger THE_HEX_COLOR_COMPONENT_SHORT_BASE = 1 << 4;
+  const ColorInteger aColorComponentBase = isShort ? THE_HEX_COLOR_COMPONENT_SHORT_BASE : THE_HEX_COLOR_COMPONENT_BASE;
+  return convertIntegerToColorRGBA (aHexColorInteger, aColorComponentBase, hasAlphaComponent, theColor);
+}