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 OUString
Color::getColorTransformationName( sal_Int32 nElement
)
462 case XML_red
: return "red";
463 case XML_redMod
: return "redMod";
464 case XML_redOff
: return "redOff";
465 case XML_green
: return "green";
466 case XML_greenMod
: return "greenMod";
467 case XML_greenOff
: return "greenOff";
468 case XML_blue
: return "blue";
469 case XML_blueMod
: return "blueMod";
470 case XML_blueOff
: return "blueOff";
471 case XML_alpha
: return "alpha";
472 case XML_alphaMod
: return "alphaMod";
473 case XML_alphaOff
: return "alphaOff";
474 case XML_hue
: return "hue";
475 case XML_hueMod
: return "hueMod";
476 case XML_hueOff
: return "hueOff";
477 case XML_sat
: return "sat";
478 case XML_satMod
: return "satMod";
479 case XML_satOff
: return "satOff";
480 case XML_lum
: return "lum";
481 case XML_lumMod
: return "lumMod";
482 case XML_lumOff
: return "lumOff";
483 case XML_shade
: return "shade";
484 case XML_tint
: return "tint";
485 case XML_gray
: return "gray";
486 case XML_comp
: return "comp";
487 case XML_inv
: return "inv";
488 case XML_gamma
: return "gamma";
489 case XML_invGamma
: return "invGamma";
491 SAL_WARN( "oox.drawingml", "Color::getColorTransformationName - unexpected transformation type" );
495 sal_Int32
Color::getColorTransformationToken( std::u16string_view sName
)
497 if( sName
== u
"red" )
499 else if( sName
== u
"redMod" )
501 else if( sName
== u
"redOff" )
503 else if( sName
== u
"green" )
505 else if( sName
== u
"greenMod" )
507 else if( sName
== u
"greenOff" )
509 else if( sName
== u
"blue" )
511 else if( sName
== u
"blueMod" )
513 else if( sName
== u
"blueOff" )
515 else if( sName
== u
"alpha" )
517 else if( sName
== u
"alphaMod" )
519 else if( sName
== u
"alphaOff" )
521 else if( sName
== u
"hue" )
523 else if( sName
== u
"hueMod" )
525 else if( sName
== u
"hueOff" )
527 else if( sName
== u
"sat" )
529 else if( sName
== u
"satMod" )
531 else if( sName
== u
"satOff" )
533 else if( sName
== u
"lum" )
535 else if( sName
== u
"lumMod" )
537 else if( sName
== u
"lumOff" )
539 else if( sName
== u
"shade" )
541 else if( sName
== u
"tint" )
543 else if( sName
== u
"gray" )
545 else if( sName
== u
"comp" )
547 else if( sName
== u
"inv" )
549 else if( sName
== u
"gamma" )
551 else if( sName
== u
"invGamma" )
554 SAL_WARN( "oox.drawingml", "Color::getColorTransformationToken - unexpected transformation type" );
555 return XML_TOKEN_INVALID
;
558 bool Color::equals(const Color
& rOther
, const GraphicHelper
& rGraphicHelper
, ::Color nPhClr
) const
560 if (getColor(rGraphicHelper
, nPhClr
) != rOther
.getColor(rGraphicHelper
, nPhClr
))
563 return getTransparency() == rOther
.getTransparency();
566 void Color::clearTransparence()
568 mnAlpha
= MAX_PERCENT
;
571 sal_Int16
Color::getTintOrShade() const
573 for(auto const& aTransform
: maTransforms
)
575 switch(aTransform
.mnToken
)
578 // from 1000th percent to 100th percent...
579 return aTransform
.mnValue
/10;
581 // from 1000th percent to 100th percent...
582 return -aTransform
.mnValue
/10;
588 sal_Int16
Color::getLumMod() const
590 for (const auto& rTransform
: maTransforms
)
592 if (rTransform
.mnToken
!= XML_lumMod
)
597 // From 1000th percent to 100th percent.
598 return rTransform
.mnValue
/ 10;
604 sal_Int16
Color::getLumOff() const
606 for (const auto& rTransform
: maTransforms
)
608 if (rTransform
.mnToken
!= XML_lumOff
)
613 // From 1000th percent to 100th percent.
614 return rTransform
.mnValue
/ 10;
620 model::ComplexColor
Color::getComplexColor() const
622 model::ComplexColor aComplexColor
;
623 aComplexColor
.setSchemeColor(model::convertToThemeColorType(getSchemeColorIndex()));
625 if (getTintOrShade() > 0)
627 aComplexColor
.addTransformation({model::TransformationType::Tint
, getTintOrShade()});
629 else if (getTintOrShade() < 0)
631 sal_Int16 nShade
= o3tl::narrowing
<sal_Int16
>(-getTintOrShade());
632 aComplexColor
.addTransformation({model::TransformationType::Shade
, nShade
});
635 if (getLumMod() != 10000)
636 aComplexColor
.addTransformation({model::TransformationType::LumMod
, getLumMod()});
638 if (getLumOff() != 0)
639 aComplexColor
.addTransformation({model::TransformationType::LumOff
, getLumOff()});
641 return aComplexColor
;
644 ::Color
Color::getColor( const GraphicHelper
& rGraphicHelper
, ::Color nPhClr
) const
646 const sal_Int32 nTempC1
= mnC1
;
647 const sal_Int32 nTempC2
= mnC2
;
648 const sal_Int32 nTempC3
= mnC3
;
649 const ColorMode eTempMode
= meMode
;
653 case COLOR_UNUSED
: mnC1
= sal_Int32(API_RGB_TRANSPARENT
); break;
655 case COLOR_RGB
: break; // nothing to do
656 case COLOR_CRGB
: break; // nothing to do
657 case COLOR_HSL
: break; // nothing to do
659 case COLOR_SCHEME
: setResolvedRgb( rGraphicHelper
.getSchemeColor( mnC1
) ); break;
660 case COLOR_PALETTE
: setResolvedRgb( rGraphicHelper
.getPaletteColor( mnC1
) ); break;
661 case COLOR_SYSTEM
: setResolvedRgb( rGraphicHelper
.getSystemColor( mnC1
, ::Color(ColorTransparency
, mnC2
) ) ); break;
662 case COLOR_PH
: setResolvedRgb( nPhClr
); break;
664 case COLOR_FINAL
: return ::Color(ColorTransparency
, mnC1
);
667 // if color is UNUSED or turns to UNUSED in setResolvedRgb, do not perform transformations
668 if( meMode
!= COLOR_UNUSED
)
670 for (auto const& transform
: maTransforms
)
672 switch( transform
.mnToken
)
674 case XML_red
: toCrgb(); lclSetValue( mnC1
, transform
.mnValue
); break;
675 case XML_redMod
: toCrgb(); lclModValue( mnC1
, transform
.mnValue
); break;
676 case XML_redOff
: toCrgb(); lclOffValue( mnC1
, transform
.mnValue
); break;
677 case XML_green
: toCrgb(); lclSetValue( mnC2
, transform
.mnValue
); break;
678 case XML_greenMod
: toCrgb(); lclModValue( mnC2
, transform
.mnValue
); break;
679 case XML_greenOff
: toCrgb(); lclOffValue( mnC2
, transform
.mnValue
); break;
680 case XML_blue
: toCrgb(); lclSetValue( mnC3
, transform
.mnValue
); break;
681 case XML_blueMod
: toCrgb(); lclModValue( mnC3
, transform
.mnValue
); break;
682 case XML_blueOff
: toCrgb(); lclOffValue( mnC3
, transform
.mnValue
); break;
684 case XML_hue
: toHsl(); lclSetValue( mnC1
, transform
.mnValue
, MAX_DEGREE
); break;
685 case XML_hueMod
: toHsl(); lclModValue( mnC1
, transform
.mnValue
, MAX_DEGREE
); break;
686 case XML_hueOff
: toHsl(); lclOffValue( mnC1
, transform
.mnValue
, MAX_DEGREE
); break;
687 case XML_sat
: toHsl(); lclSetValue( mnC2
, transform
.mnValue
); break;
688 case XML_satMod
: toHsl(); lclModValue( mnC2
, transform
.mnValue
); break;
689 case XML_satOff
: toHsl(); lclOffValue( mnC2
, transform
.mnValue
); break;
693 lclSetValue( mnC3
, transform
.mnValue
);
694 // if color changes to black or white, it will stay gray if luminance changes again
695 if( (mnC3
== 0) || (mnC3
== MAX_PERCENT
) ) mnC2
= 0;
699 lclModValue( mnC3
, transform
.mnValue
);
700 // if color changes to black or white, it will stay gray if luminance changes again
701 if( (mnC3
== 0) || (mnC3
== MAX_PERCENT
) ) mnC2
= 0;
705 lclOffValue( mnC3
, transform
.mnValue
);
706 // if color changes to black or white, it will stay gray if luminance changes again
707 if( (mnC3
== 0) || (mnC3
== MAX_PERCENT
) ) mnC2
= 0;
711 // shade: 0% = black, 100% = original color
713 OSL_ENSURE( (0 <= transform
.mnValue
) && (transform
.mnValue
<= MAX_PERCENT
), "Color::getColor - invalid shade value" );
714 if( (0 <= transform
.mnValue
) && (transform
.mnValue
<= MAX_PERCENT
) )
716 double fFactor
= static_cast< double >( transform
.mnValue
) / MAX_PERCENT
;
717 mnC1
= static_cast< sal_Int32
>( mnC1
* fFactor
);
718 mnC2
= static_cast< sal_Int32
>( mnC2
* fFactor
);
719 mnC3
= static_cast< sal_Int32
>( mnC3
* fFactor
);
723 // tint: 0% = white, 100% = original color
725 OSL_ENSURE( (0 <= transform
.mnValue
) && (transform
.mnValue
<= MAX_PERCENT
), "Color::getColor - invalid tint value" );
726 if( (0 <= transform
.mnValue
) && (transform
.mnValue
<= MAX_PERCENT
) )
728 double fFactor
= static_cast< double >( transform
.mnValue
) / MAX_PERCENT
;
729 mnC1
= static_cast< sal_Int32
>( MAX_PERCENT
- (MAX_PERCENT
- mnC1
) * fFactor
);
730 mnC2
= static_cast< sal_Int32
>( MAX_PERCENT
- (MAX_PERCENT
- mnC2
) * fFactor
);
731 mnC3
= static_cast< sal_Int32
>( MAX_PERCENT
- (MAX_PERCENT
- mnC3
) * fFactor
);
736 // change color to gray, weighted RGB: 22% red, 72% green, 6% blue
738 mnC1
= mnC2
= mnC3
= (mnC1
* 22 + mnC2
* 72 + mnC3
* 6) / 100;
742 // comp: rotate hue by 180 degrees, do not change lum/sat
744 mnC1
= (mnC1
+ (180 * PER_DEGREE
)) % MAX_DEGREE
;
747 // invert percentual RGB values
749 mnC1
= MAX_PERCENT
- mnC1
;
750 mnC2
= MAX_PERCENT
- mnC2
;
751 mnC3
= MAX_PERCENT
- mnC3
;
755 // increase gamma of color
757 mnC1
= lclGamma( mnC1
, INC_GAMMA
);
758 mnC2
= lclGamma( mnC2
, INC_GAMMA
);
759 mnC3
= lclGamma( mnC3
, INC_GAMMA
);
762 // decrease gamma of color
764 mnC1
= lclGamma( mnC1
, DEC_GAMMA
);
765 mnC2
= lclGamma( mnC2
, DEC_GAMMA
);
766 mnC3
= lclGamma( mnC3
, DEC_GAMMA
);
771 // store resulting RGB value in mnC1
773 mnC1
= lclRgbComponentsToRgb( mnC1
, mnC2
, mnC3
);
775 else // if( meMode != COLOR_UNUSED )
777 mnC1
= sal_Int32(API_RGB_TRANSPARENT
);
780 sal_Int32 nRet
= mnC1
;
781 // Restore the original values when the color depends on one of the input
782 // parameters (rGraphicHelper or nPhClr)
783 if( eTempMode
>= COLOR_SCHEME
&& eTempMode
<= COLOR_PH
)
792 meMode
= COLOR_FINAL
;
794 if( meMode
== COLOR_FINAL
)
795 maTransforms
.clear();
796 return ::Color(ColorTransparency
, nRet
);
799 bool Color::hasTransparency() const
801 return mnAlpha
< MAX_PERCENT
;
804 sal_Int16
Color::getTransparency() const
806 return sal_Int16(std::round( (1.0 * (MAX_PERCENT
- mnAlpha
)) / PER_PERCENT
) );
809 sal_Int16
Color::getSchemeColorIndex() const
811 auto eThemeType
= schemeNameToThemeColorType(msSchemeName
);
812 return sal_Int16(eThemeType
);
815 model::ComplexColor
Color::createComplexColor(const GraphicHelper
& /*rGraphicHelper*/, sal_Int16 nPhClrTheme
) const
817 model::ComplexColor aNewComplexColor
;
818 if (meMode
== COLOR_PH
)
820 auto eTheme
= model::convertToThemeColorType(nPhClrTheme
);
821 aNewComplexColor
.setSchemeColor(eTheme
);
823 else if (meMode
== COLOR_SCHEME
)
825 auto eTheme
= getThemeColorType();
826 aNewComplexColor
.setSchemeColor(eTheme
);
828 else if (meMode
== COLOR_RGB
)
830 ::Color
aColor(ColorTransparency
, lclRgbComponentsToRgb(mnC1
, mnC2
, mnC3
));
831 aNewComplexColor
= model::ComplexColor::RGB(aColor
);
835 // TODO - Add other options
836 return aNewComplexColor
;
839 for(auto const& aTransform
: maTransforms
)
841 sal_Int16 nValue
= sal_Int16(aTransform
.mnValue
/ 10);
843 switch (aTransform
.mnToken
)
846 if (nValue
!= 10'000)
847 aNewComplexColor
.addTransformation({model::TransformationType::LumMod
, nValue
});
851 aNewComplexColor
.addTransformation({model::TransformationType::LumOff
, nValue
});
855 aNewComplexColor
.addTransformation({model::TransformationType::Tint
, nValue
});
859 aNewComplexColor
.addTransformation({model::TransformationType::Shade
, nValue
});
864 return aNewComplexColor
;
867 // private --------------------------------------------------------------------
869 void Color::setResolvedRgb( ::Color nRgb
) const
871 meMode
= (sal_Int32(nRgb
) < 0) ? COLOR_UNUSED
: COLOR_RGB
;
872 lclRgbToRgbComponents( mnC1
, mnC2
, mnC3
, nRgb
);
875 void Color::toRgb() const
884 mnC1
= lclCrgbCompToRgbComp( lclGamma( mnC1
, INC_GAMMA
) );
885 mnC2
= lclCrgbCompToRgbComp( lclGamma( mnC2
, INC_GAMMA
) );
886 mnC3
= lclCrgbCompToRgbComp( lclGamma( mnC3
, INC_GAMMA
) );
891 double fR
= 0.0, fG
= 0.0, fB
= 0.0;
892 if( (mnC2
== 0) || (mnC3
== MAX_PERCENT
) )
894 fR
= fG
= fB
= static_cast< double >( mnC3
) / MAX_PERCENT
;
898 // base color from hue
899 double fHue
= static_cast< double >( mnC1
) / MAX_DEGREE
* 6.0; // interval [0.0, 6.0)
900 if( fHue
<= 1.0 ) { fR
= 1.0; fG
= fHue
; } // red...yellow
901 else if( fHue
<= 2.0 ) { fR
= 2.0 - fHue
; fG
= 1.0; } // yellow...green
902 else if( fHue
<= 3.0 ) { fG
= 1.0; fB
= fHue
- 2.0; } // green...cyan
903 else if( fHue
<= 4.0 ) { fG
= 4.0 - fHue
; fB
= 1.0; } // cyan...blue
904 else if( fHue
<= 5.0 ) { fR
= fHue
- 4.0; fB
= 1.0; } // blue...magenta
905 else { fR
= 1.0; fB
= 6.0 - fHue
; } // magenta...red
908 double fSat
= static_cast< double >( mnC2
) / MAX_PERCENT
;
909 fR
= (fR
- 0.5) * fSat
+ 0.5;
910 fG
= (fG
- 0.5) * fSat
+ 0.5;
911 fB
= (fB
- 0.5) * fSat
+ 0.5;
914 double fLum
= 2.0 * static_cast< double >( mnC3
) / MAX_PERCENT
- 1.0; // interval [-1.0, 1.0]
917 double fShade
= fLum
+ 1.0; // interval [0.0, 1.0] (black...full color)
922 else if( fLum
> 0.0 )
924 double fTint
= 1.0 - fLum
; // interval [0.0, 1.0] (white...full color)
925 fR
= 1.0 - ((1.0 - fR
) * fTint
);
926 fG
= 1.0 - ((1.0 - fG
) * fTint
);
927 fB
= 1.0 - ((1.0 - fB
) * fTint
);
930 mnC1
= static_cast< sal_Int32
>( fR
* 255.0 + 0.5 );
931 mnC2
= static_cast< sal_Int32
>( fG
* 255.0 + 0.5 );
932 mnC3
= static_cast< sal_Int32
>( fB
* 255.0 + 0.5 );
936 OSL_FAIL( "Color::toRgb - unexpected color mode" );
940 void Color::toCrgb() const
949 mnC1
= lclGamma( lclRgbCompToCrgbComp( mnC1
), DEC_GAMMA
);
950 mnC2
= lclGamma( lclRgbCompToCrgbComp( mnC2
), DEC_GAMMA
);
951 mnC3
= lclGamma( lclRgbCompToCrgbComp( mnC3
), DEC_GAMMA
);
957 OSL_FAIL( "Color::toCrgb - unexpected color mode" );
961 void Color::toHsl() const
971 double fR
= static_cast< double >( mnC1
) / 255.0; // red [0.0, 1.0]
972 double fG
= static_cast< double >( mnC2
) / 255.0; // green [0.0, 1.0]
973 double fB
= static_cast< double >( mnC3
) / 255.0; // blue [0.0, 1.0]
974 double fMin
= ::std::min( ::std::min( fR
, fG
), fB
);
975 double fMax
= ::std::max( ::std::max( fR
, fG
), fB
);
976 double fD
= fMax
- fMin
;
978 using ::rtl::math::approxEqual
;
980 // hue: 0deg = red, 120deg = green, 240deg = blue
981 if( fD
== 0.0 ) // black/gray/white
983 else if( approxEqual(fMax
, fR
, 64) ) // magenta...red...yellow
984 mnC1
= static_cast< sal_Int32
>( ((fG
- fB
) / fD
* 60.0 + 360.0) * PER_DEGREE
+ 0.5 ) % MAX_DEGREE
;
985 else if( approxEqual(fMax
, fG
, 64) ) // yellow...green...cyan
986 mnC1
= static_cast< sal_Int32
>( ((fB
- fR
) / fD
* 60.0 + 120.0) * PER_DEGREE
+ 0.5 );
987 else // cyan...blue...magenta
988 mnC1
= static_cast< sal_Int32
>( ((fR
- fG
) / fD
* 60.0 + 240.0) * PER_DEGREE
+ 0.5 );
990 // luminance: 0% = black, 50% = full color, 100% = white
991 mnC3
= static_cast< sal_Int32
>( (fMin
+ fMax
) / 2.0 * MAX_PERCENT
+ 0.5 );
993 // saturation: 0% = gray, 100% = full color
994 if( (mnC3
== 0) || (mnC3
== MAX_PERCENT
) ) // black/white
996 else if( mnC3
<= 50 * PER_PERCENT
) // dark...full color
997 mnC2
= static_cast< sal_Int32
>( fD
/ (fMin
+ fMax
) * MAX_PERCENT
+ 0.5 );
998 else // full color...light
999 mnC2
= static_cast< sal_Int32
>( fD
/ (2.0 - fMax
- fMin
) * MAX_PERCENT
+ 0.5 );
1006 OSL_FAIL( "Color::toHsl - unexpected color mode" );
1012 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */