Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / oox / source / drawingml / color.cxx
blob09b03335287775c09dfbe2b84f308878dcccef00
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
21 #include <algorithm>
22 #include <math.h>
23 #include <osl/diagnose.h>
24 #include <sal/log.hxx>
25 #include <oox/helper/containerhelper.hxx>
26 #include <oox/helper/graphichelper.hxx>
27 #include <oox/drawingml/drawingmltypes.hxx>
28 #include <oox/token/namespaces.hxx>
29 #include <oox/token/tokens.hxx>
31 namespace oox {
32 namespace drawingml {
34 namespace {
36 /** Global storage for predefined color values used in OOXML file formats. */
37 struct PresetColorsPool
39 typedef ::std::vector< ::Color > ColorVector;
41 ColorVector maDmlColors; /// Predefined colors in DrawingML, indexed by XML token.
42 ColorVector maVmlColors; /// Predefined colors in VML, indexed by XML token.
44 explicit PresetColorsPool();
47 PresetColorsPool::PresetColorsPool() :
48 maDmlColors( static_cast< size_t >( XML_TOKEN_COUNT ), API_RGB_TRANSPARENT ),
49 maVmlColors( static_cast< size_t >( XML_TOKEN_COUNT ), API_RGB_TRANSPARENT )
51 // predefined colors in DrawingML (map XML token identifiers to RGB values)
52 static const std::pair<sal_Int32, ::Color> spnDmlColors[] =
54 {XML_aliceBlue, ::Color(0xF0F8FF)}, {XML_antiqueWhite, ::Color(0xFAEBD7)},
55 {XML_aqua, ::Color(0x00FFFF)}, {XML_aquamarine, ::Color(0x7FFFD4)},
56 {XML_azure, ::Color(0xF0FFFF)}, {XML_beige, ::Color(0xF5F5DC)},
57 {XML_bisque, ::Color(0xFFE4C4)}, {XML_black, ::Color(0x000000)},
58 {XML_blanchedAlmond, ::Color(0xFFEBCD)}, {XML_blue, ::Color(0x0000FF)},
59 {XML_blueViolet, ::Color(0x8A2BE2)}, {XML_brown, ::Color(0xA52A2A)},
60 {XML_burlyWood, ::Color(0xDEB887)}, {XML_cadetBlue, ::Color(0x5F9EA0)},
61 {XML_chartreuse, ::Color(0x7FFF00)}, {XML_chocolate, ::Color(0xD2691E)},
62 {XML_coral, ::Color(0xFF7F50)}, {XML_cornflowerBlue, ::Color(0x6495ED)},
63 {XML_cornsilk, ::Color(0xFFF8DC)}, {XML_crimson, ::Color(0xDC143C)},
64 {XML_cyan, ::Color(0x00FFFF)}, {XML_deepPink, ::Color(0xFF1493)},
65 {XML_deepSkyBlue, ::Color(0x00BFFF)}, {XML_dimGray, ::Color(0x696969)},
66 {XML_dkBlue, ::Color(0x00008B)}, {XML_dkCyan, ::Color(0x008B8B)},
67 {XML_dkGoldenrod, ::Color(0xB8860B)}, {XML_dkGray, ::Color(0xA9A9A9)},
68 {XML_dkGreen, ::Color(0x006400)}, {XML_dkKhaki, ::Color(0xBDB76B)},
69 {XML_dkMagenta, ::Color(0x8B008B)}, {XML_dkOliveGreen, ::Color(0x556B2F)},
70 {XML_dkOrange, ::Color(0xFF8C00)}, {XML_dkOrchid, ::Color(0x9932CC)},
71 {XML_dkRed, ::Color(0x8B0000)}, {XML_dkSalmon, ::Color(0xE9967A)},
72 {XML_dkSeaGreen, ::Color(0x8FBC8B)}, {XML_dkSlateBlue, ::Color(0x483D8B)},
73 {XML_dkSlateGray, ::Color(0x2F4F4F)}, {XML_dkTurquoise, ::Color(0x00CED1)},
74 {XML_dkViolet, ::Color(0x9400D3)}, {XML_dodgerBlue, ::Color(0x1E90FF)},
75 {XML_firebrick, ::Color(0xB22222)}, {XML_floralWhite, ::Color(0xFFFAF0)},
76 {XML_forestGreen, ::Color(0x228B22)}, {XML_fuchsia, ::Color(0xFF00FF)},
77 {XML_gainsboro, ::Color(0xDCDCDC)}, {XML_ghostWhite, ::Color(0xF8F8FF)},
78 {XML_gold, ::Color(0xFFD700)}, {XML_goldenrod, ::Color(0xDAA520)},
79 {XML_gray, ::Color(0x808080)}, {XML_green, ::Color(0x008000)},
80 {XML_greenYellow, ::Color(0xADFF2F)}, {XML_honeydew, ::Color(0xF0FFF0)},
81 {XML_hotPink, ::Color(0xFF69B4)}, {XML_indianRed, ::Color(0xCD5C5C)},
82 {XML_indigo, ::Color(0x4B0082)}, {XML_ivory, ::Color(0xFFFFF0)},
83 {XML_khaki, ::Color(0xF0E68C)}, {XML_lavender, ::Color(0xE6E6FA)},
84 {XML_lavenderBlush, ::Color(0xFFF0F5)}, {XML_lawnGreen, ::Color(0x7CFC00)},
85 {XML_lemonChiffon, ::Color(0xFFFACD)}, {XML_lime, ::Color(0x00FF00)},
86 {XML_limeGreen, ::Color(0x32CD32)}, {XML_linen, ::Color(0xFAF0E6)},
87 {XML_ltBlue, ::Color(0xADD8E6)}, {XML_ltCoral, ::Color(0xF08080)},
88 {XML_ltCyan, ::Color(0xE0FFFF)}, {XML_ltGoldenrodYellow, ::Color(0xFAFA78)},
89 {XML_ltGray, ::Color(0xD3D3D3)}, {XML_ltGreen, ::Color(0x90EE90)},
90 {XML_ltPink, ::Color(0xFFB6C1)}, {XML_ltSalmon, ::Color(0xFFA07A)},
91 {XML_ltSeaGreen, ::Color(0x20B2AA)}, {XML_ltSkyBlue, ::Color(0x87CEFA)},
92 {XML_ltSlateGray, ::Color(0x778899)}, {XML_ltSteelBlue, ::Color(0xB0C4DE)},
93 {XML_ltYellow, ::Color(0xFFFFE0)}, {XML_magenta, ::Color(0xFF00FF)},
94 {XML_maroon, ::Color(0x800000)}, {XML_medAquamarine, ::Color(0x66CDAA)},
95 {XML_medBlue, ::Color(0x0000CD)}, {XML_medOrchid, ::Color(0xBA55D3)},
96 {XML_medPurple, ::Color(0x9370DB)}, {XML_medSeaGreen, ::Color(0x3CB371)},
97 {XML_medSlateBlue, ::Color(0x7B68EE)}, {XML_medSpringGreen, ::Color(0x00FA9A)},
98 {XML_medTurquoise, ::Color(0x48D1CC)}, {XML_medVioletRed, ::Color(0xC71585)},
99 {XML_midnightBlue, ::Color(0x191970)}, {XML_mintCream, ::Color(0xF5FFFA)},
100 {XML_mistyRose, ::Color(0xFFE4E1)}, {XML_moccasin, ::Color(0xFFE4B5)},
101 {XML_navajoWhite, ::Color(0xFFDEAD)}, {XML_navy, ::Color(0x000080)},
102 {XML_oldLace, ::Color(0xFDF5E6)}, {XML_olive, ::Color(0x808000)},
103 {XML_oliveDrab, ::Color(0x6B8E23)}, {XML_orange, ::Color(0xFFA500)},
104 {XML_orangeRed, ::Color(0xFF4500)}, {XML_orchid, ::Color(0xDA70D6)},
105 {XML_paleGoldenrod, ::Color(0xEEE8AA)}, {XML_paleGreen, ::Color(0x98FB98)},
106 {XML_paleTurquoise, ::Color(0xAFEEEE)}, {XML_paleVioletRed, ::Color(0xDB7093)},
107 {XML_papayaWhip, ::Color(0xFFEFD5)}, {XML_peachPuff, ::Color(0xFFDAB9)},
108 {XML_peru, ::Color(0xCD853F)}, {XML_pink, ::Color(0xFFC0CB)},
109 {XML_plum, ::Color(0xDDA0DD)}, {XML_powderBlue, ::Color(0xB0E0E6)},
110 {XML_purple, ::Color(0x800080)}, {XML_red, ::Color(0xFF0000)},
111 {XML_rosyBrown, ::Color(0xBC8F8F)}, {XML_royalBlue, ::Color(0x4169E1)},
112 {XML_saddleBrown, ::Color(0x8B4513)}, {XML_salmon, ::Color(0xFA8072)},
113 {XML_sandyBrown, ::Color(0xF4A460)}, {XML_seaGreen, ::Color(0x2E8B57)},
114 {XML_seaShell, ::Color(0xFFF5EE)}, {XML_sienna, ::Color(0xA0522D)},
115 {XML_silver, ::Color(0xC0C0C0)}, {XML_skyBlue, ::Color(0x87CEEB)},
116 {XML_slateBlue, ::Color(0x6A5ACD)}, {XML_slateGray, ::Color(0x708090)},
117 {XML_snow, ::Color(0xFFFAFA)}, {XML_springGreen, ::Color(0x00FF7F)},
118 {XML_steelBlue, ::Color(0x4682B4)}, {XML_tan, ::Color(0xD2B48C)},
119 {XML_teal, ::Color(0x008080)}, {XML_thistle, ::Color(0xD8BFD8)},
120 {XML_tomato, ::Color(0xFF6347)}, {XML_turquoise, ::Color(0x40E0D0)},
121 {XML_violet, ::Color(0xEE82EE)}, {XML_wheat, ::Color(0xF5DEB3)},
122 {XML_white, ::Color(0xFFFFFF)}, {XML_whiteSmoke, ::Color(0xF5F5F5)},
123 {XML_yellow, ::Color(0xFFFF00)}, {XML_yellowGreen, ::Color(0x9ACD32)}
125 for(auto const& nEntry : spnDmlColors)
126 maDmlColors[ static_cast< size_t >(nEntry.first) ] = nEntry.second;
128 // predefined colors in VML (map XML token identifiers to RGB values)
129 static const std::pair<sal_Int32, ::Color> spnVmlColors[] =
131 {XML_aqua, ::Color(0x00FFFF)}, {XML_black, ::Color(0x000000)},
132 {XML_blue, ::Color(0x0000FF)}, {XML_fuchsia, ::Color(0xFF00FF)},
133 {XML_gray, ::Color(0x808080)}, {XML_green, ::Color(0x008000)},
134 {XML_lime, ::Color(0x00FF00)}, {XML_maroon, ::Color(0x800000)},
135 {XML_navy, ::Color(0x000080)}, {XML_olive, ::Color(0x808000)},
136 {XML_purple, ::Color(0x800080)}, {XML_red, ::Color(0xFF0000)},
137 {XML_silver, ::Color(0xC0C0C0)}, {XML_teal, ::Color(0x008080)},
138 {XML_white, ::Color(0xFFFFFF)}, {XML_yellow, ::Color(0xFFFF00)}
140 for(auto const& nEntry : spnVmlColors)
141 maVmlColors[ static_cast< size_t >(nEntry.first) ] = nEntry.second;
144 struct StaticPresetColorsPool : public ::rtl::Static< PresetColorsPool, StaticPresetColorsPool > {};
146 const double DEC_GAMMA = 2.3;
147 const double INC_GAMMA = 1.0 / DEC_GAMMA;
149 void lclRgbToRgbComponents( sal_Int32& ornR, sal_Int32& ornG, sal_Int32& ornB, ::Color nRgb )
151 ornR = nRgb.GetRed();
152 ornG = nRgb.GetGreen();
153 ornB = nRgb.GetBlue();
156 sal_Int32 lclRgbComponentsToRgb( sal_Int32 nR, sal_Int32 nG, sal_Int32 nB )
158 return static_cast< sal_Int32 >( (nR << 16) | (nG << 8) | nB );
161 sal_Int32 lclRgbCompToCrgbComp( sal_Int32 nRgbComp )
163 return static_cast< sal_Int32 >( nRgbComp * MAX_PERCENT / 255 );
166 sal_Int32 lclCrgbCompToRgbComp( sal_Int32 nCrgbComp )
168 return static_cast< sal_Int32 >( nCrgbComp * 255 / MAX_PERCENT );
171 sal_Int32 lclGamma( sal_Int32 nComp, double fGamma )
173 return static_cast< sal_Int32 >( pow( static_cast< double >( nComp ) / MAX_PERCENT, fGamma ) * MAX_PERCENT + 0.5 );
176 void lclSetValue( sal_Int32& ornValue, sal_Int32 nNew, sal_Int32 nMax = MAX_PERCENT )
178 OSL_ENSURE( (0 <= nNew) && (nNew <= nMax), "lclSetValue - invalid value" );
179 if( (0 <= nNew) && (nNew <= nMax) )
180 ornValue = nNew;
183 void lclModValue( sal_Int32& ornValue, sal_Int32 nMod, sal_Int32 nMax = MAX_PERCENT )
185 OSL_ENSURE( (0 <= nMod), "lclModValue - invalid modificator" );
186 ornValue = getLimitedValue< sal_Int32, double >( static_cast< double >( ornValue ) * nMod / MAX_PERCENT, 0, nMax );
189 void lclOffValue( sal_Int32& ornValue, sal_Int32 nOff, sal_Int32 nMax = MAX_PERCENT )
191 OSL_ENSURE( (-nMax <= nOff) && (nOff <= nMax), "lclOffValue - invalid offset" );
192 ornValue = getLimitedValue< sal_Int32, sal_Int32 >( ornValue + nOff, 0, nMax );
195 } // namespace
197 Color::Color() :
198 meMode( COLOR_UNUSED ),
199 mnC1( 0 ),
200 mnC2( 0 ),
201 mnC3( 0 ),
202 mnAlpha( MAX_PERCENT )
206 ::Color Color::getDmlPresetColor( sal_Int32 nToken, ::Color nDefaultRgb )
208 /* Do not pass nDefaultRgb to ContainerHelper::getVectorElement(), to be
209 able to catch the existing vector entries without corresponding XML
210 token identifier. */
211 ::Color nRgbValue = ContainerHelper::getVectorElement( StaticPresetColorsPool::get().maDmlColors, nToken, API_RGB_TRANSPARENT );
212 return (sal_Int32(nRgbValue) >= 0) ? nRgbValue : nDefaultRgb;
215 ::Color Color::getVmlPresetColor( sal_Int32 nToken, ::Color nDefaultRgb )
217 /* Do not pass nDefaultRgb to ContainerHelper::getVectorElement(), to be
218 able to catch the existing vector entries without corresponding XML
219 token identifier. */
220 ::Color nRgbValue = ContainerHelper::getVectorElement( StaticPresetColorsPool::get().maVmlColors, nToken, API_RGB_TRANSPARENT );
221 return (sal_Int32(nRgbValue) >= 0) ? nRgbValue : nDefaultRgb;
224 void Color::setUnused()
226 meMode = COLOR_UNUSED;
229 void Color::setSrgbClr( ::Color nRgb )
231 setSrgbClr(sal_Int32(nRgb));
234 void Color::setSrgbClr( sal_Int32 nRgb )
236 OSL_ENSURE( (0 <= nRgb) && (nRgb <= 0xFFFFFF), "Color::setSrgbClr - invalid RGB value" );
237 meMode = COLOR_RGB;
238 lclRgbToRgbComponents( mnC1, mnC2, mnC3, ::Color(nRgb) );
241 void Color::setScrgbClr( sal_Int32 nR, sal_Int32 nG, sal_Int32 nB )
243 OSL_ENSURE( (0 <= nR) && (nR <= MAX_PERCENT), "Color::setScrgbClr - invalid red value" );
244 OSL_ENSURE( (0 <= nG) && (nG <= MAX_PERCENT), "Color::setScrgbClr - invalid green value" );
245 OSL_ENSURE( (0 <= nB) && (nB <= MAX_PERCENT), "Color::setScrgbClr - invalid blue value" );
246 meMode = COLOR_CRGB;
247 mnC1 = getLimitedValue< sal_Int32, sal_Int32 >( nR, 0, MAX_PERCENT );
248 mnC2 = getLimitedValue< sal_Int32, sal_Int32 >( nG, 0, MAX_PERCENT );
249 mnC3 = getLimitedValue< sal_Int32, sal_Int32 >( nB, 0, MAX_PERCENT );
252 void Color::setHslClr( sal_Int32 nHue, sal_Int32 nSat, sal_Int32 nLum )
254 OSL_ENSURE( (0 <= nHue) && (nHue <= MAX_DEGREE), "Color::setHslClr - invalid hue value" );
255 OSL_ENSURE( (0 <= nSat) && (nSat <= MAX_PERCENT), "Color::setHslClr - invalid saturation value" );
256 OSL_ENSURE( (0 <= nLum) && (nLum <= MAX_PERCENT), "Color::setHslClr - invalid luminance value" );
257 meMode = COLOR_HSL;
258 mnC1 = getLimitedValue< sal_Int32, sal_Int32 >( nHue, 0, MAX_DEGREE );
259 mnC2 = getLimitedValue< sal_Int32, sal_Int32 >( nSat, 0, MAX_PERCENT );
260 mnC3 = getLimitedValue< sal_Int32, sal_Int32 >( nLum, 0, MAX_PERCENT );
263 void Color::setPrstClr( sal_Int32 nToken )
265 ::Color nRgbValue = getDmlPresetColor( nToken, API_RGB_TRANSPARENT );
266 OSL_ENSURE( sal_Int32(nRgbValue) >= 0, "Color::setPrstClr - invalid preset color token" );
267 if( sal_Int32(nRgbValue) >= 0 )
268 setSrgbClr( nRgbValue );
271 void Color::setSchemeClr( sal_Int32 nToken )
273 OSL_ENSURE( nToken != XML_TOKEN_INVALID, "Color::setSchemeClr - invalid color token" );
274 meMode = (nToken == XML_phClr) ? COLOR_PH : COLOR_SCHEME;
275 mnC1 = nToken;
278 void Color::setPaletteClr( sal_Int32 nPaletteIdx )
280 OSL_ENSURE( nPaletteIdx >= 0, "Color::setPaletteClr - invalid palette index" );
281 meMode = COLOR_PALETTE;
282 mnC1 = nPaletteIdx;
285 void Color::setSysClr( sal_Int32 nToken, sal_Int32 nLastRgb )
287 OSL_ENSURE( (-1 <= nLastRgb) && (nLastRgb <= 0xFFFFFF), "Color::setSysClr - invalid RGB value" );
288 meMode = COLOR_SYSTEM;
289 mnC1 = nToken;
290 mnC2 = nLastRgb;
293 void Color::addTransformation( sal_Int32 nElement, sal_Int32 nValue )
295 /* Execute alpha transformations directly, store other transformations in
296 a vector, they may depend on a scheme base color which will be resolved
297 in Color::getColor(). */
298 sal_Int32 nToken = getBaseToken( nElement );
299 switch( nToken )
301 case XML_alpha: lclSetValue( mnAlpha, nValue ); break;
302 case XML_alphaMod: lclModValue( mnAlpha, nValue ); break;
303 case XML_alphaOff: lclOffValue( mnAlpha, nValue ); break;
304 default: maTransforms.emplace_back( nToken, nValue );
306 sal_Int32 nSize = maInteropTransformations.getLength();
307 maInteropTransformations.realloc(nSize + 1);
308 maInteropTransformations[nSize].Name = getColorTransformationName( nToken );
309 maInteropTransformations[nSize].Value <<= nValue;
312 void Color::addChartTintTransformation( double fTint )
314 sal_Int32 nValue = getLimitedValue< sal_Int32, double >( fTint * MAX_PERCENT + 0.5, -MAX_PERCENT, MAX_PERCENT );
315 if( nValue < 0 )
316 maTransforms.emplace_back( XML_shade, nValue + MAX_PERCENT );
317 else if( nValue > 0 )
318 maTransforms.emplace_back( XML_tint, MAX_PERCENT - nValue );
321 void Color::addExcelTintTransformation( double fTint )
323 sal_Int32 nValue = getLimitedValue< sal_Int32, double >( fTint * MAX_PERCENT + 0.5, -MAX_PERCENT, MAX_PERCENT );
324 maTransforms.emplace_back( XLS_TOKEN( tint ), nValue );
327 void Color::clearTransformations()
329 maTransforms.clear();
330 maInteropTransformations.realloc(0);
331 clearTransparence();
334 OUString Color::getColorTransformationName( sal_Int32 nElement )
336 switch( nElement )
338 case XML_red: return "red";
339 case XML_redMod: return "redMod";
340 case XML_redOff: return "redOff";
341 case XML_green: return "green";
342 case XML_greenMod: return "greenMod";
343 case XML_greenOff: return "greenOff";
344 case XML_blue: return "blue";
345 case XML_blueMod: return "blueMod";
346 case XML_blueOff: return "blueOff";
347 case XML_alpha: return "alpha";
348 case XML_alphaMod: return "alphaMod";
349 case XML_alphaOff: return "alphaOff";
350 case XML_hue: return "hue";
351 case XML_hueMod: return "hueMod";
352 case XML_hueOff: return "hueOff";
353 case XML_sat: return "sat";
354 case XML_satMod: return "satMod";
355 case XML_satOff: return "satOff";
356 case XML_lum: return "lum";
357 case XML_lumMod: return "lumMod";
358 case XML_lumOff: return "lumOff";
359 case XML_shade: return "shade";
360 case XML_tint: return "tint";
361 case XML_gray: return "gray";
362 case XML_comp: return "comp";
363 case XML_inv: return "inv";
364 case XML_gamma: return "gamma";
365 case XML_invGamma: return "invGamma";
367 SAL_WARN( "oox.drawingml", "Color::getColorTransformationName - unexpected transformation type" );
368 return OUString();
371 sal_Int32 Color::getColorTransformationToken( const OUString& sName )
373 if( sName == "red" )
374 return XML_red;
375 else if( sName == "redMod" )
376 return XML_redMod;
377 else if( sName == "redOff" )
378 return XML_redOff;
379 else if( sName == "green" )
380 return XML_green;
381 else if( sName == "greenMod" )
382 return XML_greenMod;
383 else if( sName == "greenOff" )
384 return XML_greenOff;
385 else if( sName == "blue" )
386 return XML_blue;
387 else if( sName == "blueMod" )
388 return XML_blueMod;
389 else if( sName == "blueOff" )
390 return XML_blueOff;
391 else if( sName == "alpha" )
392 return XML_alpha;
393 else if( sName == "alphaMod" )
394 return XML_alphaMod;
395 else if( sName == "alphaOff" )
396 return XML_alphaOff;
397 else if( sName == "hue" )
398 return XML_hue;
399 else if( sName == "hueMod" )
400 return XML_hueMod;
401 else if( sName == "hueOff" )
402 return XML_hueOff;
403 else if( sName == "sat" )
404 return XML_sat;
405 else if( sName == "satMod" )
406 return XML_satMod;
407 else if( sName == "satOff" )
408 return XML_satOff;
409 else if( sName == "lum" )
410 return XML_lum;
411 else if( sName == "lumMod" )
412 return XML_lumMod;
413 else if( sName == "lumOff" )
414 return XML_lumOff;
415 else if( sName == "shade" )
416 return XML_shade;
417 else if( sName == "tint" )
418 return XML_tint;
419 else if( sName == "gray" )
420 return XML_gray;
421 else if( sName == "comp" )
422 return XML_comp;
423 else if( sName == "inv" )
424 return XML_inv;
425 else if( sName == "gamma" )
426 return XML_gamma;
427 else if( sName == "invGamma" )
428 return XML_invGamma;
430 SAL_WARN( "oox.drawingml", "Color::getColorTransformationToken - unexpected transformation type" );
431 return XML_TOKEN_INVALID;
434 bool Color::equals(const Color& rOther, const GraphicHelper& rGraphicHelper, ::Color nPhClr) const
436 if (getColor(rGraphicHelper, nPhClr) != rOther.getColor(rGraphicHelper, nPhClr))
437 return false;
439 return getTransparency() == rOther.getTransparency();
442 void Color::clearTransparence()
444 mnAlpha = MAX_PERCENT;
447 ::Color Color::getColor( const GraphicHelper& rGraphicHelper, ::Color nPhClr ) const
449 const sal_Int32 nTempC1 = mnC1;
450 const sal_Int32 nTempC2 = mnC2;
451 const sal_Int32 nTempC3 = mnC3;
452 const ColorMode eTempMode = meMode;
454 switch( meMode )
456 case COLOR_UNUSED: mnC1 = sal_Int32(API_RGB_TRANSPARENT); break;
458 case COLOR_RGB: break; // nothing to do
459 case COLOR_CRGB: break; // nothing to do
460 case COLOR_HSL: break; // nothing to do
462 case COLOR_SCHEME: setResolvedRgb( rGraphicHelper.getSchemeColor( mnC1 ) ); break;
463 case COLOR_PALETTE: setResolvedRgb( rGraphicHelper.getPaletteColor( mnC1 ) ); break;
464 case COLOR_SYSTEM: setResolvedRgb( rGraphicHelper.getSystemColor( mnC1, ::Color(mnC2) ) ); break;
465 case COLOR_PH: setResolvedRgb( nPhClr ); break;
467 case COLOR_FINAL: return ::Color(mnC1);
470 // if color is UNUSED or turns to UNUSED in setResolvedRgb, do not perform transformations
471 if( meMode != COLOR_UNUSED )
473 for (auto const& transform : maTransforms)
475 switch( transform.mnToken )
477 case XML_red: toCrgb(); lclSetValue( mnC1, transform.mnValue ); break;
478 case XML_redMod: toCrgb(); lclModValue( mnC1, transform.mnValue ); break;
479 case XML_redOff: toCrgb(); lclOffValue( mnC1, transform.mnValue ); break;
480 case XML_green: toCrgb(); lclSetValue( mnC2, transform.mnValue ); break;
481 case XML_greenMod: toCrgb(); lclModValue( mnC2, transform.mnValue ); break;
482 case XML_greenOff: toCrgb(); lclOffValue( mnC2, transform.mnValue ); break;
483 case XML_blue: toCrgb(); lclSetValue( mnC3, transform.mnValue ); break;
484 case XML_blueMod: toCrgb(); lclModValue( mnC3, transform.mnValue ); break;
485 case XML_blueOff: toCrgb(); lclOffValue( mnC3, transform.mnValue ); break;
487 case XML_hue: toHsl(); lclSetValue( mnC1, transform.mnValue, MAX_DEGREE ); break;
488 case XML_hueMod: toHsl(); lclModValue( mnC1, transform.mnValue, MAX_DEGREE ); break;
489 case XML_hueOff: toHsl(); lclOffValue( mnC1, transform.mnValue, MAX_DEGREE ); break;
490 case XML_sat: toHsl(); lclSetValue( mnC2, transform.mnValue ); break;
491 case XML_satMod: toHsl(); lclModValue( mnC2, transform.mnValue ); break;
492 case XML_satOff: toHsl(); lclOffValue( mnC2, transform.mnValue ); break;
494 case XML_lum:
495 toHsl();
496 lclSetValue( mnC3, transform.mnValue );
497 // if color changes to black or white, it will stay gray if luminance changes again
498 if( (mnC3 == 0) || (mnC3 == MAX_PERCENT) ) mnC2 = 0;
499 break;
500 case XML_lumMod:
501 toHsl();
502 lclModValue( mnC3, transform.mnValue );
503 // if color changes to black or white, it will stay gray if luminance changes again
504 if( (mnC3 == 0) || (mnC3 == MAX_PERCENT) ) mnC2 = 0;
505 break;
506 case XML_lumOff:
507 toHsl();
508 lclOffValue( mnC3, transform.mnValue );
509 // if color changes to black or white, it will stay gray if luminance changes again
510 if( (mnC3 == 0) || (mnC3 == MAX_PERCENT) ) mnC2 = 0;
511 break;
513 case XML_shade:
514 // shade: 0% = black, 100% = original color
515 toCrgb();
516 OSL_ENSURE( (0 <= transform.mnValue) && (transform.mnValue <= MAX_PERCENT), "Color::getColor - invalid shade value" );
517 if( (0 <= transform.mnValue) && (transform.mnValue <= MAX_PERCENT) )
519 double fFactor = static_cast< double >( transform.mnValue ) / MAX_PERCENT;
520 mnC1 = static_cast< sal_Int32 >( mnC1 * fFactor );
521 mnC2 = static_cast< sal_Int32 >( mnC2 * fFactor );
522 mnC3 = static_cast< sal_Int32 >( mnC3 * fFactor );
524 break;
525 case XML_tint:
526 // tint: 0% = white, 100% = original color
527 toCrgb();
528 OSL_ENSURE( (0 <= transform.mnValue) && (transform.mnValue <= MAX_PERCENT), "Color::getColor - invalid tint value" );
529 if( (0 <= transform.mnValue) && (transform.mnValue <= MAX_PERCENT) )
531 double fFactor = static_cast< double >( transform.mnValue ) / MAX_PERCENT;
532 mnC1 = static_cast< sal_Int32 >( MAX_PERCENT - (MAX_PERCENT - mnC1) * fFactor );
533 mnC2 = static_cast< sal_Int32 >( MAX_PERCENT - (MAX_PERCENT - mnC2) * fFactor );
534 mnC3 = static_cast< sal_Int32 >( MAX_PERCENT - (MAX_PERCENT - mnC3) * fFactor );
536 break;
537 case XLS_TOKEN( tint ):
538 // Excel tint: move luminance relative to current value
539 toHsl();
540 OSL_ENSURE( (-MAX_PERCENT <= transform.mnValue) && (transform.mnValue <= MAX_PERCENT), "Color::getColor - invalid tint value" );
541 if( (-MAX_PERCENT <= transform.mnValue) && (transform.mnValue < 0) )
543 // negative: luminance towards 0% (black)
544 lclModValue( mnC3, transform.mnValue + MAX_PERCENT );
546 else if( (0 < transform.mnValue) && (transform.mnValue <= MAX_PERCENT) )
548 // positive: luminance towards 100% (white)
549 mnC3 = MAX_PERCENT - mnC3;
550 lclModValue( mnC3, MAX_PERCENT - transform.mnValue );
551 mnC3 = MAX_PERCENT - mnC3;
553 break;
555 case XML_gray:
556 // change color to gray, weighted RGB: 22% red, 72% green, 6% blue
557 toRgb();
558 mnC1 = mnC2 = mnC3 = (mnC1 * 22 + mnC2 * 72 + mnC3 * 6) / 100;
559 break;
561 case XML_comp:
562 // comp: rotate hue by 180 degrees, do not change lum/sat
563 toHsl();
564 mnC1 = (mnC1 + (180 * PER_DEGREE)) % MAX_DEGREE;
565 break;
566 case XML_inv:
567 // invert percentual RGB values
568 toCrgb();
569 mnC1 = MAX_PERCENT - mnC1;
570 mnC2 = MAX_PERCENT - mnC2;
571 mnC3 = MAX_PERCENT - mnC3;
572 break;
574 case XML_gamma:
575 // increase gamma of color
576 toCrgb();
577 mnC1 = lclGamma( mnC1, INC_GAMMA );
578 mnC2 = lclGamma( mnC2, INC_GAMMA );
579 mnC3 = lclGamma( mnC3, INC_GAMMA );
580 break;
581 case XML_invGamma:
582 // decrease gamma of color
583 toCrgb();
584 mnC1 = lclGamma( mnC1, DEC_GAMMA );
585 mnC2 = lclGamma( mnC2, DEC_GAMMA );
586 mnC3 = lclGamma( mnC3, DEC_GAMMA );
587 break;
591 // store resulting RGB value in mnC1
592 toRgb();
593 mnC1 = lclRgbComponentsToRgb( mnC1, mnC2, mnC3 );
595 else // if( meMode != COLOR_UNUSED )
597 mnC1 = sal_Int32(API_RGB_TRANSPARENT);
600 sal_Int32 nRet = mnC1;
601 // Restore the original values when the color depends on one of the input
602 // parameters (rGraphicHelper or nPhClr)
603 if( eTempMode >= COLOR_SCHEME && eTempMode <= COLOR_PH )
605 mnC1 = nTempC1;
606 mnC2 = nTempC2;
607 mnC3 = nTempC3;
608 meMode = eTempMode;
610 else
612 meMode = COLOR_FINAL;
614 if( meMode == COLOR_FINAL )
615 maTransforms.clear();
616 return ::Color(nRet);
619 bool Color::hasTransparency() const
621 return mnAlpha < MAX_PERCENT;
624 sal_Int16 Color::getTransparency() const
626 return sal_Int16(std::round( (1.0 * (MAX_PERCENT - mnAlpha)) / PER_PERCENT) );
629 // private --------------------------------------------------------------------
631 void Color::setResolvedRgb( ::Color nRgb ) const
633 meMode = (sal_Int32(nRgb) < 0) ? COLOR_UNUSED : COLOR_RGB;
634 lclRgbToRgbComponents( mnC1, mnC2, mnC3, nRgb );
637 void Color::toRgb() const
639 switch( meMode )
641 case COLOR_RGB:
642 // nothing to do
643 break;
644 case COLOR_CRGB:
645 meMode = COLOR_RGB;
646 mnC1 = lclCrgbCompToRgbComp( lclGamma( mnC1, INC_GAMMA ) );
647 mnC2 = lclCrgbCompToRgbComp( lclGamma( mnC2, INC_GAMMA ) );
648 mnC3 = lclCrgbCompToRgbComp( lclGamma( mnC3, INC_GAMMA ) );
649 break;
650 case COLOR_HSL:
652 meMode = COLOR_RGB;
653 double fR = 0.0, fG = 0.0, fB = 0.0;
654 if( (mnC2 == 0) || (mnC3 == MAX_PERCENT) )
656 fR = fG = fB = static_cast< double >( mnC3 ) / MAX_PERCENT;
658 else if( mnC3 > 0 )
660 // base color from hue
661 double fHue = static_cast< double >( mnC1 ) / MAX_DEGREE * 6.0; // interval [0.0, 6.0)
662 if( fHue <= 1.0 ) { fR = 1.0; fG = fHue; } // red...yellow
663 else if( fHue <= 2.0 ) { fR = 2.0 - fHue; fG = 1.0; } // yellow...green
664 else if( fHue <= 3.0 ) { fG = 1.0; fB = fHue - 2.0; } // green...cyan
665 else if( fHue <= 4.0 ) { fG = 4.0 - fHue; fB = 1.0; } // cyan...blue
666 else if( fHue <= 5.0 ) { fR = fHue - 4.0; fB = 1.0; } // blue...magenta
667 else { fR = 1.0; fB = 6.0 - fHue; } // magenta...red
669 // apply saturation
670 double fSat = static_cast< double >( mnC2 ) / MAX_PERCENT;
671 fR = (fR - 0.5) * fSat + 0.5;
672 fG = (fG - 0.5) * fSat + 0.5;
673 fB = (fB - 0.5) * fSat + 0.5;
675 // apply luminance
676 double fLum = 2.0 * static_cast< double >( mnC3 ) / MAX_PERCENT - 1.0; // interval [-1.0, 1.0]
677 if( fLum < 0.0 )
679 double fShade = fLum + 1.0; // interval [0.0, 1.0] (black...full color)
680 fR *= fShade;
681 fG *= fShade;
682 fB *= fShade;
684 else if( fLum > 0.0 )
686 double fTint = 1.0 - fLum; // interval [0.0, 1.0] (white...full color)
687 fR = 1.0 - ((1.0 - fR) * fTint);
688 fG = 1.0 - ((1.0 - fG) * fTint);
689 fB = 1.0 - ((1.0 - fB) * fTint);
692 mnC1 = static_cast< sal_Int32 >( fR * 255.0 + 0.5 );
693 mnC2 = static_cast< sal_Int32 >( fG * 255.0 + 0.5 );
694 mnC3 = static_cast< sal_Int32 >( fB * 255.0 + 0.5 );
696 break;
697 default:
698 OSL_FAIL( "Color::toRgb - unexpected color mode" );
702 void Color::toCrgb() const
704 switch( meMode )
706 case COLOR_HSL:
707 toRgb();
708 [[fallthrough]];
709 case COLOR_RGB:
710 meMode = COLOR_CRGB;
711 mnC1 = lclGamma( lclRgbCompToCrgbComp( mnC1 ), DEC_GAMMA );
712 mnC2 = lclGamma( lclRgbCompToCrgbComp( mnC2 ), DEC_GAMMA );
713 mnC3 = lclGamma( lclRgbCompToCrgbComp( mnC3 ), DEC_GAMMA );
714 break;
715 case COLOR_CRGB:
716 // nothing to do
717 break;
718 default:
719 OSL_FAIL( "Color::toCrgb - unexpected color mode" );
723 void Color::toHsl() const
725 switch( meMode )
727 case COLOR_CRGB:
728 toRgb();
729 [[fallthrough]];
730 case COLOR_RGB:
732 meMode = COLOR_HSL;
733 double fR = static_cast< double >( mnC1 ) / 255.0; // red [0.0, 1.0]
734 double fG = static_cast< double >( mnC2 ) / 255.0; // green [0.0, 1.0]
735 double fB = static_cast< double >( mnC3 ) / 255.0; // blue [0.0, 1.0]
736 double fMin = ::std::min( ::std::min( fR, fG ), fB );
737 double fMax = ::std::max( ::std::max( fR, fG ), fB );
738 double fD = fMax - fMin;
740 using ::rtl::math::approxEqual;
742 // hue: 0deg = red, 120deg = green, 240deg = blue
743 if( fD == 0.0 ) // black/gray/white
744 mnC1 = 0;
745 else if( approxEqual(fMax, fR, 64) ) // magenta...red...yellow
746 mnC1 = static_cast< sal_Int32 >( ((fG - fB) / fD * 60.0 + 360.0) * PER_DEGREE + 0.5 ) % MAX_DEGREE;
747 else if( approxEqual(fMax, fG, 64) ) // yellow...green...cyan
748 mnC1 = static_cast< sal_Int32 >( ((fB - fR) / fD * 60.0 + 120.0) * PER_DEGREE + 0.5 );
749 else // cyan...blue...magenta
750 mnC1 = static_cast< sal_Int32 >( ((fR - fG) / fD * 60.0 + 240.0) * PER_DEGREE + 0.5 );
752 // luminance: 0% = black, 50% = full color, 100% = white
753 mnC3 = static_cast< sal_Int32 >( (fMin + fMax) / 2.0 * MAX_PERCENT + 0.5 );
755 // saturation: 0% = gray, 100% = full color
756 if( (mnC3 == 0) || (mnC3 == MAX_PERCENT) ) // black/white
757 mnC2 = 0;
758 else if( mnC3 <= 50 * PER_PERCENT ) // dark...full color
759 mnC2 = static_cast< sal_Int32 >( fD / (fMin + fMax) * MAX_PERCENT + 0.5 );
760 else // full color...light
761 mnC2 = static_cast< sal_Int32 >( fD / (2.0 - fMax - fMin) * MAX_PERCENT + 0.5 );
763 break;
764 case COLOR_HSL:
765 // nothing to do
766 break;
767 default:
768 OSL_FAIL( "Color::toHsl - unexpected color mode" );
772 } // namespace drawingml
773 } // namespace oox
775 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */