Skip to content

Commit

Permalink
Merge pull request #499 from nyalldawson/fix_alpha
Browse files Browse the repository at this point in the history
Fixes loss of alpha after adjusting brightness/contrast/hue/saturation for rasters
  • Loading branch information
alexbruy committed Apr 3, 2013
2 parents 80f7b5a + 378a858 commit cf4fd4a
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 87 deletions.
32 changes: 26 additions & 6 deletions src/core/raster/qgsbrightnesscontrastfilter.cpp
Expand Up @@ -22,7 +22,6 @@
#include <QDomDocument>
#include <QDomElement>


QgsBrightnessContrastFilter::QgsBrightnessContrastFilter( QgsRasterInterface* input )
: QgsRasterInterface( input ),
mBrightness( 0 ),
Expand Down Expand Up @@ -147,7 +146,7 @@ QgsRasterBlock * QgsBrightnessContrastFilter::block( int bandNo, QgsRectangle c
QRgb myNoDataColor = qRgba( 0, 0, 0, 0 );
QRgb myColor;

int r, g, b;
int r, g, b, alpha;
double f = qPow(( mContrast + 100 ) / 100.0, 2 );

for ( size_t i = 0; i < ( size_t )width*height; i++ )
Expand All @@ -159,17 +158,38 @@ QgsRasterBlock * QgsBrightnessContrastFilter::block( int bandNo, QgsRectangle c
}

myColor = inputBlock->color( i );
r = qBound( 0, ( int )((((( qRed( myColor ) / 255.0 ) - 0.5 ) * f ) + 0.5 ) * 255 ) + mBrightness, 255 );
g = qBound( 0, ( int )((((( qGreen( myColor ) / 255.0 ) - 0.5 ) * f ) + 0.5 ) * 255 ) + mBrightness, 255 );
b = qBound( 0, ( int )((((( qBlue( myColor ) / 255.0 ) - 0.5 ) * f ) + 0.5 ) * 255 ) + mBrightness, 255 );
alpha = qAlpha( myColor );

r = adjustColorComponent( qRed( myColor ), alpha, mBrightness, f );
g = adjustColorComponent( qGreen( myColor ), alpha, mBrightness, f );
b = adjustColorComponent( qBlue( myColor ), alpha, mBrightness, f );

outputBlock->setColor( i, qRgb( r, g, b ) );
outputBlock->setColor( i, qRgba( r, g, b, alpha ) );
}

delete inputBlock;
return outputBlock;
}

int QgsBrightnessContrastFilter::adjustColorComponent( int colorComponent, int alpha, int brightness, double contrastFactor ) const
{
if ( alpha == 255 )
{
// Opaque pixel, do simpler math
return qBound( 0, ( int )(((((( colorComponent / 255.0 ) - 0.5 ) * contrastFactor ) + 0.5 ) * 255 ) + brightness ), 255 );
}
else
{
// Semi-transparent pixel. We need to adjust the math since we are using QGis::ARGB32_Premultiplied
// and color values have been premultiplied by alpha
double alphaFactor = alpha / 255.;
double adjustedColor = colorComponent / alphaFactor;

// Make sure to return a premultiplied color
return alphaFactor * qBound( 0., (((((( adjustedColor / 255.0 ) - 0.5 ) * contrastFactor ) + 0.5 ) * 255 ) + brightness ), 255. );
}
}

void QgsBrightnessContrastFilter::writeXML( QDomDocument& doc, QDomElement& parentElem )
{
if ( parentElem.isNull() )
Expand Down
3 changes: 3 additions & 0 deletions src/core/raster/qgsbrightnesscontrastfilter.h
Expand Up @@ -54,6 +54,9 @@ class CORE_EXPORT QgsBrightnessContrastFilter : public QgsRasterInterface
void readXML( const QDomElement& filterElem );

private:
/**Adjusts a color component by the specified brightness and contrast factor*/
int adjustColorComponent( int colorComponent, int alpha, int brightness, double contrastFactor ) const;

/** Current brightness coefficient value. Default: 0. Range: -255...255 */
int mBrightness;

Expand Down
231 changes: 152 additions & 79 deletions src/core/raster/qgshuesaturationfilter.cpp
Expand Up @@ -147,17 +147,11 @@ QgsRasterBlock * QgsHueSaturationFilter::block( int bandNo, QgsRectangle const

// adjust image
QRgb myNoDataColor = qRgba( 0, 0, 0, 0 );
QRgb myRgb;
QColor myColor;
int h, s, l;
int r, g, b;

// Scale saturation value to [0-2], where 0 = desaturated
double saturationScale = (( double ) mSaturation / 100 ) + 1;

// Get hue, saturation for colorized color
int colorizeH, colorizeS;
colorizeH = mColorizeColor.hue();
colorizeS = mColorizeColor.saturation();
int r, g, b, alpha;
double alphaFactor;

for ( size_t i = 0; i < ( size_t )width*height; i++ )
{
Expand All @@ -167,93 +161,172 @@ QgsRasterBlock * QgsHueSaturationFilter::block( int bandNo, QgsRectangle const
continue;
}

// Get hsv and rgb for color
myColor = QColor( inputBlock->color( i ) );
myColor.getHsl( &h, &s, &l );
myRgb = inputBlock->color( i );
myColor = QColor( myRgb );

// Alpha must be taken from QRgb, since conversion from QRgb->QColor loses alpha
alpha = qAlpha( myRgb );

// Get rgb for color
myColor.getRgb( &r, &g, &b );
if ( alpha != 255 )
{
// Semi-transparent pixel. We need to adjust the colors since we are using QGis::ARGB32_Premultiplied
// and color values have been premultiplied by alpha
alphaFactor = alpha / 255.;
r /= alphaFactor;
g /= alphaFactor;
b /= alphaFactor;
myColor = QColor::fromRgb( r, g, b );
}

myColor.getHsl( &h, &s, &l );

switch ( mGrayscaleMode )
// Changing saturation?
if (( mGrayscaleMode != GrayscaleOff ) || ( mSaturationScale != 1 ) )
{
case GrayscaleLightness:
{
// Lightness mode, set saturation to zero
s = 0;
myColor = QColor::fromHsl( h, s, l );
break;
}
case GrayscaleLuminosity:
{
// Grayscale by weighted rgb components
int luminosity = 0.21 * r + 0.72 * g + 0.07 * b;
r = g = b = luminosity;
myColor = QColor::fromRgb( r, g, b );
break;
}
case GrayscaleAverage:
{
// Grayscale by average of rgb components
int average = ( r + g + b ) / 3;
r = g = b = average;
myColor = QColor::fromRgb( r, g, b );
break;
}
case GrayscaleOff:
{
// Not being made grayscale, do saturation change
if ( saturationScale < 1 )
{
// Lowering the saturation. Use a simple linear relationship
s = qMin(( int )( s * saturationScale ), 255 );
}
else
{
// Raising the saturation. Use a saturation curve to prevent
// clipping at maximum saturation with ugly results.
s = qMin(( int )( 255. * ( 1 - pow( 1 - (( double )s / 255. ) , saturationScale * 2 ) ) ), 255 );
}
myColor = QColor::fromHsl( h, s, l );
break;
}
processSaturation( r, g, b, h, s, l );
}

// Colorizing?
if ( mColorizeOn )
{
// Update hsl, rgb values (these may have changed with saturation/grayscale adjustments)
myColor.getHsl( &h, &s, &l );
processColorization( r, g, b, h, s, l );
}

// Convert back to rgb
if ( alpha != 255 )
{
// Transparent pixel, need to premultiply color components
r *= alphaFactor;
g *= alphaFactor;
b *= alphaFactor;
}

outputBlock->setColor( i, qRgba( r, g, b, alpha ) );
}

delete inputBlock;
return outputBlock;
}

// Process a colorization and update resultant HSL & RGB values
void QgsHueSaturationFilter::processColorization( int &r, int &g, int &b, int &h, int &s, int &l )
{
QColor myColor;

// Overwrite hue and saturation with values from colorize color
h = mColorizeH;
s = mColorizeS;


QColor colorizedColor = QColor::fromHsl( h, s, l );

if ( mColorizeStrength == 100 )
{
// Full strength
myColor = colorizedColor;

// RGB may have changed, update them
myColor.getRgb( &r, &g, &b );
}
else
{
// Get rgb for colorized color
int colorizedR, colorizedG, colorizedB;
colorizedColor.getRgb( &colorizedR, &colorizedG, &colorizedB );

// Now, linearly scale by colorize strength
double p = ( double ) mColorizeStrength / 100.;
r = p * colorizedR + ( 1 - p ) * r;
g = p * colorizedG + ( 1 - p ) * g;
b = p * colorizedB + ( 1 - p ) * b;

// RGB changed, so update HSL values
myColor = QColor::fromRgb( r, g, b );
myColor.getHsl( &h, &s, &l );
}
}

// Process a change in saturation and update resultant HSL & RGB values
void QgsHueSaturationFilter::processSaturation( int &r, int &g, int &b, int &h, int &s, int &l )
{

QColor myColor;

// Are we converting layer to grayscale?
switch ( mGrayscaleMode )
{
case GrayscaleLightness:
{
// Lightness mode, set saturation to zero
s = 0;

// Saturation changed, so update rgb values
myColor = QColor::fromHsl( h, s, l );
myColor.getRgb( &r, &g, &b );
return;
}
case GrayscaleLuminosity:
{
// Grayscale by weighted rgb components
int luminosity = 0.21 * r + 0.72 * g + 0.07 * b;
r = g = b = luminosity;

// Overwrite hue and saturation with values from colorize color
h = colorizeH;
s = colorizeS;
QColor colorizedColor = QColor::fromHsl( h, s, l );
// RGB changed, so update HSL values
myColor = QColor::fromRgb( r, g, b );
myColor.getHsl( &h, &s, &l );
return;
}
case GrayscaleAverage:
{
// Grayscale by average of rgb components
int average = ( r + g + b ) / 3;
r = g = b = average;

if ( mColorizeStrength == 100 )
// RGB changed, so update HSL values
myColor = QColor::fromRgb( r, g, b );
myColor.getHsl( &h, &s, &l );
return;
}
case GrayscaleOff:
{
// Not being made grayscale, do saturation change
if ( mSaturationScale < 1 )
{
// Full strength
myColor = colorizedColor;
// Lowering the saturation. Use a simple linear relationship
s = qMin(( int )( s * mSaturationScale ), 255 );
}
else
{
// Get rgb for colorized color
int colorizedR, colorizedG, colorizedB;
colorizedColor.getRgb( &colorizedR, &colorizedG, &colorizedB );

// Now, linearly scale by colorize strength
double p = ( double ) mColorizeStrength / 100.;
r = p * colorizedR + ( 1 - p ) * r;
g = p * colorizedG + ( 1 - p ) * g;
b = p * colorizedB + ( 1 - p ) * b;
myColor = QColor::fromRgb( r, g, b );
// Raising the saturation. Use a saturation curve to prevent
// clipping at maximum saturation with ugly results.
s = qMin(( int )( 255. * ( 1 - pow( 1 - ( s / 255. ) , mSaturationScale * 2 ) ) ), 255 );
}
}

// Convert back to rgb
outputBlock->setColor( i, myColor.rgb() );
// Saturation changed, so update rgb values
myColor = QColor::fromHsl( h, s, l );
myColor.getRgb( &r, &g, &b );
return;
}
}
}

delete inputBlock;
return outputBlock;
void QgsHueSaturationFilter::setSaturation( int saturation )
{
mSaturation = qBound( -100, saturation, 100 );

// Scale saturation value to [0-2], where 0 = desaturated
mSaturationScale = (( double ) mSaturation / 100 ) + 1;
}

void QgsHueSaturationFilter::setColorizeColor( QColor colorizeColor )
{
mColorizeColor = colorizeColor;

// Get hue, saturation for colorized color
mColorizeH = mColorizeColor.hue();
mColorizeS = mColorizeColor.saturation();
}

void QgsHueSaturationFilter::writeXML( QDomDocument& doc, QDomElement& parentElem )
Expand Down Expand Up @@ -283,14 +356,14 @@ void QgsHueSaturationFilter::readXML( const QDomElement& filterElem )
return;
}

mSaturation = filterElem.attribute( "saturation", "0" ).toInt();
setSaturation( filterElem.attribute( "saturation", "0" ).toInt() );
mGrayscaleMode = ( QgsHueSaturationFilter::GrayscaleMode )filterElem.attribute( "grayscaleMode", "0" ).toInt();

mColorizeOn = ( bool )filterElem.attribute( "colorizeOn", "0" ).toInt();
int mColorizeRed = filterElem.attribute( "colorizeRed", "255" ).toInt();
int mColorizeGreen = filterElem.attribute( "colorizeGreen", "0" ).toInt();
int mColorizeBlue = filterElem.attribute( "colorizeBlue", "0" ).toInt();
mColorizeColor = QColor::fromRgb( mColorizeRed, mColorizeGreen, mColorizeBlue );
setColorizeColor( QColor::fromRgb( mColorizeRed, mColorizeGreen, mColorizeBlue ) );
mColorizeStrength = filterElem.attribute( "colorizeStrength", "100" ).toInt();

}
11 changes: 9 additions & 2 deletions src/core/raster/qgshuesaturationfilter.h
Expand Up @@ -52,15 +52,15 @@ class CORE_EXPORT QgsHueSaturationFilter : public QgsRasterInterface

QgsRasterBlock *block( int bandNo, const QgsRectangle &extent, int width, int height );

void setSaturation( int saturation ) { mSaturation = qBound( -100, saturation, 100 ); }
void setSaturation( int saturation );
int saturation() const { return mSaturation; }

void setGrayscaleMode( QgsHueSaturationFilter::GrayscaleMode grayscaleMode ) { mGrayscaleMode = grayscaleMode; }
QgsHueSaturationFilter::GrayscaleMode grayscaleMode() const { return mGrayscaleMode; }

void setColorizeOn( bool colorizeOn ) { mColorizeOn = colorizeOn; }
bool colorizeOn() const { return mColorizeOn; }
void setColorizeColor( QColor colorizeColor ) { mColorizeColor = colorizeColor; }
void setColorizeColor( QColor colorizeColor );
QColor colorizeColor() const { return mColorizeColor; }
void setColorizeStrength( int colorizeStrength ) { mColorizeStrength = colorizeStrength; }
int colorizeStrength() const { return mColorizeStrength; }
Expand All @@ -71,15 +71,22 @@ class CORE_EXPORT QgsHueSaturationFilter : public QgsRasterInterface
void readXML( const QDomElement& filterElem );

private:
/** Process a change in saturation and update resultant HSL & RGB values*/
void processSaturation( int &r, int &g, int &b, int &h, int &s, int &l );
/** Process a colorization and update resultant HSL & RGB values*/
void processColorization( int &r, int &g, int &b, int &h, int &s, int &l ) ;

/**Current saturation value. Range: -100 (desaturated) ... 0 (no change) ... 100 (increased)*/
int mSaturation;
double mSaturationScale;

/**Current grayscale mode*/
QgsHueSaturationFilter::GrayscaleMode mGrayscaleMode;

/**Colorize settings*/
bool mColorizeOn;
QColor mColorizeColor;
int mColorizeH, mColorizeS;
int mColorizeStrength;

};
Expand Down

0 comments on commit cf4fd4a

Please sign in to comment.