Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : : /*
3 : : * This file is part of the LibreOffice project.
4 : : *
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/.
8 : : *
9 : : * This file incorporates work covered by the following license notice:
10 : : *
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 .
18 : : */
19 : :
20 : :
21 : : #include <hslcolor.hxx>
22 : : #include <rgbcolor.hxx>
23 : :
24 : : #include <basegfx/numeric/ftools.hxx>
25 : :
26 : : #include <cmath> // for fmod
27 : : #include <algorithm>
28 : :
29 : :
30 : : namespace slideshow
31 : : {
32 : : namespace internal
33 : : {
34 : : namespace
35 : : {
36 : : // helper functions
37 : : // ================
38 : :
39 : 0 : double getMagic( double nLuminance, double nSaturation )
40 : : {
41 : 0 : if( nLuminance <= 0.5 )
42 : 0 : return nLuminance*(1.0 + nSaturation);
43 : : else
44 : 0 : return nLuminance + nSaturation - nLuminance*nSaturation;
45 : : }
46 : :
47 : 0 : HSLColor::HSLTriple rgb2hsl( double nRed, double nGreen, double nBlue )
48 : : {
49 : : // r,g,b in [0,1], h in [0,360] and s,l in [0,1]
50 : 0 : HSLColor::HSLTriple aRes;
51 : :
52 : 0 : const double nMax( ::std::max(nRed,::std::max(nGreen, nBlue)) );
53 : 0 : const double nMin( ::std::min(nRed,::std::min(nGreen, nBlue)) );
54 : :
55 : 0 : const double nDelta( nMax - nMin );
56 : :
57 : 0 : aRes.mnLuminance = (nMax + nMin) / 2.0;
58 : :
59 : 0 : if( ::basegfx::fTools::equalZero( nDelta ) )
60 : : {
61 : 0 : aRes.mnSaturation = 0.0;
62 : :
63 : : // hue undefined (achromatic case)
64 : 0 : aRes.mnHue = 0.0;
65 : : }
66 : : else
67 : : {
68 : : aRes.mnSaturation = aRes.mnLuminance > 0.5 ?
69 : : nDelta/(2.0-nMax-nMin) :
70 : 0 : nDelta/(nMax + nMin);
71 : :
72 : 0 : if( nRed == nMax )
73 : 0 : aRes.mnHue = (nGreen - nBlue)/nDelta;
74 : 0 : else if( nGreen == nMax )
75 : 0 : aRes.mnHue = 2.0 + (nBlue - nRed)/nDelta;
76 : 0 : else if( nBlue == nMax )
77 : 0 : aRes.mnHue = 4.0 + (nRed - nGreen)/nDelta;
78 : :
79 : 0 : aRes.mnHue *= 60.0;
80 : :
81 : 0 : if( aRes.mnHue < 0.0 )
82 : 0 : aRes.mnHue += 360.0;
83 : : }
84 : :
85 : 0 : return aRes;
86 : : }
87 : :
88 : 0 : double hsl2rgbHelper( double nValue1, double nValue2, double nHue )
89 : : {
90 : : // clamp hue to [0,360]
91 : 0 : nHue = fmod( nHue, 360.0 );
92 : :
93 : : // cope with wrap-arounds
94 : 0 : if( nHue < 0.0 )
95 : 0 : nHue += 360.0;
96 : :
97 : 0 : if( nHue < 60.0 )
98 : 0 : return nValue1 + (nValue2 - nValue1)*nHue/60.0;
99 : 0 : else if( nHue < 180.0 )
100 : 0 : return nValue2;
101 : 0 : else if( nHue < 240.0 )
102 : 0 : return nValue1 + (nValue2 - nValue1)*(240.0 - nHue)/60.0;
103 : : else
104 : 0 : return nValue1;
105 : : }
106 : :
107 : 0 : RGBColor::RGBTriple hsl2rgb( double nHue, double nSaturation, double nLuminance )
108 : : {
109 : 0 : if( ::basegfx::fTools::equalZero( nSaturation ) )
110 : 0 : return RGBColor::RGBTriple(0.0, 0.0, nLuminance );
111 : :
112 : 0 : const double nVal1( getMagic(nLuminance, nSaturation) );
113 : 0 : const double nVal2( 2.0*nLuminance - nVal1 );
114 : :
115 : 0 : RGBColor::RGBTriple aRes;
116 : :
117 : : aRes.mnRed = hsl2rgbHelper( nVal2,
118 : : nVal1,
119 : 0 : nHue + 120.0 );
120 : : aRes.mnGreen = hsl2rgbHelper( nVal2,
121 : : nVal1,
122 : 0 : nHue );
123 : : aRes.mnBlue = hsl2rgbHelper( nVal2,
124 : : nVal1,
125 : 0 : nHue - 120.0 );
126 : :
127 : 0 : return aRes;
128 : : }
129 : :
130 : : /// Truncate range of value to [0,1]
131 : 0 : double truncateRangeStd( double nVal )
132 : : {
133 : : return ::std::max( 0.0,
134 : : ::std::min( 1.0,
135 : 0 : nVal ) );
136 : : }
137 : :
138 : : /// Truncate range of value to [0,360]
139 : 0 : double truncateRangeHue( double nVal )
140 : : {
141 : : return ::std::max( 0.0,
142 : : ::std::min( 360.0,
143 : 0 : nVal ) );
144 : : }
145 : :
146 : : /// convert RGB color to sal_uInt8, truncate range appropriately before
147 : 0 : sal_uInt8 colorToInt( double nCol )
148 : : {
149 : : return static_cast< sal_uInt8 >(
150 : 0 : ::basegfx::fround( truncateRangeStd( nCol ) * 255.0 ) );
151 : : }
152 : : }
153 : :
154 : :
155 : :
156 : : // HSLColor
157 : : // ===============================================
158 : :
159 : 0 : HSLColor::HSLTriple::HSLTriple() :
160 : : mnHue(),
161 : : mnSaturation(),
162 : 0 : mnLuminance()
163 : : {
164 : 0 : }
165 : :
166 : 0 : HSLColor::HSLTriple::HSLTriple( double nHue, double nSaturation, double nLuminance ) :
167 : : mnHue( nHue ),
168 : : mnSaturation( nSaturation ),
169 : 0 : mnLuminance( nLuminance )
170 : : {
171 : 0 : }
172 : :
173 : 0 : HSLColor::HSLColor() :
174 : : maHSLTriple( 0.0, 0.0, 0.0 ),
175 : : mnMagicValue( getMagic( maHSLTriple.mnLuminance,
176 : 0 : maHSLTriple.mnSaturation ) )
177 : : {
178 : 0 : }
179 : :
180 : 0 : HSLColor::HSLColor( double nHue, double nSaturation, double nLuminance ) :
181 : : maHSLTriple( nHue, nSaturation, nLuminance ),
182 : : mnMagicValue( getMagic( maHSLTriple.mnLuminance,
183 : 0 : maHSLTriple.mnSaturation ) )
184 : : {
185 : 0 : }
186 : :
187 : 0 : HSLColor::HSLColor( const RGBColor& rColor ) :
188 : : maHSLTriple( rgb2hsl( truncateRangeStd( rColor.getRed() ),
189 : : truncateRangeStd( rColor.getGreen() ),
190 : 0 : truncateRangeStd( rColor.getBlue() ) ) ),
191 : : mnMagicValue( getMagic( maHSLTriple.mnLuminance,
192 : 0 : maHSLTriple.mnSaturation ) )
193 : : {
194 : 0 : }
195 : :
196 : 0 : double HSLColor::getHue() const
197 : : {
198 : 0 : return maHSLTriple.mnHue;
199 : : }
200 : :
201 : 0 : double HSLColor::getSaturation() const
202 : : {
203 : 0 : return maHSLTriple.mnSaturation;
204 : : }
205 : :
206 : 0 : double HSLColor::getLuminance() const
207 : : {
208 : 0 : return maHSLTriple.mnLuminance;
209 : : }
210 : :
211 : :
212 : 0 : sal_Bool operator==( const HSLColor& rLHS, const HSLColor& rRHS )
213 : : {
214 : 0 : return ( rLHS.getHue() == rRHS.getHue() &&
215 : 0 : rLHS.getSaturation() == rRHS.getSaturation() &&
216 : 0 : rLHS.getLuminance() == rRHS.getLuminance() );
217 : : }
218 : :
219 : 0 : sal_Bool operator!=( const HSLColor& rLHS, const HSLColor& rRHS )
220 : : {
221 : 0 : return !( rLHS == rRHS );
222 : : }
223 : :
224 : 0 : HSLColor operator+( const HSLColor& rLHS, const HSLColor& rRHS )
225 : : {
226 : 0 : return HSLColor( rLHS.getHue() + rRHS.getHue(),
227 : 0 : rLHS.getSaturation() + rRHS.getSaturation(),
228 : 0 : rLHS.getLuminance() + rRHS.getLuminance() );
229 : : }
230 : :
231 : 0 : HSLColor operator*( const HSLColor& rLHS, const HSLColor& rRHS )
232 : : {
233 : 0 : return HSLColor( rLHS.getHue() * rRHS.getHue(),
234 : 0 : rLHS.getSaturation() * rRHS.getSaturation(),
235 : 0 : rLHS.getLuminance() * rRHS.getLuminance() );
236 : : }
237 : :
238 : 0 : HSLColor operator*( double nFactor, const HSLColor& rRHS )
239 : : {
240 : 0 : return HSLColor( nFactor * rRHS.getHue(),
241 : 0 : nFactor * rRHS.getSaturation(),
242 : 0 : nFactor * rRHS.getLuminance() );
243 : : }
244 : :
245 : 0 : HSLColor interpolate( const HSLColor& rFrom, const HSLColor& rTo, double t, bool bCCW )
246 : : {
247 : 0 : const double nFromHue( rFrom.getHue() );
248 : 0 : const double nToHue ( rTo.getHue() );
249 : :
250 : 0 : double nHue=0.0;
251 : :
252 : 0 : if( nFromHue <= nToHue && !bCCW )
253 : : {
254 : : // interpolate hue clockwise. That is, hue starts at
255 : : // high values and ends at low ones. Therefore, we
256 : : // must 'cross' the 360 degrees and start at low
257 : : // values again (imagine the hues to lie on the
258 : : // circle, where values above 360 degrees are mapped
259 : : // back to [0,360)).
260 : 0 : nHue = (1.0-t)*(nFromHue + 360.0) + t*nToHue;
261 : : }
262 : 0 : else if( nFromHue > nToHue && bCCW )
263 : : {
264 : : // interpolate hue counter-clockwise. That is, hue
265 : : // starts at high values and ends at low
266 : : // ones. Therefore, we must 'cross' the 360 degrees
267 : : // and start at low values again (imagine the hues to
268 : : // lie on the circle, where values above 360 degrees
269 : : // are mapped back to [0,360)).
270 : 0 : nHue = (1.0-t)*nFromHue + t*(nToHue + 360.0);
271 : : }
272 : : else
273 : : {
274 : : // interpolate hue counter-clockwise. That is, hue
275 : : // starts at low values and ends at high ones (imagine
276 : : // the hue value as degrees on a circle, with
277 : : // increasing values going counter-clockwise)
278 : 0 : nHue = (1.0-t)*nFromHue + t*nToHue;
279 : : }
280 : :
281 : : return HSLColor( nHue,
282 : 0 : (1.0-t)*rFrom.getSaturation() + t*rTo.getSaturation(),
283 : 0 : (1.0-t)*rFrom.getLuminance() + t*rTo.getLuminance() );
284 : : }
285 : :
286 : :
287 : :
288 : : // RGBColor
289 : : // ===============================================
290 : :
291 : :
292 : 0 : RGBColor::RGBTriple::RGBTriple() :
293 : : mnRed(),
294 : : mnGreen(),
295 : 0 : mnBlue()
296 : : {
297 : 0 : }
298 : :
299 : 0 : RGBColor::RGBTriple::RGBTriple( double nRed, double nGreen, double nBlue ) :
300 : : mnRed( nRed ),
301 : : mnGreen( nGreen ),
302 : 0 : mnBlue( nBlue )
303 : : {
304 : 0 : }
305 : :
306 : 0 : RGBColor::RGBColor() :
307 : 0 : maRGBTriple( 0.0, 0.0, 0.0 )
308 : : {
309 : 0 : }
310 : :
311 : 0 : RGBColor::RGBColor( ::cppcanvas::Color::IntSRGBA nRGBColor ) :
312 : 0 : maRGBTriple( ::cppcanvas::getRed( nRGBColor ) / 255.0,
313 : 0 : ::cppcanvas::getGreen( nRGBColor ) / 255.0,
314 : 0 : ::cppcanvas::getBlue( nRGBColor ) / 255.0 )
315 : : {
316 : 0 : }
317 : :
318 : 0 : RGBColor::RGBColor( double nRed, double nGreen, double nBlue ) :
319 : 0 : maRGBTriple( nRed, nGreen, nBlue )
320 : : {
321 : 0 : }
322 : :
323 : 0 : RGBColor::RGBColor( const HSLColor& rColor ) :
324 : : maRGBTriple( hsl2rgb( truncateRangeHue( rColor.getHue() ),
325 : : truncateRangeStd( rColor.getSaturation() ),
326 : 0 : truncateRangeStd( rColor.getLuminance() ) ) )
327 : : {
328 : 0 : }
329 : :
330 : 0 : double RGBColor::getRed() const
331 : : {
332 : 0 : return maRGBTriple.mnRed;
333 : : }
334 : :
335 : 0 : double RGBColor::getGreen() const
336 : : {
337 : 0 : return maRGBTriple.mnGreen;
338 : : }
339 : :
340 : 0 : double RGBColor::getBlue() const
341 : : {
342 : 0 : return maRGBTriple.mnBlue;
343 : : }
344 : :
345 : 0 : ::cppcanvas::Color::IntSRGBA RGBColor::getIntegerColor() const
346 : : {
347 : 0 : return ::cppcanvas::makeColor( colorToInt( getRed() ),
348 : 0 : colorToInt( getGreen() ),
349 : 0 : colorToInt( getBlue() ),
350 : 0 : 255 );
351 : : }
352 : :
353 : 0 : sal_Bool operator==( const RGBColor& rLHS, const RGBColor& rRHS )
354 : : {
355 : 0 : return ( rLHS.getRed() == rRHS.getRed() &&
356 : 0 : rLHS.getGreen() == rRHS.getGreen() &&
357 : 0 : rLHS.getBlue() == rRHS.getBlue() );
358 : : }
359 : :
360 : 0 : sal_Bool operator!=( const RGBColor& rLHS, const RGBColor& rRHS )
361 : : {
362 : 0 : return !( rLHS == rRHS );
363 : : }
364 : :
365 : 0 : RGBColor operator+( const RGBColor& rLHS, const RGBColor& rRHS )
366 : : {
367 : 0 : return RGBColor( rLHS.getRed() + rRHS.getRed(),
368 : 0 : rLHS.getGreen() + rRHS.getGreen(),
369 : 0 : rLHS.getBlue() + rRHS.getBlue() );
370 : : }
371 : :
372 : 0 : RGBColor operator*( const RGBColor& rLHS, const RGBColor& rRHS )
373 : : {
374 : 0 : return RGBColor( rLHS.getRed() * rRHS.getRed(),
375 : 0 : rLHS.getGreen() * rRHS.getGreen(),
376 : 0 : rLHS.getBlue() * rRHS.getBlue() );
377 : : }
378 : :
379 : 0 : RGBColor operator*( double nFactor, const RGBColor& rRHS )
380 : : {
381 : 0 : return RGBColor( nFactor * rRHS.getRed(),
382 : 0 : nFactor * rRHS.getGreen(),
383 : 0 : nFactor * rRHS.getBlue() );
384 : : }
385 : :
386 : 0 : RGBColor interpolate( const RGBColor& rFrom, const RGBColor& rTo, double t )
387 : : {
388 : 0 : return RGBColor( (1.0-t)*rFrom.getRed() + t*rTo.getRed(),
389 : 0 : (1.0-t)*rFrom.getGreen() + t*rTo.getGreen(),
390 : 0 : (1.0-t)*rFrom.getBlue() + t*rTo.getBlue() );
391 : : }
392 : : }
393 : : }
394 : :
395 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|