0029528: Visualization, TKOpenGl - allow defining sRGB textures
[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 #include <Standard_Dump.hxx>
20
21 #include <algorithm>
22
23 namespace
24 {
25
26   //! The integer type used to represent some color or color component
27   typedef unsigned int ColorInteger;
28
29   //! Defines all possible lengths of strings representing color in hex format
30   enum HexColorLength
31   {
32     HexColorLength_ShortRGB  = 3, //!< short RGB hex color format
33     HexColorLength_ShortRGBA = 4, //!< short RGBA hex color format
34     HexColorLength_RGB       = 6, //!< RGB hex color format
35     HexColorLength_RGBA      = 8  //!< RGBA hex color format
36   };
37
38   //! Takes next color component from the integer representing a color (it is a step in a process of a conversion
39   //! implemented by the function ConvertIntegerToColorRGBA)
40   //! @param theColorInteger the integer representing a color
41   //! @param theColorComponentBase the base of the numeral system used to represent a color
42   //! @return a color component taken from the integer
43   static Standard_ShortReal takeColorComponentFromInteger (ColorInteger&      theColorInteger,
44                                                            const ColorInteger theColorComponentBase)
45   {
46     Standard_ASSERT_RETURN (theColorComponentBase >= 2,
47                             __FUNCTION__ ": 'theColorComponentBase' must be greater than 1.",
48                             0.0f);
49     const ColorInteger       aColorComponentMaxValue  = theColorComponentBase - 1;
50     const ColorInteger       aColorComponentAsInteger = theColorInteger % theColorComponentBase;
51     const Standard_ShortReal aColorComponent          = aColorComponentAsInteger * 1.0f / aColorComponentMaxValue;
52     theColorInteger /= theColorComponentBase;
53     return aColorComponent;
54   }
55
56   //! Converts the integer representing a color to a RGBA color object
57   //! @param theColorInteger the integer representing a color (using the numerical system based
58   //! on theColorComponentBase value, where color components represent digits:
59   //! an alpha component is a low number and a red component is a high number)
60   //! @param theColorComponentBase the base of the numeral system used to represent a color
61   //! @param hasAlphaComponent true if the integer to be converted contains an alpha component value
62   //! @param theColor a color that is a result of a conversion
63   //! @return true if a conversion was successful, or false otherwise
64   static bool convertIntegerToColorRGBA (ColorInteger        theColorInteger,
65                                          const ColorInteger  theColorComponentBase,
66                                          const bool          hasAlphaComponent,
67                                          Quantity_ColorRGBA& theColor)
68   {
69     Standard_ASSERT_RETURN (theColorComponentBase >= 2,
70                             __FUNCTION__ ": 'theColorComponentBase' must be greater than 1.",
71                             0.0f);
72     Graphic3d_Vec4 aColor (1.0f);
73     if (hasAlphaComponent)
74     {
75       const Standard_ShortReal anAlphaComponent = takeColorComponentFromInteger (theColorInteger,
76                                                                                  theColorComponentBase);
77       aColor.a()                                = anAlphaComponent;
78     }
79     for (Standard_Integer aColorComponentIndex = 2; aColorComponentIndex >= 0; --aColorComponentIndex)
80     {
81       const Standard_ShortReal aColorComponent = takeColorComponentFromInteger (theColorInteger, theColorComponentBase);
82       aColor[aColorComponentIndex]             = Quantity_Color::Convert_sRGB_To_LinearRGB (aColorComponent);
83     }
84     if (theColorInteger != 0)
85     {
86       return false;
87     }
88     theColor = Quantity_ColorRGBA (aColor);
89     return true;
90   }
91
92   //! Converts the string to an integer number using the number base
93   //! @tparam TheNumber the type of a resulting number
94   //! @param theString the string to be converted
95   //! @param theNumber a number that is the result of the conversion
96   //! @param theBase the base of a numeral system used to represent a number in a string form
97   //! @return true if a conversion was successful, or false otherwise
98   template <typename TheNumber>
99   static bool convertStringToInteger (const char* const theString, TheNumber& theNumber, const TheNumber theBase = 10)
100   {
101     std::stringstream aConversionStringStream;
102     aConversionStringStream << std::setbase (theBase) << theString;
103     if (aConversionStringStream.fail())
104     {
105       return false;
106     }
107     aConversionStringStream >> theNumber;
108     if (aConversionStringStream.fail())
109     {
110       return false;
111     }
112     return true;
113   }
114
115   //! Checks if the character is a hexadecimal digit (0 .. 9, a .. f, A .. F)
116   //! @param theCharacter the character to be checked
117   //! @return true if the checking character is a hexadecimal digit, or false otherwise
118   static bool isHexDigit (const char theCharacter)
119   {
120     return std::isxdigit (static_cast<unsigned char> (theCharacter)) != 0;
121   }
122
123   //! Checks if the string consists only of hexadecimal digits (0 .. 9, a .. f, A .. F)
124   //! @param theString the string to be checked
125   //! @param theLength the length of the checked string
126   //! @return true if the checking string consists only of hexadecimal digits, or false otherwise
127   //! an empty string is not interpreted as a hex string
128   static bool isHexString (const char* const theString, const std::size_t theLength)
129   {
130     if (theLength == 0)
131     {
132       return false;
133     }
134     // std::all_of is not used due to VS2008 compilability limitation
135     return std::count_if (theString, theString + theLength, isHexDigit) == static_cast<std::ptrdiff_t> (theLength);
136   }
137
138 } // namespace
139
140 //=======================================================================
141 // function : ColorFromHex
142 // purpose  :
143 //=======================================================================
144 bool Quantity_ColorRGBA::ColorFromHex (const char* const   theHexColorString,
145                                        Quantity_ColorRGBA& theColor,
146                                        const bool          theAlphaComponentIsOff)
147 {
148   std::size_t aHexColorStringLength = std::strlen (theHexColorString);
149   if (aHexColorStringLength == 0)
150   {
151     return false;
152   }
153
154   const bool        hasPrefix       = (theHexColorString[0] == '#');
155   const std::size_t aPrefixLength   = hasPrefix ? 1 : 0;
156   const char* const aHexColorString = theHexColorString + aPrefixLength;
157   aHexColorStringLength -= aPrefixLength;
158   if (!isHexString (aHexColorString, aHexColorStringLength))
159   {
160     return false;
161   }
162
163   ColorInteger aHexColorInteger;
164   if (!convertStringToInteger (aHexColorString, aHexColorInteger, 16u))
165   {
166     return false;
167   }
168
169   bool hasAlphaComponent = false;
170   bool isShort           = false;
171   switch (static_cast<HexColorLength> (aHexColorStringLength))
172   {
173     case HexColorLength_ShortRGBA:
174       hasAlphaComponent = true;
175       Standard_FALLTHROUGH
176     case HexColorLength_ShortRGB:
177       isShort = true;
178       break;
179     case HexColorLength_RGBA:
180       hasAlphaComponent = true;
181       break;
182     case HexColorLength_RGB:
183       break;
184     default:
185       return false;
186   }
187   if (theAlphaComponentIsOff && hasAlphaComponent)
188   {
189     return false;
190   }
191   // to distinguish with a usual integer color component value
192   if (isShort && !hasAlphaComponent && !hasPrefix)
193   {
194     return false;
195   }
196
197   const ColorInteger THE_HEX_COLOR_COMPONENT_BASE       = 1 << 8;
198   const ColorInteger THE_HEX_COLOR_COMPONENT_SHORT_BASE = 1 << 4;
199   const ColorInteger aColorComponentBase = isShort ? THE_HEX_COLOR_COMPONENT_SHORT_BASE : THE_HEX_COLOR_COMPONENT_BASE;
200   return convertIntegerToColorRGBA (aHexColorInteger, aColorComponentBase, hasAlphaComponent, theColor);
201 }
202
203 //=======================================================================
204 //function : DumpJson
205 //purpose  : 
206 //=======================================================================
207 void Quantity_ColorRGBA::DumpJson (Standard_OStream& theOStream, const Standard_Integer theDepth) const
208 {
209   OCCT_DUMP_CLASS_BEGIN (theOStream, Quantity_ColorRGBA);
210
211   OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myRgb);
212   OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myAlpha);
213 }