You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
518 lines
15 KiB
518 lines
15 KiB
|
|
/*
|
|
Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions
|
|
are met:
|
|
|
|
1. Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
2. Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#define DEBUG_KP_EFFECT_BALANCE 0
|
|
|
|
|
|
#include <kpeffectbalance.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <tqfontmetrics.h>
|
|
#include <tqimage.h>
|
|
#include <tqlabel.h>
|
|
#include <tqlayout.h>
|
|
#include <tqpixmap.h>
|
|
#include <tqpushbutton.h>
|
|
|
|
#include <kcombobox.h>
|
|
#include <kdebug.h>
|
|
#include <kimageeffect.h>
|
|
#include <tdelocale.h>
|
|
#include <knuminput.h>
|
|
|
|
#include <kppixmapfx.h>
|
|
|
|
|
|
#if DEBUG_KP_EFFECT_BALANCE
|
|
#include <tqdatetime.h>
|
|
#endif
|
|
|
|
|
|
kpEffectBalanceCommand::kpEffectBalanceCommand (int channels,
|
|
int brightness, int contrast, int gamma,
|
|
bool actOnSelection,
|
|
kpMainWindow *mainWindow)
|
|
: kpColorEffectCommand (i18n ("Balance"), actOnSelection, mainWindow),
|
|
m_channels (channels),
|
|
m_brightness (brightness), m_contrast (contrast), m_gamma (gamma)
|
|
{
|
|
}
|
|
|
|
kpEffectBalanceCommand::~kpEffectBalanceCommand ()
|
|
{
|
|
}
|
|
|
|
|
|
static inline int between0And255 (int val)
|
|
{
|
|
if (val < 0)
|
|
return 0;
|
|
else if (val > 255)
|
|
return 255;
|
|
else
|
|
return val;
|
|
}
|
|
|
|
|
|
static inline int brightness (int base, int strength)
|
|
{
|
|
return between0And255 (base + strength * 255 / 50);
|
|
}
|
|
|
|
static inline int contrast (int base, int strength)
|
|
{
|
|
return between0And255 ((base - 127) * (strength + 50) / 50 + 127);
|
|
}
|
|
|
|
static inline int gamma (int base, int strength)
|
|
{
|
|
return between0And255 (tqRound (255.0 * pow (base / 255.0, 1.0 / pow (10, strength / 50.0))));
|
|
}
|
|
|
|
|
|
static inline int brightnessContrastGamma (int base,
|
|
int newBrightness,
|
|
int newContrast,
|
|
int newGamma)
|
|
{
|
|
return gamma (contrast (brightness (base, newBrightness),
|
|
newContrast),
|
|
newGamma);
|
|
}
|
|
|
|
static inline TQRgb brightnessContrastGammaForRGB (TQRgb rgb,
|
|
int channels,
|
|
int brightness, int contrast, int gamma)
|
|
{
|
|
int red = tqRed (rgb);
|
|
int green = tqGreen (rgb);
|
|
int blue = tqBlue (rgb);
|
|
|
|
|
|
if (channels & kpEffectBalanceCommand::Red)
|
|
red = brightnessContrastGamma (red, brightness, contrast, gamma);
|
|
if (channels & kpEffectBalanceCommand::Green)
|
|
green = brightnessContrastGamma (green, brightness, contrast, gamma);
|
|
if (channels & kpEffectBalanceCommand::Blue)
|
|
blue = brightnessContrastGamma (blue, brightness, contrast, gamma);
|
|
|
|
|
|
return tqRgba (red, green, blue, tqAlpha (rgb));
|
|
}
|
|
|
|
|
|
// public static
|
|
TQPixmap kpEffectBalanceCommand::applyColorEffect (const TQPixmap &pixmap,
|
|
int channels,
|
|
int brightness, int contrast, int gamma)
|
|
{
|
|
#if DEBUG_KP_EFFECT_BALANCE
|
|
kdDebug () << "kpEffectBalanceCommand::applyColorEffect("
|
|
<< "channels=" << channels
|
|
<< ",brightness=" << brightness
|
|
<< ",contrast=" << contrast
|
|
<< ",gamma=" << gamma
|
|
<< ")" << endl;
|
|
TQTime timer; timer.start ();
|
|
#endif
|
|
|
|
TQImage image = kpPixmapFX::convertToImage (pixmap);
|
|
#if DEBUG_KP_EFFECT_BALANCE
|
|
kdDebug () << "\tconvertToImage=" << timer.restart () << endl;
|
|
#endif
|
|
|
|
|
|
TQ_UINT8 transformRed [256],
|
|
transformGreen [256],
|
|
transformBlue [256];
|
|
|
|
for (int i = 0; i < 256; i++)
|
|
{
|
|
TQ_UINT8 applied = (TQ_UINT8) brightnessContrastGamma (i, brightness, contrast, gamma);
|
|
|
|
if (channels & kpEffectBalanceCommand::Red)
|
|
transformRed [i] = applied;
|
|
else
|
|
transformRed [i] = i;
|
|
|
|
if (channels & kpEffectBalanceCommand::Green)
|
|
transformGreen [i] = applied;
|
|
else
|
|
transformGreen [i] = i;
|
|
|
|
if (channels & kpEffectBalanceCommand::Blue)
|
|
transformBlue [i] = applied;
|
|
else
|
|
transformBlue [i] = i;
|
|
}
|
|
|
|
#if DEBUG_KP_EFFECT_BALANCE
|
|
kdDebug () << "\tbuild lookup=" << timer.restart () << endl;
|
|
#endif
|
|
|
|
|
|
if (image.depth () > 8)
|
|
{
|
|
for (int y = 0; y < image.height (); y++)
|
|
{
|
|
for (int x = 0; x < image.width (); x++)
|
|
{
|
|
const TQRgb rgb = image.pixel (x, y);
|
|
|
|
const TQ_UINT8 red = (TQ_UINT8) tqRed (rgb);
|
|
const TQ_UINT8 green = (TQ_UINT8) tqGreen (rgb);
|
|
const TQ_UINT8 blue = (TQ_UINT8) tqBlue (rgb);
|
|
const TQ_UINT8 alpha = (TQ_UINT8) tqAlpha (rgb);
|
|
|
|
image.setPixel (x, y,
|
|
tqRgba (transformRed [red],
|
|
transformGreen [green],
|
|
transformBlue [blue],
|
|
alpha));
|
|
|
|
#if 0
|
|
image.setPixel (x, y,
|
|
brightnessContrastGammaForRGB (image.pixel (x, y),
|
|
channels,
|
|
brightness, contrast, gamma));
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < image.numColors (); i++)
|
|
{
|
|
const TQRgb rgb = image.color (i);
|
|
|
|
const TQ_UINT8 red = (TQ_UINT8) tqRed (rgb);
|
|
const TQ_UINT8 green = (TQ_UINT8) tqGreen (rgb);
|
|
const TQ_UINT8 blue = (TQ_UINT8) tqBlue (rgb);
|
|
const TQ_UINT8 alpha = (TQ_UINT8) tqAlpha (rgb);
|
|
|
|
image.setColor (i,
|
|
tqRgba (transformRed [red],
|
|
transformGreen [green],
|
|
transformBlue [blue],
|
|
alpha));
|
|
|
|
#if 0
|
|
image.setColor (i,
|
|
brightnessContrastGammaForRGB (image.color (i),
|
|
channels,
|
|
brightness, contrast, gamma));
|
|
#endif
|
|
}
|
|
|
|
}
|
|
#if DEBUG_KP_EFFECT_BALANCE
|
|
kdDebug () << "\teffect=" << timer.restart () << endl;
|
|
#endif
|
|
|
|
const TQPixmap retPixmap = kpPixmapFX::convertToPixmap (image);
|
|
#if DEBUG_KP_EFFECT_BALANCE
|
|
kdDebug () << "\tconvertToPixmap=" << timer.restart () << endl;
|
|
#endif
|
|
|
|
return retPixmap;
|
|
}
|
|
|
|
// protected virtual [base kpColorEffectCommand]
|
|
TQPixmap kpEffectBalanceCommand::applyColorEffect (const TQPixmap &pixmap)
|
|
{
|
|
return applyColorEffect (pixmap, m_channels,
|
|
m_brightness, m_contrast, m_gamma);
|
|
}
|
|
|
|
|
|
|
|
kpEffectBalanceWidget::kpEffectBalanceWidget (bool actOnSelection,
|
|
kpMainWindow *mainWindow,
|
|
TQWidget *parent, const char *name)
|
|
: kpColorEffectWidget (actOnSelection, mainWindow, parent, name)
|
|
{
|
|
TQGridLayout *lay = new TQGridLayout (this, 5, 5, marginHint (), spacingHint ());
|
|
|
|
|
|
TQLabel *brightnessLabel = new TQLabel (i18n ("&Brightness:"), this);
|
|
m_brightnessInput = new KIntNumInput (0/*value*/, this);
|
|
m_brightnessInput->setRange (-50, 50, 1/*step*/, true/*slider*/);
|
|
TQPushButton *brightnessResetPushButton = new TQPushButton (i18n ("Re&set"), this);
|
|
|
|
TQLabel *contrastLabel = new TQLabel (i18n ("Co&ntrast:"), this);
|
|
m_contrastInput = new KIntNumInput (0/*value*/, this);
|
|
m_contrastInput->setRange (-50, 50, 1/*step*/, true/*slider*/);
|
|
TQPushButton *contrastResetPushButton = new TQPushButton (i18n ("&Reset"), this);
|
|
|
|
TQLabel *gammaLabel = new TQLabel (i18n ("&Gamma:"), this);
|
|
m_gammaInput = new KIntNumInput (0/*value*/, this);
|
|
m_gammaInput->setRange (-50, 50, 1/*step*/, true/*slider*/);
|
|
// TODO: This is what should be shown in the m_gammaInput spinbox
|
|
m_gammaLabel = new TQLabel (this);
|
|
// TODO: This doesn't seem to be wide enough with some fonts so the
|
|
// whole layout moves when we drag the gamma slider.
|
|
m_gammaLabel->setMinimumWidth (m_gammaLabel->fontMetrics ().width (" 10.00 "));
|
|
m_gammaLabel->setAlignment (m_gammaLabel->alignment () | TQt::AlignRight);
|
|
TQPushButton *gammaResetPushButton = new TQPushButton (i18n ("Rese&t"), this);
|
|
|
|
|
|
TQWidget *spaceWidget = new TQLabel (this);
|
|
spaceWidget->setFixedSize (1, spacingHint ());
|
|
|
|
|
|
TQLabel *channelLabel = new TQLabel (i18n ("C&hannels:"), this);
|
|
m_channelsComboBox = new KComboBox (this);
|
|
m_channelsComboBox->insertItem (i18n ("All"));
|
|
m_channelsComboBox->insertItem (i18n ("Red"));
|
|
m_channelsComboBox->insertItem (i18n ("Green"));
|
|
m_channelsComboBox->insertItem (i18n ("Blue"));
|
|
|
|
|
|
TQPushButton *resetPushButton = new TQPushButton (i18n ("Reset &All Values"), this);
|
|
|
|
|
|
brightnessLabel->setBuddy (m_brightnessInput);
|
|
contrastLabel->setBuddy (m_contrastInput);
|
|
gammaLabel->setBuddy (m_gammaInput);
|
|
|
|
channelLabel->setBuddy (m_channelsComboBox);
|
|
|
|
|
|
lay->addWidget (brightnessLabel, 0, 0);
|
|
lay->addMultiCellWidget (m_brightnessInput, 0, 0, 1, 2);
|
|
lay->addWidget (brightnessResetPushButton, 0, 4);
|
|
|
|
lay->addWidget (contrastLabel, 1, 0);
|
|
lay->addMultiCellWidget (m_contrastInput, 1, 1, 1, 2);
|
|
lay->addWidget (contrastResetPushButton, 1, 4);
|
|
|
|
lay->addWidget (gammaLabel, 2, 0);
|
|
lay->addMultiCellWidget (m_gammaInput, 2, 2, 1, 2);
|
|
lay->addWidget (m_gammaLabel, 2, 3);
|
|
lay->addWidget (gammaResetPushButton, 2, 4);
|
|
|
|
lay->addMultiCellWidget (spaceWidget, 3, 3, 0, 4);
|
|
lay->addMultiCellWidget (resetPushButton, 4, 4, 2, 4, TQt::AlignRight);
|
|
|
|
lay->addWidget (channelLabel, 4, 0);
|
|
lay->addWidget (m_channelsComboBox, 4, 1, TQt::AlignLeft);
|
|
//lay->addWidget (resetPushButton, 4, 2, TQt::AlignRight);
|
|
|
|
lay->setColStretch (1, 1);
|
|
|
|
|
|
// (no need for settingsChangedDelayed() since BCG effect is so fast :))
|
|
connect (m_brightnessInput, TQ_SIGNAL (valueChanged (int)),
|
|
this, TQ_SIGNAL (settingsChangedNoWaitCursor ()));
|
|
connect (m_contrastInput, TQ_SIGNAL (valueChanged (int)),
|
|
this, TQ_SIGNAL (settingsChangedNoWaitCursor ()));
|
|
|
|
connect (m_gammaInput, TQ_SIGNAL (valueChanged (int)),
|
|
this, TQ_SLOT (recalculateGammaLabel ()));
|
|
connect (m_gammaInput, TQ_SIGNAL (valueChanged (int)),
|
|
this, TQ_SIGNAL (settingsChangedNoWaitCursor ()));
|
|
|
|
connect (m_channelsComboBox, TQ_SIGNAL (activated (int)),
|
|
this, TQ_SIGNAL (settingsChanged ()));
|
|
|
|
connect (brightnessResetPushButton, TQ_SIGNAL (clicked ()),
|
|
this, TQ_SLOT (resetBrightness ()));
|
|
connect (contrastResetPushButton, TQ_SIGNAL (clicked ()),
|
|
this, TQ_SLOT (resetContrast ()));
|
|
connect (gammaResetPushButton, TQ_SIGNAL (clicked ()),
|
|
this, TQ_SLOT (resetGamma ()));
|
|
|
|
connect (resetPushButton, TQ_SIGNAL (clicked ()),
|
|
this, TQ_SLOT (resetAll ()));
|
|
|
|
|
|
recalculateGammaLabel ();
|
|
}
|
|
|
|
kpEffectBalanceWidget::~kpEffectBalanceWidget ()
|
|
{
|
|
}
|
|
|
|
|
|
// public virtual [base kpColorEffectWidget]
|
|
TQString kpEffectBalanceWidget::caption () const
|
|
{
|
|
return i18n ("Settings");
|
|
}
|
|
|
|
|
|
// public virtual [base kpColorEffectWidget]
|
|
bool kpEffectBalanceWidget::isNoOp () const
|
|
{
|
|
return (brightness () == 0 && contrast () == 0 && gamma () == 0);
|
|
}
|
|
|
|
// public virtual [base kpColorEffectWidget]
|
|
TQPixmap kpEffectBalanceWidget::applyColorEffect (const TQPixmap &pixmap)
|
|
{
|
|
return kpEffectBalanceCommand::applyColorEffect (pixmap,
|
|
channels (), brightness (), contrast (), gamma ());
|
|
}
|
|
|
|
// public virtual [base kpColorEffectWidget]
|
|
kpColorEffectCommand *kpEffectBalanceWidget::createCommand () const
|
|
{
|
|
return new kpEffectBalanceCommand (channels (),
|
|
brightness (), contrast (), gamma (),
|
|
m_actOnSelection,
|
|
m_mainWindow);
|
|
}
|
|
|
|
|
|
// protected
|
|
int kpEffectBalanceWidget::channels () const
|
|
{
|
|
switch (m_channelsComboBox->currentItem ())
|
|
{
|
|
default:
|
|
case 0:
|
|
return kpEffectBalanceCommand::RGB;
|
|
|
|
case 1:
|
|
return kpEffectBalanceCommand::Red;
|
|
|
|
case 2:
|
|
return kpEffectBalanceCommand::Green;
|
|
|
|
case 3:
|
|
return kpEffectBalanceCommand::Blue;
|
|
}
|
|
}
|
|
|
|
|
|
// protected
|
|
int kpEffectBalanceWidget::brightness () const
|
|
{
|
|
return m_brightnessInput->value ();
|
|
}
|
|
|
|
// protected
|
|
int kpEffectBalanceWidget::contrast () const
|
|
{
|
|
return m_contrastInput->value ();
|
|
}
|
|
|
|
// protected
|
|
int kpEffectBalanceWidget::gamma () const
|
|
{
|
|
return m_gammaInput->value ();
|
|
}
|
|
|
|
|
|
// protected slot
|
|
void kpEffectBalanceWidget::recalculateGammaLabel ()
|
|
{
|
|
m_gammaLabel->setText (
|
|
" " +
|
|
TQString::number (pow (10, gamma () / 50.0),
|
|
'f'/*[-]9.9*/,
|
|
2/*precision*/) +
|
|
" ");
|
|
m_gammaLabel->repaint ();
|
|
}
|
|
|
|
|
|
// protected slot
|
|
void kpEffectBalanceWidget::resetBrightness ()
|
|
{
|
|
if (brightness () == 0)
|
|
return;
|
|
|
|
bool sb = signalsBlocked ();
|
|
|
|
if (!sb) blockSignals (true);
|
|
m_brightnessInput->setValue (0);
|
|
if (!sb) blockSignals (false);
|
|
|
|
// Immediate update (if signals aren't blocked)
|
|
emit settingsChanged ();
|
|
}
|
|
|
|
// protected slot
|
|
void kpEffectBalanceWidget::resetContrast ()
|
|
{
|
|
if (contrast () == 0)
|
|
return;
|
|
|
|
bool sb = signalsBlocked ();
|
|
|
|
if (!sb) blockSignals (true);
|
|
m_contrastInput->setValue (0);
|
|
if (!sb) blockSignals (false);
|
|
|
|
// Immediate update (if signals aren't blocked)
|
|
emit settingsChanged ();
|
|
}
|
|
|
|
// protected slot
|
|
void kpEffectBalanceWidget::resetGamma ()
|
|
{
|
|
if (gamma () == 0)
|
|
return;
|
|
|
|
bool sb = signalsBlocked ();
|
|
|
|
if (!sb) blockSignals (true);
|
|
m_gammaInput->setValue (0);
|
|
recalculateGammaLabel ();
|
|
if (!sb) blockSignals (false);
|
|
|
|
// Immediate update (if signals aren't blocked)
|
|
emit settingsChanged ();
|
|
}
|
|
|
|
|
|
// protected slot
|
|
void kpEffectBalanceWidget::resetAll ()
|
|
{
|
|
if (isNoOp ())
|
|
return;
|
|
|
|
// Prevent multiple settingsChanged() which would normally result in
|
|
// redundant, expensive preview repaints
|
|
blockSignals (true);
|
|
|
|
resetBrightness ();
|
|
resetContrast ();
|
|
resetGamma ();
|
|
|
|
recalculateGammaLabel ();
|
|
|
|
blockSignals (false);
|
|
|
|
emit settingsChanged ();
|
|
}
|
|
|
|
|
|
#include <kpeffectbalance.moc>
|