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