Skip to content

Commit 378a858

Browse files
committedApr 3, 2013
Fix loss of alpha when adjusting brightness/hue/saturation for raster. Clean and optimize code
1 parent 2f2ca9f commit 378a858

File tree

4 files changed

+190
-87
lines changed

4 files changed

+190
-87
lines changed
 

‎src/core/raster/qgsbrightnesscontrastfilter.cpp

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
#include <QDomDocument>
2323
#include <QDomElement>
2424

25-
2625
QgsBrightnessContrastFilter::QgsBrightnessContrastFilter( QgsRasterInterface* input )
2726
: QgsRasterInterface( input ),
2827
mBrightness( 0 ),
@@ -147,7 +146,7 @@ QgsRasterBlock * QgsBrightnessContrastFilter::block( int bandNo, QgsRectangle c
147146
QRgb myNoDataColor = qRgba( 0, 0, 0, 0 );
148147
QRgb myColor;
149148

150-
int r, g, b;
149+
int r, g, b, alpha;
151150
double f = qPow(( mContrast + 100 ) / 100.0, 2 );
152151

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

161160
myColor = inputBlock->color( i );
162-
r = qBound( 0, ( int )((((( qRed( myColor ) / 255.0 ) - 0.5 ) * f ) + 0.5 ) * 255 ) + mBrightness, 255 );
163-
g = qBound( 0, ( int )((((( qGreen( myColor ) / 255.0 ) - 0.5 ) * f ) + 0.5 ) * 255 ) + mBrightness, 255 );
164-
b = qBound( 0, ( int )((((( qBlue( myColor ) / 255.0 ) - 0.5 ) * f ) + 0.5 ) * 255 ) + mBrightness, 255 );
161+
alpha = qAlpha( myColor );
162+
163+
r = adjustColorComponent( qRed( myColor ), alpha, mBrightness, f );
164+
g = adjustColorComponent( qGreen( myColor ), alpha, mBrightness, f );
165+
b = adjustColorComponent( qBlue( myColor ), alpha, mBrightness, f );
165166

166-
outputBlock->setColor( i, qRgb( r, g, b ) );
167+
outputBlock->setColor( i, qRgba( r, g, b, alpha ) );
167168
}
168169

169170
delete inputBlock;
170171
return outputBlock;
171172
}
172173

174+
int QgsBrightnessContrastFilter::adjustColorComponent( int colorComponent, int alpha, int brightness, double contrastFactor ) const
175+
{
176+
if ( alpha == 255 )
177+
{
178+
// Opaque pixel, do simpler math
179+
return qBound( 0, ( int )(((((( colorComponent / 255.0 ) - 0.5 ) * contrastFactor ) + 0.5 ) * 255 ) + brightness ), 255 );
180+
}
181+
else
182+
{
183+
// Semi-transparent pixel. We need to adjust the math since we are using QGis::ARGB32_Premultiplied
184+
// and color values have been premultiplied by alpha
185+
double alphaFactor = alpha / 255.;
186+
double adjustedColor = colorComponent / alphaFactor;
187+
188+
// Make sure to return a premultiplied color
189+
return alphaFactor * qBound( 0., (((((( adjustedColor / 255.0 ) - 0.5 ) * contrastFactor ) + 0.5 ) * 255 ) + brightness ), 255. );
190+
}
191+
}
192+
173193
void QgsBrightnessContrastFilter::writeXML( QDomDocument& doc, QDomElement& parentElem )
174194
{
175195
if ( parentElem.isNull() )

‎src/core/raster/qgsbrightnesscontrastfilter.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ class CORE_EXPORT QgsBrightnessContrastFilter : public QgsRasterInterface
5454
void readXML( const QDomElement& filterElem );
5555

5656
private:
57+
/**Adjusts a color component by the specified brightness and contrast factor*/
58+
int adjustColorComponent( int colorComponent, int alpha, int brightness, double contrastFactor ) const;
59+
5760
/** Current brightness coefficient value. Default: 0. Range: -255...255 */
5861
int mBrightness;
5962

‎src/core/raster/qgshuesaturationfilter.cpp

Lines changed: 152 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -147,17 +147,11 @@ QgsRasterBlock * QgsHueSaturationFilter::block( int bandNo, QgsRectangle const
147147

148148
// adjust image
149149
QRgb myNoDataColor = qRgba( 0, 0, 0, 0 );
150+
QRgb myRgb;
150151
QColor myColor;
151152
int h, s, l;
152-
int r, g, b;
153-
154-
// Scale saturation value to [0-2], where 0 = desaturated
155-
double saturationScale = (( double ) mSaturation / 100 ) + 1;
156-
157-
// Get hue, saturation for colorized color
158-
int colorizeH, colorizeS;
159-
colorizeH = mColorizeColor.hue();
160-
colorizeS = mColorizeColor.saturation();
153+
int r, g, b, alpha;
154+
double alphaFactor;
161155

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

170-
// Get hsv and rgb for color
171-
myColor = QColor( inputBlock->color( i ) );
172-
myColor.getHsl( &h, &s, &l );
164+
myRgb = inputBlock->color( i );
165+
myColor = QColor( myRgb );
166+
167+
// Alpha must be taken from QRgb, since conversion from QRgb->QColor loses alpha
168+
alpha = qAlpha( myRgb );
169+
170+
// Get rgb for color
173171
myColor.getRgb( &r, &g, &b );
172+
if ( alpha != 255 )
173+
{
174+
// Semi-transparent pixel. We need to adjust the colors since we are using QGis::ARGB32_Premultiplied
175+
// and color values have been premultiplied by alpha
176+
alphaFactor = alpha / 255.;
177+
r /= alphaFactor;
178+
g /= alphaFactor;
179+
b /= alphaFactor;
180+
myColor = QColor::fromRgb( r, g, b );
181+
}
182+
183+
myColor.getHsl( &h, &s, &l );
174184

175-
switch ( mGrayscaleMode )
185+
// Changing saturation?
186+
if (( mGrayscaleMode != GrayscaleOff ) || ( mSaturationScale != 1 ) )
176187
{
177-
case GrayscaleLightness:
178-
{
179-
// Lightness mode, set saturation to zero
180-
s = 0;
181-
myColor = QColor::fromHsl( h, s, l );
182-
break;
183-
}
184-
case GrayscaleLuminosity:
185-
{
186-
// Grayscale by weighted rgb components
187-
int luminosity = 0.21 * r + 0.72 * g + 0.07 * b;
188-
r = g = b = luminosity;
189-
myColor = QColor::fromRgb( r, g, b );
190-
break;
191-
}
192-
case GrayscaleAverage:
193-
{
194-
// Grayscale by average of rgb components
195-
int average = ( r + g + b ) / 3;
196-
r = g = b = average;
197-
myColor = QColor::fromRgb( r, g, b );
198-
break;
199-
}
200-
case GrayscaleOff:
201-
{
202-
// Not being made grayscale, do saturation change
203-
if ( saturationScale < 1 )
204-
{
205-
// Lowering the saturation. Use a simple linear relationship
206-
s = qMin(( int )( s * saturationScale ), 255 );
207-
}
208-
else
209-
{
210-
// Raising the saturation. Use a saturation curve to prevent
211-
// clipping at maximum saturation with ugly results.
212-
s = qMin(( int )( 255. * ( 1 - pow( 1 - (( double )s / 255. ) , saturationScale * 2 ) ) ), 255 );
213-
}
214-
myColor = QColor::fromHsl( h, s, l );
215-
break;
216-
}
188+
processSaturation( r, g, b, h, s, l );
217189
}
218190

219191
// Colorizing?
220192
if ( mColorizeOn )
221193
{
222-
// Update hsl, rgb values (these may have changed with saturation/grayscale adjustments)
223-
myColor.getHsl( &h, &s, &l );
194+
processColorization( r, g, b, h, s, l );
195+
}
196+
197+
// Convert back to rgb
198+
if ( alpha != 255 )
199+
{
200+
// Transparent pixel, need to premultiply color components
201+
r *= alphaFactor;
202+
g *= alphaFactor;
203+
b *= alphaFactor;
204+
}
205+
206+
outputBlock->setColor( i, qRgba( r, g, b, alpha ) );
207+
}
208+
209+
delete inputBlock;
210+
return outputBlock;
211+
}
212+
213+
// Process a colorization and update resultant HSL & RGB values
214+
void QgsHueSaturationFilter::processColorization( int &r, int &g, int &b, int &h, int &s, int &l )
215+
{
216+
QColor myColor;
217+
218+
// Overwrite hue and saturation with values from colorize color
219+
h = mColorizeH;
220+
s = mColorizeS;
221+
222+
223+
QColor colorizedColor = QColor::fromHsl( h, s, l );
224+
225+
if ( mColorizeStrength == 100 )
226+
{
227+
// Full strength
228+
myColor = colorizedColor;
229+
230+
// RGB may have changed, update them
231+
myColor.getRgb( &r, &g, &b );
232+
}
233+
else
234+
{
235+
// Get rgb for colorized color
236+
int colorizedR, colorizedG, colorizedB;
237+
colorizedColor.getRgb( &colorizedR, &colorizedG, &colorizedB );
238+
239+
// Now, linearly scale by colorize strength
240+
double p = ( double ) mColorizeStrength / 100.;
241+
r = p * colorizedR + ( 1 - p ) * r;
242+
g = p * colorizedG + ( 1 - p ) * g;
243+
b = p * colorizedB + ( 1 - p ) * b;
244+
245+
// RGB changed, so update HSL values
246+
myColor = QColor::fromRgb( r, g, b );
247+
myColor.getHsl( &h, &s, &l );
248+
}
249+
}
250+
251+
// Process a change in saturation and update resultant HSL & RGB values
252+
void QgsHueSaturationFilter::processSaturation( int &r, int &g, int &b, int &h, int &s, int &l )
253+
{
254+
255+
QColor myColor;
256+
257+
// Are we converting layer to grayscale?
258+
switch ( mGrayscaleMode )
259+
{
260+
case GrayscaleLightness:
261+
{
262+
// Lightness mode, set saturation to zero
263+
s = 0;
264+
265+
// Saturation changed, so update rgb values
266+
myColor = QColor::fromHsl( h, s, l );
224267
myColor.getRgb( &r, &g, &b );
268+
return;
269+
}
270+
case GrayscaleLuminosity:
271+
{
272+
// Grayscale by weighted rgb components
273+
int luminosity = 0.21 * r + 0.72 * g + 0.07 * b;
274+
r = g = b = luminosity;
225275

226-
// Overwrite hue and saturation with values from colorize color
227-
h = colorizeH;
228-
s = colorizeS;
229-
QColor colorizedColor = QColor::fromHsl( h, s, l );
276+
// RGB changed, so update HSL values
277+
myColor = QColor::fromRgb( r, g, b );
278+
myColor.getHsl( &h, &s, &l );
279+
return;
280+
}
281+
case GrayscaleAverage:
282+
{
283+
// Grayscale by average of rgb components
284+
int average = ( r + g + b ) / 3;
285+
r = g = b = average;
230286

231-
if ( mColorizeStrength == 100 )
287+
// RGB changed, so update HSL values
288+
myColor = QColor::fromRgb( r, g, b );
289+
myColor.getHsl( &h, &s, &l );
290+
return;
291+
}
292+
case GrayscaleOff:
293+
{
294+
// Not being made grayscale, do saturation change
295+
if ( mSaturationScale < 1 )
232296
{
233-
// Full strength
234-
myColor = colorizedColor;
297+
// Lowering the saturation. Use a simple linear relationship
298+
s = qMin(( int )( s * mSaturationScale ), 255 );
235299
}
236300
else
237301
{
238-
// Get rgb for colorized color
239-
int colorizedR, colorizedG, colorizedB;
240-
colorizedColor.getRgb( &colorizedR, &colorizedG, &colorizedB );
241-
242-
// Now, linearly scale by colorize strength
243-
double p = ( double ) mColorizeStrength / 100.;
244-
r = p * colorizedR + ( 1 - p ) * r;
245-
g = p * colorizedG + ( 1 - p ) * g;
246-
b = p * colorizedB + ( 1 - p ) * b;
247-
myColor = QColor::fromRgb( r, g, b );
302+
// Raising the saturation. Use a saturation curve to prevent
303+
// clipping at maximum saturation with ugly results.
304+
s = qMin(( int )( 255. * ( 1 - pow( 1 - ( s / 255. ) , mSaturationScale * 2 ) ) ), 255 );
248305
}
249-
}
250306

251-
// Convert back to rgb
252-
outputBlock->setColor( i, myColor.rgb() );
307+
// Saturation changed, so update rgb values
308+
myColor = QColor::fromHsl( h, s, l );
309+
myColor.getRgb( &r, &g, &b );
310+
return;
311+
}
253312
}
313+
}
254314

255-
delete inputBlock;
256-
return outputBlock;
315+
void QgsHueSaturationFilter::setSaturation( int saturation )
316+
{
317+
mSaturation = qBound( -100, saturation, 100 );
318+
319+
// Scale saturation value to [0-2], where 0 = desaturated
320+
mSaturationScale = (( double ) mSaturation / 100 ) + 1;
321+
}
322+
323+
void QgsHueSaturationFilter::setColorizeColor( QColor colorizeColor )
324+
{
325+
mColorizeColor = colorizeColor;
326+
327+
// Get hue, saturation for colorized color
328+
mColorizeH = mColorizeColor.hue();
329+
mColorizeS = mColorizeColor.saturation();
257330
}
258331

259332
void QgsHueSaturationFilter::writeXML( QDomDocument& doc, QDomElement& parentElem )
@@ -283,14 +356,14 @@ void QgsHueSaturationFilter::readXML( const QDomElement& filterElem )
283356
return;
284357
}
285358

286-
mSaturation = filterElem.attribute( "saturation", "0" ).toInt();
359+
setSaturation( filterElem.attribute( "saturation", "0" ).toInt() );
287360
mGrayscaleMode = ( QgsHueSaturationFilter::GrayscaleMode )filterElem.attribute( "grayscaleMode", "0" ).toInt();
288361

289362
mColorizeOn = ( bool )filterElem.attribute( "colorizeOn", "0" ).toInt();
290363
int mColorizeRed = filterElem.attribute( "colorizeRed", "255" ).toInt();
291364
int mColorizeGreen = filterElem.attribute( "colorizeGreen", "0" ).toInt();
292365
int mColorizeBlue = filterElem.attribute( "colorizeBlue", "0" ).toInt();
293-
mColorizeColor = QColor::fromRgb( mColorizeRed, mColorizeGreen, mColorizeBlue );
366+
setColorizeColor( QColor::fromRgb( mColorizeRed, mColorizeGreen, mColorizeBlue ) );
294367
mColorizeStrength = filterElem.attribute( "colorizeStrength", "100" ).toInt();
295368

296369
}

‎src/core/raster/qgshuesaturationfilter.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,15 @@ class CORE_EXPORT QgsHueSaturationFilter : public QgsRasterInterface
5252

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

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

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

6161
void setColorizeOn( bool colorizeOn ) { mColorizeOn = colorizeOn; }
6262
bool colorizeOn() const { return mColorizeOn; }
63-
void setColorizeColor( QColor colorizeColor ) { mColorizeColor = colorizeColor; }
63+
void setColorizeColor( QColor colorizeColor );
6464
QColor colorizeColor() const { return mColorizeColor; }
6565
void setColorizeStrength( int colorizeStrength ) { mColorizeStrength = colorizeStrength; }
6666
int colorizeStrength() const { return mColorizeStrength; }
@@ -71,15 +71,22 @@ class CORE_EXPORT QgsHueSaturationFilter : public QgsRasterInterface
7171
void readXML( const QDomElement& filterElem );
7272

7373
private:
74+
/** Process a change in saturation and update resultant HSL & RGB values*/
75+
void processSaturation( int &r, int &g, int &b, int &h, int &s, int &l );
76+
/** Process a colorization and update resultant HSL & RGB values*/
77+
void processColorization( int &r, int &g, int &b, int &h, int &s, int &l ) ;
78+
7479
/**Current saturation value. Range: -100 (desaturated) ... 0 (no change) ... 100 (increased)*/
7580
int mSaturation;
81+
double mSaturationScale;
7682

7783
/**Current grayscale mode*/
7884
QgsHueSaturationFilter::GrayscaleMode mGrayscaleMode;
7985

8086
/**Colorize settings*/
8187
bool mColorizeOn;
8288
QColor mColorizeColor;
89+
int mColorizeH, mColorizeS;
8390
int mColorizeStrength;
8491

8592
};

0 commit comments

Comments
 (0)
Please sign in to comment.