• 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_ALPHA_OFFSET = 24;
29 constexpr uint32_t COLOR_STRING_SIZE_STANDARD = 8;
30 constexpr uint32_t COLOR_STRING_BASE = 16;
31 constexpr uint32_t RGB_SUB_MATCH_SIZE = 4;
32 constexpr uint32_t RGBA_SUB_MATCH_SIZE = 5;
33 
34 const std::regex COLOR_WITH_MAGIC("#[0-9A-Fa-f]{6,8}");
35 const std::regex COLOR_WITH_MAGIC_MINI("#[0-9A-Fa-f]{3,4}");
36 const std::regex COLOR_WITH_RGB(R"(rgb\(([0-9]{1,3})\,([0-9]{1,3})\,([0-9]{1,3})\))", std::regex::icase);
37 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);
38 constexpr double GAMMA_FACTOR = 2.2;
39 constexpr float MAX_ALPHA = 255.0f;
40 constexpr char HEX[] = "0123456789ABCDEF";
41 constexpr uint8_t BIT_LENGTH_INT32 = 8;
42 constexpr uint8_t MIN_RGB_VALUE = 0;
43 constexpr uint8_t MAX_RGB_VALUE = 255;
44 constexpr double MIN_RGBA_OPACITY = 0.0;
45 constexpr double MAX_RGBA_OPACITY = 1.0;
46 
47 } // namespace
48 
49 const Color Color::TRANSPARENT = Color(0x00000000);
50 const Color Color::WHITE = Color(0xffffffff);
51 const Color Color::BLACK = Color(0xff000000);
52 const Color Color::RED = Color(0xffff0000);
53 const Color Color::GREEN = Color(0xff00ff00);
54 const Color Color::BLUE = Color(0xff0000ff);
55 const Color Color::GRAY = Color(0xffc0c0c0);
56 const Color Color::FOREGROUND = Color(0x00000001); // foreground color and foreground color strategy identification
57 
58 const LinearColor LinearColor::TRANSPARENT = LinearColor(0x00000000);
59 const LinearColor LinearColor::WHITE = LinearColor(0xffffffff);
60 const LinearColor LinearColor::BLACK = LinearColor(0xff000000);
61 const LinearColor LinearColor::RED = LinearColor(0xffff0000);
62 const LinearColor LinearColor::GREEN = LinearColor(0xff00ff00);
63 const LinearColor LinearColor::BLUE = LinearColor(0xff0000ff);
64 const LinearColor LinearColor::GRAY = LinearColor(0xffc0c0c0);
65 
FromString(std::string colorStr,uint32_t maskAlpha,Color defaultColor)66 Color Color::FromString(std::string colorStr, uint32_t maskAlpha, Color defaultColor)
67 {
68     if (colorStr.empty()) {
69         // empty string, return transparent
70         return Color::TRANSPARENT;
71     }
72 
73     // Remove all " ".
74     colorStr.erase(std::remove(colorStr.begin(), colorStr.end(), ' '), colorStr.end());
75 
76     std::smatch matches;
77     // Regex match for #909090 or #90909090.
78     if (std::regex_match(colorStr, matches, COLOR_WITH_MAGIC)) {
79         colorStr.erase(0, 1);
80         auto value = stoul(colorStr, nullptr, COLOR_STRING_BASE);
81         if (colorStr.length() < COLOR_STRING_SIZE_STANDARD) {
82             // no alpha specified, set alpha to 0xff
83             value |= maskAlpha;
84         }
85         return Color(value);
86     }
87     // Regex match for #rgb or #rgba.
88     if (std::regex_match(colorStr, matches, COLOR_WITH_MAGIC_MINI)) {
89         colorStr.erase(0, 1);
90         std::string newColorStr;
91         // translate #rgb or #rgba to #rrggbb or #rrggbbaa
92         for (auto& c : colorStr) {
93             newColorStr += c;
94             newColorStr += c;
95         }
96         auto value = stoul(newColorStr, nullptr, COLOR_STRING_BASE);
97         if (newColorStr.length() < COLOR_STRING_SIZE_STANDARD) {
98             // no alpha specified, set alpha to 0xff
99             value |= maskAlpha;
100         }
101         return Color(value);
102     }
103     // Regex match for rgb(90,254,180).
104     if (std::regex_match(colorStr, matches, COLOR_WITH_RGB)) {
105         if (matches.size() == RGB_SUB_MATCH_SIZE) {
106             auto red = static_cast<uint8_t>(std::stoi(matches[1]));   // red value.
107             auto green = static_cast<uint8_t>(std::stoi(matches[2])); // green value.
108             auto blue = static_cast<uint8_t>(std::stoi(matches[3]));  // blue value.
109             return FromRGB(red, green, blue);
110         }
111     }
112     // Regex match for rgba(90,254,180,0.5).
113     if (std::regex_match(colorStr, matches, COLOR_WITH_RGBA)) {
114         if (matches.size() == RGBA_SUB_MATCH_SIZE) {
115             auto red = static_cast<uint8_t>(std::stoi(matches[1]));
116             auto green = static_cast<uint8_t>(std::stoi(matches[2]));
117             auto blue = static_cast<uint8_t>(std::stoi(matches[3]));
118             auto opacity = static_cast<double>(std::stod(matches[4]));
119             return FromRGBO(red, green, blue, opacity);
120         }
121     }
122     // match for special string
123     static const LinearMapNode<Color> colorTable[] = {
124         { "black", Color(0xff000000) },
125         { "blue", Color(0xff0000ff) },
126         { "gray", Color(0xffc0c0c0) },
127         { "green", Color(0xff00ff00) },
128         { "red", Color(0xffff0000) },
129         { "white", Color(0xffffffff) },
130     };
131     int64_t colorIndex = BinarySearchFindIndex(colorTable, ArraySize(colorTable), colorStr.c_str());
132     if (colorIndex != -1) {
133         return colorTable[colorIndex].value;
134     }
135 
136     // parse uint32_t color string.
137     auto uint32Color = StringUtils::StringToUint(colorStr);
138     if (uint32Color > 0) {
139         Color value;
140         if (uint32Color >> COLOR_ALPHA_OFFSET == 0) {
141             value = Color(uint32Color).ChangeAlpha(MAX_ALPHA);
142         } else {
143             value = Color(uint32Color);
144         }
145         return value;
146     }
147 
148     // Default color.
149     return defaultColor;
150 }
151 
ParseColorString(std::string colorStr,Color & color,uint32_t maskAlpha)152 bool Color::ParseColorString(std::string colorStr, Color& color, uint32_t maskAlpha)
153 {
154     if (colorStr.empty()) {
155         return false;
156     }
157 
158     // Remove all " ".
159     colorStr.erase(std::remove(colorStr.begin(), colorStr.end(), ' '), colorStr.end());
160 
161     return (MatchColorWithMagic(colorStr, maskAlpha, color) || MatchColorWithMagicMini(colorStr, maskAlpha, color) ||
162             MatchColorWithRGB(colorStr, color) || MatchColorWithRGBA(colorStr, color) ||
163             MatchColorSpecialString(colorStr, color) || ParseUintColorString(colorStr, color));
164 }
165 
ColorToString() const166 std::string Color::ColorToString() const
167 {
168     std::string colorStr;
169     int count = 0;
170     uint32_t value = GetValue();
171     while (count++ < BIT_LENGTH_INT32) {
172         colorStr = HEX[(value & 0xf)] + colorStr;
173         value >>= 4;
174     }
175     colorStr = "#" + colorStr;
176     return colorStr;
177 }
178 
179 // for example str = #FFFFFFFF
ColorFromString(const std::string & str)180 Color Color::ColorFromString(const std::string& str)
181 {
182     static const int32_t colorStrLen = 9;
183     static const int32_t offset = 4;
184 
185     LOGD("UITree str=%{public}s", str.c_str());
186     if (str.length() != colorStrLen || str.find('#') != 0) {
187         LOGE("UITree |ERROR| invalid %{public}s", str.c_str());
188         return Color::BLACK;
189     }
190 
191     std::string colorStr = str.substr(1, colorStrLen - 1);
192     uint32_t value = 0;
193     for (const auto& it : colorStr) {
194         value <<= offset;
195         value += it < 'A' ? it - '0' : it - '7';
196     }
197 
198     LOGD("UITree [%{public}s, #%{public}08X]", str.c_str(), value);
199     return Color(value);
200 }
201 
FromARGB(uint8_t alpha,uint8_t red,uint8_t green,uint8_t blue)202 Color Color::FromARGB(uint8_t alpha, uint8_t red, uint8_t green, uint8_t blue)
203 {
204     ColorParam colorValue {
205 #if BIG_ENDIANNESS
206         .argb = { .alpha = alpha, .red = red, .green = green, .blue = blue }
207 #else
208         .argb = { .blue = blue, .green = green, .red = red, .alpha = alpha }
209 #endif
210     };
211     return Color(colorValue);
212 }
213 
FromRGBO(uint8_t red,uint8_t green,uint8_t blue,double opacity)214 Color Color::FromRGBO(uint8_t red, uint8_t green, uint8_t blue, double opacity)
215 {
216     return FromARGB(static_cast<uint8_t>(round(opacity * 0xff)) & 0xff, red, green, blue);
217 }
218 
FromRGB(uint8_t red,uint8_t green,uint8_t blue)219 Color Color::FromRGB(uint8_t red, uint8_t green, uint8_t blue)
220 {
221     return FromARGB(0xff, red, green, blue);
222 }
223 
BlendColor(const Color & overlayColor) const224 Color Color::BlendColor(const Color& overlayColor) const
225 {
226     if (GetValue() == Color::TRANSPARENT.GetValue()) {
227         return overlayColor;
228     }
229     if (GetAlpha() < static_cast<uint8_t>(MAX_ALPHA)) {
230         return BlendColorWithAlpha(overlayColor);
231     }
232     auto alphaRate = static_cast<float>(overlayColor.GetAlpha()) / MAX_ALPHA;
233     auto newRed = static_cast<uint8_t>(GetRed() * (1.0f - alphaRate) + overlayColor.GetRed() * alphaRate);
234     auto newGreen = static_cast<uint8_t>(GetGreen() * (1.0f - alphaRate) + overlayColor.GetGreen() * alphaRate);
235     auto newBlue = static_cast<uint8_t>(GetBlue() * (1.0f - alphaRate) + overlayColor.GetBlue() * alphaRate);
236     return Color::FromRGB(newRed, newGreen, newBlue);
237 }
238 
CalculateBlend(float alphaLeft,float alphaRight,float valueLeft,float valueRight) const239 float Color::CalculateBlend(float alphaLeft, float alphaRight, float valueLeft, float valueRight) const
240 {
241     return (valueLeft * alphaLeft * (1.0 - alphaRight) + valueRight * alphaRight) /
242            (alphaLeft + alphaRight - alphaLeft * alphaRight);
243 }
244 
BlendColorWithAlpha(const Color & overlayColor) const245 Color Color::BlendColorWithAlpha(const Color& overlayColor) const
246 {
247     float alphaA = GetAlpha() / 255.0;
248     float alphaB = overlayColor.GetAlpha() / 255.0;
249     float blendAlpha = alphaA + alphaB - alphaA * alphaB;
250     float blendRed = CalculateBlend(alphaA, alphaB, GetRed() / 255.0, overlayColor.GetRed() / 255.0);
251     float blendGreen = CalculateBlend(alphaA, alphaB, GetGreen() / 255.0, overlayColor.GetGreen() / 255.0);
252     float blendBlue = CalculateBlend(alphaA, alphaB, GetBlue() / 255.0, overlayColor.GetBlue() / 255.0);
253 
254     return Color::FromARGB(blendAlpha * 255, blendRed * 255, blendGreen * 255, blendBlue * 255);
255 }
256 
LineColorTransition(const Color & startColor,const Color & endColor,double percent)257 const Color Color::LineColorTransition(const Color& startColor, const Color& endColor, double percent)
258 {
259     uint8_t red = 0;
260     uint8_t green = 0;
261     uint8_t blue = 0;
262     uint8_t alpha = 0;
263 
264     red = static_cast<uint8_t>((endColor.GetRed()- startColor.GetRed()) * percent) + startColor.GetRed();
265     green = static_cast<uint8_t>((endColor.GetGreen() - startColor.GetGreen()) * percent) + startColor.GetGreen();
266     blue = static_cast<uint8_t>((endColor.GetBlue() - startColor.GetBlue()) * percent) + startColor.GetBlue();
267     alpha = static_cast<uint8_t>((endColor.GetAlpha() - startColor.GetAlpha()) * percent) + startColor.GetAlpha();
268 
269     return Color::FromARGB(alpha, red, green, blue);
270 }
271 
BlendOpacity(double opacityRatio) const272 Color Color::BlendOpacity(double opacityRatio) const
273 {
274     int32_t alpha = static_cast<int32_t>(GetAlpha() * opacityRatio);
275     alpha = std::clamp(alpha, 0, UINT8_MAX);
276     return Color::FromARGB(alpha, GetRed(), GetGreen(), GetBlue());
277 }
278 
ChangeOpacity(double opacity) const279 Color Color::ChangeOpacity(double opacity) const
280 {
281     return Color::FromRGBO(GetRed(), GetGreen(), GetBlue(), opacity);
282 }
283 
ChangeAlpha(uint8_t alpha) const284 Color Color::ChangeAlpha(uint8_t alpha) const
285 {
286     return Color::FromARGB(alpha, GetRed(), GetGreen(), GetBlue());
287 }
288 
operator +(const Color & color) const289 Color Color::operator+(const Color& color) const
290 {
291     // convert first color from ARGB to linear
292     double firstLinearRed = 0.0;
293     double firstLinearGreen = 0.0;
294     double firstLinearBlue = 0.0;
295     ConvertGammaToLinear(*this, firstLinearRed, firstLinearGreen, firstLinearBlue);
296 
297     // convert second color from ARGB to linear
298     double secondLinearRed = 0.0;
299     double secondLinearGreen = 0.0;
300     double secondLinearBlue = 0.0;
301     ConvertGammaToLinear(color, secondLinearRed, secondLinearGreen, secondLinearBlue);
302 
303     // get linear result and convert to gamma
304     return ConvertLinearToGamma(GetAlpha() + color.GetAlpha(), firstLinearRed + secondLinearRed,
305         firstLinearGreen + secondLinearGreen, firstLinearBlue + secondLinearBlue);
306 }
307 
operator -(const Color & color) const308 Color Color::operator-(const Color& color) const
309 {
310     // convert first color from ARGB to linear
311     double firstLinearRed = 0.0;
312     double firstLinearGreen = 0.0;
313     double firstLinearBlue = 0.0;
314     ConvertGammaToLinear(*this, firstLinearRed, firstLinearGreen, firstLinearBlue);
315 
316     // convert second color from ARGB to linear
317     double secondLinearRed = 0.0;
318     double secondLinearGreen = 0.0;
319     double secondLinearBlue = 0.0;
320     ConvertGammaToLinear(color, secondLinearRed, secondLinearGreen, secondLinearBlue);
321 
322     // get linear result and convert to gamma
323     return ConvertLinearToGamma(GetAlpha() - color.GetAlpha(), firstLinearRed - secondLinearRed,
324         firstLinearGreen - secondLinearGreen, firstLinearBlue - secondLinearBlue);
325 }
326 
operator *(double value) const327 Color Color::operator*(double value) const
328 {
329     // convert color from ARGB to linear
330     double linearRed = 0.0;
331     double linearGreen = 0.0;
332     double linearBlue = 0.0;
333     ConvertGammaToLinear(*this, linearRed, linearGreen, linearBlue);
334 
335     // get linear result and convert to gamma
336     return ConvertLinearToGamma(GetAlpha() * value, linearRed * value, linearGreen * value, linearBlue * value);
337 }
338 
operator /(double value) const339 Color Color::operator/(double value) const
340 {
341     if (NearZero(value)) {
342         return *this;
343     }
344     // convert color from ARGB to linear
345     double linearRed = 0.0;
346     double linearGreen = 0.0;
347     double LinearBlue = 0.0;
348     ConvertGammaToLinear(*this, linearRed, linearGreen, LinearBlue);
349 
350     // get linear result and convert to gamma
351     return ConvertLinearToGamma(GetAlpha() / value, linearRed / value, linearGreen / value, LinearBlue / value);
352 }
353 
ConvertGammaToLinear(uint8_t value)354 double Color::ConvertGammaToLinear(uint8_t value)
355 {
356     return std::pow(value, GAMMA_FACTOR);
357 }
358 
ConvertLinearToGamma(double value)359 uint8_t Color::ConvertLinearToGamma(double value)
360 {
361     return std::clamp(static_cast<int32_t>(round(std::pow(value, 1.0 / GAMMA_FACTOR))), 0, UINT8_MAX);
362 }
363 
ConvertGammaToLinear(const Color & gammaColor,double & linearRed,double & linearGreen,double & linearBlue)364 void Color::ConvertGammaToLinear(const Color& gammaColor, double& linearRed, double& linearGreen, double& linearBlue)
365 {
366     linearRed = ConvertGammaToLinear(gammaColor.GetRed());
367     linearGreen = ConvertGammaToLinear(gammaColor.GetGreen());
368     linearBlue = ConvertGammaToLinear(gammaColor.GetBlue());
369 }
370 
ConvertLinearToGamma(double alpha,double linearRed,double linearGreen,double linearBlue)371 Color Color::ConvertLinearToGamma(double alpha, double linearRed, double linearGreen, double linearBlue)
372 {
373     uint8_t gammaRed = ConvertLinearToGamma(linearRed);
374     uint8_t gammaGreen = ConvertLinearToGamma(linearGreen);
375     uint8_t gammaBlue = ConvertLinearToGamma(linearBlue);
376     uint8_t gammaAlpha = std::clamp(static_cast<int32_t>(round(alpha)), 0, UINT8_MAX);
377 
378     return FromARGB(gammaAlpha, gammaRed, gammaGreen, gammaBlue);
379 }
380 
MatchColorWithMagic(std::string & colorStr,uint32_t maskAlpha,Color & color)381 bool Color::MatchColorWithMagic(std::string& colorStr, uint32_t maskAlpha, Color& color)
382 {
383     std::smatch matches;
384     // Regex match for #909090 or #90909090.
385     if (std::regex_match(colorStr, matches, COLOR_WITH_MAGIC)) {
386         colorStr.erase(0, 1);
387         auto value = stoul(colorStr, nullptr, COLOR_STRING_BASE);
388         if (colorStr.length() < COLOR_STRING_SIZE_STANDARD) {
389             // no alpha specified, set alpha to 0xff
390             value |= maskAlpha;
391         }
392         color = Color(value);
393         return true;
394     }
395 
396     return false;
397 }
398 
MatchColorWithMagicMini(std::string & colorStr,uint32_t maskAlpha,Color & color)399 bool Color::MatchColorWithMagicMini(std::string& colorStr, uint32_t maskAlpha, Color& color)
400 {
401     std::smatch matches;
402     if (std::regex_match(colorStr, matches, COLOR_WITH_MAGIC_MINI)) {
403         colorStr.erase(0, 1);
404         std::string newColorStr;
405         // translate #rgb or #rgba to #rrggbb or #rrggbbaa
406         for (auto& c : colorStr) {
407             newColorStr += c;
408             newColorStr += c;
409         }
410         auto value = stoul(newColorStr, nullptr, COLOR_STRING_BASE);
411         if (newColorStr.length() < COLOR_STRING_SIZE_STANDARD) {
412             // no alpha specified, set alpha to 0xff
413             value |= maskAlpha;
414         }
415         color = Color(value);
416         return true;
417     }
418 
419     return false;
420 }
421 
MatchColorWithRGB(const std::string & colorStr,Color & color)422 bool Color::MatchColorWithRGB(const std::string& colorStr, Color& color)
423 {
424     std::smatch matches;
425     if (std::regex_match(colorStr, matches, COLOR_WITH_RGB)) {
426         if (matches.size() == RGB_SUB_MATCH_SIZE) {
427             auto redInt = std::stoi(matches[1]);
428             auto greenInt = std::stoi(matches[2]);
429             auto blueInt = std::stoi(matches[3]);
430             if (!IsRGBValid(redInt) || !IsRGBValid(greenInt) || !IsRGBValid(blueInt)) {
431                 return false;
432             }
433 
434             auto red = static_cast<uint8_t>(redInt);
435             auto green = static_cast<uint8_t>(greenInt);
436             auto blue = static_cast<uint8_t>(blueInt);
437             color = FromRGB(red, green, blue);
438             return true;
439         }
440     }
441     return false;
442 }
443 
MatchColorWithRGBA(const std::string & colorStr,Color & color)444 bool Color::MatchColorWithRGBA(const std::string& colorStr, Color& color)
445 {
446     std::smatch matches;
447     if (std::regex_match(colorStr, matches, COLOR_WITH_RGBA)) {
448         if (matches.size() == RGBA_SUB_MATCH_SIZE) {
449             auto redInt = std::stoi(matches[1]);
450             auto greenInt = std::stoi(matches[2]);
451             auto blueInt = std::stoi(matches[3]);
452             auto opacityDouble = std::stod(matches[4]);
453             if (!IsRGBValid(redInt) || !IsRGBValid(greenInt) || !IsRGBValid(blueInt) ||
454                 !IsOpacityValid(opacityDouble)) {
455                 return false;
456             }
457 
458             auto red = static_cast<uint8_t>(redInt);
459             auto green = static_cast<uint8_t>(greenInt);
460             auto blue = static_cast<uint8_t>(blueInt);
461             auto opacity = static_cast<double>(opacityDouble);
462 
463             color = FromRGBO(red, green, blue, opacity);
464             return true;
465         }
466     }
467 
468     return false;
469 }
470 
MatchColorSpecialString(const std::string & colorStr,Color & color)471 bool Color::MatchColorSpecialString(const std::string& colorStr, Color& color)
472 {
473     static const LinearMapNode<Color> colorTable[] = {
474         { "black", Color(0xff000000) },
475         { "blue", Color(0xff0000ff) },
476         { "gray", Color(0xffc0c0c0) },
477         { "green", Color(0xff00ff00) },
478         { "red", Color(0xffff0000) },
479         { "white", Color(0xffffffff) },
480     };
481 
482     int64_t colorIndex = BinarySearchFindIndex(colorTable, ArraySize(colorTable), colorStr.c_str());
483     if (colorIndex != -1) {
484         color = colorTable[colorIndex].value;
485         return true;
486     }
487 
488     return false;
489 }
490 
ParseUintColorString(const std::string & colorStr,Color & color)491 bool Color::ParseUintColorString(const std::string& colorStr, Color& color)
492 {
493     auto uint32Color = StringUtils::StringToUint(colorStr);
494     if (uint32Color > 0) {
495         if (uint32Color >> COLOR_ALPHA_OFFSET == 0) {
496             color = Color(uint32Color).ChangeAlpha(MAX_ALPHA);
497         } else {
498             color = Color(uint32Color);
499         }
500         return true;
501     }
502 
503     return false;
504 }
505 
IsRGBValid(int value)506 bool Color::IsRGBValid(int value)
507 {
508     return value >= MIN_RGB_VALUE && value <= MAX_RGB_VALUE;
509 }
510 
IsOpacityValid(double value)511 bool Color::IsOpacityValid(double value)
512 {
513     return value >= MIN_RGBA_OPACITY && value <= MAX_RGBA_OPACITY;
514 }
515 
516 } // namespace OHOS::Ace
517