0030623: Draw Harness - support hex color codes within ViewerTest::ParseColor()
[occt.git] / src / Quantity / Quantity_ColorRGBA.cxx
1 // Created on: 2019-03-22
2 // Created by: Timur Izmaylov
3 // Copyright (c) 2019 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 #include <Quantity_ColorRGBA.hxx>
17
18 #include <Graphic3d_Vec4.hxx>
19
20 #include <algorithm>
21
22 namespace
23 {
24
25   //! The integer type used to represent some color or color component
26   typedef unsigned int ColorInteger;
27
28   //! Defines all possible lengths of strings representing color in hex format
29   enum HexColorLength
30   {
31     HexColorLength_ShortRGB  = 3, //!< short RGB hex color format
32     HexColorLength_ShortRGBA = 4, //!< short RGBA hex color format
33     HexColorLength_RGB       = 6, //!< RGB hex color format
34     HexColorLength_RGBA      = 8  //!< RGBA hex color format
35   };
36
37   //! Takes next color component from the integer representing a color (it is a step in a process of a conversion
38   //! implemented by the function ConvertIntegerToColorRGBA)
39   //! @param theColorInteger the integer representing a color
40   //! @param theColorComponentBase the base of the numeral system used to represent a color
41   //! @return a color component taken from the integer
42   static Standard_ShortReal takeColorComponentFromInteger (ColorInteger&      theColorInteger,
43                                                            const ColorInteger theColorComponentBase)
44   {
45     Standard_ASSERT_RETURN (theColorComponentBase >= 2,
46                             __FUNCTION__ ": 'theColorComponentBase' must be greater than 1.",
47                             0.0f);
48     const ColorInteger       aColorComponentMaxValue  = theColorComponentBase - 1;
49     const ColorInteger       aColorComponentAsInteger = theColorInteger % theColorComponentBase;
50     const Standard_ShortReal aColorComponent          = aColorComponentAsInteger * 1.0f / aColorComponentMaxValue;
51     theColorInteger /= theColorComponentBase;
52     return aColorComponent;
53   }
54
55   //! Converts the integer representing a color to a RGBA color object
56   //! @param theColorInteger the integer representing a color (using the numerical system based
57   //! on theColorComponentBase value, where color components represent digits:
58   //! an alpha component is a low number and a red component is a high number)
59   //! @param theColorComponentBase the base of the numeral system used to represent a color
60   //! @param hasAlphaComponent true if the integer to be converted contains an alpha component value
61   //! @param theColor a color that is a result of a conversion
62   //! @return true if a conversion was successful, or false otherwise
63   static bool convertIntegerToColorRGBA (ColorInteger        theColorInteger,
64                                          const ColorInteger  theColorComponentBase,
65                                          const bool          hasAlphaComponent,
66                                          Quantity_ColorRGBA& theColor)
67   {
68     Standard_ASSERT_RETURN (theColorComponentBase >= 2,
69                             __FUNCTION__ ": 'theColorComponentBase' must be greater than 1.",
70                             0.0f);
71     Graphic3d_Vec4 aColor (1.0f);
72     if (hasAlphaComponent)
73     {
74       const Standard_ShortReal anAlphaComponent = takeColorComponentFromInteger (theColorInteger,
75                                                                                  theColorComponentBase);
76       aColor.a()                                = anAlphaComponent;
77     }
78     for (Standard_Integer aColorComponentIndex = 2; aColorComponentIndex >= 0; --aColorComponentIndex)
79     {
80       const Standard_ShortReal aColorComponent = takeColorComponentFromInteger (theColorInteger, theColorComponentBase);
81       aColor[aColorComponentIndex]             = aColorComponent;
82     }
83     if (theColorInteger != 0)
84     {
85       return false;
86     }
87     theColor = Quantity_ColorRGBA (aColor);
88     return true;
89   }
90
91   //! Converts the string to an integer number using the number base
92   //! @tparam TheNumber the type of a resulting number
93   //! @param theString the string to be converted
94   //! @param theNumber a number that is the result of the conversion
95   //! @param theBase the base of a numeral system used to represent a number in a string form
96   //! @return true if a conversion was successful, or false otherwise
97   template <typename TheNumber>
98   static bool convertStringToInteger (const char* const theString, TheNumber& theNumber, const TheNumber theBase = 10)
99   {
100     std::stringstream aConversionStringStream;
101     aConversionStringStream << std::setbase (theBase) << theString;
102     if (aConversionStringStream.fail())
103     {
104       return false;
105     }
106     aConversionStringStream >> theNumber;
107     if (aConversionStringStream.fail())
108     {
109       return false;
110     }
111     return true;
112   }
113
114   //! Checks if the character is a hexadecimal digit (0 .. 9, a .. f, A .. F)
115   //! @param theCharacter the character to be checked
116   //! @return true if the checking character is a hexadecimal digit, or false otherwise
117   static bool isHexDigit (const char theCharacter)
118   {
119     return std::isxdigit (static_cast<unsigned char> (theCharacter)) != 0;
120   }
121
122   //! Checks if the string consists only of hexadecimal digits (0 .. 9, a .. f, A .. F)
123   //! @param theString the string to be checked
124   //! @param theLength the length of the checked string
125   //! @return true if the checking string consists only of hexadecimal digits, or false otherwise
126   //! an empty string is not interpreted as a hex string
127   static bool isHexString (const char* const theString, const std::size_t theLength)
128   {
129     if (theLength == 0)
130     {
131       return false;
132     }
133     // std::all_of is not used due to VS2008 compilability limitation
134     return std::count_if (theString, theString + theLength, isHexDigit) == static_cast<std::ptrdiff_t> (theLength);
135   }
136
137 } // namespace
138
139 //=======================================================================
140 // function : ColorFromHex
141 // purpose  :
142 //=======================================================================
143 bool Quantity_ColorRGBA::ColorFromHex (const char* const   theHexColorString,
144                                        Quantity_ColorRGBA& theColor,
145                                        const bool          theAlphaComponentIsOff)
146 {
147   std::size_t aHexColorStringLength = std::strlen (theHexColorString);
148   if (aHexColorStringLength == 0)
149   {
150     return false;
151   }
152
153   const bool        hasPrefix       = (theHexColorString[0] == '#');
154   const std::size_t aPrefixLength   = hasPrefix ? 1 : 0;
155   const char* const aHexColorString = theHexColorString + aPrefixLength;
156   aHexColorStringLength -= aPrefixLength;
157   if (!isHexString (aHexColorString, aHexColorStringLength))
158   {
159     return false;
160   }
161
162   ColorInteger aHexColorInteger;
163   if (!convertStringToInteger (aHexColorString, aHexColorInteger, 16u))
164   {
165     return false;
166   }
167
168   bool hasAlphaComponent = false;
169   bool isShort           = false;
170   switch (static_cast<HexColorLength> (aHexColorStringLength))
171   {
172     case HexColorLength_ShortRGBA:
173       hasAlphaComponent = true;
174       Standard_FALLTHROUGH
175     case HexColorLength_ShortRGB:
176       isShort = true;
177       break;
178     case HexColorLength_RGBA:
179       hasAlphaComponent = true;
180       break;
181     case HexColorLength_RGB:
182       break;
183     default:
184       return false;
185   }
186   if (theAlphaComponentIsOff && hasAlphaComponent)
187   {
188     return false;
189   }
190   // to distinguish with a usual integer color component value
191   if (isShort && !hasAlphaComponent && !hasPrefix)
192   {
193     return false;
194   }
195
196   const ColorInteger THE_HEX_COLOR_COMPONENT_BASE       = 1 << 8;
197   const ColorInteger THE_HEX_COLOR_COMPONENT_SHORT_BASE = 1 << 4;
198   const ColorInteger aColorComponentBase = isShort ? THE_HEX_COLOR_COMPONENT_SHORT_BASE : THE_HEX_COLOR_COMPONENT_BASE;
199   return convertIntegerToColorRGBA (aHexColorInteger, aColorComponentBase, hasAlphaComponent, theColor);
200 }