1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
27 #include <tools/color.hxx>
28 #include <tools/helpers.hxx>
29 #include <tools/long.hxx>
30 #include <o3tl/string_view.hxx>
31 #include <basegfx/color/bcolortools.hxx>
32 #include <basegfx/numeric/ftools.hxx>
34 static inline double NormalizeRGB(double nValue
)
39 return pow((nValue
+0.055)/1.055, 2.4);
42 sal_uInt8
Color::GetWCAGLuminance() const
44 // https://www.w3.org/TR/WCAG21/#dfn-relative-luminance
45 const double nRed
= NormalizeRGB(R
/255.0);
46 const double nGreen
= NormalizeRGB(G
/255.0);
47 const double nBlue
= NormalizeRGB(B
/255.0);
48 return (nRed
* 0.2126 + nGreen
* 0.7152 + nBlue
* 0.0722) * 255UL;
51 bool Color::IsDark() const
53 if (mValue
== 0x729fcf) // COL_DEFAULT_SHAPE_FILLING
54 return GetLuminance() <= 62;
56 return GetWCAGLuminance() <= 87;
59 bool Color::IsBright() const
62 // return GetLuminance() >= 245;
65 void Color::IncreaseLuminance(sal_uInt8 cLumInc
)
67 R
= sal_uInt8(std::clamp(R
+ cLumInc
, 0, 255));
68 G
= sal_uInt8(std::clamp(G
+ cLumInc
, 0, 255));
69 B
= sal_uInt8(std::clamp(B
+ cLumInc
, 0, 255));
72 void Color::DecreaseLuminance(sal_uInt8 cLumDec
)
74 R
= sal_uInt8(std::clamp(R
- cLumDec
, 0, 255));
75 G
= sal_uInt8(std::clamp(G
- cLumDec
, 0, 255));
76 B
= sal_uInt8(std::clamp(B
- cLumDec
, 0, 255));
79 void Color::DecreaseContrast(sal_uInt8 nContDec
)
83 const double fM
= (128.0 - 0.4985 * nContDec
) / 128.0;
84 const double fOff
= 128.0 - fM
* 128.0;
86 R
= basegfx::fround
<sal_uInt8
>(R
* fM
+ fOff
);
87 G
= basegfx::fround
<sal_uInt8
>(G
* fM
+ fOff
);
88 B
= basegfx::fround
<sal_uInt8
>(B
* fM
+ fOff
);
92 // color space conversion
94 void Color::RGBtoHSB( sal_uInt16
& nHue
, sal_uInt16
& nSat
, sal_uInt16
& nBri
) const
109 // Brightness = max(R, G, B);
110 nBri
= cMax
* 100 / 255;
118 sal_uInt8 cDelta
= cMax
- cMin
;
120 // Saturation = max - min / max
122 nSat
= cDelta
* 100 / cMax
;
127 nHue
= 0; // Default = undefined
134 dHue
= static_cast<double>( c
[1] - c
[2] ) / static_cast<double>(cDelta
);
136 else if( c
[1] == cMax
)
138 dHue
= 2.0 + static_cast<double>( c
[2] - c
[0] ) / static_cast<double>(cDelta
);
140 else if ( c
[2] == cMax
)
142 dHue
= 4.0 + static_cast<double>( c
[0] - c
[1] ) / static_cast<double>(cDelta
);
149 nHue
= static_cast<sal_uInt16
>(dHue
);
153 Color
Color::HSBtoRGB( sal_uInt16 nHue
, sal_uInt16 nSat
, sal_uInt16 nBri
)
155 sal_uInt8 cR
=0,cG
=0,cB
=0;
156 sal_uInt8 nB
= static_cast<sal_uInt8
>( nBri
* 255 / 100 );
173 n
= static_cast<sal_uInt16
>(dH
);
176 sal_uInt8 a
= static_cast<sal_uInt8
>( nB
* ( 100 - nSat
) / 100 );
177 sal_uInt8 b
= static_cast<sal_uInt8
>( nB
* ( 100 - ( static_cast<double>(nSat
) * f
) ) / 100 );
178 sal_uInt8 c
= static_cast<sal_uInt8
>( nB
* ( 100 - ( static_cast<double>(nSat
) * ( 1.0 - f
) ) ) / 100 );
182 case 0: cR
= nB
; cG
= c
; cB
= a
; break;
183 case 1: cR
= b
; cG
= nB
; cB
= a
; break;
184 case 2: cR
= a
; cG
= nB
; cB
= c
; break;
185 case 3: cR
= a
; cG
= b
; cB
= nB
; break;
186 case 4: cR
= c
; cG
= a
; cB
= nB
; break;
187 case 5: cR
= nB
; cG
= a
; cB
= b
; break;
191 return Color( cR
, cG
, cB
);
194 Color
Color::STRtoRGB(std::u16string_view colorname
)
197 if(colorname
.empty()) return col
;
199 switch(colorname
.size()){
201 col
.mValue
= o3tl::toUInt32(colorname
.substr(1,6), 16);
204 col
.mValue
= o3tl::toUInt32(colorname
, 16);
208 sal_Unicode data
[6] = { colorname
[1], colorname
[1], colorname
[2],
209 colorname
[2], colorname
[3], colorname
[3] };
210 col
.mValue
= o3tl::toUInt32(std::u16string_view(data
,6), 16);
215 sal_Unicode data
[6] = { colorname
[0], colorname
[0], colorname
[1],
216 colorname
[1], colorname
[2], colorname
[2] };
217 col
.mValue
= o3tl::toUInt32(std::u16string_view(data
,6), 16);
226 OUString
Color::AsRGBHexString() const
228 std::stringstream ss
;
229 ss
<< std::hex
<< std::setfill ('0') << std::setw(6) << sal_uInt32(GetRGBColor());
230 return OUString::createFromAscii(ss
.str());
233 OUString
Color::AsRGBHEXString() const
235 std::stringstream ss
;
236 ss
<< std::hex
<< std::uppercase
<< std::setfill ('0') << std::setw(6) << sal_uInt32(GetRGBColor());
237 return OUString::createFromAscii(ss
.str());
240 void Color::ApplyTintOrShade(sal_Int16 n100thPercent
)
242 if (n100thPercent
== 0)
245 basegfx::BColor aBColor
= basegfx::utils::rgb2hsl(getBColor());
246 double fFactor
= 1.0 - (std::abs(double(n100thPercent
)) / 10000.0);
249 if (n100thPercent
> 0) // tint
251 fResult
= aBColor
.getBlue() * fFactor
+ (1.0 - fFactor
);
255 fResult
= aBColor
.getBlue() * fFactor
;
258 aBColor
.setBlue(fResult
);
259 aBColor
= basegfx::utils::hsl2rgb(aBColor
);
261 R
= sal_uInt8(std::lround(aBColor
.getRed() * 255.0));
262 G
= sal_uInt8(std::lround(aBColor
.getGreen() * 255.0));
263 B
= sal_uInt8(std::lround(aBColor
.getBlue() * 255.0));
266 void Color::ApplyLumModOff(sal_Int16 nMod
, sal_Int16 nOff
)
268 if (nMod
== 10000 && nOff
== 0)
272 // Switch to HSL, where applying these transforms is easier.
273 basegfx::BColor aBColor
= basegfx::utils::rgb2hsl(getBColor());
275 // 50% is half luminance, 200% is double luminance. Unit is 100th percent.
276 aBColor
.setBlue(std::clamp(aBColor
.getBlue() * nMod
/ 10000, 0.0, 1.0));
277 // If color changes to black or white, it will stay gray if luminance changes again.
278 if ((aBColor
.getBlue() == 0.0) || (aBColor
.getBlue() == 1.0))
280 aBColor
.setGreen(0.0);
283 // Luminance offset means hue and saturation is left unchanged. Unit is 100th percent.
284 aBColor
.setBlue(std::clamp(aBColor
.getBlue() + static_cast<double>(nOff
) / 10000, 0.0, 1.0));
285 // If color changes to black or white, it will stay gray if luminance changes again.
286 if ((aBColor
.getBlue() == 0.0) || (aBColor
.getBlue() == 1.0))
288 aBColor
.setGreen(0.0);
291 // Switch back to RGB.
292 aBColor
= basegfx::utils::hsl2rgb(aBColor
);
293 R
= sal_uInt8(std::lround(aBColor
.getRed() * 255.0));
294 G
= sal_uInt8(std::lround(aBColor
.getGreen() * 255.0));
295 B
= sal_uInt8(std::lround(aBColor
.getBlue() * 255.0));
298 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */