summaryrefslogtreecommitdiffstats
path: root/kolourpaint/pixmapfx/kpeffectbalance.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kolourpaint/pixmapfx/kpeffectbalance.cpp')
-rw-r--r--kolourpaint/pixmapfx/kpeffectbalance.cpp517
1 files changed, 517 insertions, 0 deletions
diff --git a/kolourpaint/pixmapfx/kpeffectbalance.cpp b/kolourpaint/pixmapfx/kpeffectbalance.cpp
new file mode 100644
index 00000000..f4494d29
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpeffectbalance.cpp
@@ -0,0 +1,517 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <[email protected]>
+ 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 <qfontmetrics.h>
+#include <qimage.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpixmap.h>
+#include <qpushbutton.h>
+
+#include <kcombobox.h>
+#include <kdebug.h>
+#include <kimageeffect.h>
+#include <klocale.h>
+#include <knuminput.h>
+
+#include <kppixmapfx.h>
+
+
+#if DEBUG_KP_EFFECT_BALANCE
+ #include <qdatetime.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 (qRound (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 QRgb brightnessContrastGammaForRGB (QRgb rgb,
+ int channels,
+ int brightness, int contrast, int gamma)
+{
+ int red = qRed (rgb);
+ int green = qGreen (rgb);
+ int blue = qBlue (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 qRgba (red, green, blue, qAlpha (rgb));
+}
+
+
+// public static
+QPixmap kpEffectBalanceCommand::applyColorEffect (const QPixmap &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;
+ QTime timer; timer.start ();
+#endif
+
+ QImage image = kpPixmapFX::convertToImage (pixmap);
+#if DEBUG_KP_EFFECT_BALANCE
+ kdDebug () << "\tconvertToImage=" << timer.restart () << endl;
+#endif
+
+
+ Q_UINT8 transformRed [256],
+ transformGreen [256],
+ transformBlue [256];
+
+ for (int i = 0; i < 256; i++)
+ {
+ Q_UINT8 applied = (Q_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 QRgb rgb = image.pixel (x, y);
+
+ const Q_UINT8 red = (Q_UINT8) qRed (rgb);
+ const Q_UINT8 green = (Q_UINT8) qGreen (rgb);
+ const Q_UINT8 blue = (Q_UINT8) qBlue (rgb);
+ const Q_UINT8 alpha = (Q_UINT8) qAlpha (rgb);
+
+ image.setPixel (x, y,
+ qRgba (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 QRgb rgb = image.color (i);
+
+ const Q_UINT8 red = (Q_UINT8) qRed (rgb);
+ const Q_UINT8 green = (Q_UINT8) qGreen (rgb);
+ const Q_UINT8 blue = (Q_UINT8) qBlue (rgb);
+ const Q_UINT8 alpha = (Q_UINT8) qAlpha (rgb);
+
+ image.setColor (i,
+ qRgba (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 QPixmap retPixmap = kpPixmapFX::convertToPixmap (image);
+#if DEBUG_KP_EFFECT_BALANCE
+ kdDebug () << "\tconvertToPixmap=" << timer.restart () << endl;
+#endif
+
+ return retPixmap;
+}
+
+// protected virtual [base kpColorEffectCommand]
+QPixmap kpEffectBalanceCommand::applyColorEffect (const QPixmap &pixmap)
+{
+ return applyColorEffect (pixmap, m_channels,
+ m_brightness, m_contrast, m_gamma);
+}
+
+
+
+kpEffectBalanceWidget::kpEffectBalanceWidget (bool actOnSelection,
+ kpMainWindow *mainWindow,
+ QWidget *parent, const char *name)
+ : kpColorEffectWidget (actOnSelection, mainWindow, parent, name)
+{
+ QGridLayout *lay = new QGridLayout (this, 5, 5, marginHint (), spacingHint ());
+
+
+ QLabel *brightnessLabel = new QLabel (i18n ("&Brightness:"), this);
+ m_brightnessInput = new KIntNumInput (0/*value*/, this);
+ m_brightnessInput->setRange (-50, 50, 1/*step*/, true/*slider*/);
+ QPushButton *brightnessResetPushButton = new QPushButton (i18n ("Re&set"), this);
+
+ QLabel *contrastLabel = new QLabel (i18n ("Co&ntrast:"), this);
+ m_contrastInput = new KIntNumInput (0/*value*/, this);
+ m_contrastInput->setRange (-50, 50, 1/*step*/, true/*slider*/);
+ QPushButton *contrastResetPushButton = new QPushButton (i18n ("&Reset"), this);
+
+ QLabel *gammaLabel = new QLabel (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 QLabel (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 () | Qt::AlignRight);
+ QPushButton *gammaResetPushButton = new QPushButton (i18n ("Rese&t"), this);
+
+
+ QWidget *spaceWidget = new QLabel (this);
+ spaceWidget->setFixedSize (1, spacingHint ());
+
+
+ QLabel *channelLabel = new QLabel (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"));
+
+
+ QPushButton *resetPushButton = new QPushButton (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, Qt::AlignRight);
+
+ lay->addWidget (channelLabel, 4, 0);
+ lay->addWidget (m_channelsComboBox, 4, 1, Qt::AlignLeft);
+ //lay->addWidget (resetPushButton, 4, 2, Qt::AlignRight);
+
+ lay->setColStretch (1, 1);
+
+
+ // (no need for settingsChangedDelayed() since BCG effect is so fast :))
+ connect (m_brightnessInput, SIGNAL (valueChanged (int)),
+ this, SIGNAL (settingsChangedNoWaitCursor ()));
+ connect (m_contrastInput, SIGNAL (valueChanged (int)),
+ this, SIGNAL (settingsChangedNoWaitCursor ()));
+
+ connect (m_gammaInput, SIGNAL (valueChanged (int)),
+ this, SLOT (recalculateGammaLabel ()));
+ connect (m_gammaInput, SIGNAL (valueChanged (int)),
+ this, SIGNAL (settingsChangedNoWaitCursor ()));
+
+ connect (m_channelsComboBox, SIGNAL (activated (int)),
+ this, SIGNAL (settingsChanged ()));
+
+ connect (brightnessResetPushButton, SIGNAL (clicked ()),
+ this, SLOT (resetBrightness ()));
+ connect (contrastResetPushButton, SIGNAL (clicked ()),
+ this, SLOT (resetContrast ()));
+ connect (gammaResetPushButton, SIGNAL (clicked ()),
+ this, SLOT (resetGamma ()));
+
+ connect (resetPushButton, SIGNAL (clicked ()),
+ this, SLOT (resetAll ()));
+
+
+ recalculateGammaLabel ();
+}
+
+kpEffectBalanceWidget::~kpEffectBalanceWidget ()
+{
+}
+
+
+// public virtual [base kpColorEffectWidget]
+QString 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]
+QPixmap kpEffectBalanceWidget::applyColorEffect (const QPixmap &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 (
+ " " +
+ QString::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>