• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components/common/properties/color.h"
17 
18 #include <cmath>
19 #include <regex>
20 
21 #include "base/utils/linear_map.h"
22 #include "base/utils/string_utils.h"
23 #include "base/utils/utils.h"
24 
25 namespace OHOS::Ace {
26 namespace {
27 
28 constexpr uint32_t COLOR_STRING_SIZE_STANDARD = 8;
29 constexpr uint32_t COLOR_STRING_BASE = 16;
30 constexpr uint32_t RGB_SUB_MATCH_SIZE = 4;
31 constexpr uint32_t RGBA_SUB_MATCH_SIZE = 5;
32 
33 const std::regex COLOR_WITH_MAGIC("#[0-9A-Fa-f]{6,8}");
34 const std::regex COLOR_WITH_MAGIC_MINI("#[0-9A-Fa-f]{3,4}");
35 const std::regex COLOR_WITH_RGB(R"(rgb\(([0-9]{1,3})\,([0-9]{1,3})\,([0-9]{1,3})\))", std::regex::icase);
36 const std::regex COLOR_WITH_RGBA(R"(rgba\(([0-9]{1,3})\,([0-9]{1,3})\,([0-9]{1,3})\,(\d+\.?\d*)\))", std::regex::icase);
37 constexpr double GAMMA_FACTOR = 2.2;
38 constexpr float MAX_ALPHA = 255.0f;
39 constexpr char HEX[] = "0123456789ABCDEF";
40 constexpr uint8_t BIT_LENGTH_INT32 = 8;
41 
42 } // namespace
43 
44 const Color Color::TRANSPARENT = Color(0x00000000);
45 const Color Color::WHITE = Color(0xffffffff);
46 const Color Color::BLACK = Color(0xff000000);
47 const Color Color::RED = Color(0xffff0000);
48 const Color Color::GREEN = Color(0xff00ff00);
49 const Color Color::BLUE = Color(0xff0000ff);
50 const Color Color::GRAY = Color(0xffc0c0c0);
51 
FromString(std::string colorStr,uint32_t maskAlpha)52 Color Color::FromString(std::string colorStr, uint32_t maskAlpha)
53 {
54     if (colorStr.empty()) {
55         // empty string, return transparent
56         return Color::TRANSPARENT;
57     }
58 
59     // Remove all " ".
60     colorStr.erase(std::remove(colorStr.begin(), colorStr.end(), ' '), colorStr.end());
61 
62     std::smatch matches;
63     // Regex match for #909090 or #90909090.
64     if (std::regex_match(colorStr, matches, COLOR_WITH_MAGIC)) {
65         colorStr.erase(0, 1);
66         auto value = stoul(colorStr, nullptr, COLOR_STRING_BASE);
67         if (colorStr.length() < COLOR_STRING_SIZE_STANDARD) {
68             // no alpha specified, set alpha to 0xff
69             value |= maskAlpha;
70         }
71         return Color(value);
72     }
73     // Regex match for #rgb or #rgba.
74     if (std::regex_match(colorStr, matches, COLOR_WITH_MAGIC_MINI)) {
75         colorStr.erase(0, 1);
76         std::string newColorStr;
77         // translate #rgb or #rgba to #rrggbb or #rrggbbaa
78         for (auto& c : colorStr) {
79             newColorStr += c;
80             newColorStr += c;
81         }
82         auto value = stoul(newColorStr, nullptr, COLOR_STRING_BASE);
83         if (newColorStr.length() < COLOR_STRING_SIZE_STANDARD) {
84             // no alpha specified, set alpha to 0xff
85             value |= maskAlpha;
86         }
87         return Color(value);
88     }
89     // Regex match for rgb(90,254,180).
90     if (std::regex_match(colorStr, matches, COLOR_WITH_RGB)) {
91         if (matches.size() == RGB_SUB_MATCH_SIZE) {
92             auto red = static_cast<uint8_t>(std::stoi(matches[1]));   // red value.
93             auto green = static_cast<uint8_t>(std::stoi(matches[2])); // green value.
94             auto blue = static_cast<uint8_t>(std::stoi(matches[3]));  // blue value.
95             return FromRGB(red, green, blue);
96         }
97     }
98     // Regex match for rgba(90,254,180,0.5).
99     if (std::regex_match(colorStr, matches, COLOR_WITH_RGBA)) {
100         if (matches.size() == RGBA_SUB_MATCH_SIZE) {
101             auto red = static_cast<uint8_t>(std::stoi(matches[1]));
102             auto green = static_cast<uint8_t>(std::stoi(matches[2]));
103             auto blue = static_cast<uint8_t>(std::stoi(matches[3]));
104             auto opacity = static_cast<double>(std::stod(matches[4]));
105             return FromRGBO(red, green, blue, opacity);
106         }
107     }
108     // match for special string
109     static const LinearMapNode<Color> colorTable[] = {
110         { "black", Color(0xff000000) },
111         { "blue", Color(0xff0000ff) },
112         { "gray", Color(0xffc0c0c0) },
113         { "green", Color(0xff00ff00) },
114         { "red", Color(0xffff0000) },
115         { "white", Color(0xffffffff) },
116     };
117     int64_t colorIndex = BinarySearchFindIndex(colorTable, ArraySize(colorTable), colorStr.c_str());
118     if (colorIndex != -1) {
119         return colorTable[colorIndex].value;
120     }
121 
122     // parse uint32_t color string.
123     auto uint32Color = StringUtils::StringToUint(colorStr);
124     if (uint32Color > 0) {
125         return Color(uint32Color);
126     }
127 
128     // Default color.
129     return Color::BLACK;
130 }
131 
ColorToString() const132 std::string Color::ColorToString() const
133 {
134     std::string colorStr;
135     int count = 0;
136     uint32_t value = GetValue();
137     while (count++ < BIT_LENGTH_INT32) {
138         colorStr = HEX[(value & 0xf)] + colorStr;
139         value >>= 4;
140     }
141     colorStr = "#" + colorStr;
142     return colorStr;
143 }
144 
FromARGB(uint8_t alpha,uint8_t red,uint8_t green,uint8_t blue)145 Color Color::FromARGB(uint8_t alpha, uint8_t red, uint8_t green, uint8_t blue)
146 {
147     ColorParam colorValue {
148 #if BIG_ENDIANNESS
149         .argb = { .alpha = alpha, .red = red, .green = green, .blue = blue }
150 #else
151         .argb = { .blue = blue, .green = green, .red = red, .alpha = alpha }
152 #endif
153     };
154     return Color(colorValue);
155 }
156 
FromRGBO(uint8_t red,uint8_t green,uint8_t blue,double opacity)157 Color Color::FromRGBO(uint8_t red, uint8_t green, uint8_t blue, double opacity)
158 {
159     return FromARGB(static_cast<uint8_t>(round(opacity * 0xff)) & 0xff, red, green, blue);
160 }
161 
FromRGB(uint8_t red,uint8_t green,uint8_t blue)162 Color Color::FromRGB(uint8_t red, uint8_t green, uint8_t blue)
163 {
164     return FromARGB(0xff, red, green, blue);
165 }
166 
BlendColor(const Color & overlayColor) const167 Color Color::BlendColor(const Color& overlayColor) const
168 {
169     if (GetValue() == Color::TRANSPARENT.GetValue()) {
170         return overlayColor;
171     }
172     if (GetAlpha() < static_cast<uint8_t>(MAX_ALPHA)) {
173         return BlendColorWithAlpha(overlayColor);
174     }
175     auto alphaRate = static_cast<float>(overlayColor.GetAlpha()) / MAX_ALPHA;
176     auto newRed = static_cast<uint8_t>(GetRed() * (1.0f - alphaRate) + overlayColor.GetRed() * alphaRate);
177     auto newGreen = static_cast<uint8_t>(GetGreen() * (1.0f - alphaRate) + overlayColor.GetGreen() * alphaRate);
178     auto newBlue = static_cast<uint8_t>(GetBlue() * (1.0f - alphaRate) + overlayColor.GetBlue() * alphaRate);
179     return Color::FromRGB(newRed, newGreen, newBlue);
180 }
181 
CalculateBlend(float alphaLeft,float alphaRight,float valueLeft,float valueRight) const182 float Color::CalculateBlend(float alphaLeft, float alphaRight, float valueLeft, float valueRight) const
183 {
184     return (valueLeft * alphaLeft * (1.0 - alphaRight) + valueRight * alphaRight) /
185            (alphaLeft + alphaRight - alphaLeft * alphaRight);
186 }
187 
BlendColorWithAlpha(const Color & overlayColor) const188 Color Color::BlendColorWithAlpha(const Color& overlayColor) const
189 {
190     float alphaA = GetAlpha() / 255.0;
191     float alphaB = overlayColor.GetAlpha() / 255.0;
192     float blendAlpha = alphaA + alphaB - alphaA * alphaB;
193     float blendRed = CalculateBlend(alphaA, alphaB, GetRed() / 255.0, overlayColor.GetRed() / 255.0);
194     float blendGreen = CalculateBlend(alphaA, alphaB, GetGreen() / 255.0, overlayColor.GetGreen() / 255.0);
195     float blendBlue = CalculateBlend(alphaA, alphaB, GetBlue() / 255.0, overlayColor.GetBlue() / 255.0);
196 
197     return Color::FromARGB(blendAlpha * 255, blendRed * 255, blendGreen * 255, blendBlue * 255);
198 }
199 
LineColorTransition(const Color & startColor,const Color & endColor,double percent)200 const Color Color::LineColorTransition(const Color& startColor, const Color& endColor, double percent)
201 {
202     uint8_t red = 0;
203     uint8_t green = 0;
204     uint8_t blue = 0;
205     uint8_t alpha = 0;
206 
207     red = static_cast<uint8_t>((endColor.GetRed()- startColor.GetRed()) * percent) + startColor.GetRed();
208     green = static_cast<uint8_t>((endColor.GetGreen() - startColor.GetGreen()) * percent) + startColor.GetGreen();
209     blue = static_cast<uint8_t>((endColor.GetBlue() - startColor.GetBlue()) * percent) + startColor.GetBlue();
210     alpha = static_cast<uint8_t>((endColor.GetAlpha() - startColor.GetAlpha()) * percent) + startColor.GetAlpha();
211 
212     return Color::FromARGB(alpha, red, green, blue);
213 }
214 
BlendOpacity(double opacityRatio) const215 Color Color::BlendOpacity(double opacityRatio) const
216 {
217     int32_t alpha = static_cast<int32_t>(GetAlpha() * opacityRatio);
218     alpha = std::clamp(alpha, 0, UINT8_MAX);
219     return Color::FromARGB(alpha, GetRed(), GetGreen(), GetBlue());
220 }
221 
ChangeOpacity(double opacity) const222 Color Color::ChangeOpacity(double opacity) const
223 {
224     return Color::FromRGBO(GetRed(), GetGreen(), GetBlue(), opacity);
225 }
226 
ChangeAlpha(uint8_t alpha) const227 Color Color::ChangeAlpha(uint8_t alpha) const
228 {
229     return Color::FromARGB(alpha, GetRed(), GetGreen(), GetBlue());
230 }
231 
operator +(const Color & color) const232 Color Color::operator+(const Color& color) const
233 {
234     // convert first color from ARGB to linear
235     double firstLinearRed = 0.0;
236     double firstLinearGreen = 0.0;
237     double firstLinearBlue = 0.0;
238     ConvertGammaToLinear(*this, firstLinearRed, firstLinearGreen, firstLinearBlue);
239 
240     // convert second color from ARGB to linear
241     double secondLinearRed = 0.0;
242     double secondLinearGreen = 0.0;
243     double secondLinearBlue = 0.0;
244     ConvertGammaToLinear(color, secondLinearRed, secondLinearGreen, secondLinearBlue);
245 
246     // get linear result and convert to gamma
247     return ConvertLinearToGamma(GetAlpha() + color.GetAlpha(), firstLinearRed + secondLinearRed,
248         firstLinearGreen + secondLinearGreen, firstLinearBlue + secondLinearBlue);
249 }
250 
operator -(const Color & color) const251 Color Color::operator-(const Color& color) const
252 {
253     // convert first color from ARGB to linear
254     double firstLinearRed = 0.0;
255     double firstLinearGreen = 0.0;
256     double firstLinearBlue = 0.0;
257     ConvertGammaToLinear(*this, firstLinearRed, firstLinearGreen, firstLinearBlue);
258 
259     // convert second color from ARGB to linear
260     double secondLinearRed = 0.0;
261     double secondLinearGreen = 0.0;
262     double secondLinearBlue = 0.0;
263     ConvertGammaToLinear(color, secondLinearRed, secondLinearGreen, secondLinearBlue);
264 
265     // get linear result and convert to gamma
266     return ConvertLinearToGamma(GetAlpha() - color.GetAlpha(), firstLinearRed - secondLinearRed,
267         firstLinearGreen - secondLinearGreen, firstLinearBlue - secondLinearBlue);
268 }
269 
operator *(double value) const270 Color Color::operator*(double value) const
271 {
272     // convert color from ARGB to linear
273     double linearRed = 0.0;
274     double linearGreen = 0.0;
275     double linearBlue = 0.0;
276     ConvertGammaToLinear(*this, linearRed, linearGreen, linearBlue);
277 
278     // get linear result and convert to gamma
279     return ConvertLinearToGamma(GetAlpha() * value, linearRed * value, linearGreen * value, linearBlue * value);
280 }
281 
operator /(double value) const282 Color Color::operator/(double value) const
283 {
284     if (NearZero(value)) {
285         return *this;
286     }
287     // convert color from ARGB to linear
288     double linearRed = 0.0;
289     double linearGreen = 0.0;
290     double LinearBlue = 0.0;
291     ConvertGammaToLinear(*this, linearRed, linearGreen, LinearBlue);
292 
293     // get linear result and convert to gamma
294     return ConvertLinearToGamma(GetAlpha() / value, linearRed / value, linearGreen / value, LinearBlue / value);
295 }
296 
ConvertGammaToLinear(uint8_t value)297 double Color::ConvertGammaToLinear(uint8_t value)
298 {
299     return std::pow(value, GAMMA_FACTOR);
300 }
301 
ConvertLinearToGamma(double value)302 uint8_t Color::ConvertLinearToGamma(double value)
303 {
304     return std::clamp(static_cast<int32_t>(std::pow(value, 1.0 / GAMMA_FACTOR)), 0, UINT8_MAX);
305 }
306 
ConvertGammaToLinear(const Color & gammaColor,double & linearRed,double & linearGreen,double & linearBlue)307 void Color::ConvertGammaToLinear(const Color& gammaColor, double& linearRed, double& linearGreen, double& linearBlue)
308 {
309     linearRed = ConvertGammaToLinear(gammaColor.GetRed());
310     linearGreen = ConvertGammaToLinear(gammaColor.GetGreen());
311     linearBlue = ConvertGammaToLinear(gammaColor.GetBlue());
312 }
313 
ConvertLinearToGamma(double alpha,double linearRed,double linearGreen,double linearBlue)314 Color Color::ConvertLinearToGamma(double alpha, double linearRed, double linearGreen, double linearBlue)
315 {
316     uint8_t gammaRed = ConvertLinearToGamma(linearRed);
317     uint8_t gammaGreen = ConvertLinearToGamma(linearGreen);
318     uint8_t gammaBlue = ConvertLinearToGamma(linearBlue);
319     uint8_t gammaAlpha = std::clamp(static_cast<int32_t>(alpha), 0, UINT8_MAX);
320 
321     return FromARGB(gammaAlpha, gammaRed, gammaGreen, gammaBlue);
322 }
323 
324 } // namespace OHOS::Ace
325