which actually does nothing useful. Looks like it was originally intended to be used to implement color profiles, but since nothing has happened on that front since it was created, safe to say it never will. Probably not the right place in the graphics stack to do it anyhow. Change-Id: I36990db4036e3b4b2b75261fc430028562a6dbd9 Reviewed-on: https://gerrit.libreoffice.org/43240 Tested-by: Jenkins <ci@libreoffice.org> Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
351 lines
12 KiB
C++
351 lines
12 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
|
|
#include <hslcolor.hxx>
|
|
#include <rgbcolor.hxx>
|
|
|
|
#include <basegfx/numeric/ftools.hxx>
|
|
|
|
#include <cmath>
|
|
#include <algorithm>
|
|
|
|
|
|
namespace slideshow
|
|
{
|
|
namespace internal
|
|
{
|
|
namespace
|
|
{
|
|
// helper functions
|
|
// ================
|
|
|
|
double getMagic( double nLuminance, double nSaturation )
|
|
{
|
|
if( nLuminance <= 0.5 )
|
|
return nLuminance*(1.0 + nSaturation);
|
|
else
|
|
return nLuminance + nSaturation - nLuminance*nSaturation;
|
|
}
|
|
|
|
HSLColor::HSLTriple rgb2hsl( double nRed, double nGreen, double nBlue )
|
|
{
|
|
// r,g,b in [0,1], h in [0,360] and s,l in [0,1]
|
|
HSLColor::HSLTriple aRes;
|
|
|
|
const double nMax( ::std::max(nRed,::std::max(nGreen, nBlue)) );
|
|
const double nMin( ::std::min(nRed,::std::min(nGreen, nBlue)) );
|
|
|
|
const double nDelta( nMax - nMin );
|
|
|
|
aRes.mnLuminance = (nMax + nMin) / 2.0;
|
|
|
|
if( ::basegfx::fTools::equalZero( nDelta ) )
|
|
{
|
|
aRes.mnSaturation = 0.0;
|
|
|
|
// hue undefined (achromatic case)
|
|
aRes.mnHue = 0.0;
|
|
}
|
|
else
|
|
{
|
|
aRes.mnSaturation = aRes.mnLuminance > 0.5 ?
|
|
nDelta/(2.0-nMax-nMin) :
|
|
nDelta/(nMax + nMin);
|
|
|
|
if( rtl::math::approxEqual(nRed, nMax) )
|
|
aRes.mnHue = (nGreen - nBlue)/nDelta;
|
|
else if( rtl::math::approxEqual(nGreen, nMax) )
|
|
aRes.mnHue = 2.0 + (nBlue - nRed)/nDelta;
|
|
else if( rtl::math::approxEqual(nBlue, nMax) )
|
|
aRes.mnHue = 4.0 + (nRed - nGreen)/nDelta;
|
|
|
|
aRes.mnHue *= 60.0;
|
|
|
|
if( aRes.mnHue < 0.0 )
|
|
aRes.mnHue += 360.0;
|
|
}
|
|
|
|
return aRes;
|
|
}
|
|
|
|
double hsl2rgbHelper( double nValue1, double nValue2, double nHue )
|
|
{
|
|
// clamp hue to [0,360]
|
|
nHue = fmod( nHue, 360.0 );
|
|
|
|
// cope with wrap-arounds
|
|
if( nHue < 0.0 )
|
|
nHue += 360.0;
|
|
|
|
if( nHue < 60.0 )
|
|
return nValue1 + (nValue2 - nValue1)*nHue/60.0;
|
|
else if( nHue < 180.0 )
|
|
return nValue2;
|
|
else if( nHue < 240.0 )
|
|
return nValue1 + (nValue2 - nValue1)*(240.0 - nHue)/60.0;
|
|
else
|
|
return nValue1;
|
|
}
|
|
|
|
RGBColor::RGBTriple hsl2rgb( double nHue, double nSaturation, double nLuminance )
|
|
{
|
|
if( ::basegfx::fTools::equalZero( nSaturation ) )
|
|
return RGBColor::RGBTriple(0.0, 0.0, nLuminance );
|
|
|
|
const double nVal1( getMagic(nLuminance, nSaturation) );
|
|
const double nVal2( 2.0*nLuminance - nVal1 );
|
|
|
|
RGBColor::RGBTriple aRes;
|
|
|
|
aRes.mnRed = hsl2rgbHelper( nVal2,
|
|
nVal1,
|
|
nHue + 120.0 );
|
|
aRes.mnGreen = hsl2rgbHelper( nVal2,
|
|
nVal1,
|
|
nHue );
|
|
aRes.mnBlue = hsl2rgbHelper( nVal2,
|
|
nVal1,
|
|
nHue - 120.0 );
|
|
|
|
return aRes;
|
|
}
|
|
|
|
/// Truncate range of value to [0,1]
|
|
double truncateRangeStd( double nVal )
|
|
{
|
|
return ::std::max( 0.0,
|
|
::std::min( 1.0,
|
|
nVal ) );
|
|
}
|
|
|
|
/// Truncate range of value to [0,360]
|
|
double truncateRangeHue( double nVal )
|
|
{
|
|
return ::std::max( 0.0,
|
|
::std::min( 360.0,
|
|
nVal ) );
|
|
}
|
|
|
|
/// convert RGB color to sal_uInt8, truncate range appropriately before
|
|
sal_uInt8 colorToInt( double nCol )
|
|
{
|
|
return static_cast< sal_uInt8 >(
|
|
::basegfx::fround( truncateRangeStd( nCol ) * 255.0 ) );
|
|
}
|
|
}
|
|
|
|
|
|
// HSLColor
|
|
|
|
|
|
HSLColor::HSLTriple::HSLTriple() :
|
|
mnHue(),
|
|
mnSaturation(),
|
|
mnLuminance()
|
|
{
|
|
}
|
|
|
|
HSLColor::HSLTriple::HSLTriple( double nHue, double nSaturation, double nLuminance ) :
|
|
mnHue( nHue ),
|
|
mnSaturation( nSaturation ),
|
|
mnLuminance( nLuminance )
|
|
{
|
|
}
|
|
|
|
HSLColor::HSLColor() :
|
|
maHSLTriple( 0.0, 0.0, 0.0 )
|
|
{
|
|
}
|
|
|
|
HSLColor::HSLColor( double nHue, double nSaturation, double nLuminance ) :
|
|
maHSLTriple( nHue, nSaturation, nLuminance )
|
|
{
|
|
}
|
|
|
|
HSLColor::HSLColor( const RGBColor& rColor ) :
|
|
maHSLTriple( rgb2hsl( truncateRangeStd( rColor.getRed() ),
|
|
truncateRangeStd( rColor.getGreen() ),
|
|
truncateRangeStd( rColor.getBlue() ) ) )
|
|
{
|
|
}
|
|
|
|
|
|
bool operator==( const HSLColor& rLHS, const HSLColor& rRHS )
|
|
{
|
|
return ( rLHS.getHue() == rRHS.getHue() &&
|
|
rLHS.getSaturation() == rRHS.getSaturation() &&
|
|
rLHS.getLuminance() == rRHS.getLuminance() );
|
|
}
|
|
|
|
bool operator!=( const HSLColor& rLHS, const HSLColor& rRHS )
|
|
{
|
|
return !( rLHS == rRHS );
|
|
}
|
|
|
|
HSLColor operator+( const HSLColor& rLHS, const HSLColor& rRHS )
|
|
{
|
|
return HSLColor( rLHS.getHue() + rRHS.getHue(),
|
|
rLHS.getSaturation() + rRHS.getSaturation(),
|
|
rLHS.getLuminance() + rRHS.getLuminance() );
|
|
}
|
|
|
|
HSLColor operator*( double nFactor, const HSLColor& rRHS )
|
|
{
|
|
return HSLColor( nFactor * rRHS.getHue(),
|
|
nFactor * rRHS.getSaturation(),
|
|
nFactor * rRHS.getLuminance() );
|
|
}
|
|
|
|
HSLColor interpolate( const HSLColor& rFrom, const HSLColor& rTo, double t, bool bCCW )
|
|
{
|
|
const double nFromHue( rFrom.getHue() );
|
|
const double nToHue ( rTo.getHue() );
|
|
|
|
double nHue=0.0;
|
|
|
|
if( nFromHue <= nToHue && !bCCW )
|
|
{
|
|
// interpolate hue clockwise. That is, hue starts at
|
|
// high values and ends at low ones. Therefore, we
|
|
// must 'cross' the 360 degrees and start at low
|
|
// values again (imagine the hues to lie on the
|
|
// circle, where values above 360 degrees are mapped
|
|
// back to [0,360)).
|
|
nHue = (1.0-t)*(nFromHue + 360.0) + t*nToHue;
|
|
}
|
|
else if( nFromHue > nToHue && bCCW )
|
|
{
|
|
// interpolate hue counter-clockwise. That is, hue
|
|
// starts at high values and ends at low
|
|
// ones. Therefore, we must 'cross' the 360 degrees
|
|
// and start at low values again (imagine the hues to
|
|
// lie on the circle, where values above 360 degrees
|
|
// are mapped back to [0,360)).
|
|
nHue = (1.0-t)*nFromHue + t*(nToHue + 360.0);
|
|
}
|
|
else
|
|
{
|
|
// interpolate hue counter-clockwise. That is, hue
|
|
// starts at low values and ends at high ones (imagine
|
|
// the hue value as degrees on a circle, with
|
|
// increasing values going counter-clockwise)
|
|
nHue = (1.0-t)*nFromHue + t*nToHue;
|
|
}
|
|
|
|
return HSLColor( nHue,
|
|
(1.0-t)*rFrom.getSaturation() + t*rTo.getSaturation(),
|
|
(1.0-t)*rFrom.getLuminance() + t*rTo.getLuminance() );
|
|
}
|
|
|
|
|
|
// RGBColor
|
|
|
|
|
|
RGBColor::RGBTriple::RGBTriple() :
|
|
mnRed(),
|
|
mnGreen(),
|
|
mnBlue()
|
|
{
|
|
}
|
|
|
|
RGBColor::RGBTriple::RGBTriple( double nRed, double nGreen, double nBlue ) :
|
|
mnRed( nRed ),
|
|
mnGreen( nGreen ),
|
|
mnBlue( nBlue )
|
|
{
|
|
}
|
|
|
|
RGBColor::RGBColor() :
|
|
maRGBTriple( 0.0, 0.0, 0.0 )
|
|
{
|
|
}
|
|
|
|
RGBColor::RGBColor( ::cppcanvas::IntSRGBA nRGBColor ) :
|
|
maRGBTriple( ::cppcanvas::getRed( nRGBColor ) / 255.0,
|
|
::cppcanvas::getGreen( nRGBColor ) / 255.0,
|
|
::cppcanvas::getBlue( nRGBColor ) / 255.0 )
|
|
{
|
|
}
|
|
|
|
RGBColor::RGBColor( double nRed, double nGreen, double nBlue ) :
|
|
maRGBTriple( nRed, nGreen, nBlue )
|
|
{
|
|
}
|
|
|
|
RGBColor::RGBColor( const HSLColor& rColor ) :
|
|
maRGBTriple( hsl2rgb( truncateRangeHue( rColor.getHue() ),
|
|
truncateRangeStd( rColor.getSaturation() ),
|
|
truncateRangeStd( rColor.getLuminance() ) ) )
|
|
{
|
|
}
|
|
|
|
|
|
::cppcanvas::IntSRGBA RGBColor::getIntegerColor() const
|
|
{
|
|
return ::cppcanvas::makeColor( colorToInt( getRed() ),
|
|
colorToInt( getGreen() ),
|
|
colorToInt( getBlue() ),
|
|
255 );
|
|
}
|
|
|
|
bool operator==( const RGBColor& rLHS, const RGBColor& rRHS )
|
|
{
|
|
return ( rLHS.getRed() == rRHS.getRed() &&
|
|
rLHS.getGreen() == rRHS.getGreen() &&
|
|
rLHS.getBlue() == rRHS.getBlue() );
|
|
}
|
|
|
|
bool operator!=( const RGBColor& rLHS, const RGBColor& rRHS )
|
|
{
|
|
return !( rLHS == rRHS );
|
|
}
|
|
|
|
RGBColor operator+( const RGBColor& rLHS, const RGBColor& rRHS )
|
|
{
|
|
return RGBColor( rLHS.getRed() + rRHS.getRed(),
|
|
rLHS.getGreen() + rRHS.getGreen(),
|
|
rLHS.getBlue() + rRHS.getBlue() );
|
|
}
|
|
|
|
RGBColor operator*( const RGBColor& rLHS, const RGBColor& rRHS )
|
|
{
|
|
return RGBColor( rLHS.getRed() * rRHS.getRed(),
|
|
rLHS.getGreen() * rRHS.getGreen(),
|
|
rLHS.getBlue() * rRHS.getBlue() );
|
|
}
|
|
|
|
RGBColor operator*( double nFactor, const RGBColor& rRHS )
|
|
{
|
|
return RGBColor( nFactor * rRHS.getRed(),
|
|
nFactor * rRHS.getGreen(),
|
|
nFactor * rRHS.getBlue() );
|
|
}
|
|
|
|
RGBColor interpolate( const RGBColor& rFrom, const RGBColor& rTo, double t )
|
|
{
|
|
return RGBColor( (1.0-t)*rFrom.getRed() + t*rTo.getRed(),
|
|
(1.0-t)*rFrom.getGreen() + t*rTo.getGreen(),
|
|
(1.0-t)*rFrom.getBlue() + t*rTo.getBlue() );
|
|
}
|
|
}
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|