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>
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 0 : aRes.mnSaturation = aRes.mnLuminance > 0.5 ?
69 0 : 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 :
197 :
198 :
199 :
200 0 : bool operator==( const HSLColor& rLHS, const HSLColor& rRHS )
201 : {
202 0 : return ( rLHS.getHue() == rRHS.getHue() &&
203 0 : rLHS.getSaturation() == rRHS.getSaturation() &&
204 0 : rLHS.getLuminance() == rRHS.getLuminance() );
205 : }
206 :
207 0 : bool operator!=( const HSLColor& rLHS, const HSLColor& rRHS )
208 : {
209 0 : return !( rLHS == rRHS );
210 : }
211 :
212 0 : HSLColor operator+( const HSLColor& rLHS, const HSLColor& rRHS )
213 : {
214 0 : return HSLColor( rLHS.getHue() + rRHS.getHue(),
215 0 : rLHS.getSaturation() + rRHS.getSaturation(),
216 0 : rLHS.getLuminance() + rRHS.getLuminance() );
217 : }
218 :
219 0 : HSLColor operator*( const HSLColor& rLHS, const HSLColor& rRHS )
220 : {
221 0 : return HSLColor( rLHS.getHue() * rRHS.getHue(),
222 0 : rLHS.getSaturation() * rRHS.getSaturation(),
223 0 : rLHS.getLuminance() * rRHS.getLuminance() );
224 : }
225 :
226 0 : HSLColor operator*( double nFactor, const HSLColor& rRHS )
227 : {
228 0 : return HSLColor( nFactor * rRHS.getHue(),
229 0 : nFactor * rRHS.getSaturation(),
230 0 : nFactor * rRHS.getLuminance() );
231 : }
232 :
233 0 : HSLColor interpolate( const HSLColor& rFrom, const HSLColor& rTo, double t, bool bCCW )
234 : {
235 0 : const double nFromHue( rFrom.getHue() );
236 0 : const double nToHue ( rTo.getHue() );
237 :
238 0 : double nHue=0.0;
239 :
240 0 : if( nFromHue <= nToHue && !bCCW )
241 : {
242 : // interpolate hue clockwise. That is, hue starts at
243 : // high values and ends at low ones. Therefore, we
244 : // must 'cross' the 360 degrees and start at low
245 : // values again (imagine the hues to lie on the
246 : // circle, where values above 360 degrees are mapped
247 : // back to [0,360)).
248 0 : nHue = (1.0-t)*(nFromHue + 360.0) + t*nToHue;
249 : }
250 0 : else if( nFromHue > nToHue && bCCW )
251 : {
252 : // interpolate hue counter-clockwise. That is, hue
253 : // starts at high values and ends at low
254 : // ones. Therefore, we must 'cross' the 360 degrees
255 : // and start at low values again (imagine the hues to
256 : // lie on the circle, where values above 360 degrees
257 : // are mapped back to [0,360)).
258 0 : nHue = (1.0-t)*nFromHue + t*(nToHue + 360.0);
259 : }
260 : else
261 : {
262 : // interpolate hue counter-clockwise. That is, hue
263 : // starts at low values and ends at high ones (imagine
264 : // the hue value as degrees on a circle, with
265 : // increasing values going counter-clockwise)
266 0 : nHue = (1.0-t)*nFromHue + t*nToHue;
267 : }
268 :
269 : return HSLColor( nHue,
270 0 : (1.0-t)*rFrom.getSaturation() + t*rTo.getSaturation(),
271 0 : (1.0-t)*rFrom.getLuminance() + t*rTo.getLuminance() );
272 : }
273 :
274 :
275 :
276 : // RGBColor
277 :
278 :
279 :
280 0 : RGBColor::RGBTriple::RGBTriple() :
281 : mnRed(),
282 : mnGreen(),
283 0 : mnBlue()
284 : {
285 0 : }
286 :
287 0 : RGBColor::RGBTriple::RGBTriple( double nRed, double nGreen, double nBlue ) :
288 : mnRed( nRed ),
289 : mnGreen( nGreen ),
290 0 : mnBlue( nBlue )
291 : {
292 0 : }
293 :
294 0 : RGBColor::RGBColor() :
295 0 : maRGBTriple( 0.0, 0.0, 0.0 )
296 : {
297 0 : }
298 :
299 0 : RGBColor::RGBColor( ::cppcanvas::Color::IntSRGBA nRGBColor ) :
300 0 : maRGBTriple( ::cppcanvas::getRed( nRGBColor ) / 255.0,
301 0 : ::cppcanvas::getGreen( nRGBColor ) / 255.0,
302 0 : ::cppcanvas::getBlue( nRGBColor ) / 255.0 )
303 : {
304 0 : }
305 :
306 0 : RGBColor::RGBColor( double nRed, double nGreen, double nBlue ) :
307 0 : maRGBTriple( nRed, nGreen, nBlue )
308 : {
309 0 : }
310 :
311 0 : RGBColor::RGBColor( const HSLColor& rColor ) :
312 : maRGBTriple( hsl2rgb( truncateRangeHue( rColor.getHue() ),
313 : truncateRangeStd( rColor.getSaturation() ),
314 0 : truncateRangeStd( rColor.getLuminance() ) ) )
315 : {
316 0 : }
317 :
318 :
319 :
320 :
321 0 : ::cppcanvas::Color::IntSRGBA RGBColor::getIntegerColor() const
322 : {
323 0 : return ::cppcanvas::makeColor( colorToInt( getRed() ),
324 0 : colorToInt( getGreen() ),
325 0 : colorToInt( getBlue() ),
326 0 : 255 );
327 : }
328 :
329 0 : bool operator==( const RGBColor& rLHS, const RGBColor& rRHS )
330 : {
331 0 : return ( rLHS.getRed() == rRHS.getRed() &&
332 0 : rLHS.getGreen() == rRHS.getGreen() &&
333 0 : rLHS.getBlue() == rRHS.getBlue() );
334 : }
335 :
336 0 : bool operator!=( const RGBColor& rLHS, const RGBColor& rRHS )
337 : {
338 0 : return !( rLHS == rRHS );
339 : }
340 :
341 0 : RGBColor operator+( const RGBColor& rLHS, const RGBColor& rRHS )
342 : {
343 0 : return RGBColor( rLHS.getRed() + rRHS.getRed(),
344 0 : rLHS.getGreen() + rRHS.getGreen(),
345 0 : rLHS.getBlue() + rRHS.getBlue() );
346 : }
347 :
348 0 : RGBColor operator*( const RGBColor& rLHS, const RGBColor& rRHS )
349 : {
350 0 : return RGBColor( rLHS.getRed() * rRHS.getRed(),
351 0 : rLHS.getGreen() * rRHS.getGreen(),
352 0 : rLHS.getBlue() * rRHS.getBlue() );
353 : }
354 :
355 0 : RGBColor operator*( double nFactor, const RGBColor& rRHS )
356 : {
357 0 : return RGBColor( nFactor * rRHS.getRed(),
358 0 : nFactor * rRHS.getGreen(),
359 0 : nFactor * rRHS.getBlue() );
360 : }
361 :
362 0 : RGBColor interpolate( const RGBColor& rFrom, const RGBColor& rTo, double t )
363 : {
364 0 : return RGBColor( (1.0-t)*rFrom.getRed() + t*rTo.getRed(),
365 0 : (1.0-t)*rFrom.getGreen() + t*rTo.getGreen(),
366 0 : (1.0-t)*rFrom.getBlue() + t*rTo.getBlue() );
367 : }
368 : }
369 : }
370 :
371 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|