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 <oox/drawingml/color.hxx>
22 #include <frozen/bits/defines.h>
23 #include <frozen/bits/elsa_std.h>
24 #include <frozen/unordered_map.h>
26 #include <osl/diagnose.h>
27 #include <sal/log.hxx>
28 #include <oox/helper/containerhelper.hxx>
29 #include <oox/helper/graphichelper.hxx>
30 #include <oox/drawingml/drawingmltypes.hxx>
31 #include <oox/token/namespaces.hxx>
32 #include <oox/token/tokens.hxx>
34 namespace oox::drawingml
{
39 // predefined colors in DrawingML (map XML token identifiers to RGB values)
40 constexpr frozen::unordered_map
<sal_Int32
, ::Color
, 140> constDmlColors
42 {XML_aliceBlue
, ::Color(0xF0F8FF)}, {XML_antiqueWhite
, ::Color(0xFAEBD7)},
43 {XML_aqua
, ::Color(0x00FFFF)}, {XML_aquamarine
, ::Color(0x7FFFD4)},
44 {XML_azure
, ::Color(0xF0FFFF)}, {XML_beige
, ::Color(0xF5F5DC)},
45 {XML_bisque
, ::Color(0xFFE4C4)}, {XML_black
, ::Color(0x000000)},
46 {XML_blanchedAlmond
, ::Color(0xFFEBCD)}, {XML_blue
, ::Color(0x0000FF)},
47 {XML_blueViolet
, ::Color(0x8A2BE2)}, {XML_brown
, ::Color(0xA52A2A)},
48 {XML_burlyWood
, ::Color(0xDEB887)}, {XML_cadetBlue
, ::Color(0x5F9EA0)},
49 {XML_chartreuse
, ::Color(0x7FFF00)}, {XML_chocolate
, ::Color(0xD2691E)},
50 {XML_coral
, ::Color(0xFF7F50)}, {XML_cornflowerBlue
, ::Color(0x6495ED)},
51 {XML_cornsilk
, ::Color(0xFFF8DC)}, {XML_crimson
, ::Color(0xDC143C)},
52 {XML_cyan
, ::Color(0x00FFFF)}, {XML_deepPink
, ::Color(0xFF1493)},
53 {XML_deepSkyBlue
, ::Color(0x00BFFF)}, {XML_dimGray
, ::Color(0x696969)},
54 {XML_dkBlue
, ::Color(0x00008B)}, {XML_dkCyan
, ::Color(0x008B8B)},
55 {XML_dkGoldenrod
, ::Color(0xB8860B)}, {XML_dkGray
, ::Color(0xA9A9A9)},
56 {XML_dkGreen
, ::Color(0x006400)}, {XML_dkKhaki
, ::Color(0xBDB76B)},
57 {XML_dkMagenta
, ::Color(0x8B008B)}, {XML_dkOliveGreen
, ::Color(0x556B2F)},
58 {XML_dkOrange
, ::Color(0xFF8C00)}, {XML_dkOrchid
, ::Color(0x9932CC)},
59 {XML_dkRed
, ::Color(0x8B0000)}, {XML_dkSalmon
, ::Color(0xE9967A)},
60 {XML_dkSeaGreen
, ::Color(0x8FBC8B)}, {XML_dkSlateBlue
, ::Color(0x483D8B)},
61 {XML_dkSlateGray
, ::Color(0x2F4F4F)}, {XML_dkTurquoise
, ::Color(0x00CED1)},
62 {XML_dkViolet
, ::Color(0x9400D3)}, {XML_dodgerBlue
, ::Color(0x1E90FF)},
63 {XML_firebrick
, ::Color(0xB22222)}, {XML_floralWhite
, ::Color(0xFFFAF0)},
64 {XML_forestGreen
, ::Color(0x228B22)}, {XML_fuchsia
, ::Color(0xFF00FF)},
65 {XML_gainsboro
, ::Color(0xDCDCDC)}, {XML_ghostWhite
, ::Color(0xF8F8FF)},
66 {XML_gold
, ::Color(0xFFD700)}, {XML_goldenrod
, ::Color(0xDAA520)},
67 {XML_gray
, ::Color(0x808080)}, {XML_green
, ::Color(0x008000)},
68 {XML_greenYellow
, ::Color(0xADFF2F)}, {XML_honeydew
, ::Color(0xF0FFF0)},
69 {XML_hotPink
, ::Color(0xFF69B4)}, {XML_indianRed
, ::Color(0xCD5C5C)},
70 {XML_indigo
, ::Color(0x4B0082)}, {XML_ivory
, ::Color(0xFFFFF0)},
71 {XML_khaki
, ::Color(0xF0E68C)}, {XML_lavender
, ::Color(0xE6E6FA)},
72 {XML_lavenderBlush
, ::Color(0xFFF0F5)}, {XML_lawnGreen
, ::Color(0x7CFC00)},
73 {XML_lemonChiffon
, ::Color(0xFFFACD)}, {XML_lime
, ::Color(0x00FF00)},
74 {XML_limeGreen
, ::Color(0x32CD32)}, {XML_linen
, ::Color(0xFAF0E6)},
75 {XML_ltBlue
, ::Color(0xADD8E6)}, {XML_ltCoral
, ::Color(0xF08080)},
76 {XML_ltCyan
, ::Color(0xE0FFFF)}, {XML_ltGoldenrodYellow
, ::Color(0xFAFA78)},
77 {XML_ltGray
, ::Color(0xD3D3D3)}, {XML_ltGreen
, ::Color(0x90EE90)},
78 {XML_ltPink
, ::Color(0xFFB6C1)}, {XML_ltSalmon
, ::Color(0xFFA07A)},
79 {XML_ltSeaGreen
, ::Color(0x20B2AA)}, {XML_ltSkyBlue
, ::Color(0x87CEFA)},
80 {XML_ltSlateGray
, ::Color(0x778899)}, {XML_ltSteelBlue
, ::Color(0xB0C4DE)},
81 {XML_ltYellow
, ::Color(0xFFFFE0)}, {XML_magenta
, ::Color(0xFF00FF)},
82 {XML_maroon
, ::Color(0x800000)}, {XML_medAquamarine
, ::Color(0x66CDAA)},
83 {XML_medBlue
, ::Color(0x0000CD)}, {XML_medOrchid
, ::Color(0xBA55D3)},
84 {XML_medPurple
, ::Color(0x9370DB)}, {XML_medSeaGreen
, ::Color(0x3CB371)},
85 {XML_medSlateBlue
, ::Color(0x7B68EE)}, {XML_medSpringGreen
, ::Color(0x00FA9A)},
86 {XML_medTurquoise
, ::Color(0x48D1CC)}, {XML_medVioletRed
, ::Color(0xC71585)},
87 {XML_midnightBlue
, ::Color(0x191970)}, {XML_mintCream
, ::Color(0xF5FFFA)},
88 {XML_mistyRose
, ::Color(0xFFE4E1)}, {XML_moccasin
, ::Color(0xFFE4B5)},
89 {XML_navajoWhite
, ::Color(0xFFDEAD)}, {XML_navy
, ::Color(0x000080)},
90 {XML_oldLace
, ::Color(0xFDF5E6)}, {XML_olive
, ::Color(0x808000)},
91 {XML_oliveDrab
, ::Color(0x6B8E23)}, {XML_orange
, ::Color(0xFFA500)},
92 {XML_orangeRed
, ::Color(0xFF4500)}, {XML_orchid
, ::Color(0xDA70D6)},
93 {XML_paleGoldenrod
, ::Color(0xEEE8AA)}, {XML_paleGreen
, ::Color(0x98FB98)},
94 {XML_paleTurquoise
, ::Color(0xAFEEEE)}, {XML_paleVioletRed
, ::Color(0xDB7093)},
95 {XML_papayaWhip
, ::Color(0xFFEFD5)}, {XML_peachPuff
, ::Color(0xFFDAB9)},
96 {XML_peru
, ::Color(0xCD853F)}, {XML_pink
, ::Color(0xFFC0CB)},
97 {XML_plum
, ::Color(0xDDA0DD)}, {XML_powderBlue
, ::Color(0xB0E0E6)},
98 {XML_purple
, ::Color(0x800080)}, {XML_red
, ::Color(0xFF0000)},
99 {XML_rosyBrown
, ::Color(0xBC8F8F)}, {XML_royalBlue
, ::Color(0x4169E1)},
100 {XML_saddleBrown
, ::Color(0x8B4513)}, {XML_salmon
, ::Color(0xFA8072)},
101 {XML_sandyBrown
, ::Color(0xF4A460)}, {XML_seaGreen
, ::Color(0x2E8B57)},
102 {XML_seaShell
, ::Color(0xFFF5EE)}, {XML_sienna
, ::Color(0xA0522D)},
103 {XML_silver
, ::Color(0xC0C0C0)}, {XML_skyBlue
, ::Color(0x87CEEB)},
104 {XML_slateBlue
, ::Color(0x6A5ACD)}, {XML_slateGray
, ::Color(0x708090)},
105 {XML_snow
, ::Color(0xFFFAFA)}, {XML_springGreen
, ::Color(0x00FF7F)},
106 {XML_steelBlue
, ::Color(0x4682B4)}, {XML_tan
, ::Color(0xD2B48C)},
107 {XML_teal
, ::Color(0x008080)}, {XML_thistle
, ::Color(0xD8BFD8)},
108 {XML_tomato
, ::Color(0xFF6347)}, {XML_turquoise
, ::Color(0x40E0D0)},
109 {XML_violet
, ::Color(0xEE82EE)}, {XML_wheat
, ::Color(0xF5DEB3)},
110 {XML_white
, ::Color(0xFFFFFF)}, {XML_whiteSmoke
, ::Color(0xF5F5F5)},
111 {XML_yellow
, ::Color(0xFFFF00)}, {XML_yellowGreen
, ::Color(0x9ACD32)}
114 constexpr ::Color
lookupDmlColor(sal_Int32 nToken
)
116 auto iterator
= constDmlColors
.find(nToken
);
117 if (iterator
== constDmlColors
.end())
118 return API_RGB_TRANSPARENT
;
119 return iterator
->second
;
122 constexpr frozen::unordered_map
<sal_Int32
, ::Color
, 16> constVmlColors
124 {XML_aqua
, ::Color(0x00FFFF)}, {XML_black
, ::Color(0x000000)},
125 {XML_blue
, ::Color(0x0000FF)}, {XML_fuchsia
, ::Color(0xFF00FF)},
126 {XML_gray
, ::Color(0x808080)}, {XML_green
, ::Color(0x008000)},
127 {XML_lime
, ::Color(0x00FF00)}, {XML_maroon
, ::Color(0x800000)},
128 {XML_navy
, ::Color(0x000080)}, {XML_olive
, ::Color(0x808000)},
129 {XML_purple
, ::Color(0x800080)}, {XML_red
, ::Color(0xFF0000)},
130 {XML_silver
, ::Color(0xC0C0C0)}, {XML_teal
, ::Color(0x008080)},
131 {XML_white
, ::Color(0xFFFFFF)}, {XML_yellow
, ::Color(0xFFFF00)}
134 constexpr ::Color
lookupVmlColor(sal_Int32 nToken
)
136 auto iterator
= constVmlColors
.find(nToken
);
137 if (iterator
== constVmlColors
.end())
138 return API_RGB_TRANSPARENT
;
139 return iterator
->second
;
142 constexpr frozen::unordered_map
<sal_Int32
, ::Color
, 16> constHighlightColors
144 // tdf#131841 Predefined color for OOXML highlight.
145 {XML_black
, ::Color(0x000000)}, {XML_blue
, ::Color(0x0000FF)},
146 {XML_cyan
, ::Color(0x00FFFF)}, {XML_darkBlue
, ::Color(0x00008B)},
147 {XML_darkCyan
, ::Color(0x008B8B)}, {XML_darkGray
, ::Color(0xA9A9A9)},
148 {XML_darkGreen
, ::Color(0x006400)}, {XML_darkMagenta
, ::Color(0x800080)},
149 {XML_darkRed
, ::Color(0x8B0000)}, {XML_darkYellow
, ::Color(0x808000)},
150 {XML_green
, ::Color(0x00FF00)}, {XML_lightGray
, ::Color(0xD3D3D3)},
151 {XML_magenta
, ::Color(0xFF00FF)}, {XML_red
, ::Color(0xFF0000)},
152 {XML_white
, ::Color(0xFFFFFF)}, {XML_yellow
, ::Color(0xFFFF00)}
155 constexpr ::Color
lookupHighlightColor(sal_Int32 nToken
)
157 auto iterator
= constHighlightColors
.find(nToken
);
158 if (iterator
== constHighlightColors
.end())
159 return API_RGB_TRANSPARENT
;
160 return iterator
->second
;
163 const double DEC_GAMMA
= 2.3;
164 const double INC_GAMMA
= 1.0 / DEC_GAMMA
;
166 void lclRgbToRgbComponents( sal_Int32
& ornR
, sal_Int32
& ornG
, sal_Int32
& ornB
, ::Color nRgb
)
168 ornR
= nRgb
.GetRed();
169 ornG
= nRgb
.GetGreen();
170 ornB
= nRgb
.GetBlue();
173 sal_Int32
lclRgbComponentsToRgb( sal_Int32 nR
, sal_Int32 nG
, sal_Int32 nB
)
175 return static_cast< sal_Int32
>( (nR
<< 16) | (nG
<< 8) | nB
);
178 sal_Int32
lclRgbCompToCrgbComp( sal_Int32 nRgbComp
)
180 return static_cast< sal_Int32
>( nRgbComp
* MAX_PERCENT
/ 255 );
183 sal_Int32
lclCrgbCompToRgbComp( sal_Int32 nCrgbComp
)
185 return static_cast< sal_Int32
>( nCrgbComp
* 255 / MAX_PERCENT
);
188 sal_Int32
lclGamma( sal_Int32 nComp
, double fGamma
)
190 return static_cast< sal_Int32
>( pow( static_cast< double >( nComp
) / MAX_PERCENT
, fGamma
) * MAX_PERCENT
+ 0.5 );
193 void lclSetValue( sal_Int32
& ornValue
, sal_Int32 nNew
, sal_Int32 nMax
= MAX_PERCENT
)
195 OSL_ENSURE( (0 <= nNew
) && (nNew
<= nMax
), "lclSetValue - invalid value" );
196 if( (0 <= nNew
) && (nNew
<= nMax
) )
200 void lclModValue( sal_Int32
& ornValue
, sal_Int32 nMod
, sal_Int32 nMax
= MAX_PERCENT
)
202 OSL_ENSURE( (0 <= nMod
), "lclModValue - invalid modificator" );
203 ornValue
= getLimitedValue
< sal_Int32
, double >( static_cast< double >( ornValue
) * nMod
/ MAX_PERCENT
, 0, nMax
);
206 void lclOffValue( sal_Int32
& ornValue
, sal_Int32 nOff
, sal_Int32 nMax
= MAX_PERCENT
)
208 OSL_ENSURE( (-nMax
<= nOff
) && (nOff
<= nMax
), "lclOffValue - invalid offset" );
209 ornValue
= getLimitedValue
< sal_Int32
, sal_Int32
>( ornValue
+ nOff
, 0, nMax
);
212 constexpr frozen::unordered_map
<std::u16string_view
, model::ThemeColorType
, 26> constSchemeColorNameToIndex
214 { u
"dk1", model::ThemeColorType::Dark1
},
215 { u
"lt1", model::ThemeColorType::Light1
},
216 { u
"dk2", model::ThemeColorType::Dark2
},
217 { u
"lt2", model::ThemeColorType::Light2
},
218 { u
"accent1", model::ThemeColorType::Accent1
},
219 { u
"accent2", model::ThemeColorType::Accent2
},
220 { u
"accent3", model::ThemeColorType::Accent3
},
221 { u
"accent4", model::ThemeColorType::Accent4
},
222 { u
"accent5", model::ThemeColorType::Accent5
},
223 { u
"accent6", model::ThemeColorType::Accent6
},
224 { u
"hlink", model::ThemeColorType::Hyperlink
},
225 { u
"folHlink", model::ThemeColorType::FollowedHyperlink
},
226 { u
"tx1", model::ThemeColorType::Dark1
},
227 { u
"bg1", model::ThemeColorType::Light1
},
228 { u
"tx2", model::ThemeColorType::Dark2
},
229 { u
"bg2", model::ThemeColorType::Light2
},
230 { u
"dark1", model::ThemeColorType::Dark1
},
231 { u
"light1", model::ThemeColorType::Light1
},
232 { u
"dark2", model::ThemeColorType::Dark2
},
233 { u
"light2", model::ThemeColorType::Light2
},
234 { u
"text1", model::ThemeColorType::Dark1
},
235 { u
"background1", model::ThemeColorType::Light1
},
236 { u
"text2", model::ThemeColorType::Dark2
},
237 { u
"background2", model::ThemeColorType::Light2
},
238 { u
"hyperlink", model::ThemeColorType::Hyperlink
},
239 { u
"followedHyperlink", model::ThemeColorType::FollowedHyperlink
}
242 constexpr frozen::unordered_map
<sal_Int32
, model::ThemeColorType
, 26> constThemeColorTokenMap
244 { XML_dk1
, model::ThemeColorType::Dark1
},
245 { XML_lt1
, model::ThemeColorType::Light1
},
246 { XML_dk2
, model::ThemeColorType::Dark2
},
247 { XML_lt2
, model::ThemeColorType::Light2
},
248 { XML_accent1
, model::ThemeColorType::Accent1
},
249 { XML_accent2
, model::ThemeColorType::Accent2
},
250 { XML_accent3
, model::ThemeColorType::Accent3
},
251 { XML_accent4
, model::ThemeColorType::Accent4
},
252 { XML_accent5
, model::ThemeColorType::Accent5
},
253 { XML_accent6
, model::ThemeColorType::Accent6
},
254 { XML_hlink
, model::ThemeColorType::Hyperlink
},
255 { XML_folHlink
, model::ThemeColorType::FollowedHyperlink
},
256 { XML_tx1
, model::ThemeColorType::Dark1
},
257 { XML_bg1
, model::ThemeColorType::Light1
},
258 { XML_tx2
, model::ThemeColorType::Dark2
},
259 { XML_bg2
, model::ThemeColorType::Light2
},
260 { XML_dark1
, model::ThemeColorType::Dark1
},
261 { XML_light1
, model::ThemeColorType::Light1
},
262 { XML_dark2
, model::ThemeColorType::Dark2
},
263 { XML_light2
, model::ThemeColorType::Light2
},
264 { XML_text1
, model::ThemeColorType::Dark1
},
265 { XML_background1
, model::ThemeColorType::Light1
},
266 { XML_text2
, model::ThemeColorType::Dark2
},
267 { XML_background2
, model::ThemeColorType::Light2
},
268 { XML_hyperlink
, model::ThemeColorType::Hyperlink
},
269 { XML_followedHyperlink
, model::ThemeColorType::FollowedHyperlink
},
272 } // end anonymous namespace
274 model::ThemeColorType
schemeNameToThemeColorType(OUString
const& rSchemeName
)
276 auto aIterator
= constSchemeColorNameToIndex
.find(rSchemeName
);
277 if (aIterator
== constSchemeColorNameToIndex
.end())
278 return model::ThemeColorType::Unknown
;
280 return aIterator
->second
;
283 model::ThemeColorType
schemeTokenToThemeColorType(sal_uInt32 nToken
)
285 auto aIterator
= constThemeColorTokenMap
.find(nToken
);
286 if (aIterator
== constThemeColorTokenMap
.end())
287 return model::ThemeColorType::Unknown
;
289 return aIterator
->second
;
293 meMode( COLOR_UNUSED
),
297 mnAlpha( MAX_PERCENT
),
298 meThemeColorType( model::ThemeColorType::Unknown
)
302 ::Color
Color::getDmlPresetColor( sal_Int32 nToken
, ::Color nDefaultRgb
)
304 /* Do not pass nDefaultRgb to ContainerHelper::getVectorElement(), to be
305 able to catch the existing vector entries without corresponding XML
307 ::Color nRgbValue
= lookupDmlColor(nToken
);
308 return (sal_Int32(nRgbValue
) >= 0) ? nRgbValue
: nDefaultRgb
;
311 ::Color
Color::getVmlPresetColor( sal_Int32 nToken
, ::Color nDefaultRgb
)
313 /* Do not pass nDefaultRgb to ContainerHelper::getVectorElement(), to be
314 able to catch the existing vector entries without corresponding XML
316 ::Color nRgbValue
= lookupVmlColor(nToken
);
317 return (sal_Int32(nRgbValue
) >= 0) ? nRgbValue
: nDefaultRgb
;
320 ::Color
Color::getHighlightColor(sal_Int32 nToken
, ::Color nDefaultRgb
)
322 /* Do not pass nDefaultRgb to ContainerHelper::getVectorElement(), to be
323 able to catch the existing vector entries without corresponding XML
325 ::Color nRgbValue
= lookupHighlightColor(nToken
);
326 return (sal_Int32(nRgbValue
) >= 0) ? nRgbValue
: nDefaultRgb
;
329 void Color::setUnused()
331 meMode
= COLOR_UNUSED
;
334 void Color::setSrgbClr( ::Color nRgb
)
336 setSrgbClr(sal_Int32(nRgb
));
339 void Color::setSrgbClr( sal_Int32 nRgb
)
341 OSL_ENSURE( (0 <= nRgb
) && (nRgb
<= 0xFFFFFF), "Color::setSrgbClr - invalid RGB value" );
343 lclRgbToRgbComponents( mnC1
, mnC2
, mnC3
, ::Color(ColorTransparency
, nRgb
) );
346 void Color::setScrgbClr( sal_Int32 nR
, sal_Int32 nG
, sal_Int32 nB
)
348 OSL_ENSURE( (0 <= nR
) && (nR
<= MAX_PERCENT
), "Color::setScrgbClr - invalid red value" );
349 OSL_ENSURE( (0 <= nG
) && (nG
<= MAX_PERCENT
), "Color::setScrgbClr - invalid green value" );
350 OSL_ENSURE( (0 <= nB
) && (nB
<= MAX_PERCENT
), "Color::setScrgbClr - invalid blue value" );
352 mnC1
= getLimitedValue
< sal_Int32
, sal_Int32
>( nR
, 0, MAX_PERCENT
);
353 mnC2
= getLimitedValue
< sal_Int32
, sal_Int32
>( nG
, 0, MAX_PERCENT
);
354 mnC3
= getLimitedValue
< sal_Int32
, sal_Int32
>( nB
, 0, MAX_PERCENT
);
357 void Color::setHslClr( sal_Int32 nHue
, sal_Int32 nSat
, sal_Int32 nLum
)
359 OSL_ENSURE( (0 <= nHue
) && (nHue
<= MAX_DEGREE
), "Color::setHslClr - invalid hue value" );
360 OSL_ENSURE( (0 <= nSat
) && (nSat
<= MAX_PERCENT
), "Color::setHslClr - invalid saturation value" );
361 OSL_ENSURE( (0 <= nLum
) && (nLum
<= MAX_PERCENT
), "Color::setHslClr - invalid luminance value" );
363 mnC1
= getLimitedValue
< sal_Int32
, sal_Int32
>( nHue
, 0, MAX_DEGREE
);
364 mnC2
= getLimitedValue
< sal_Int32
, sal_Int32
>( nSat
, 0, MAX_PERCENT
);
365 mnC3
= getLimitedValue
< sal_Int32
, sal_Int32
>( nLum
, 0, MAX_PERCENT
);
368 void Color::setPrstClr( sal_Int32 nToken
)
370 ::Color nRgbValue
= getDmlPresetColor( nToken
, API_RGB_TRANSPARENT
);
371 OSL_ENSURE( sal_Int32(nRgbValue
) >= 0, "Color::setPrstClr - invalid preset color token" );
372 if( sal_Int32(nRgbValue
) >= 0 )
373 setSrgbClr( nRgbValue
);
376 void Color::setHighlight(sal_Int32 nToken
)
378 ::Color nRgbValue
= getHighlightColor(nToken
, API_RGB_TRANSPARENT
);
379 OSL_ENSURE( sal_Int32(nRgbValue
) >= 0, "Color::setPrstClr - invalid preset color token" );
380 if ( sal_Int32(nRgbValue
) >= 0 )
381 setSrgbClr( nRgbValue
);
384 void Color::setSchemeClr( sal_Int32 nToken
)
386 OSL_ENSURE( nToken
!= XML_TOKEN_INVALID
, "Color::setSchemeClr - invalid color token" );
387 meMode
= (nToken
== XML_phClr
) ? COLOR_PH
: COLOR_SCHEME
;
389 if (meMode
== COLOR_SCHEME
)
390 meThemeColorType
= schemeTokenToThemeColorType(nToken
);
393 void Color::setPaletteClr( sal_Int32 nPaletteIdx
)
395 OSL_ENSURE( nPaletteIdx
>= 0, "Color::setPaletteClr - invalid palette index" );
396 meMode
= COLOR_PALETTE
;
400 void Color::setSysClr( sal_Int32 nToken
, sal_Int32 nLastRgb
)
402 OSL_ENSURE( (-1 <= nLastRgb
) && (nLastRgb
<= 0xFFFFFF), "Color::setSysClr - invalid RGB value" );
403 meMode
= COLOR_SYSTEM
;
408 void Color::addTransformation( sal_Int32 nElement
, sal_Int32 nValue
)
410 /* Execute alpha transformations directly, store other transformations in
411 a vector, they may depend on a scheme base color which will be resolved
412 in Color::getColor(). */
413 sal_Int32 nToken
= getBaseToken( nElement
);
416 case XML_alpha
: lclSetValue( mnAlpha
, nValue
); break;
417 case XML_alphaMod
: lclModValue( mnAlpha
, nValue
); break;
418 case XML_alphaOff
: lclOffValue( mnAlpha
, nValue
); break;
419 default: maTransforms
.emplace_back( nToken
, nValue
);
421 sal_Int32 nSize
= maInteropTransformations
.getLength();
422 maInteropTransformations
.realloc(nSize
+ 1);
423 auto pInteropTransformations
= maInteropTransformations
.getArray();
424 pInteropTransformations
[nSize
].Name
= getColorTransformationName( nToken
);
425 pInteropTransformations
[nSize
].Value
<<= nValue
;
428 void Color::addChartTintTransformation( double fTint
)
430 sal_Int32 nValue
= getLimitedValue
< sal_Int32
, double >( fTint
* MAX_PERCENT
+ 0.5, -MAX_PERCENT
, MAX_PERCENT
);
432 maTransforms
.emplace_back( XML_shade
, nValue
+ MAX_PERCENT
);
433 else if( nValue
> 0 )
434 maTransforms
.emplace_back( XML_tint
, MAX_PERCENT
- nValue
);
437 void Color::addExcelTintTransformation(double fTint
)
439 sal_Int32 nValue
= std::round(std::abs(fTint
) * 100'000.0);
442 maTransforms
.emplace_back(XML_lumMod
, 100'000 - nValue
);
443 maTransforms
.emplace_back(XML_lumOff
, nValue
);
445 else if (fTint
< 0.0)
447 maTransforms
.emplace_back(XML_lumMod
, 100'000 - nValue
);
451 void Color::clearTransformations()
453 maTransforms
.clear();
454 maInteropTransformations
.realloc(0);
458 constexpr frozen::unordered_map
<std::u16string_view
, sal_Int32
, 12> constColorMapTokenMap
459 = { { u
"bg1", XML_bg1
}, { u
"tx1", XML_tx1
}, { u
"bg2", XML_bg2
},
460 { u
"tx2", XML_tx2
}, { u
"accent1", XML_accent1
}, { u
"accent2", XML_accent2
},
461 { u
"accent3", XML_accent3
}, { u
"accent4", XML_accent4
}, { u
"accent5", XML_accent5
},
462 { u
"accent6", XML_accent6
}, { u
"hlink", XML_hlink
}, { u
"folHlink", XML_folHlink
} };
464 sal_Int32
Color::getColorMapToken(std::u16string_view sName
)
466 auto aIterator
= constColorMapTokenMap
.find(sName
);
467 if (aIterator
== constColorMapTokenMap
.end())
468 return XML_TOKEN_INVALID
;
470 return aIterator
->second
;
473 OUString
Color::getColorTransformationName( sal_Int32 nElement
)
477 case XML_red
: return u
"red"_ustr
;
478 case XML_redMod
: return u
"redMod"_ustr
;
479 case XML_redOff
: return u
"redOff"_ustr
;
480 case XML_green
: return u
"green"_ustr
;
481 case XML_greenMod
: return u
"greenMod"_ustr
;
482 case XML_greenOff
: return u
"greenOff"_ustr
;
483 case XML_blue
: return u
"blue"_ustr
;
484 case XML_blueMod
: return u
"blueMod"_ustr
;
485 case XML_blueOff
: return u
"blueOff"_ustr
;
486 case XML_alpha
: return u
"alpha"_ustr
;
487 case XML_alphaMod
: return u
"alphaMod"_ustr
;
488 case XML_alphaOff
: return u
"alphaOff"_ustr
;
489 case XML_hue
: return u
"hue"_ustr
;
490 case XML_hueMod
: return u
"hueMod"_ustr
;
491 case XML_hueOff
: return u
"hueOff"_ustr
;
492 case XML_sat
: return u
"sat"_ustr
;
493 case XML_satMod
: return u
"satMod"_ustr
;
494 case XML_satOff
: return u
"satOff"_ustr
;
495 case XML_lum
: return u
"lum"_ustr
;
496 case XML_lumMod
: return u
"lumMod"_ustr
;
497 case XML_lumOff
: return u
"lumOff"_ustr
;
498 case XML_shade
: return u
"shade"_ustr
;
499 case XML_tint
: return u
"tint"_ustr
;
500 case XML_gray
: return u
"gray"_ustr
;
501 case XML_comp
: return u
"comp"_ustr
;
502 case XML_inv
: return u
"inv"_ustr
;
503 case XML_gamma
: return u
"gamma"_ustr
;
504 case XML_invGamma
: return u
"invGamma"_ustr
;
506 SAL_WARN( "oox.drawingml", "Color::getColorTransformationName - unexpected transformation type" );
510 sal_Int32
Color::getColorTransformationToken( std::u16string_view sName
)
512 if( sName
== u
"red" )
514 else if( sName
== u
"redMod" )
516 else if( sName
== u
"redOff" )
518 else if( sName
== u
"green" )
520 else if( sName
== u
"greenMod" )
522 else if( sName
== u
"greenOff" )
524 else if( sName
== u
"blue" )
526 else if( sName
== u
"blueMod" )
528 else if( sName
== u
"blueOff" )
530 else if( sName
== u
"alpha" )
532 else if( sName
== u
"alphaMod" )
534 else if( sName
== u
"alphaOff" )
536 else if( sName
== u
"hue" )
538 else if( sName
== u
"hueMod" )
540 else if( sName
== u
"hueOff" )
542 else if( sName
== u
"sat" )
544 else if( sName
== u
"satMod" )
546 else if( sName
== u
"satOff" )
548 else if( sName
== u
"lum" )
550 else if( sName
== u
"lumMod" )
552 else if( sName
== u
"lumOff" )
554 else if( sName
== u
"shade" )
556 else if( sName
== u
"tint" )
558 else if( sName
== u
"gray" )
560 else if( sName
== u
"comp" )
562 else if( sName
== u
"inv" )
564 else if( sName
== u
"gamma" )
566 else if( sName
== u
"invGamma" )
569 SAL_WARN( "oox.drawingml", "Color::getColorTransformationToken - unexpected transformation type" );
570 return XML_TOKEN_INVALID
;
573 bool Color::equals(const Color
& rOther
, const GraphicHelper
& rGraphicHelper
, ::Color nPhClr
) const
575 if (getColor(rGraphicHelper
, nPhClr
) != rOther
.getColor(rGraphicHelper
, nPhClr
))
578 return getTransparency() == rOther
.getTransparency();
581 void Color::clearTransparence()
583 mnAlpha
= MAX_PERCENT
;
586 sal_Int16
Color::getTintOrShade() const
588 for(auto const& aTransform
: maTransforms
)
590 switch(aTransform
.mnToken
)
593 // from 1000th percent to 100th percent...
594 return aTransform
.mnValue
/10;
596 // from 1000th percent to 100th percent...
597 return -aTransform
.mnValue
/10;
603 sal_Int16
Color::getLumMod() const
605 for (const auto& rTransform
: maTransforms
)
607 if (rTransform
.mnToken
!= XML_lumMod
)
612 // From 1000th percent to 100th percent.
613 return rTransform
.mnValue
/ 10;
619 sal_Int16
Color::getLumOff() const
621 for (const auto& rTransform
: maTransforms
)
623 if (rTransform
.mnToken
!= XML_lumOff
)
628 // From 1000th percent to 100th percent.
629 return rTransform
.mnValue
/ 10;
635 model::ComplexColor
Color::getComplexColor() const
637 auto aComplexColor
= model::ComplexColor::Theme(model::convertToThemeColorType(getSchemeColorIndex()));
639 if (getTintOrShade() > 0)
641 aComplexColor
.addTransformation({model::TransformationType::Tint
, getTintOrShade()});
643 else if (getTintOrShade() < 0)
645 sal_Int16 nShade
= o3tl::narrowing
<sal_Int16
>(-getTintOrShade());
646 aComplexColor
.addTransformation({model::TransformationType::Shade
, nShade
});
649 if (getLumMod() != 10000)
650 aComplexColor
.addTransformation({model::TransformationType::LumMod
, getLumMod()});
652 if (getLumOff() != 0)
653 aComplexColor
.addTransformation({model::TransformationType::LumOff
, getLumOff()});
655 return aComplexColor
;
658 ::Color
Color::getColor( const GraphicHelper
& rGraphicHelper
, ::Color nPhClr
) const
660 const sal_Int32 nTempC1
= mnC1
;
661 const sal_Int32 nTempC2
= mnC2
;
662 const sal_Int32 nTempC3
= mnC3
;
663 const ColorMode eTempMode
= meMode
;
667 case COLOR_UNUSED
: mnC1
= sal_Int32(API_RGB_TRANSPARENT
); break;
669 case COLOR_RGB
: break; // nothing to do
670 case COLOR_CRGB
: break; // nothing to do
671 case COLOR_HSL
: break; // nothing to do
673 case COLOR_SCHEME
: setResolvedRgb( rGraphicHelper
.getSchemeColor( mnC1
) ); break;
674 case COLOR_PALETTE
: setResolvedRgb( rGraphicHelper
.getPaletteColor( mnC1
) ); break;
675 case COLOR_SYSTEM
: setResolvedRgb( rGraphicHelper
.getSystemColor( mnC1
, ::Color(ColorTransparency
, mnC2
) ) ); break;
676 case COLOR_PH
: setResolvedRgb( nPhClr
); break;
678 case COLOR_FINAL
: return ::Color(ColorTransparency
, mnC1
);
681 // if color is UNUSED or turns to UNUSED in setResolvedRgb, do not perform transformations
682 if( meMode
!= COLOR_UNUSED
)
684 for (auto const& transform
: maTransforms
)
686 switch( transform
.mnToken
)
688 case XML_red
: toCrgb(); lclSetValue( mnC1
, transform
.mnValue
); break;
689 case XML_redMod
: toCrgb(); lclModValue( mnC1
, transform
.mnValue
); break;
690 case XML_redOff
: toCrgb(); lclOffValue( mnC1
, transform
.mnValue
); break;
691 case XML_green
: toCrgb(); lclSetValue( mnC2
, transform
.mnValue
); break;
692 case XML_greenMod
: toCrgb(); lclModValue( mnC2
, transform
.mnValue
); break;
693 case XML_greenOff
: toCrgb(); lclOffValue( mnC2
, transform
.mnValue
); break;
694 case XML_blue
: toCrgb(); lclSetValue( mnC3
, transform
.mnValue
); break;
695 case XML_blueMod
: toCrgb(); lclModValue( mnC3
, transform
.mnValue
); break;
696 case XML_blueOff
: toCrgb(); lclOffValue( mnC3
, transform
.mnValue
); break;
698 case XML_hue
: toHsl(); lclSetValue( mnC1
, transform
.mnValue
, MAX_DEGREE
); break;
699 case XML_hueMod
: toHsl(); lclModValue( mnC1
, transform
.mnValue
, MAX_DEGREE
); break;
700 case XML_hueOff
: toHsl(); lclOffValue( mnC1
, transform
.mnValue
, MAX_DEGREE
); break;
701 case XML_sat
: toHsl(); lclSetValue( mnC2
, transform
.mnValue
); break;
702 case XML_satMod
: toHsl(); lclModValue( mnC2
, transform
.mnValue
); break;
703 case XML_satOff
: toHsl(); lclOffValue( mnC2
, transform
.mnValue
); break;
707 lclSetValue( mnC3
, transform
.mnValue
);
708 // if color changes to black or white, it will stay gray if luminance changes again
709 if( (mnC3
== 0) || (mnC3
== MAX_PERCENT
) ) mnC2
= 0;
713 lclModValue( mnC3
, transform
.mnValue
);
714 // if color changes to black or white, it will stay gray if luminance changes again
715 if( (mnC3
== 0) || (mnC3
== MAX_PERCENT
) ) mnC2
= 0;
719 lclOffValue( mnC3
, transform
.mnValue
);
720 // if color changes to black or white, it will stay gray if luminance changes again
721 if( (mnC3
== 0) || (mnC3
== MAX_PERCENT
) ) mnC2
= 0;
725 // shade: 0% = black, 100% = original color
727 OSL_ENSURE( (0 <= transform
.mnValue
) && (transform
.mnValue
<= MAX_PERCENT
), "Color::getColor - invalid shade value" );
728 if( (0 <= transform
.mnValue
) && (transform
.mnValue
<= MAX_PERCENT
) )
730 double fFactor
= static_cast< double >( transform
.mnValue
) / MAX_PERCENT
;
731 mnC1
= static_cast< sal_Int32
>( mnC1
* fFactor
);
732 mnC2
= static_cast< sal_Int32
>( mnC2
* fFactor
);
733 mnC3
= static_cast< sal_Int32
>( mnC3
* fFactor
);
737 // tint: 0% = white, 100% = original color
739 OSL_ENSURE( (0 <= transform
.mnValue
) && (transform
.mnValue
<= MAX_PERCENT
), "Color::getColor - invalid tint value" );
740 if( (0 <= transform
.mnValue
) && (transform
.mnValue
<= MAX_PERCENT
) )
742 double fFactor
= static_cast< double >( transform
.mnValue
) / MAX_PERCENT
;
743 mnC1
= static_cast< sal_Int32
>( MAX_PERCENT
- (MAX_PERCENT
- mnC1
) * fFactor
);
744 mnC2
= static_cast< sal_Int32
>( MAX_PERCENT
- (MAX_PERCENT
- mnC2
) * fFactor
);
745 mnC3
= static_cast< sal_Int32
>( MAX_PERCENT
- (MAX_PERCENT
- mnC3
) * fFactor
);
750 // change color to gray, weighted RGB: 22% red, 72% green, 6% blue
752 mnC1
= mnC2
= mnC3
= (mnC1
* 22 + mnC2
* 72 + mnC3
* 6) / 100;
756 // comp: rotate hue by 180 degrees, do not change lum/sat
758 mnC1
= (mnC1
+ (180 * PER_DEGREE
)) % MAX_DEGREE
;
761 // invert percentual RGB values
763 mnC1
= MAX_PERCENT
- mnC1
;
764 mnC2
= MAX_PERCENT
- mnC2
;
765 mnC3
= MAX_PERCENT
- mnC3
;
769 // increase gamma of color
771 mnC1
= lclGamma( mnC1
, INC_GAMMA
);
772 mnC2
= lclGamma( mnC2
, INC_GAMMA
);
773 mnC3
= lclGamma( mnC3
, INC_GAMMA
);
776 // decrease gamma of color
778 mnC1
= lclGamma( mnC1
, DEC_GAMMA
);
779 mnC2
= lclGamma( mnC2
, DEC_GAMMA
);
780 mnC3
= lclGamma( mnC3
, DEC_GAMMA
);
785 // store resulting RGB value in mnC1
787 mnC1
= lclRgbComponentsToRgb( mnC1
, mnC2
, mnC3
);
789 else // if( meMode != COLOR_UNUSED )
791 mnC1
= sal_Int32(API_RGB_TRANSPARENT
);
794 sal_Int32 nRet
= mnC1
;
795 // Restore the original values when the color depends on one of the input
796 // parameters (rGraphicHelper or nPhClr)
797 if( eTempMode
>= COLOR_SCHEME
&& eTempMode
<= COLOR_PH
)
806 meMode
= COLOR_FINAL
;
808 if( meMode
== COLOR_FINAL
)
809 maTransforms
.clear();
810 return ::Color(ColorTransparency
, nRet
);
813 bool Color::hasTransparency() const
815 return mnAlpha
< MAX_PERCENT
;
818 sal_Int16
Color::getTransparency() const
820 return sal_Int16(std::round( (1.0 * (MAX_PERCENT
- mnAlpha
)) / PER_PERCENT
) );
823 sal_Int16
Color::getSchemeColorIndex() const
825 auto eThemeType
= schemeNameToThemeColorType(msSchemeName
);
826 return sal_Int16(eThemeType
);
829 model::ComplexColor
Color::createComplexColor(const GraphicHelper
& /*rGraphicHelper*/, sal_Int16 nPhClrTheme
) const
831 model::ComplexColor aNewComplexColor
;
832 if (meMode
== COLOR_PH
)
834 auto eTheme
= model::convertToThemeColorType(nPhClrTheme
);
835 aNewComplexColor
.setThemeColor(eTheme
);
837 else if (meMode
== COLOR_SCHEME
)
839 auto eTheme
= getThemeColorType();
840 aNewComplexColor
.setThemeColor(eTheme
);
842 else if (meMode
== COLOR_RGB
)
844 ::Color
aColor(ColorTransparency
, lclRgbComponentsToRgb(mnC1
, mnC2
, mnC3
));
845 aNewComplexColor
= model::ComplexColor::createRGB(aColor
);
849 // TODO - Add other options
850 return aNewComplexColor
;
853 for(auto const& aTransform
: maTransforms
)
855 sal_Int16 nValue
= sal_Int16(aTransform
.mnValue
/ 10);
857 switch (aTransform
.mnToken
)
860 if (nValue
!= 10'000)
861 aNewComplexColor
.addTransformation({model::TransformationType::LumMod
, nValue
});
865 aNewComplexColor
.addTransformation({model::TransformationType::LumOff
, nValue
});
869 aNewComplexColor
.addTransformation({model::TransformationType::Tint
, nValue
});
873 aNewComplexColor
.addTransformation({model::TransformationType::Shade
, nValue
});
878 return aNewComplexColor
;
881 // private --------------------------------------------------------------------
883 void Color::setResolvedRgb( ::Color nRgb
) const
885 meMode
= (sal_Int32(nRgb
) < 0) ? COLOR_UNUSED
: COLOR_RGB
;
886 lclRgbToRgbComponents( mnC1
, mnC2
, mnC3
, nRgb
);
889 void Color::toRgb() const
898 mnC1
= lclCrgbCompToRgbComp( lclGamma( mnC1
, INC_GAMMA
) );
899 mnC2
= lclCrgbCompToRgbComp( lclGamma( mnC2
, INC_GAMMA
) );
900 mnC3
= lclCrgbCompToRgbComp( lclGamma( mnC3
, INC_GAMMA
) );
905 double fR
= 0.0, fG
= 0.0, fB
= 0.0;
906 if( (mnC2
== 0) || (mnC3
== MAX_PERCENT
) )
908 fR
= fG
= fB
= static_cast< double >( mnC3
) / MAX_PERCENT
;
912 // base color from hue
913 double fHue
= static_cast< double >( mnC1
) / MAX_DEGREE
* 6.0; // interval [0.0, 6.0)
914 if( fHue
<= 1.0 ) { fR
= 1.0; fG
= fHue
; } // red...yellow
915 else if( fHue
<= 2.0 ) { fR
= 2.0 - fHue
; fG
= 1.0; } // yellow...green
916 else if( fHue
<= 3.0 ) { fG
= 1.0; fB
= fHue
- 2.0; } // green...cyan
917 else if( fHue
<= 4.0 ) { fG
= 4.0 - fHue
; fB
= 1.0; } // cyan...blue
918 else if( fHue
<= 5.0 ) { fR
= fHue
- 4.0; fB
= 1.0; } // blue...magenta
919 else { fR
= 1.0; fB
= 6.0 - fHue
; } // magenta...red
922 double fSat
= static_cast< double >( mnC2
) / MAX_PERCENT
;
923 fR
= (fR
- 0.5) * fSat
+ 0.5;
924 fG
= (fG
- 0.5) * fSat
+ 0.5;
925 fB
= (fB
- 0.5) * fSat
+ 0.5;
928 double fLum
= 2.0 * static_cast< double >( mnC3
) / MAX_PERCENT
- 1.0; // interval [-1.0, 1.0]
931 double fShade
= fLum
+ 1.0; // interval [0.0, 1.0] (black...full color)
936 else if( fLum
> 0.0 )
938 double fTint
= 1.0 - fLum
; // interval [0.0, 1.0] (white...full color)
939 fR
= 1.0 - ((1.0 - fR
) * fTint
);
940 fG
= 1.0 - ((1.0 - fG
) * fTint
);
941 fB
= 1.0 - ((1.0 - fB
) * fTint
);
944 mnC1
= static_cast< sal_Int32
>( fR
* 255.0 + 0.5 );
945 mnC2
= static_cast< sal_Int32
>( fG
* 255.0 + 0.5 );
946 mnC3
= static_cast< sal_Int32
>( fB
* 255.0 + 0.5 );
950 OSL_FAIL( "Color::toRgb - unexpected color mode" );
954 void Color::toCrgb() const
963 mnC1
= lclGamma( lclRgbCompToCrgbComp( mnC1
), DEC_GAMMA
);
964 mnC2
= lclGamma( lclRgbCompToCrgbComp( mnC2
), DEC_GAMMA
);
965 mnC3
= lclGamma( lclRgbCompToCrgbComp( mnC3
), DEC_GAMMA
);
971 OSL_FAIL( "Color::toCrgb - unexpected color mode" );
975 void Color::toHsl() const
985 double fR
= static_cast< double >( mnC1
) / 255.0; // red [0.0, 1.0]
986 double fG
= static_cast< double >( mnC2
) / 255.0; // green [0.0, 1.0]
987 double fB
= static_cast< double >( mnC3
) / 255.0; // blue [0.0, 1.0]
988 double fMin
= ::std::min( ::std::min( fR
, fG
), fB
);
989 double fMax
= ::std::max( ::std::max( fR
, fG
), fB
);
990 double fD
= fMax
- fMin
;
992 using ::rtl::math::approxEqual
;
994 // hue: 0deg = red, 120deg = green, 240deg = blue
995 if( fD
== 0.0 ) // black/gray/white
997 else if( approxEqual(fMax
, fR
, 64) ) // magenta...red...yellow
998 mnC1
= static_cast< sal_Int32
>( ((fG
- fB
) / fD
* 60.0 + 360.0) * PER_DEGREE
+ 0.5 ) % MAX_DEGREE
;
999 else if( approxEqual(fMax
, fG
, 64) ) // yellow...green...cyan
1000 mnC1
= static_cast< sal_Int32
>( ((fB
- fR
) / fD
* 60.0 + 120.0) * PER_DEGREE
+ 0.5 );
1001 else // cyan...blue...magenta
1002 mnC1
= static_cast< sal_Int32
>( ((fR
- fG
) / fD
* 60.0 + 240.0) * PER_DEGREE
+ 0.5 );
1004 // luminance: 0% = black, 50% = full color, 100% = white
1005 mnC3
= static_cast< sal_Int32
>( (fMin
+ fMax
) / 2.0 * MAX_PERCENT
+ 0.5 );
1007 // saturation: 0% = gray, 100% = full color
1008 if( (mnC3
== 0) || (mnC3
== MAX_PERCENT
) ) // black/white
1010 else if( mnC3
<= 50 * PER_PERCENT
) // dark...full color
1011 mnC2
= static_cast< sal_Int32
>( fD
/ (fMin
+ fMax
) * MAX_PERCENT
+ 0.5 );
1012 else // full color...light
1013 mnC2
= static_cast< sal_Int32
>( fD
/ (2.0 - fMax
- fMin
) * MAX_PERCENT
+ 0.5 );
1020 OSL_FAIL( "Color::toHsl - unexpected color mode" );
1026 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */