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