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: */
|