handle strange brightness+contrast adjustment from msoffice (fdo#38410)

LO uses basically the formula "newpixel=(oldpixel-128)*contrast+128+brightness",
i.e. contrast is applied first. It looks like there's no "oficial" formula for this,
so a formula that applies brightness first would be ok too. MSO for some weird reason
apparently uses a formula that applies half of brightness before contrast and
half afterwards (insert funny political correctness or compromise joke here).
While the result is the same like with the LO formula if only either brightness
or contrast is adjusted, the result is different if both are involved. Just modify
the image using the MSO algorithm if this is the case.

Change-Id: I55fe8f395832685b90f024cf2f58b0797c1ba588
This commit is contained in:
Luboš Luňák
2014-04-18 20:46:34 +02:00
parent 8afabd3942
commit 1139d618b8
7 changed files with 60 additions and 22 deletions

View File

@@ -3818,7 +3818,12 @@ SdrObject* SvxMSDffManager::ImportGraphic( SvStream& rSt, SfxItemSet& rSet, cons
if ( nContrast || nBrightness || ( nGamma != 0x10000 ) || ( eDrawMode != GRAPHICDRAWMODE_STANDARD ) )
{
if ( ( rObjData.nSpFlags & SP_FOLESHAPE ) == 0 )
// MSO uses a different algorithm for contrast+brightness, LO applies contrast before brightness,
// while MSO apparently applies half of brightness before contrast and half after. So if only
// contrast or brightness need to be altered, the result is the same, but if both are involved,
// there's no way to map that, so just force a conversion of the image.
bool needsConversion = nContrast != 0 && nBrightness != 0;
if ( ( rObjData.nSpFlags & SP_FOLESHAPE ) == 0 && !needsConversion )
{
if ( nBrightness )
rSet.Put( SdrGrafLuminanceItem( nBrightness ) );
@@ -3843,7 +3848,7 @@ SdrObject* SvxMSDffManager::ImportGraphic( SvStream& rSt, SfxItemSet& rSet, cons
{
BitmapEx aBitmapEx( aGraf.GetBitmapEx() );
if ( nBrightness || nContrast || ( nGamma != 0x10000 ) )
aBitmapEx.Adjust( nBrightness, (sal_Int16)nContrast, 0, 0, 0, (double)nGamma / 0x10000, false );
aBitmapEx.Adjust( nBrightness, (sal_Int16)nContrast, 0, 0, 0, (double)nGamma / 0x10000, false, true );
if ( eDrawMode == GRAPHICDRAWMODE_GREYS )
aBitmapEx.Convert( BMP_CONVERSION_8BIT_GREYS );
else if ( eDrawMode == GRAPHICDRAWMODE_MONO )
@@ -3857,7 +3862,7 @@ SdrObject* SvxMSDffManager::ImportGraphic( SvStream& rSt, SfxItemSet& rSet, cons
{
GDIMetaFile aGdiMetaFile( aGraf.GetGDIMetaFile() );
if ( nBrightness || nContrast || ( nGamma != 0x10000 ) )
aGdiMetaFile.Adjust( nBrightness, (sal_Int16)nContrast, 0, 0, 0, (double)nGamma / 0x10000, false );
aGdiMetaFile.Adjust( nBrightness, (sal_Int16)nContrast, 0, 0, 0, (double)nGamma / 0x10000, false, true );
if ( eDrawMode == GRAPHICDRAWMODE_GREYS )
aGdiMetaFile.Convert( MTF_CONVERSION_8BIT_GREYS );
else if ( eDrawMode == GRAPHICDRAWMODE_MONO )

View File

@@ -778,6 +778,9 @@ public:
@param bInvert
If true, invert the channel values with the logical 'not' operator
@param msoBrightness
Use the same formula for brightness as used by MSOffice.
@return true, if the operation was completed successfully.
*/
bool Adjust( short nLuminancePercent = 0,
@@ -786,7 +789,8 @@ public:
short nChannelGPercent = 0,
short nChannelBPercent = 0,
double fGamma = 1.0,
bool bInvert = false );
bool bInvert = false,
bool msoBrightness = false );
/** Apply specified filter to the bitmap

View File

@@ -348,6 +348,9 @@ public:
@param bInvert
If true, invert the channel values with the logical 'not' operator
@param msoFormula
Use the same formula for brightness as used by MSOffice.
@return true, if the operation was completed successfully.
*/
bool Adjust( short nLuminancePercent = 0,
@@ -356,7 +359,8 @@ public:
short nChannelGPercent = 0,
short nChannelBPercent = 0,
double fGamma = 1.0,
bool bInvert = false );
bool bInvert = false,
bool msoBrightness = false );
/** Apply specified filter to the bitmap

View File

@@ -150,7 +150,7 @@ public:
void Adjust( short nLuminancePercent = 0, short nContrastPercent = 0,
short nChannelRPercent = 0, short nChannelGPercent = 0,
short nChannelBPercent = 0, double fGamma = 1.0,
bool bInvert = false
bool bInvert = false, bool msoBrightness = false
);
void Convert( MtfConversion eConversion );

View File

@@ -3215,7 +3215,7 @@ bool Bitmap::Vectorize( GDIMetaFile& rMtf, sal_uInt8 cReduce, sal_uLong nFlags,
bool Bitmap::Adjust( short nLuminancePercent, short nContrastPercent,
short nChannelRPercent, short nChannelGPercent, short nChannelBPercent,
double fGamma, bool bInvert )
double fGamma, bool bInvert, bool msoBrightness )
{
bool bRet = false;
@@ -3246,8 +3246,11 @@ bool Bitmap::Adjust( short nLuminancePercent, short nContrastPercent,
else
fM = ( 128.0 + 1.27 * MinMax( nContrastPercent, -100L, 0L ) ) / 128.0;
// total offset = luminance offset + contrast offset
fOff = MinMax( nLuminancePercent, -100L, 100L ) * 2.55 + 128.0 - fM * 128.0;
if(!msoBrightness)
// total offset = luminance offset + contrast offset
fOff = MinMax( nLuminancePercent, -100L, 100L ) * 2.55 + 128.0 - fM * 128.0;
else
fOff = MinMax( nLuminancePercent, -100L, 100L ) * 2.55;
// channel offset = channel offset + total offset
fROff = nChannelRPercent * 2.55 + fOff;
@@ -3261,10 +3264,21 @@ bool Bitmap::Adjust( short nLuminancePercent, short nContrastPercent,
// create mapping table
for( long nX = 0L; nX < 256L; nX++ )
{
cMapR[ nX ] = (sal_uInt8) MinMax( FRound( nX * fM + fROff ), 0L, 255L );
cMapG[ nX ] = (sal_uInt8) MinMax( FRound( nX * fM + fGOff ), 0L, 255L );
cMapB[ nX ] = (sal_uInt8) MinMax( FRound( nX * fM + fBOff ), 0L, 255L );
if(!msoBrightness)
{
cMapR[ nX ] = (sal_uInt8) MinMax( FRound( nX * fM + fROff ), 0L, 255L );
cMapG[ nX ] = (sal_uInt8) MinMax( FRound( nX * fM + fGOff ), 0L, 255L );
cMapB[ nX ] = (sal_uInt8) MinMax( FRound( nX * fM + fBOff ), 0L, 255L );
}
else
{
// LO simply uses (in a somewhat optimized form) "newcolor = (oldcolor-128)*contrast+brightness+128"
// as the formula, i.e. contrast first, brightness afterwards. MSOffice, for whatever weird reason,
// use neither first, but apparently it applies half of brightness before contrast and half afterwards.
cMapR[ nX ] = (sal_uInt8) MinMax( FRound( (nX+fROff/2-128) * fM + 128 + fROff/2 ), 0L, 255L );
cMapG[ nX ] = (sal_uInt8) MinMax( FRound( (nX+fGOff/2-128) * fM + 128 + fGOff/2 ), 0L, 255L );
cMapB[ nX ] = (sal_uInt8) MinMax( FRound( (nX+fBOff/2-128) * fM + 128 + fBOff/2 ), 0L, 255L );
}
if( bGamma )
{
cMapR[ nX ] = GAMMA( cMapR[ nX ], fGamma );

View File

@@ -629,11 +629,11 @@ bool BitmapEx::Replace( const Color* pSearchColors, const Color* pReplaceColors,
bool BitmapEx::Adjust( short nLuminancePercent, short nContrastPercent,
short nChannelRPercent, short nChannelGPercent, short nChannelBPercent,
double fGamma, bool bInvert )
double fGamma, bool bInvert, bool msoBrightness )
{
return( !!aBitmap ? aBitmap.Adjust( nLuminancePercent, nContrastPercent,
nChannelRPercent, nChannelGPercent, nChannelBPercent,
fGamma, bInvert ) : false );
fGamma, bInvert, msoBrightness ) : false );
}
bool BitmapEx::Filter( BmpFilter eFilter, const BmpFilterParam* pFilterParam, const Link* pProgress )

View File

@@ -2176,7 +2176,7 @@ void GDIMetaFile::ImplExchangeColors( ColorExchangeFnc pFncCol, const void* pCol
void GDIMetaFile::Adjust( short nLuminancePercent, short nContrastPercent,
short nChannelRPercent, short nChannelGPercent,
short nChannelBPercent, double fGamma, bool bInvert )
short nChannelBPercent, double fGamma, bool bInvert, bool msoBrightness )
{
// nothing to do? => return quickly
if( nLuminancePercent || nContrastPercent ||
@@ -2197,8 +2197,11 @@ void GDIMetaFile::Adjust( short nLuminancePercent, short nContrastPercent,
else
fM = ( 128.0 + 1.27 * MinMax( nContrastPercent, -100L, 0L ) ) / 128.0;
// total offset = luminance offset + contrast offset
fOff = MinMax( nLuminancePercent, -100L, 100L ) * 2.55 + 128.0 - fM * 128.0;
if(!msoBrightness)
// total offset = luminance offset + contrast offset
fOff = MinMax( nLuminancePercent, -100L, 100L ) * 2.55 + 128.0 - fM * 128.0;
else
fOff = MinMax( nLuminancePercent, -100L, 100L ) * 2.55;
// channel offset = channel offset + total offset
fROff = nChannelRPercent * 2.55 + fOff;
@@ -2212,10 +2215,18 @@ void GDIMetaFile::Adjust( short nLuminancePercent, short nContrastPercent,
// create mapping table
for( long nX = 0L; nX < 256L; nX++ )
{
aColParam.pMapR[ nX ] = (sal_uInt8) MinMax( FRound( nX * fM + fROff ), 0L, 255L );
aColParam.pMapG[ nX ] = (sal_uInt8) MinMax( FRound( nX * fM + fGOff ), 0L, 255L );
aColParam.pMapB[ nX ] = (sal_uInt8) MinMax( FRound( nX * fM + fBOff ), 0L, 255L );
if(!msoBrightness)
{
aColParam.pMapR[ nX ] = (sal_uInt8) MinMax( FRound( nX * fM + fROff ), 0L, 255L );
aColParam.pMapG[ nX ] = (sal_uInt8) MinMax( FRound( nX * fM + fGOff ), 0L, 255L );
aColParam.pMapB[ nX ] = (sal_uInt8) MinMax( FRound( nX * fM + fBOff ), 0L, 255L );
}
else
{
aColParam.pMapR[ nX ] = (sal_uInt8) MinMax( FRound( (nX+fROff/2-128) * fM + 128 + fROff/2 ), 0L, 255L );
aColParam.pMapG[ nX ] = (sal_uInt8) MinMax( FRound( (nX+fGOff/2-128) * fM + 128 + fGOff/2 ), 0L, 255L );
aColParam.pMapB[ nX ] = (sal_uInt8) MinMax( FRound( (nX+fBOff/2-128) * fM + 128 + fBOff/2 ), 0L, 255L );
}
if( bGamma )
{
aColParam.pMapR[ nX ] = GAMMA( aColParam.pMapR[ nX ], fGamma );