summaryrefslogtreecommitdiffstats
path: root/kolourpaint/pixmapfx
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commit47d455dd55be855e4cc691c32f687f723d9247ee (patch)
tree52e236aaa2576bdb3840ebede26619692fed6d7d /kolourpaint/pixmapfx
downloadtdegraphics-47d455dd55be855e4cc691c32f687f723d9247ee.tar.gz
tdegraphics-47d455dd55be855e4cc691c32f687f723d9247ee.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdegraphics@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kolourpaint/pixmapfx')
-rw-r--r--kolourpaint/pixmapfx/Makefile.am19
-rw-r--r--kolourpaint/pixmapfx/kpcoloreffect.cpp168
-rw-r--r--kolourpaint/pixmapfx/kpcoloreffect.h111
-rw-r--r--kolourpaint/pixmapfx/kpeffectbalance.cpp517
-rw-r--r--kolourpaint/pixmapfx/kpeffectbalance.h116
-rw-r--r--kolourpaint/pixmapfx/kpeffectblursharpen.cpp291
-rw-r--r--kolourpaint/pixmapfx/kpeffectblursharpen.h105
-rw-r--r--kolourpaint/pixmapfx/kpeffectemboss.cpp228
-rw-r--r--kolourpaint/pixmapfx/kpeffectemboss.h93
-rw-r--r--kolourpaint/pixmapfx/kpeffectflatten.cpp266
-rw-r--r--kolourpaint/pixmapfx/kpeffectflatten.h115
-rw-r--r--kolourpaint/pixmapfx/kpeffectinvert.cpp315
-rw-r--r--kolourpaint/pixmapfx/kpeffectinvert.h130
-rw-r--r--kolourpaint/pixmapfx/kpeffectreducecolors.cpp446
-rw-r--r--kolourpaint/pixmapfx/kpeffectreducecolors.h110
-rw-r--r--kolourpaint/pixmapfx/kpeffectsdialog.cpp369
-rw-r--r--kolourpaint/pixmapfx/kpeffectsdialog.h90
-rw-r--r--kolourpaint/pixmapfx/kpfloodfill.cpp362
-rw-r--r--kolourpaint/pixmapfx/kpfloodfill.h106
-rw-r--r--kolourpaint/pixmapfx/kppixmapfx.cpp1677
-rw-r--r--kolourpaint/pixmapfx/kppixmapfx.h450
21 files changed, 6084 insertions, 0 deletions
diff --git a/kolourpaint/pixmapfx/Makefile.am b/kolourpaint/pixmapfx/Makefile.am
new file mode 100644
index 00000000..dfa1d697
--- /dev/null
+++ b/kolourpaint/pixmapfx/Makefile.am
@@ -0,0 +1,19 @@
+INCLUDES = -I$(srcdir)/.. -I$(srcdir)/../cursors -I$(srcdir)/../interfaces \
+ -I$(srcdir)/../pixmapfx \
+ -I$(srcdir)/../tools \
+ -I$(srcdir)/../views \
+ -I$(srcdir)/../widgets $(all_includes)
+
+noinst_LTLIBRARIES = libkolourpaintpixmapfx.la
+libkolourpaintpixmapfx_la_SOURCES = kpcoloreffect.cpp \
+ kpeffectbalance.cpp \
+ kpeffectblursharpen.cpp \
+ kpeffectemboss.cpp \
+ kpeffectflatten.cpp \
+ kpeffectinvert.cpp \
+ kpeffectreducecolors.cpp \
+ kpeffectsdialog.cpp \
+ kpfloodfill.cpp \
+ kppixmapfx.cpp
+
+METASOURCES = AUTO
diff --git a/kolourpaint/pixmapfx/kpcoloreffect.cpp b/kolourpaint/pixmapfx/kpcoloreffect.cpp
new file mode 100644
index 00000000..1660c1fa
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpcoloreffect.cpp
@@ -0,0 +1,168 @@
+
+/*
+ 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.
+*/
+
+
+#include <kpcoloreffect.h>
+
+#include <qapplication.h>
+#include <qpixmap.h>
+
+#include <kdialog.h>
+#include <klocale.h>
+
+#include <kpdefs.h>
+#include <kpdocument.h>
+#include <kpmainwindow.h>
+#include <kpselection.h>
+
+
+kpColorEffectCommand::kpColorEffectCommand (const QString &name,
+ bool actOnSelection,
+ kpMainWindow *mainWindow)
+ : kpCommand (mainWindow),
+ m_name (name),
+ m_actOnSelection (actOnSelection),
+ m_oldPixmapPtr (0)
+{
+}
+
+kpColorEffectCommand::~kpColorEffectCommand ()
+{
+ delete m_oldPixmapPtr; m_oldPixmapPtr = 0;
+}
+
+
+// public virtual [base kpCommand]
+QString kpColorEffectCommand::name () const
+{
+ if (m_actOnSelection)
+ return i18n ("Selection: %1").arg (m_name);
+ else
+ return m_name;
+}
+
+
+// public virtual [base kpCommand]
+int kpColorEffectCommand::size () const
+{
+ return kpPixmapFX::pixmapSize (m_oldPixmapPtr);
+}
+
+
+// public virtual [base kpCommand]
+void kpColorEffectCommand::execute ()
+{
+ kpDocument *doc = document ();
+ if (!doc)
+ return;
+
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+
+ const QPixmap oldPixmap = *doc->pixmap (m_actOnSelection);
+
+ if (!isInvertible ())
+ {
+ m_oldPixmapPtr = new QPixmap ();
+ *m_oldPixmapPtr = oldPixmap;
+ }
+
+
+ QPixmap newPixmap = /*pure virtual*/applyColorEffect (oldPixmap);
+
+ doc->setPixmap (m_actOnSelection, newPixmap);
+
+
+ QApplication::restoreOverrideCursor ();
+}
+
+// public virtual [base kpCommand]
+void kpColorEffectCommand::unexecute ()
+{
+ kpDocument *doc = document ();
+ if (!doc)
+ return;
+
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+
+ QPixmap newPixmap;
+
+ if (!isInvertible ())
+ {
+ newPixmap = *m_oldPixmapPtr;
+ }
+ else
+ {
+ newPixmap = /*pure virtual*/applyColorEffect (*doc->pixmap (m_actOnSelection));
+ }
+
+ doc->setPixmap (m_actOnSelection, newPixmap);
+
+
+ delete m_oldPixmapPtr; m_oldPixmapPtr = 0;
+
+
+ QApplication::restoreOverrideCursor ();
+}
+
+
+kpColorEffectWidget::kpColorEffectWidget (bool actOnSelection,
+ kpMainWindow *mainWindow,
+ QWidget *parent, const char *name)
+ : QWidget (parent, name),
+ m_actOnSelection (actOnSelection),
+ m_mainWindow (mainWindow)
+{
+}
+
+kpColorEffectWidget::~kpColorEffectWidget ()
+{
+}
+
+
+// public
+QString kpColorEffectWidget::caption () const
+{
+ return QString::null;
+}
+
+
+// protected
+int kpColorEffectWidget::marginHint () const
+{
+ return 0;
+}
+
+// protected
+int kpColorEffectWidget::spacingHint () const
+{
+ return KDialog::spacingHint ();
+}
+
+
+#include <kpcoloreffect.moc>
diff --git a/kolourpaint/pixmapfx/kpcoloreffect.h b/kolourpaint/pixmapfx/kpcoloreffect.h
new file mode 100644
index 00000000..8b3dfd09
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpcoloreffect.h
@@ -0,0 +1,111 @@
+
+/*
+ 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.
+*/
+
+
+#ifndef KP_COLOR_EFFECT_H
+#define KP_COLOR_EFFECT_H
+
+#include <qstring.h>
+#include <qwidget.h>
+
+#include <kpcommandhistory.h>
+
+class QPixmap;
+
+class kpDocument;
+class kpMainWindow;
+
+
+class kpColorEffectCommand : public kpCommand
+{
+public:
+ kpColorEffectCommand (const QString &name,
+ bool actOnSelection,
+ kpMainWindow *mainWindow);
+ virtual ~kpColorEffectCommand ();
+
+ virtual QString name () const;
+ virtual int size () const;
+
+public:
+ virtual void execute ();
+ virtual void unexecute ();
+
+public:
+ // Return true if applyColorEffect(applyColorEffect(pixmap)) == pixmap
+ // to avoid storing the old pixmap, saving memory.
+ virtual bool isInvertible () const { return false; }
+
+protected:
+ virtual QPixmap applyColorEffect (const QPixmap &pixmap) = 0;
+
+private:
+ QString m_name;
+ bool m_actOnSelection;
+
+ QPixmap *m_oldPixmapPtr;
+};
+
+
+class kpColorEffectWidget : public QWidget
+{
+Q_OBJECT
+
+public:
+ kpColorEffectWidget (bool actOnSelection,
+ kpMainWindow *mainWindow,
+ QWidget *parent, const char *name = 0);
+ virtual ~kpColorEffectWidget ();
+
+signals:
+ void settingsChangedNoWaitCursor ();
+
+ void settingsChanged ();
+
+ // (same as settingsChanged() but preview doesn't update until there
+ // has been no activity for a while - used for sliders in slow effects)
+ void settingsChangedDelayed ();
+
+public:
+ virtual QString caption () const;
+
+ virtual bool isNoOp () const = 0;
+ virtual QPixmap applyColorEffect (const QPixmap &pixmap) = 0;
+
+ virtual kpColorEffectCommand *createCommand () const = 0;
+
+protected:
+ int marginHint () const;
+ int spacingHint () const;
+
+protected:
+ bool m_actOnSelection;
+ kpMainWindow *m_mainWindow;
+};
+
+
+#endif // KP_COLOR_EFFECT_H
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 <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 <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>
diff --git a/kolourpaint/pixmapfx/kpeffectbalance.h b/kolourpaint/pixmapfx/kpeffectbalance.h
new file mode 100644
index 00000000..b045159f
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpeffectbalance.h
@@ -0,0 +1,116 @@
+
+/*
+ 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.
+*/
+
+
+#ifndef KP_EFFECT_BALANCE_H
+#define KP_EFFECT_BALANCE_H
+
+#include <kpcoloreffect.h>
+
+
+class QLabel;
+
+class KComboBox;
+class KIntNumInput;
+
+class kpMainWindowe;
+
+
+class kpEffectBalanceCommand : public kpColorEffectCommand
+{
+public:
+ enum Channel
+ {
+ None = 0,
+ Red = 1, Green = 2, Blue = 4,
+ RGB = Red | Green | Blue
+ };
+
+ // <brightness>, <contrast> & <gamma> are from -50 to 50
+
+ kpEffectBalanceCommand (int channels,
+ int brightness, int contrast, int gamma,
+ bool actOnSelection,
+ kpMainWindow *mainWindow);
+ virtual ~kpEffectBalanceCommand ();
+
+ static QPixmap applyColorEffect (const QPixmap &pixmap,
+ int channels,
+ int brightness, int contrast, int gamma);
+
+protected:
+ virtual QPixmap applyColorEffect (const QPixmap &pixmap);
+
+protected:
+ int m_channels;
+ int m_brightness, m_contrast, m_gamma;
+};
+
+
+class kpEffectBalanceWidget : public kpColorEffectWidget
+{
+Q_OBJECT
+
+public:
+ kpEffectBalanceWidget (bool actOnSelection,
+ kpMainWindow *mainWindow,
+ QWidget *parent, const char *name = 0);
+ virtual ~kpEffectBalanceWidget ();
+
+ virtual QString caption () const;
+
+ virtual bool isNoOp () const;
+ virtual QPixmap applyColorEffect (const QPixmap &pixmap);
+
+ virtual kpColorEffectCommand *createCommand () const;
+
+protected:
+ int channels () const;
+
+ int brightness () const;
+ int contrast () const;
+ int gamma () const;
+
+protected slots:
+ void recalculateGammaLabel ();
+
+ void resetBrightness ();
+ void resetContrast ();
+ void resetGamma ();
+
+ void resetAll ();
+
+protected:
+ KIntNumInput *m_brightnessInput,
+ *m_contrastInput,
+ *m_gammaInput;
+ QLabel *m_gammaLabel;
+ KComboBox *m_channelsComboBox;
+};
+
+
+#endif // KP_EFFECT_BALANCE_H
diff --git a/kolourpaint/pixmapfx/kpeffectblursharpen.cpp b/kolourpaint/pixmapfx/kpeffectblursharpen.cpp
new file mode 100644
index 00000000..50c0b27d
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpeffectblursharpen.cpp
@@ -0,0 +1,291 @@
+
+/*
+ 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_BLUR_SHARPEN 0
+
+
+#include <kpeffectblursharpen.h>
+
+#include <qimage.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpixmap.h>
+#include <qpushbutton.h>
+
+#include <kdebug.h>
+#include <kimageeffect.h>
+#include <klocale.h>
+#include <knuminput.h>
+
+#include <kpmainwindow.h>
+#include <kppixmapfx.h>
+
+
+static QString nameForType (kpEffectBlurSharpenCommand::Type type)
+{
+ if (type == kpEffectBlurSharpenCommand::Blur)
+ return i18n ("Soften");
+ else if (type == kpEffectBlurSharpenCommand::Sharpen)
+ return i18n ("Sharpen");
+ else
+ return QString::null;
+}
+
+
+kpEffectBlurSharpenCommand::kpEffectBlurSharpenCommand (Type type,
+ double radius, double sigma,
+ int repeat,
+ bool actOnSelection,
+ kpMainWindow *mainWindow)
+ : kpColorEffectCommand (::nameForType (type), actOnSelection, mainWindow),
+ m_type (type),
+ m_radius (radius), m_sigma (sigma),
+ m_repeat (repeat)
+{
+}
+
+kpEffectBlurSharpenCommand::~kpEffectBlurSharpenCommand ()
+{
+}
+
+
+// public static
+QPixmap kpEffectBlurSharpenCommand::apply (const QPixmap &pixmap,
+ Type type, double radius, double sigma,
+ int repeat)
+{
+#if DEBUG_KP_EFFECT_BLUR_SHARPEN
+ kdDebug () << "kpEffectBlurSharpenCommand::apply(type="
+ << int (type)
+ << " radius=" << radius
+ << " sigma=" << sigma
+ << " repeat=" << repeat
+ << ")"
+ << endl;
+#endif
+
+ // (KImageEffect::(blur|sharpen)() ignores mask)
+ QPixmap usePixmap = kpPixmapFX::pixmapWithDefinedTransparentPixels (
+ pixmap,
+ Qt::white/*arbitrarily chosen*/);
+
+
+ QImage image = kpPixmapFX::convertToImage (usePixmap);
+
+ for (int i = 0; i < repeat; i++)
+ {
+ if (type == Blur)
+ image = KImageEffect::blur (image, radius, sigma);
+ else if (type == Sharpen)
+ image = KImageEffect::sharpen (image, radius, sigma);
+ }
+
+ QPixmap retPixmap = kpPixmapFX::convertToPixmap (image);
+
+
+ // KImageEffect::(blur|sharpen)() nukes mask - restore it
+ if (usePixmap.mask ())
+ retPixmap.setMask (*usePixmap.mask ());
+
+
+ return retPixmap;
+}
+
+// protected virtual [base kpColorEffectCommand]
+QPixmap kpEffectBlurSharpenCommand::applyColorEffect (const QPixmap &pixmap)
+{
+ return apply (pixmap, m_type, m_radius, m_sigma, m_repeat);
+}
+
+
+
+kpEffectBlurSharpenWidget::kpEffectBlurSharpenWidget (bool actOnSelection,
+ kpMainWindow *mainWindow,
+ QWidget *parent, const char *name)
+ : kpColorEffectWidget (actOnSelection, mainWindow, parent, name)
+{
+ QGridLayout *lay = new QGridLayout (this, 4, 2, marginHint (), spacingHint ());
+
+
+ QLabel *amountLabel = new QLabel (i18n ("&Amount:"), this);
+ m_amountInput = new KIntNumInput (this);
+ m_amountInput->setRange (-10, 10, 1/*step*/, true/*slider*/);
+
+ m_typeLabel = new QLabel (this);
+
+
+ amountLabel->setBuddy (m_amountInput);
+
+
+ lay->addWidget (amountLabel, 0, 0);
+ lay->addWidget (m_amountInput, 0, 1);
+
+ lay->addMultiCellWidget (m_typeLabel, 1, 1, 0, 1, Qt::AlignCenter);
+
+ lay->setColStretch (1, 1);
+
+
+ connect (m_amountInput, SIGNAL (valueChanged (int)),
+ this, SIGNAL (settingsChangedDelayed ()));
+
+ connect (m_amountInput, SIGNAL (valueChanged (int)),
+ this, SLOT (slotUpdateTypeLabel ()));
+}
+
+kpEffectBlurSharpenWidget::~kpEffectBlurSharpenWidget ()
+{
+}
+
+
+// public virtual [base kpColorEffectWidget]
+QString kpEffectBlurSharpenWidget::caption () const
+{
+ return QString::null;
+}
+
+
+// public virtual [base kpColorEffectWidget]
+bool kpEffectBlurSharpenWidget::isNoOp () const
+{
+ return (type () == kpEffectBlurSharpenCommand::None);
+}
+
+// public virtual [base kpColorEffectWidget]
+QPixmap kpEffectBlurSharpenWidget::applyColorEffect (const QPixmap &pixmap)
+{
+ return kpEffectBlurSharpenCommand::apply (pixmap,
+ type (), radius (), sigma (), repeat ());
+}
+
+// public virtual [base kpColorEffectWidget]
+kpColorEffectCommand *kpEffectBlurSharpenWidget::createCommand () const
+{
+ return new kpEffectBlurSharpenCommand (type (), radius (), sigma (), repeat (),
+ m_actOnSelection,
+ m_mainWindow);
+}
+
+
+// protected slot
+void kpEffectBlurSharpenWidget::slotUpdateTypeLabel ()
+{
+ QString text = ::nameForType (type ());
+
+#if DEBUG_KP_EFFECT_BLUR_SHARPEN
+ kdDebug () << "kpEffectBlurSharpenWidget::slotUpdateTypeLabel() text="
+ << text << endl;
+#endif
+ m_typeLabel->setText (text);
+}
+
+
+// protected
+kpEffectBlurSharpenCommand::Type kpEffectBlurSharpenWidget::type () const
+{
+ if (m_amountInput->value () == 0)
+ return kpEffectBlurSharpenCommand::None;
+ else if (m_amountInput->value () < 0)
+ return kpEffectBlurSharpenCommand::Blur;
+ else
+ return kpEffectBlurSharpenCommand::Sharpen;
+}
+
+// The numbers that follow were picked by experimentation.
+// I still have no idea what "radius" and "sigma" mean
+// (even after reading the API).
+
+// protected
+double kpEffectBlurSharpenWidget::radius () const
+{
+ if (m_amountInput->value () == 0)
+ return 0;
+
+ if (m_amountInput->value () < 0)
+ {
+ return 8;
+ }
+ else
+ {
+ const double SharpenMin = .1;
+ const double SharpenMax = 2.5;
+
+ return SharpenMin +
+ (m_amountInput->value () - 1) *
+ (SharpenMax - SharpenMin) /
+ (m_amountInput->maxValue () - 1);
+ }
+}
+
+// protected
+double kpEffectBlurSharpenWidget::sigma () const
+{
+ if (m_amountInput->value () == 0)
+ return 0;
+
+ if (m_amountInput->value () < 0)
+ {
+ const double BlurMin = .5;
+ const double BlurMax = 4;
+
+ return BlurMin +
+ (-m_amountInput->value () - 1) *
+ (BlurMax - BlurMin) /
+ (-m_amountInput->minValue () - 1);
+ }
+ else
+ {
+ const double SharpenMin = .5;
+ const double SharpenMax = 3.0;
+
+ return SharpenMin +
+ (m_amountInput->value () - 1) *
+ (SharpenMax - SharpenMin) /
+ (m_amountInput->maxValue () - 1);
+ }
+}
+
+// protected
+int kpEffectBlurSharpenWidget::repeat () const
+{
+ if (m_amountInput->value () == 0)
+ return 0;
+
+ if (m_amountInput->value () < 0)
+ return 1;
+ else
+ {
+ const double SharpenMin = 1;
+ const double SharpenMax = 2;
+
+ return qRound (SharpenMin +
+ (m_amountInput->value () - 1) *
+ (SharpenMax - SharpenMin) /
+ (m_amountInput->maxValue () - 1));
+ }
+}
+
+#include <kpeffectblursharpen.moc>
diff --git a/kolourpaint/pixmapfx/kpeffectblursharpen.h b/kolourpaint/pixmapfx/kpeffectblursharpen.h
new file mode 100644
index 00000000..3b12def1
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpeffectblursharpen.h
@@ -0,0 +1,105 @@
+
+/*
+ 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.
+*/
+
+
+#ifndef KP_EFFECT_BLUR_SHARPEN_H
+#define KP_EFFECT_BLUR_SHARPEN_H
+
+
+#include <kpcolor.h>
+
+#include <kpcoloreffect.h>
+
+
+class QLabel;
+
+class KIntNumInput;
+
+class kpMainWindow;
+
+
+class kpEffectBlurSharpenCommand : public kpColorEffectCommand
+{
+public:
+ enum Type
+ {
+ None = 0, Blur, Sharpen
+ };
+
+ kpEffectBlurSharpenCommand (Type type,
+ double radius, double sigma,
+ int repeat,
+ bool actOnSelection,
+ kpMainWindow *mainWindow);
+ virtual ~kpEffectBlurSharpenCommand ();
+
+ static QPixmap apply (const QPixmap &pixmap,
+ Type type, double radius, double sigma,
+ int repeat);
+
+protected:
+ virtual QPixmap applyColorEffect (const QPixmap &pixmap);
+
+protected:
+ Type m_type;
+ double m_radius, m_sigma;
+ int m_repeat;
+};
+
+
+class kpEffectBlurSharpenWidget : public kpColorEffectWidget
+{
+Q_OBJECT
+
+public:
+ kpEffectBlurSharpenWidget (bool actOnSelection,
+ kpMainWindow *mainWindow,
+ QWidget *parent, const char *name = 0);
+ virtual ~kpEffectBlurSharpenWidget ();
+
+ virtual QString caption () const;
+
+ virtual bool isNoOp () const;
+ virtual QPixmap applyColorEffect (const QPixmap &pixmap);
+
+ virtual kpColorEffectCommand *createCommand () const;
+
+protected slots:
+ void slotUpdateTypeLabel ();
+
+protected:
+ kpEffectBlurSharpenCommand::Type type () const;
+ double radius () const;
+ double sigma () const;
+ int repeat () const;
+
+ KIntNumInput *m_amountInput;
+ QLabel *m_typeLabel;
+};
+
+
+#endif // KP_EFFECT_BLUR_SHARPEN_H
diff --git a/kolourpaint/pixmapfx/kpeffectemboss.cpp b/kolourpaint/pixmapfx/kpeffectemboss.cpp
new file mode 100644
index 00000000..e33f3a42
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpeffectemboss.cpp
@@ -0,0 +1,228 @@
+
+/*
+ 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_EMBOSS 0
+
+
+#include <kpeffectemboss.h>
+
+#include <qcheckbox.h>
+#include <qimage.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpixmap.h>
+#include <qpushbutton.h>
+
+#include <kdebug.h>
+#include <kimageeffect.h>
+#include <klocale.h>
+#include <knuminput.h>
+
+#include <kpmainwindow.h>
+#include <kppixmapfx.h>
+
+
+kpEffectEmbossCommand::kpEffectEmbossCommand (double radius, double sigma,
+ int repeat,
+ bool actOnSelection,
+ kpMainWindow *mainWindow)
+ : kpColorEffectCommand (i18n ("Emboss"), actOnSelection, mainWindow),
+ m_radius (radius), m_sigma (sigma),
+ m_repeat (repeat)
+{
+}
+
+kpEffectEmbossCommand::~kpEffectEmbossCommand ()
+{
+}
+
+
+// public static
+QPixmap kpEffectEmbossCommand::apply (const QPixmap &pixmap,
+ double radius, double sigma,
+ int repeat)
+{
+#if DEBUG_KP_EFFECT_EMBOSS
+ kdDebug () << "kpEffectEmbossCommand::apply()"
+ << " radius=" << radius
+ << " sigma=" << sigma
+ << " repeat=" << repeat
+ << ")"
+ << endl;
+#endif
+
+ // (KImageEffect::emboss() ignores mask)
+ QPixmap usePixmap = kpPixmapFX::pixmapWithDefinedTransparentPixels (
+ pixmap,
+ Qt::white/*arbitrarily chosen*/);
+
+
+ QImage image = kpPixmapFX::convertToImage (usePixmap);
+
+ for (int i = 0; i < repeat; i++)
+ {
+ image = KImageEffect::emboss (image, radius, sigma);
+ }
+
+ QPixmap retPixmap = kpPixmapFX::convertToPixmap (image);
+
+
+ // KImageEffect::emboss() nukes mask - restore it
+ if (usePixmap.mask ())
+ retPixmap.setMask (*usePixmap.mask ());
+
+
+ return retPixmap;
+}
+
+// protected virtual [base kpColorEffectCommand]
+QPixmap kpEffectEmbossCommand::applyColorEffect (const QPixmap &pixmap)
+{
+ return apply (pixmap, m_radius, m_sigma, m_repeat);
+}
+
+
+
+kpEffectEmbossWidget::kpEffectEmbossWidget (bool actOnSelection,
+ kpMainWindow *mainWindow,
+ QWidget *parent, const char *name)
+ : kpColorEffectWidget (actOnSelection, mainWindow, parent, name)
+{
+ QGridLayout *lay = new QGridLayout (this, 4, 2, marginHint (), spacingHint ());
+
+
+#if 0
+ QLabel *amountLabel = new QLabel (i18n ("&Amount:"), this);
+ m_amountInput = new KIntNumInput (this);
+ m_amountInput->setRange (0, 10, 1/*step*/, true/*slider*/);
+ m_amountInput->setSpecialValueText (i18n ("None"));
+
+
+ amountLabel->setBuddy (m_amountInput);
+
+
+ lay->addWidget (amountLabel, 0, 0);
+ lay->addWidget (m_amountInput, 0, 1);
+
+ lay->setColStretch (1, 1);
+
+
+ connect (m_amountInput, SIGNAL (valueChanged (int)),
+ this, SIGNAL (settingsChanged ()));
+#endif
+
+ m_enableCheckBox = new QCheckBox (i18n ("E&nable"), this);
+
+
+ lay->addMultiCellWidget (m_enableCheckBox, 0, 0, 0, 1, Qt::AlignCenter);
+
+
+ // (settingsChangedDelayed() instead of settingsChanged() so that the
+ // user can quickly press OK to apply effect to document directly and
+ // not have to wait for the also slow preview)
+ connect (m_enableCheckBox, SIGNAL (toggled (bool)),
+ this, SIGNAL (settingsChangedDelayed ()));
+}
+
+kpEffectEmbossWidget::~kpEffectEmbossWidget ()
+{
+}
+
+
+// public virtual [base kpColorEffectWidget]
+QString kpEffectEmbossWidget::caption () const
+{
+ return QString::null;
+}
+
+
+// public virtual [base kpColorEffectWidget]
+bool kpEffectEmbossWidget::isNoOp () const
+{
+ //return (m_amountInput->value () == 0);
+ return !m_enableCheckBox->isChecked ();
+}
+
+// public virtual [base kpColorEffectWidget]
+QPixmap kpEffectEmbossWidget::applyColorEffect (const QPixmap &pixmap)
+{
+ if (isNoOp ())
+ return pixmap;
+
+ return kpEffectEmbossCommand::apply (pixmap, radius (), sigma (), repeat ());
+}
+
+// public virtual [base kpColorEffectWidget]
+kpColorEffectCommand *kpEffectEmbossWidget::createCommand () const
+{
+ return new kpEffectEmbossCommand (radius (), sigma (), repeat (),
+ m_actOnSelection,
+ m_mainWindow);
+}
+
+
+// The numbers that follow were picked by experimentation.
+// I still have no idea what "radius" and "sigma" mean
+// (even after reading the API).
+
+// protected
+double kpEffectEmbossWidget::radius () const
+{
+ //if (m_amountInput->value () == 0)
+ // return 0;
+
+ return 0;
+}
+
+
+// protected
+double kpEffectEmbossWidget::sigma () const
+{
+#if 0
+ if (m_amountInput->value () == 0)
+ return 0;
+
+ const double Min = 1;
+ const double Max = 1.2;
+
+ return Min +
+ (m_amountInput->maxValue () - m_amountInput->value ()) *
+ (Max - Min) /
+ (m_amountInput->maxValue () - 1);
+#endif
+
+ return 1;
+}
+
+// protected
+int kpEffectEmbossWidget::repeat () const
+{
+ return 1;
+}
+
+
+#include <kpeffectemboss.moc>
diff --git a/kolourpaint/pixmapfx/kpeffectemboss.h b/kolourpaint/pixmapfx/kpeffectemboss.h
new file mode 100644
index 00000000..0234627f
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpeffectemboss.h
@@ -0,0 +1,93 @@
+
+/*
+ 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.
+*/
+
+
+#ifndef KP_EFFECT_EMBOSS_H
+#define KP_EFFECT_EMBOSS_H
+
+
+#include <kpcolor.h>
+
+#include <kpcoloreffect.h>
+
+
+class QCheckBox;
+class KIntNumInput;
+
+class kpMainWindow;
+
+
+class kpEffectEmbossCommand : public kpColorEffectCommand
+{
+public:
+ kpEffectEmbossCommand (double radius, double sigma,
+ int repeat,
+ bool actOnSelection,
+ kpMainWindow *mainWindow);
+ virtual ~kpEffectEmbossCommand ();
+
+ static QPixmap apply (const QPixmap &pixmap,
+ double radius, double sigma,
+ int repeat);
+
+protected:
+ virtual QPixmap applyColorEffect (const QPixmap &pixmap);
+
+protected:
+ double m_radius, m_sigma;
+ int m_repeat;
+};
+
+
+class kpEffectEmbossWidget : public kpColorEffectWidget
+{
+Q_OBJECT
+
+public:
+ kpEffectEmbossWidget (bool actOnSelection,
+ kpMainWindow *mainWindow,
+ QWidget *parent, const char *name = 0);
+ virtual ~kpEffectEmbossWidget ();
+
+ virtual QString caption () const;
+
+ virtual bool isNoOp () const;
+ virtual QPixmap applyColorEffect (const QPixmap &pixmap);
+
+ virtual kpColorEffectCommand *createCommand () const;
+
+protected:
+ double radius () const;
+ double sigma () const;
+ int repeat () const;
+
+ //KIntNumInput *m_amountInput;
+ QCheckBox *m_enableCheckBox;
+};
+
+
+#endif // KP_EFFECT_EMBOSS_H
diff --git a/kolourpaint/pixmapfx/kpeffectflatten.cpp b/kolourpaint/pixmapfx/kpeffectflatten.cpp
new file mode 100644
index 00000000..6a81bca0
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpeffectflatten.cpp
@@ -0,0 +1,266 @@
+
+/*
+ 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_FLATTEN 0
+
+
+#include <kpeffectflatten.h>
+
+#include <qcheckbox.h>
+#include <qimage.h>
+#include <qlayout.h>
+#include <qpixmap.h>
+#include <qvbox.h>
+
+#include <kcolorbutton.h>
+#include <kconfig.h>
+#include <kdialog.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kimageeffect.h>
+#include <klocale.h>
+
+#include <kpdefs.h>
+#include <kppixmapfx.h>
+
+
+//
+// kpEffectFlattenCommand
+//
+
+kpEffectFlattenCommand::kpEffectFlattenCommand (const QColor &color1,
+ const QColor &color2,
+ bool actOnSelection,
+ kpMainWindow *mainWindow)
+ : kpColorEffectCommand (i18n ("Flatten"), actOnSelection, mainWindow),
+ m_color1 (color1), m_color2 (color2)
+{
+}
+
+kpEffectFlattenCommand::~kpEffectFlattenCommand ()
+{
+}
+
+
+// public static
+void kpEffectFlattenCommand::apply (QPixmap *destPixmapPtr,
+ const QColor &color1, const QColor &color2)
+{
+ if (!destPixmapPtr)
+ return;
+
+ QImage image = kpPixmapFX::convertToImage (*destPixmapPtr);
+ apply (&image, color1, color2);
+ *destPixmapPtr = kpPixmapFX::convertToPixmap (image);
+}
+
+// public static
+QPixmap kpEffectFlattenCommand::apply (const QPixmap &pm,
+ const QColor &color1, const QColor &color2)
+{
+ QImage image = kpPixmapFX::convertToImage (pm);
+ apply (&image, color1, color2);
+ return kpPixmapFX::convertToPixmap (image);
+}
+
+// public static
+void kpEffectFlattenCommand::apply (QImage *destImagePtr,
+ const QColor &color1, const QColor &color2)
+{
+ if (!destImagePtr)
+ return;
+
+ KImageEffect::flatten (*destImagePtr/*ref*/, color1, color2);
+}
+
+// public static
+QImage kpEffectFlattenCommand::apply (const QImage &img,
+ const QColor &color1, const QColor &color2)
+{
+ QImage retImage = img;
+ apply (&retImage, color1, color2);
+ return retImage;
+}
+
+
+//
+// kpEffectFlattenCommand implements kpColorEffectCommand interface
+//
+
+// protected virtual [base kpColorEffectCommand]
+QPixmap kpEffectFlattenCommand::applyColorEffect (const QPixmap &pixmap)
+{
+ return apply (pixmap, m_color1, m_color2);
+}
+
+
+//
+// kpEffectFlattenWidget
+//
+
+// public static
+// Don't initialise globally when we probably don't have a colour
+// allocation context. This way, the colours aren't sometimes invalid
+// (e.g. at 8-bit).
+QColor kpEffectFlattenWidget::s_lastColor1;
+QColor kpEffectFlattenWidget::s_lastColor2;
+
+kpEffectFlattenWidget::kpEffectFlattenWidget (bool actOnSelection,
+ kpMainWindow *mainWindow,
+ QWidget *parent,
+ const char *name)
+ : kpColorEffectWidget (actOnSelection, mainWindow, parent, name)
+{
+ if (!s_lastColor1.isValid () || !s_lastColor2.isValid ())
+ {
+ KConfigGroupSaver cfgGroupSaver (KGlobal::config (), kpSettingsGroupFlattenEffect);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ s_lastColor1 = cfg->readColorEntry (kpSettingFlattenEffectColor1);
+ if (!s_lastColor1.isValid ())
+ s_lastColor1 = Qt::red;
+
+ s_lastColor2 = cfg->readColorEntry (kpSettingFlattenEffectColor2);
+ if (!s_lastColor2.isValid ())
+ s_lastColor2 = Qt::blue;
+ }
+
+
+ m_enableCheckBox = new QCheckBox (i18n ("E&nable"), this);
+
+ QVBox *colorButtonContainer = new QVBox (this);
+ colorButtonContainer->setMargin (KDialog::marginHint () / 2);
+ colorButtonContainer->setSpacing (spacingHint ());
+ m_color1Button = new KColorButton (s_lastColor1, colorButtonContainer);
+ m_color2Button = new KColorButton (s_lastColor2, colorButtonContainer);
+
+
+ m_color1Button->setEnabled (false);
+ m_color2Button->setEnabled (false);
+
+
+ QVBoxLayout *lay = new QVBoxLayout (this, marginHint (), spacingHint ());
+ lay->addWidget (m_enableCheckBox);
+ lay->addWidget (colorButtonContainer);
+
+
+ connect (m_enableCheckBox, SIGNAL (toggled (bool)),
+ this, SLOT (slotEnableChanged (bool)));
+
+ connect (m_color1Button, SIGNAL (changed (const QColor &)),
+ this, SIGNAL (settingsChanged ()));
+ connect (m_color2Button, SIGNAL (changed (const QColor &)),
+ this, SIGNAL (settingsChanged ()));
+}
+
+kpEffectFlattenWidget::~kpEffectFlattenWidget ()
+{
+ s_lastColor1 = color1 ();
+ s_lastColor2 = color2 ();
+
+
+ KConfigGroupSaver cfgGroupSaver (KGlobal::config (), kpSettingsGroupFlattenEffect);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ cfg->writeEntry (kpSettingFlattenEffectColor1, s_lastColor1);
+ cfg->writeEntry (kpSettingFlattenEffectColor2, s_lastColor2);
+ cfg->sync ();
+}
+
+
+// public
+QColor kpEffectFlattenWidget::color1 () const
+{
+ return m_color1Button->color ();
+}
+
+// public
+QColor kpEffectFlattenWidget::color2 () const
+{
+ return m_color2Button->color ();
+}
+
+
+//
+// kpEffectFlattenWidget implements kpColorEffectWidget interface
+//
+
+// public virtual [base kpColorEffectWidget]
+QString kpEffectFlattenWidget::caption () const
+{
+ return i18n ("Colors");
+}
+
+
+// public virtual [base kpColorEffectWidget]
+bool kpEffectFlattenWidget::isNoOp () const
+{
+ return !m_enableCheckBox->isChecked ();
+}
+
+// public virtual [base kpColorEffectWidget]
+QPixmap kpEffectFlattenWidget::applyColorEffect (const QPixmap &pixmap)
+{
+#if DEBUG_KP_EFFECT_FLATTEN
+ kdDebug () << "kpEffectFlattenWidget::applyColorEffect() nop="
+ << isNoOp () << endl;
+#endif
+
+ if (isNoOp ())
+ return pixmap;
+
+ return kpEffectFlattenCommand::apply (pixmap, color1 (), color2 ());
+}
+
+
+// public virtual [base kpColorEffectWidget]
+kpColorEffectCommand *kpEffectFlattenWidget::createCommand () const
+{
+ return new kpEffectFlattenCommand (color1 (), color2 (),
+ m_actOnSelection,
+ m_mainWindow);
+}
+
+
+// protected slot:
+void kpEffectFlattenWidget::slotEnableChanged (bool enable)
+{
+#if DEBUG_KP_EFFECT_FLATTEN
+ kdDebug () << "kpEffectFlattenWidget::slotEnableChanged(" << enable
+ << ") enableButton=" << m_enableCheckBox->isChecked ()
+ << endl;
+#endif
+
+ m_color1Button->setEnabled (enable);
+ m_color2Button->setEnabled (enable);
+
+ emit settingsChanged ();
+}
+
+
+#include <kpeffectflatten.moc>
+
diff --git a/kolourpaint/pixmapfx/kpeffectflatten.h b/kolourpaint/pixmapfx/kpeffectflatten.h
new file mode 100644
index 00000000..79c9bbaf
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpeffectflatten.h
@@ -0,0 +1,115 @@
+
+/*
+ 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.
+*/
+
+
+#ifndef KP_EFFECT_FLATTEN_H
+#define KP_EFFECT_FLATTEN_H
+
+
+#include <qcolor.h>
+
+#include <kpcoloreffect.h>
+
+
+class QCheckBox;
+class QImage;
+
+class KColorButton;
+
+class kpMainWindow;
+
+
+class kpEffectFlattenCommand : public kpColorEffectCommand
+{
+public:
+ kpEffectFlattenCommand (const QColor &color1, const QColor &color2,
+ bool actOnSelection,
+ kpMainWindow *mainWindow);
+ virtual ~kpEffectFlattenCommand ();
+
+
+ static void apply (QPixmap *destPixmapPtr,
+ const QColor &color1, const QColor &color2);
+ static QPixmap apply (const QPixmap &pm,
+ const QColor &color1, const QColor &color2);
+ static void apply (QImage *destImagePtr,
+ const QColor &color1, const QColor &color2);
+ static QImage apply (const QImage &img,
+ const QColor &color1, const QColor &color2);
+
+
+ //
+ // kpColorEffectCommand interface
+ //
+
+protected:
+ virtual QPixmap applyColorEffect (const QPixmap &pixmap);
+
+ QColor m_color1, m_color2;
+};
+
+
+class kpEffectFlattenWidget : public kpColorEffectWidget
+{
+Q_OBJECT
+
+public:
+ kpEffectFlattenWidget (bool actOnSelection,
+ kpMainWindow *mainWindow,
+ QWidget *parent, const char *name = 0);
+ virtual ~kpEffectFlattenWidget ();
+
+
+ static QColor s_lastColor1, s_lastColor2;
+
+
+ QColor color1 () const;
+ QColor color2 () const;
+
+
+ //
+ // kpColorEffectWidget interface
+ //
+
+ virtual QString caption () const;
+
+ virtual bool isNoOp () const;
+ virtual QPixmap applyColorEffect (const QPixmap &pixmap);
+
+ virtual kpColorEffectCommand *createCommand () const;
+
+protected slots:
+ void slotEnableChanged (bool enable);
+
+protected:
+ QCheckBox *m_enableCheckBox;
+ KColorButton *m_color1Button, *m_color2Button;
+};
+
+
+
+#endif // KP_EFFECT_FLATTEN_H
diff --git a/kolourpaint/pixmapfx/kpeffectinvert.cpp b/kolourpaint/pixmapfx/kpeffectinvert.cpp
new file mode 100644
index 00000000..b9bb00a8
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpeffectinvert.cpp
@@ -0,0 +1,315 @@
+
+/*
+ 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_INVERT 0
+
+
+#include <kpeffectinvert.h>
+
+#include <qcheckbox.h>
+#include <qimage.h>
+#include <qlayout.h>
+#include <qpixmap.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kppixmapfx.h>
+
+
+//
+// kpEffectInvertCommand
+//
+
+kpEffectInvertCommand::kpEffectInvertCommand (int channels,
+ bool actOnSelection,
+ kpMainWindow *mainWindow)
+ : kpColorEffectCommand (channels == RGB ?
+ i18n ("Invert Colors") : i18n ("Invert"),
+ actOnSelection, mainWindow),
+ m_channels (channels)
+{
+}
+
+kpEffectInvertCommand::kpEffectInvertCommand (bool actOnSelection,
+ kpMainWindow *mainWindow)
+ : kpColorEffectCommand (i18n ("Invert Colors"), actOnSelection, mainWindow),
+ m_channels (RGB)
+{
+}
+
+kpEffectInvertCommand::~kpEffectInvertCommand ()
+{
+}
+
+
+// public static
+void kpEffectInvertCommand::apply (QPixmap *destPixmapPtr, int channels)
+{
+ QImage image = kpPixmapFX::convertToImage (*destPixmapPtr);
+ apply (&image, channels);
+ *destPixmapPtr = kpPixmapFX::convertToPixmap (image);
+}
+
+// public static
+QPixmap kpEffectInvertCommand::apply (const QPixmap &pm, int channels)
+{
+ QImage image = kpPixmapFX::convertToImage (pm);
+ apply (&image, channels);
+ return kpPixmapFX::convertToPixmap (image);
+}
+
+// public static
+void kpEffectInvertCommand::apply (QImage *destImagePtr, int channels)
+{
+ QRgb mask = qRgba ((channels & Red) ? 0xFF : 0,
+ (channels & Green) ? 0xFF : 0,
+ (channels & Blue) ? 0xFF : 0,
+ 0/*don't invert alpha*/);
+#if DEBUG_KP_EFFECT_INVERT
+ kdDebug () << "kpEffectInvertCommand::apply(channels=" << channels
+ << ") mask=" << (int *) mask
+ << endl;
+#endif
+
+ if (destImagePtr->depth () > 8)
+ {
+ #if 0
+ // SYNC: TODO: Qt BUG - invertAlpha argument is inverted!!!
+ destImagePtr->invertPixels (true/*no invert alpha (Qt 3.2)*/);
+ #else
+ // Above version works for Qt 3.2 at least.
+ // But this version will always work (slower, though) and supports
+ // inverting particular channels.
+ for (int y = 0; y < destImagePtr->height (); y++)
+ {
+ for (int x = 0; x < destImagePtr->width (); x++)
+ {
+ destImagePtr->setPixel (x, y, destImagePtr->pixel (x, y) ^ mask);
+ }
+ }
+ #endif
+ }
+ else
+ {
+ for (int i = 0; i < destImagePtr->numColors (); i++)
+ {
+ destImagePtr->setColor (i, destImagePtr->color (i) ^ mask);
+ }
+ }
+}
+
+// public static
+QImage kpEffectInvertCommand::apply (const QImage &img, int channels)
+{
+ QImage retImage = img;
+ apply (&retImage, channels);
+ return retImage;
+}
+
+
+//
+// kpEffectInvertCommand implements kpColorEffectCommand interface
+//
+
+// protected virtual [base kpColorEffectCommand]
+QPixmap kpEffectInvertCommand::applyColorEffect (const QPixmap &pixmap)
+{
+ return apply (pixmap, m_channels);
+}
+
+
+//
+// kpEffectInvertWidget
+//
+
+kpEffectInvertWidget::kpEffectInvertWidget (bool actOnSelection,
+ kpMainWindow *mainWindow,
+ QWidget *parent,
+ const char *name)
+ : kpColorEffectWidget (actOnSelection, mainWindow, parent, name)
+{
+ QVBoxLayout *topLevelLay = new QVBoxLayout (this, marginHint (), spacingHint ());
+
+
+ QWidget *centerWidget = new QWidget (this);
+ topLevelLay->addWidget (centerWidget, 0/*stretch*/, Qt::AlignCenter);
+
+
+ QVBoxLayout *centerWidgetLay = new QVBoxLayout (centerWidget,
+ 0/*margin*/,
+ spacingHint ());
+
+
+ m_redCheckBox = new QCheckBox (i18n ("&Red"), centerWidget);
+ m_greenCheckBox = new QCheckBox (i18n ("&Green"), centerWidget);
+ m_blueCheckBox = new QCheckBox (i18n ("&Blue"), centerWidget);
+
+ QWidget *spaceWidget = new QWidget (centerWidget);
+ spaceWidget->setFixedSize (1, spacingHint ());
+
+ m_allCheckBox = new QCheckBox (i18n ("&All"), centerWidget);
+
+
+ m_redCheckBox->setChecked (false);
+ m_greenCheckBox->setChecked (false);
+ m_blueCheckBox->setChecked (false);
+
+ m_allCheckBox->setChecked (false);
+
+
+ centerWidgetLay->addWidget (m_redCheckBox);
+ centerWidgetLay->addWidget (m_greenCheckBox);
+ centerWidgetLay->addWidget (m_blueCheckBox);
+
+ centerWidgetLay->addWidget (spaceWidget);
+
+ centerWidgetLay->addWidget (m_allCheckBox);
+
+
+ m_inSignalHandler = false;
+ connect (m_redCheckBox, SIGNAL (toggled (bool)),
+ this, SLOT (slotRGBCheckBoxToggled ()));
+ connect (m_greenCheckBox, SIGNAL (toggled (bool)),
+ this, SLOT (slotRGBCheckBoxToggled ()));
+ connect (m_blueCheckBox, SIGNAL (toggled (bool)),
+ this, SLOT (slotRGBCheckBoxToggled ()));
+
+ connect (m_allCheckBox, SIGNAL (toggled (bool)),
+ this, SLOT (slotAllCheckBoxToggled ()));
+}
+
+kpEffectInvertWidget::~kpEffectInvertWidget ()
+{
+}
+
+
+// public
+int kpEffectInvertWidget::channels () const
+{
+#if DEBUG_KP_EFFECT_INVERT
+ kdDebug () << "kpEffectInvertWidget::channels()"
+ << " isChecked: r=" << m_redCheckBox->isChecked ()
+ << " g=" << m_greenCheckBox->isChecked ()
+ << " b=" << m_blueCheckBox->isChecked ()
+ << endl;
+#endif
+
+ int channels = 0;
+
+
+ if (m_redCheckBox->isChecked ())
+ channels |= kpEffectInvertCommand::Red;
+
+ if (m_greenCheckBox->isChecked ())
+ channels |= kpEffectInvertCommand::Green;
+
+ if (m_blueCheckBox->isChecked ())
+ channels |= kpEffectInvertCommand::Blue;
+
+
+#if DEBUG_KP_EFFECT_INVERT
+ kdDebug () << "\treturning channels=" << (int *) channels << endl;
+#endif
+ return channels;
+}
+
+
+//
+// kpEffectInvertWidget implements kpColorEffectWidget interface
+//
+
+// public virtual [base kpColorEffectWidget]
+QString kpEffectInvertWidget::caption () const
+{
+ return i18n ("Channels");
+}
+
+
+// public virtual [base kpColorEffectWidget]
+bool kpEffectInvertWidget::isNoOp () const
+{
+ return (channels () == kpEffectInvertCommand::None);
+}
+
+// public virtual [base kpColorEffectWidget]
+QPixmap kpEffectInvertWidget::applyColorEffect (const QPixmap &pixmap)
+{
+ return kpEffectInvertCommand::apply (pixmap, channels ());
+}
+
+
+// public virtual [base kpColorEffectWidget]
+kpColorEffectCommand *kpEffectInvertWidget::createCommand () const
+{
+ return new kpEffectInvertCommand (channels (),
+ m_actOnSelection,
+ m_mainWindow);
+}
+
+
+// protected slots
+void kpEffectInvertWidget::slotRGBCheckBoxToggled ()
+{
+ if (m_inSignalHandler)
+ return;
+
+ m_inSignalHandler = true;
+
+ //blockSignals (true);
+ m_allCheckBox->setChecked (m_redCheckBox->isChecked () &&
+ m_blueCheckBox->isChecked () &&
+ m_greenCheckBox->isChecked ());
+ //blockSignals (false);
+
+ emit settingsChanged ();
+
+ m_inSignalHandler = false;
+}
+
+// protected slot
+void kpEffectInvertWidget::slotAllCheckBoxToggled ()
+{
+ if (m_inSignalHandler)
+ return;
+
+ m_inSignalHandler = true;
+
+ //blockSignals (true);
+ m_redCheckBox->setChecked (m_allCheckBox->isChecked ());
+ m_greenCheckBox->setChecked (m_allCheckBox->isChecked ());
+ m_blueCheckBox->setChecked (m_allCheckBox->isChecked ());
+ //blockSignals (false);
+
+ emit settingsChanged ();
+
+ m_inSignalHandler = false;
+}
+
+
+#include <kpeffectinvert.moc>
+
diff --git a/kolourpaint/pixmapfx/kpeffectinvert.h b/kolourpaint/pixmapfx/kpeffectinvert.h
new file mode 100644
index 00000000..61d6cfda
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpeffectinvert.h
@@ -0,0 +1,130 @@
+
+/*
+ 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.
+*/
+
+
+#ifndef KP_EFFECT_INVERT_H
+#define KP_EFFECT_INVERT_H
+
+
+#include <kpcoloreffect.h>
+
+
+class QCheckBox;
+class QImage;
+
+class kpMainWindow;
+
+
+class kpEffectInvertCommand : public kpColorEffectCommand
+{
+public:
+ enum Channel
+ {
+ None = 0,
+ Red = 1, Green = 2, Blue = 4,
+ RGB = Red | Green | Blue
+ };
+
+ kpEffectInvertCommand (int channels,
+ bool actOnSelection,
+ kpMainWindow *mainWindow);
+ kpEffectInvertCommand (bool actOnSelection,
+ kpMainWindow *mainWindow);
+ virtual ~kpEffectInvertCommand ();
+
+
+ //
+ // Inverts the colours of each pixel in the given image.
+ // These functions differ from QImage::invertPixels() in the following ways:
+ //
+ // 1. for 8-bit images, it inverts the colours of the Colour Table
+ // (this means that you would get visually similar results to inversion
+ // at higher bit depths - rather than a "random-looking" inversion
+ // depending on the contents of the Colour Table)
+ // 2. never inverts the Alpha Buffer
+ //
+
+ static void apply (QPixmap *destPixmapPtr, int channels = RGB);
+ static QPixmap apply (const QPixmap &pm, int channels = RGB);
+ static void apply (QImage *destImagePtr, int channels = RGB);
+ static QImage apply (const QImage &img, int channels = RGB);
+
+
+ //
+ // kpColorEffectCommand interface
+ //
+
+public:
+ virtual bool isInvertible () const { return true; }
+
+protected:
+ virtual QPixmap applyColorEffect (const QPixmap &pixmap);
+
+ int m_channels;
+};
+
+
+class kpEffectInvertWidget : public kpColorEffectWidget
+{
+Q_OBJECT
+
+public:
+ kpEffectInvertWidget (bool actOnSelection,
+ kpMainWindow *mainWindow,
+ QWidget *parent, const char *name = 0);
+ virtual ~kpEffectInvertWidget ();
+
+
+ int channels () const;
+
+
+ //
+ // kpColorEffectWidget interface
+ //
+
+ virtual QString caption () const;
+
+ virtual bool isNoOp () const;
+ virtual QPixmap applyColorEffect (const QPixmap &pixmap);
+
+ virtual kpColorEffectCommand *createCommand () const;
+
+protected slots:
+ void slotRGBCheckBoxToggled ();
+ void slotAllCheckBoxToggled ();
+
+protected:
+ QCheckBox *m_redCheckBox, *m_greenCheckBox, *m_blueCheckBox,
+ *m_allCheckBox;
+
+ // blockSignals() didn't seem to work
+ bool m_inSignalHandler;
+};
+
+
+
+#endif // KP_EFFECT_INVERT_H
diff --git a/kolourpaint/pixmapfx/kpeffectreducecolors.cpp b/kolourpaint/pixmapfx/kpeffectreducecolors.cpp
new file mode 100644
index 00000000..b6eb7a42
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpeffectreducecolors.cpp
@@ -0,0 +1,446 @@
+
+/*
+ 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_REDUCE_COLORS 0
+
+
+#include <kpeffectreducecolors.h>
+
+#include <qbuttongroup.h>
+#include <qcheckbox.h>
+#include <qimage.h>
+#include <qlayout.h>
+#include <qpixmap.h>
+#include <qradiobutton.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kppixmapfx.h>
+
+
+QImage convertImageDepth (const QImage &image, int depth, bool dither)
+{
+#if DEBUG_KP_EFFECT_REDUCE_COLORS
+ kdDebug () << "::convertImageDepth() changing image (w=" << image.width ()
+ << ",h=" << image.height ()
+ << ") depth from " << image.depth ()
+ << " to " << depth
+ << " (dither=" << dither << ")"
+ << endl;
+#endif
+
+ if (image.isNull ())
+ return image;
+
+ if (depth == image.depth ())
+ return image;
+
+
+#if DEBUG_KP_EFFECT_REDUCE_COLORS && 0
+ for (int y = 0; y < image.height (); y++)
+ {
+ for (int x = 0; x < image.width (); x++)
+ {
+ fprintf (stderr, " %08X", image.pixel (x, y));
+ }
+ fprintf (stderr, "\n");
+ }
+#endif
+
+
+ // Hack around Qt's braindead QImage::convertDepth(1, ...) (with
+ // dithering off) which produces pathetic results with an image that
+ // only has 2 colours - sometimes it just gives a completely black
+ // result. Instead, we simply preserve the 2 colours. One use case
+ // is resaving a "colour monochrome" image (<= 2 colours but not
+ // necessarily black & white).
+ if (depth == 1 && !dither)
+ {
+ #if DEBUG_KP_EFFECT_REDUCE_COLORS
+ kdDebug () << "\tinvoking convert-to-depth 1 hack" << endl;
+ #endif
+ QRgb color0, color1;
+ bool color0Valid = false, color1Valid = false;
+
+ bool moreThan2Colors = false;
+
+ QImage monoImage (image.width (), image.height (),
+ 1/*depth*/, 2/*numColors*/, QImage::LittleEndian);
+ #if DEBUG_KP_EFFECT_REDUCE_COLORS
+ kdDebug () << "\t\tinitialising output image w=" << monoImage.width ()
+ << ",h=" << monoImage.height ()
+ << ",d=" << monoImage.depth ()
+ << endl;
+ #endif
+ for (int y = 0; y < image.height (); y++)
+ {
+ for (int x = 0; x < image.width (); x++)
+ {
+ QRgb imagePixel = image.pixel (x, y);
+
+ if (color0Valid && imagePixel == color0)
+ monoImage.setPixel (x, y, 0);
+ else if (color1Valid && imagePixel == color1)
+ monoImage.setPixel (x, y, 1);
+ else if (!color0Valid)
+ {
+ color0 = imagePixel;
+ color0Valid = true;
+ monoImage.setPixel (x, y, 0);
+ #if DEBUG_KP_EFFECT_REDUCE_COLORS
+ kdDebug () << "\t\t\tcolor0=" << (int *) color0
+ << " at x=" << x << ",y=" << y << endl;
+ #endif
+ }
+ else if (!color1Valid)
+ {
+ color1 = imagePixel;
+ color1Valid = true;
+ monoImage.setPixel (x, y, 1);
+ #if DEBUG_KP_EFFECT_REDUCE_COLORS
+ kdDebug () << "\t\t\tcolor1=" << (int *) color1
+ << " at x=" << x << ",y=" << y << endl;
+ #endif
+ }
+ else
+ {
+ #if DEBUG_KP_EFFECT_REDUCE_COLORS
+ kdDebug () << "\t\t\timagePixel=" << (int *) imagePixel
+ << " at x=" << x << ",y=" << y
+ << " moreThan2Colors - abort hack" << endl;
+ #endif
+ moreThan2Colors = true;
+
+ // Dijkstra, this is clearer than double break'ing or
+ // a check in both loops
+ goto exit_loop;
+ }
+ }
+ }
+ exit_loop:
+
+ if (!moreThan2Colors)
+ {
+ monoImage.setColor (0, color0Valid ? color0 : 0xFFFFFF);
+ monoImage.setColor (1, color1Valid ? color1 : 0x000000);
+ return monoImage;
+ }
+ }
+
+
+ QImage retImage = image.convertDepth (depth,
+ Qt::AutoColor |
+ (dither ? Qt::DiffuseDither : Qt::ThresholdDither) |
+ Qt::ThresholdAlphaDither |
+ (dither ? Qt::PreferDither : Qt::AvoidDither));
+
+#if DEBUG_KP_EFFECT_REDUCE_COLORS && 0
+ kdDebug () << "After colour reduction:" << endl;
+ for (int y = 0; y < image.height (); y++)
+ {
+ for (int x = 0; x < image.width (); x++)
+ {
+ fprintf (stderr, " %08X", image.pixel (x, y));
+ }
+ fprintf (stderr, "\n");
+ }
+#endif
+
+ return retImage;
+}
+
+
+//
+// kpEffectReduceColorsCommand
+//
+
+kpEffectReduceColorsCommand::kpEffectReduceColorsCommand (int depth, bool dither,
+ bool actOnSelection,
+ kpMainWindow *mainWindow)
+ : kpColorEffectCommand (commandName (depth, dither), actOnSelection, mainWindow),
+ m_depth (depth), m_dither (dither)
+{
+}
+
+kpEffectReduceColorsCommand::~kpEffectReduceColorsCommand ()
+{
+}
+
+
+// public
+QString kpEffectReduceColorsCommand::commandName (int depth, int dither) const
+{
+ if (depth == 1)
+ {
+ if (dither)
+ return i18n ("Reduce to Monochrome (Dithered)");
+ else
+ return i18n ("Reduce to Monochrome");
+ }
+ else if (depth == 8)
+ {
+ if (dither)
+ return i18n ("Reduce to 256 Color (Dithered)");
+ else
+ return i18n ("Reduce to 256 Color");
+ }
+ else
+ {
+ return QString::null;
+ }
+}
+
+
+// public static
+void kpEffectReduceColorsCommand::apply (QPixmap *destPixmapPtr, int depth, bool dither)
+{
+ if (!destPixmapPtr)
+ return;
+
+ if (depth != 1 && depth != 8)
+ return;
+
+
+ QImage image = kpPixmapFX::convertToImage (*destPixmapPtr);
+
+
+ image = ::convertImageDepth (image, depth, dither);
+
+ if (image.isNull ())
+ return;
+
+
+ QPixmap pixmap = kpPixmapFX::convertToPixmap (image, false/*no dither*/);
+
+
+ // HACK: The above "image.convertDepth()" erases the Alpha Channel
+ // (at least for monochrome).
+ // qpixmap.html says "alpha masks on monochrome images are ignored."
+ //
+ // Put the mask back.
+ //
+ if (destPixmapPtr->mask ())
+ pixmap.setMask (*destPixmapPtr->mask ());
+
+ *destPixmapPtr = pixmap;
+}
+
+// public static
+QPixmap kpEffectReduceColorsCommand::apply (const QPixmap &pm, int depth, bool dither)
+{
+ QPixmap ret = pm;
+ apply (&ret, depth, dither);
+ return ret;
+}
+
+
+//
+// kpEffectReduceColorsCommand implements kpColorEffectCommand interface
+//
+
+// protected virtual [base kpColorEffectCommand]
+QPixmap kpEffectReduceColorsCommand::applyColorEffect (const QPixmap &pixmap)
+{
+ return apply (pixmap, m_depth, m_dither);
+}
+
+
+//
+// kpEffectReduceColorsWidget
+//
+
+kpEffectReduceColorsWidget::kpEffectReduceColorsWidget (bool actOnSelection,
+ kpMainWindow *mainWindow,
+ QWidget *parent,
+ const char *name)
+ : kpColorEffectWidget (actOnSelection, mainWindow, parent, name)
+{
+ QVBoxLayout *lay = new QVBoxLayout (this, marginHint (), spacingHint ());
+
+
+ m_blackAndWhiteRadioButton =
+ new QRadioButton (i18n ("&Monochrome"), this);
+
+ m_blackAndWhiteDitheredRadioButton =
+ new QRadioButton (i18n ("Mo&nochrome (dithered)"), this);
+
+ m_8BitRadioButton = new QRadioButton (i18n ("256 co&lor"), this);
+
+ m_8BitDitheredRadioButton = new QRadioButton (i18n ("256 colo&r (dithered)"), this);
+
+ m_24BitRadioButton = new QRadioButton (i18n ("24-&bit color"), this);
+
+
+ QButtonGroup *buttonGroup = new QButtonGroup (this);
+ buttonGroup->hide ();
+
+ buttonGroup->insert (m_blackAndWhiteRadioButton);
+ buttonGroup->insert (m_blackAndWhiteDitheredRadioButton);
+ buttonGroup->insert (m_8BitRadioButton);
+ buttonGroup->insert (m_8BitDitheredRadioButton);
+ buttonGroup->insert (m_24BitRadioButton);
+
+
+ const int screenDepth = QPixmap::defaultDepth ();
+#if DEBUG_KP_EFFECT_REDUCE_COLORS
+ kdDebug () << "kpEffectReduceColorsWidget::<ctor> screenDepth="
+ << screenDepth
+ << endl;
+#endif
+
+ // Note that everything is disabled for a 1-bit screen since there
+ // would be no effect. I won't support 2-bit or 4-bit screens either :)
+ m_blackAndWhiteRadioButton->setEnabled (screenDepth >= 8);
+ m_blackAndWhiteDitheredRadioButton->setEnabled (screenDepth >= 8);
+ m_8BitRadioButton->setEnabled (screenDepth >= 8);
+ // (not enabled if screenDepth==8 as m_8BitRadioButton already serves
+ // as NOP default)
+ m_8BitDitheredRadioButton->setEnabled (screenDepth > 8);
+ // (not "screenDepth >= 24" as we need a NOP default for 15/16-bit
+ // screens)
+ m_24BitRadioButton->setEnabled (screenDepth > 8);
+
+
+ m_defaultRadioButton = 0;
+
+ if (m_24BitRadioButton->isEnabled ())
+ {
+ #if DEBUG_KP_EFFECT_REDUCE_COLORS
+ kdDebug () << "\tdefault is 24-bit button" << endl;
+ #endif
+ m_defaultRadioButton = m_24BitRadioButton;
+ }
+ else if (m_8BitRadioButton->isEnabled ())
+ {
+ #if DEBUG_KP_EFFECT_REDUCE_COLORS
+ kdDebug () << "\tdefault is 8-bit button" << endl;
+ #endif
+ m_defaultRadioButton = m_8BitRadioButton;
+ }
+ else
+ {
+ #if DEBUG_KP_EFFECT_REDUCE_COLORS
+ kdDebug () << "\tuser must have a 1-bit screen - no default" << endl;
+ #endif
+ }
+
+
+ if (m_defaultRadioButton)
+ m_defaultRadioButton->setChecked (true);
+
+
+ lay->addWidget (m_blackAndWhiteRadioButton);
+ lay->addWidget (m_blackAndWhiteDitheredRadioButton);
+ lay->addWidget (m_8BitRadioButton);
+ lay->addWidget (m_8BitDitheredRadioButton);
+ lay->addWidget (m_24BitRadioButton);
+
+
+ connect (m_blackAndWhiteRadioButton, SIGNAL (toggled (bool)),
+ this, SIGNAL (settingsChanged ()));
+ connect (m_blackAndWhiteDitheredRadioButton, SIGNAL (toggled (bool)),
+ this, SIGNAL (settingsChanged ()));
+ connect (m_8BitRadioButton, SIGNAL (toggled (bool)),
+ this, SIGNAL (settingsChanged ()));
+ connect (m_8BitDitheredRadioButton, SIGNAL (toggled (bool)),
+ this, SIGNAL (settingsChanged ()));
+ connect (m_24BitRadioButton, SIGNAL (toggled (bool)),
+ this, SIGNAL (settingsChanged ()));
+}
+
+kpEffectReduceColorsWidget::~kpEffectReduceColorsWidget ()
+{
+}
+
+
+// public
+int kpEffectReduceColorsWidget::depth () const
+{
+ if (m_blackAndWhiteRadioButton->isChecked () ||
+ m_blackAndWhiteDitheredRadioButton->isChecked ())
+ {
+ return 1;
+ }
+ else if (m_8BitRadioButton->isChecked () ||
+ m_8BitDitheredRadioButton->isChecked ())
+ {
+ return 8;
+ }
+ else if (m_24BitRadioButton->isChecked ())
+ {
+ return 24;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+// public
+bool kpEffectReduceColorsWidget::dither () const
+{
+ return (m_blackAndWhiteDitheredRadioButton->isChecked () ||
+ m_8BitDitheredRadioButton->isChecked ());
+}
+
+
+//
+// kpEffectReduceColorsWidget implements kpColorEffectWidget interface
+//
+
+// public virtual [base kpColorEffectWidget]
+QString kpEffectReduceColorsWidget::caption () const
+{
+ return i18n ("Reduce To");
+}
+
+
+// public virtual [base kpColorEffectWidget]
+bool kpEffectReduceColorsWidget::isNoOp () const
+{
+ return (!m_defaultRadioButton || m_defaultRadioButton->isChecked ());
+}
+
+// public virtual [base kpColorEffectWidget]
+QPixmap kpEffectReduceColorsWidget::applyColorEffect (const QPixmap &pixmap)
+{
+ return kpEffectReduceColorsCommand::apply (pixmap, depth (), dither ());
+}
+
+
+// public virtual [base kpColorEffectWidget]
+kpColorEffectCommand *kpEffectReduceColorsWidget::createCommand () const
+{
+ return new kpEffectReduceColorsCommand (depth (), dither (),
+ m_actOnSelection,
+ m_mainWindow);
+}
+
+
+#include <kpeffectreducecolors.moc>
+
diff --git a/kolourpaint/pixmapfx/kpeffectreducecolors.h b/kolourpaint/pixmapfx/kpeffectreducecolors.h
new file mode 100644
index 00000000..a14cffc7
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpeffectreducecolors.h
@@ -0,0 +1,110 @@
+
+/*
+ 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.
+*/
+
+
+#ifndef KP_EFFECT_REDUCE_COLORS_H
+#define KP_EFFECT_REDUCE_COLORS_H
+
+
+#include <kpcoloreffect.h>
+
+
+class QRadioButton;
+class QImage;
+
+class kpMainWindow;
+
+
+QImage convertImageDepth (const QImage &image, int depth, bool dither);
+
+
+class kpEffectReduceColorsCommand : public kpColorEffectCommand
+{
+public:
+ // depth must be 1 or 8
+ kpEffectReduceColorsCommand (int depth, bool dither,
+ bool actOnSelection,
+ kpMainWindow *mainWindow);
+ virtual ~kpEffectReduceColorsCommand ();
+
+ QString commandName (int depth, int dither) const;
+
+ // (always preserves mask)
+ static void apply (QPixmap *destPixmapPtr, int depth, bool dither);
+ static QPixmap apply (const QPixmap &pm, int depth, bool dither);
+
+
+ //
+ // kpColorEffectCommand interface
+ //
+
+protected:
+ virtual QPixmap applyColorEffect (const QPixmap &pixmap);
+
+ int m_depth;
+ bool m_dither;
+};
+
+
+class kpEffectReduceColorsWidget : public kpColorEffectWidget
+{
+Q_OBJECT
+
+public:
+ kpEffectReduceColorsWidget (bool actOnSelection,
+ kpMainWindow *mainWindow,
+ QWidget *parent, const char *name = 0);
+ virtual ~kpEffectReduceColorsWidget ();
+
+
+ int depth () const;
+ bool dither () const;
+
+
+ //
+ // kpColorEffectWidget interface
+ //
+
+ virtual QString caption () const;
+
+ virtual bool isNoOp () const;
+ virtual QPixmap applyColorEffect (const QPixmap &pixmap);
+
+ virtual kpColorEffectCommand *createCommand () const;
+
+protected:
+ QRadioButton *m_blackAndWhiteRadioButton,
+ *m_blackAndWhiteDitheredRadioButton,
+ *m_8BitRadioButton,
+ *m_8BitDitheredRadioButton,
+ *m_24BitRadioButton;
+ QRadioButton *m_defaultRadioButton;
+};
+
+
+
+#endif // KP_EFFECT_REDUCE_COLORS_H
diff --git a/kolourpaint/pixmapfx/kpeffectsdialog.cpp b/kolourpaint/pixmapfx/kpeffectsdialog.cpp
new file mode 100644
index 00000000..666f81cf
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpeffectsdialog.cpp
@@ -0,0 +1,369 @@
+
+/*
+ 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_EFFECTS_DIALOG 0
+
+
+#include <kpeffectsdialog.h>
+
+#include <qgroupbox.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qtimer.h>
+
+#include <kapplication.h>
+#include <kcombobox.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kpdefs.h>
+#include <kpdocument.h>
+#include <kpeffectbalance.h>
+#include <kpeffectblursharpen.h>
+#include <kpeffectemboss.h>
+#include <kpeffectflatten.h>
+#include <kpeffectinvert.h>
+#include <kpeffectreducecolors.h>
+#include <kppixmapfx.h>
+
+
+// protected static
+int kpEffectsDialog::s_lastWidth = 640;
+int kpEffectsDialog::s_lastHeight = 620;
+
+
+kpEffectsDialog::kpEffectsDialog (bool actOnSelection,
+ kpMainWindow *parent,
+ const char *name)
+ : kpToolPreviewDialog (kpToolPreviewDialog::Preview,
+ true/*reserve top row*/,
+ QString::null/*caption*/,
+ QString::null/*afterActionText (no Dimensions Group Box)*/,
+ actOnSelection,
+ parent,
+ name),
+ m_delayedUpdateTimer (new QTimer (this)),
+ m_effectsComboBox (0),
+ m_settingsGroupBox (0),
+ m_settingsLayout (0),
+ m_colorEffectWidget (0)
+{
+#if DEBUG_KP_EFFECTS_DIALOG
+ kdDebug () << "kpEffectsDialog::kpEffectsDialog()" << endl;
+#endif
+
+ if (actOnSelection)
+ setCaption (i18n ("More Image Effects (Selection)"));
+ else
+ setCaption (i18n ("More Image Effects"));
+
+
+ connect (m_delayedUpdateTimer, SIGNAL (timeout ()),
+ this, SLOT (slotUpdateWithWaitCursor ()));
+
+
+ QHBox *effectContainer = new QHBox (mainWidget ());
+ effectContainer->setSpacing (spacingHint () * 4
+ /*need more space for QGroupBox titles*/);
+ effectContainer->setMargin (0);
+
+ QLabel *label = new QLabel (i18n ("&Effect:"), effectContainer);
+
+ m_effectsComboBox = new KComboBox (effectContainer);
+ m_effectsComboBox->insertItem (i18n ("Balance"));
+ m_effectsComboBox->insertItem (i18n ("Emboss"));
+ m_effectsComboBox->insertItem (i18n ("Flatten"));
+ m_effectsComboBox->insertItem (i18n ("Invert"));
+ m_effectsComboBox->insertItem (i18n ("Reduce Colors"));
+ m_effectsComboBox->insertItem (i18n ("Soften & Sharpen"));
+
+ label->setBuddy (m_effectsComboBox);
+ effectContainer->setStretchFactor (m_effectsComboBox, 1);
+
+ addCustomWidgetToFront (effectContainer);
+
+
+ m_settingsGroupBox = new QGroupBox (mainWidget ());
+ m_settingsLayout = new QVBoxLayout (m_settingsGroupBox,
+ marginHint () * 2,
+ spacingHint ());
+ addCustomWidgetToBack (m_settingsGroupBox);
+
+
+ connect (m_effectsComboBox, SIGNAL (activated (int)),
+ this, SLOT (selectEffect (int)));
+ selectEffect (0);
+
+
+ resize (s_lastWidth, s_lastHeight);
+
+
+#if DEBUG_KP_EFFECTS_DIALOG
+ kdDebug () << "\tabout to slotUpdate()" << endl;
+#endif
+ slotUpdate ();
+}
+
+kpEffectsDialog::~kpEffectsDialog ()
+{
+ s_lastWidth = width ();
+ s_lastHeight = height ();
+}
+
+
+// public virtual [base kpToolPreviewDialog]
+bool kpEffectsDialog::isNoOp () const
+{
+ if (!m_colorEffectWidget)
+ return true;
+
+ return m_colorEffectWidget->isNoOp ();
+}
+
+// public
+kpColorEffectCommand *kpEffectsDialog::createCommand () const
+{
+ if (!m_colorEffectWidget)
+ return 0;
+
+ return m_colorEffectWidget->createCommand ();
+}
+
+
+// protected virtual [base kpToolPreviewDialog]
+QSize kpEffectsDialog::newDimensions () const
+{
+ kpDocument *doc = document ();
+ if (!doc)
+ return QSize ();
+
+ return QSize (doc->width (m_actOnSelection),
+ doc->height (m_actOnSelection));
+}
+
+// protected virtual [base kpToolPreviewDialog]
+QPixmap kpEffectsDialog::transformPixmap (const QPixmap &pixmap,
+ int targetWidth, int targetHeight) const
+{
+ QPixmap pixmapWithEffect;
+
+ if (m_colorEffectWidget)
+ pixmapWithEffect = m_colorEffectWidget->applyColorEffect (pixmap);
+ else
+ pixmapWithEffect = pixmap;
+
+ return kpPixmapFX::scale (pixmapWithEffect, targetWidth, targetHeight);
+}
+
+
+// public
+int kpEffectsDialog::selectedEffect () const
+{
+ return m_effectsComboBox->currentItem ();
+}
+
+// public slot
+void kpEffectsDialog::selectEffect (int which)
+{
+#if DEBUG_KP_EFFECTS_DIALOG
+ kdDebug () << "kpEffectsDialog::selectEffect(" << which << ")" << endl;
+#endif
+
+ if (which < 0 ||
+ which >= m_effectsComboBox->count ())
+ {
+ return;
+ }
+
+ if (which != m_effectsComboBox->currentItem ())
+ m_effectsComboBox->setCurrentItem (which);
+
+
+ delete m_colorEffectWidget;
+ m_colorEffectWidget = 0;
+
+
+ m_settingsGroupBox->setCaption (QString::null);
+
+#define CREATE_EFFECT_WIDGET(name) \
+ m_colorEffectWidget = new name (m_actOnSelection, \
+ m_mainWindow, \
+ m_settingsGroupBox)
+ switch (which)
+ {
+ case 0:
+ CREATE_EFFECT_WIDGET (kpEffectBalanceWidget);
+ break;
+
+ case 1:
+ CREATE_EFFECT_WIDGET (kpEffectEmbossWidget);
+ break;
+
+ case 2:
+ CREATE_EFFECT_WIDGET (kpEffectFlattenWidget);
+ break;
+
+ case 3:
+ CREATE_EFFECT_WIDGET (kpEffectInvertWidget);
+ break;
+
+ case 4:
+ CREATE_EFFECT_WIDGET (kpEffectReduceColorsWidget);
+ break;
+
+ case 5:
+ CREATE_EFFECT_WIDGET (kpEffectBlurSharpenWidget);
+ break;
+ }
+#undef CREATE_EFFECT_WIDGET
+
+
+ if (m_colorEffectWidget)
+ {
+ #if DEBUG_KP_EFFECTS_DIALOG
+ kdDebug () << "\twidget exists for effect #" << endl;
+ #endif
+ m_settingsGroupBox->setTitle (m_colorEffectWidget->caption ());
+
+
+ // Don't resize the preview when showing the widget:
+ // TODO: actually work
+
+ QSize previewGroupBoxMinSize = m_previewGroupBox->minimumSize ();
+ QSize previewGroupBoxMaxSize = m_previewGroupBox->maximumSize ();
+ QLayout::ResizeMode previewGroupBoxResizeMode =
+ m_previewGroupBox->layout () ?
+ m_previewGroupBox->layout ()->resizeMode () :
+ QLayout::Auto;
+ #if DEBUG_KP_EFFECTS_DIALOG
+ kdDebug () << "\tpreviewGroupBox: minSize=" << previewGroupBoxMinSize
+ << " maxSize=" << previewGroupBoxMaxSize
+ << " size=" << m_previewGroupBox->size ()
+ << " layout=" << m_previewGroupBox->layout ()
+ << " resizeMode=" << previewGroupBoxResizeMode
+ << endl;
+ #endif
+
+ if (m_previewGroupBox->layout ())
+ m_previewGroupBox->layout ()->setResizeMode (QLayout::FreeResize);
+ #if DEBUG_KP_EFFECTS_DIALOG
+ kdDebug () << "\tafter set resizeMode, previewGroupBox.size="
+ << m_previewGroupBox->size () << endl;
+ #endif
+ m_previewGroupBox->setFixedSize (m_previewGroupBox->size ());
+ #if DEBUG_KP_EFFECTS_DIALOG
+ kdDebug () << "\tafter set fixedSize, previewGroupBox.size="
+ << m_previewGroupBox->size () << endl;
+ #endif
+
+ // Show widget
+ m_settingsLayout->addWidget (m_colorEffectWidget);
+ #if DEBUG_KP_EFFECTS_DIALOG
+ kdDebug () << "\tafter addWidget, previewGroupBox.size="
+ << m_previewGroupBox->size () << endl;
+ #endif
+ m_colorEffectWidget->show ();
+ #if DEBUG_KP_EFFECTS_DIALOG
+ kdDebug () << "\tafter addWidget show, previewGroupBox.size="
+ << m_previewGroupBox->size () << endl;
+ #endif
+
+ m_previewGroupBox->setMinimumSize (previewGroupBoxMinSize);
+ m_previewGroupBox->setMaximumSize (previewGroupBoxMaxSize);
+ #if DEBUG_KP_EFFECTS_DIALOG
+ kdDebug () << "\tafter set fixedSize, previewGroupBox.size="
+ << m_previewGroupBox->size () << endl;
+ #endif
+ if (m_previewGroupBox->layout ())
+ m_previewGroupBox->layout ()->setResizeMode (previewGroupBoxResizeMode);
+ #if DEBUG_KP_EFFECTS_DIALOG
+ kdDebug () << "\tafter restore resizeMode, previewGroupBox.size="
+ << m_previewGroupBox->size () << endl;
+ #endif
+
+
+ connect (m_colorEffectWidget, SIGNAL (settingsChangedNoWaitCursor ()),
+ this, SLOT (slotUpdate ()));
+ connect (m_colorEffectWidget, SIGNAL (settingsChanged ()),
+ this, SLOT (slotUpdateWithWaitCursor ()));
+ connect (m_colorEffectWidget, SIGNAL (settingsChangedDelayed ()),
+ this, SLOT (slotDelayedUpdate ()));
+ slotUpdateWithWaitCursor ();
+ #if DEBUG_KP_EFFECTS_DIALOG
+ kdDebug () << "\tafter slotUpdateWithWaitCursor, previewGroupBox.size="
+ << m_previewGroupBox->size () << endl;
+ #endif
+ }
+}
+
+
+// protected slot virtual [base kpToolPreviewDialog]
+void kpEffectsDialog::slotUpdate ()
+{
+#if DEBUG_KP_EFFECTS_DIALOG
+ kdDebug () << "kpEffectsDialog::slotUpdate()"
+ << " timerActive=" << m_delayedUpdateTimer->isActive ()
+ << endl;
+#endif
+
+ m_delayedUpdateTimer->stop ();
+
+ kpToolPreviewDialog::slotUpdate ();
+}
+
+// protected slot virtual [base kpToolPreviewDialog]
+void kpEffectsDialog::slotUpdateWithWaitCursor ()
+{
+#if DEBUG_KP_EFFECTS_DIALOG
+ kdDebug () << "kpEffectsDialog::slotUpdateWithWaitCursor()"
+ << " timerActive=" << m_delayedUpdateTimer->isActive ()
+ << endl;
+#endif
+
+ m_delayedUpdateTimer->stop ();
+
+ kpToolPreviewDialog::slotUpdateWithWaitCursor ();
+}
+
+
+// protected slot
+void kpEffectsDialog::slotDelayedUpdate ()
+{
+#if DEBUG_KP_EFFECTS_DIALOG
+ kdDebug () << "kpEffectsDialog::slotDelayedUpdate()"
+ << " timerActive=" << m_delayedUpdateTimer->isActive ()
+ << endl;
+#endif
+ m_delayedUpdateTimer->stop ();
+
+ m_delayedUpdateTimer->start (400/*ms*/, true/*single shot*/);
+}
+
+
+#include <kpeffectsdialog.moc>
diff --git a/kolourpaint/pixmapfx/kpeffectsdialog.h b/kolourpaint/pixmapfx/kpeffectsdialog.h
new file mode 100644
index 00000000..fe7265cc
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpeffectsdialog.h
@@ -0,0 +1,90 @@
+
+/*
+ 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.
+*/
+
+
+#ifndef KP_EFFECTS_DIALOG_H
+#define KP_EFFECTS_DIALOG_H
+
+
+#include <kptoolpreviewdialog.h>
+
+
+class QGroupBox;
+class QStringList;
+class QTimer;
+class QVBoxLayout;
+
+class KComboBox;
+
+class kpColorEffectCommand;
+class kpColorEffectWidget;
+class kpMainWindow;
+
+
+class kpEffectsDialog : public kpToolPreviewDialog
+{
+Q_OBJECT
+
+public:
+ kpEffectsDialog (bool actOnSelection,
+ kpMainWindow *parent,
+ const char *name = 0);
+ virtual ~kpEffectsDialog ();
+
+ virtual bool isNoOp () const;
+ kpColorEffectCommand *createCommand () const;
+
+protected:
+ virtual QSize newDimensions () const;
+ virtual QPixmap transformPixmap (const QPixmap &pixmap,
+ int targetWidth, int targetHeight) const;
+
+public:
+ int selectedEffect () const;
+public slots:
+ void selectEffect (int which);
+
+protected slots:
+ virtual void slotUpdate ();
+ virtual void slotUpdateWithWaitCursor ();
+
+ void slotDelayedUpdate ();
+
+protected:
+ static int s_lastWidth, s_lastHeight;
+
+ QTimer *m_delayedUpdateTimer;
+
+ KComboBox *m_effectsComboBox;
+ QGroupBox *m_settingsGroupBox;
+ QVBoxLayout *m_settingsLayout;
+
+ kpColorEffectWidget *m_colorEffectWidget;
+};
+
+
+#endif // KP_EFFECTS_DIALOG_H
diff --git a/kolourpaint/pixmapfx/kpfloodfill.cpp b/kolourpaint/pixmapfx/kpfloodfill.cpp
new file mode 100644
index 00000000..602e8acf
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpfloodfill.cpp
@@ -0,0 +1,362 @@
+
+/*
+ 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_FLOOD_FILL 0
+
+
+#include <kpfloodfill.h>
+
+#include <qapplication.h>
+#include <qbitmap.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+
+#include <kdebug.h>
+#include <kpdefs.h>
+
+#include <kppixmapfx.h>
+#include <kptool.h>
+
+
+kpFloodFill::kpFloodFill (QPixmap *pixmap, int x, int y,
+ const kpColor &color, int processedColorSimilarity)
+ : m_pixmapPtr (pixmap), m_x (x), m_y (y),
+ m_color (color), m_processedColorSimilarity (processedColorSimilarity),
+ m_initState (0)
+{
+}
+
+kpFloodFill::~kpFloodFill ()
+{
+}
+
+
+// private
+int kpFloodFill::fillLinesListSize (const QValueList <kpFloodFill::FillLine> &fillLines) const
+{
+ return (fillLines.size () * kpFloodFill::FillLine::size ());
+}
+
+// public
+int kpFloodFill::size () const
+{
+ int fillLinesCacheSize = 0;
+ for (QValueVector < QValueList <kpFloodFill::FillLine > >::const_iterator it = m_fillLinesCache.begin ();
+ it != m_fillLinesCache.end ();
+ it++)
+ {
+ fillLinesCacheSize += fillLinesListSize (*it);
+ }
+
+ return fillLinesListSize (m_fillLines) +
+ kpPixmapFX::imageSize (m_image) +
+ fillLinesCacheSize;
+}
+
+
+QRect kpFloodFill::boundingRect () const
+{
+ return m_boundingRect;
+}
+
+bool kpFloodFill::fill ()
+{
+ if (m_initState < 2 && !prepare ())
+ {
+ kdError () << "kpFloodFill:fill() could not prepare()!" << endl;
+ return false;
+ }
+
+ // not trying to do a NOP fill
+ if (m_boundingRect.isValid ())
+ {
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+ QPainter painter, maskPainter;
+ QBitmap maskBitmap;
+
+ if (m_pixmapPtr->mask () || m_color.isTransparent ())
+ {
+ maskBitmap = kpPixmapFX::getNonNullMask (*m_pixmapPtr);
+ maskPainter.begin (&maskBitmap);
+ maskPainter.setPen (m_color.maskColor ());
+ }
+
+ if (m_color.isOpaque ())
+ {
+ painter.begin (m_pixmapPtr);
+ painter.setPen (m_color.toQColor ());
+ }
+
+ const QValueList <FillLine>::ConstIterator fillLinesEnd = m_fillLines.end ();
+ for (QValueList <FillLine>::ConstIterator it = m_fillLines.begin ();
+ it != fillLinesEnd;
+ it++)
+ {
+ QPoint p1 = QPoint ((*it).m_x1, (*it).m_y);
+ QPoint p2 = QPoint ((*it).m_x2, (*it).m_y);
+
+ if (painter.isActive ())
+ painter.drawLine (p1, p2);
+
+ if (maskPainter.isActive ())
+ maskPainter.drawLine (p1, p2);
+ }
+
+ if (painter.isActive ())
+ painter.end ();
+
+ if (maskPainter.isActive ())
+ maskPainter.end ();
+
+ if (!maskBitmap.isNull ())
+ m_pixmapPtr->setMask (maskBitmap);
+
+ QApplication::restoreOverrideCursor ();
+ }
+ else
+ {
+ #if DEBUG_KP_FLOOD_FILL && 1
+ kdDebug () << "kpFloodFill::fill() performing NOP fill" << endl;
+ #endif
+ }
+
+ return true;
+}
+
+bool kpFloodFill::prepareColorToChange ()
+{
+#if DEBUG_KP_FLOOD_FILL && 1
+ kdDebug () << "kpFloodFill::prepareColorToChange" << endl;
+#endif
+
+ m_colorToChange = kpPixmapFX::getColorAtPixel (*m_pixmapPtr, QPoint (m_x, m_y));
+
+ if (m_colorToChange.isOpaque ())
+ {
+ #if DEBUG_KP_FLOOD_FILL && 1
+ kdDebug () << "\tcolorToChange: r=" << m_colorToChange.red ()
+ << ", b=" << m_colorToChange.blue ()
+ << ", g=" << m_colorToChange.green ()
+ << endl;
+ #endif
+ }
+ else
+ {
+ #if DEBUG_KP_FLOOD_FILL && 1
+ kdDebug () << "\tcolorToChange: transparent" << endl;
+ #endif
+ }
+
+ m_initState = 1;
+ return true;
+}
+
+// Derived from the zSprite2 Graphics Engine
+
+bool kpFloodFill::prepare ()
+{
+#if DEBUG_KP_FLOOD_FILL && 1
+ kdDebug () << "kpFloodFill::prepare()" << endl;
+#endif
+ m_boundingRect = QRect ();
+
+ if (m_initState < 1 && !prepareColorToChange ())
+ {
+ kdError () << "kpFloodFill:prepare() could not prepareColorToChange()!" << endl;
+ return false;
+ }
+
+#if DEBUG_KP_FLOOD_FILL && 1
+ kdDebug () << "\tperforming NOP check" << endl;
+#endif
+
+ // get the color we need to replace
+ if (m_processedColorSimilarity == 0 && m_color == m_colorToChange)
+ {
+ // need to do absolutely nothing (this is a significant optimisation
+ // for people who randomly click a lot over already-filled areas)
+ m_initState = 2; // sync with all "return true"'s
+ return true;
+ }
+
+#if DEBUG_KP_FLOOD_FILL && 1
+ kdDebug () << "\tconverting to image" << endl;
+#endif
+
+ // is this the only way to read pixels?
+ m_image = kpPixmapFX::convertToImage (*m_pixmapPtr);
+ if (m_image.isNull ())
+ {
+ kdError () << "kpFloodFill::prepare() could not convert to QImage" << endl;
+ return false;
+ }
+
+#if DEBUG_KP_FLOOD_FILL && 1
+ kdDebug () << "\tcreating fillLinesCache" << endl;
+#endif
+
+ // ready cache
+ m_fillLinesCache.resize (m_pixmapPtr->height ());
+
+#if DEBUG_KP_FLOOD_FILL && 1
+ kdDebug () << "\tcreating fill lines" << endl;
+#endif
+
+ // draw initial line
+ addLine (m_y, findMinX (m_y, m_x), findMaxX (m_y, m_x));
+
+ for (QValueList <FillLine>::ConstIterator it = m_fillLines.begin ();
+ it != m_fillLines.end ();
+ it++)
+ {
+ #if DEBUG_KP_FLOOD_FILL && 0
+ kdDebug () << "Expanding from y=" << (*it).m_y
+ << " x1=" << (*it).m_x1
+ << " x2=" << (*it).m_x2
+ << endl;
+ #endif
+
+ // make more lines above and below current line
+ findAndAddLines (*it, -1);
+ findAndAddLines (*it, +1);
+ }
+
+#if DEBUG_KP_FLOOD_FILL && 1
+ kdDebug () << "\tfinalising memory usage" << endl;
+#endif
+
+ // finalize memory usage
+ m_image.reset ();
+ m_fillLinesCache.clear ();
+
+ m_initState = 2; // sync with all "return true"'s
+ return true;
+}
+
+void kpFloodFill::addLine (int y, int x1, int x2)
+{
+#if DEBUG_KP_FLOOD_FILL && 0
+ kdDebug () << "kpFillCommand::fillAddLine (" << y << "," << x1 << "," << x2 << ")" << endl;
+#endif
+
+ m_fillLines.append (FillLine (y, x1, x2));
+ m_fillLinesCache [y].append (FillLine (y /* OPT */, x1, x2));
+ m_boundingRect = m_boundingRect.unite (QRect (QPoint (x1, y), QPoint (x2, y)));
+}
+
+kpColor kpFloodFill::pixelColor (int x, int y, bool *beenHere) const
+{
+ if (beenHere)
+ *beenHere = false;
+
+ if (y >= (int) m_fillLinesCache.count ())
+ {
+ kdError () << "kpFloodFill::pixelColor("
+ << x << ","
+ << y << ") y out of range=" << m_pixmapPtr->height () << endl;
+ return kpColor::invalid;
+ }
+
+ const QValueList <FillLine>::ConstIterator theEnd = m_fillLinesCache [y].end ();
+ for (QValueList <FillLine>::ConstIterator it = m_fillLinesCache [y].begin ();
+ it != theEnd;
+ it++)
+ {
+ if (x >= (*it).m_x1 && x <= (*it).m_x2)
+ {
+ if (beenHere)
+ *beenHere = true;
+ return m_color;
+ }
+ }
+
+ return kpPixmapFX::getColorAtPixel (m_image, QPoint (x, y));
+}
+
+bool kpFloodFill::shouldGoTo (int x, int y) const
+{
+ bool beenThere;
+ const kpColor col = pixelColor (x, y, &beenThere);
+
+ return (!beenThere && col.isSimilarTo (m_colorToChange, m_processedColorSimilarity));
+}
+
+void kpFloodFill::findAndAddLines (const FillLine &fillLine, int dy)
+{
+ // out of bounds?
+ if (fillLine.m_y + dy < 0 || fillLine.m_y + dy >= m_pixmapPtr->height ())
+ return;
+
+ for (int xnow = fillLine.m_x1; xnow <= fillLine.m_x2; xnow++)
+ {
+ // At current position, right colour?
+ if (shouldGoTo (xnow, fillLine.m_y + dy))
+ {
+ // Find minimum and maximum x values
+ int minxnow = findMinX (fillLine.m_y + dy, xnow);
+ int maxxnow = findMaxX (fillLine.m_y + dy, xnow);
+
+ // Draw line
+ addLine (fillLine.m_y + dy, minxnow, maxxnow);
+
+ // Move x pointer
+ xnow = maxxnow;
+ }
+ }
+}
+
+// finds the minimum x value at a certain line to be filled
+int kpFloodFill::findMinX (int y, int x) const
+{
+ for (;;)
+ {
+ if (x < 0)
+ return 0;
+
+ if (shouldGoTo (x, y))
+ x--;
+ else
+ return x + 1;
+ }
+}
+
+// finds the maximum x value at a certain line to be filled
+int kpFloodFill::findMaxX (int y, int x) const
+{
+ for (;;)
+ {
+ if (x > m_pixmapPtr->width () - 1)
+ return m_pixmapPtr->width () - 1;
+
+ if (shouldGoTo (x, y))
+ x++;
+ else
+ return x - 1;
+ }
+}
diff --git a/kolourpaint/pixmapfx/kpfloodfill.h b/kolourpaint/pixmapfx/kpfloodfill.h
new file mode 100644
index 00000000..5c0d8001
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpfloodfill.h
@@ -0,0 +1,106 @@
+
+/*
+ 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.
+*/
+
+
+#ifndef __kpfloodfill_h__
+#define __kpfloodfill_h__
+
+#include <qimage.h>
+#include <qvaluelist.h>
+#include <qvaluevector.h>
+
+#include <kpcolor.h>
+
+class QPixmap;
+
+class kpFloodFill
+{
+public:
+ kpFloodFill (QPixmap *pixmap, int x, int y,
+ const kpColor &color,
+ int processedColorSimilarity);
+ ~kpFloodFill ();
+
+ int size () const;
+
+ kpColor color () const { return m_color; }
+ int processedColorSimilarity () const { return m_processedColorSimilarity; }
+
+ // you should call [prepareColorToChange(),[prepare(),[fill()]]]
+ bool prepareColorToChange ();
+
+ // (only valid after prepareColorToChange())
+ kpColor colorToChange () const { return m_colorToChange; };
+
+ bool prepare ();
+ QRect boundingRect () const; // only valid after prepare()
+
+ bool fill ();
+
+private:
+ QPixmap *m_pixmapPtr;
+ int m_x, m_y;
+ kpColor m_color;
+ int m_processedColorSimilarity;
+
+ int m_initState;
+
+ QRect m_boundingRect;
+
+ struct FillLine
+ {
+ FillLine (int y = -1, int x1 = -1, int x2 = -1)
+ : m_y (y), m_x1 (x1), m_x2 (x2)
+ {
+ }
+
+ static int size ()
+ {
+ return sizeof (FillLine);
+ }
+
+ int m_y, m_x1, m_x2;
+ };
+
+ int fillLinesListSize (const QValueList <kpFloodFill::FillLine> &fillLines) const;
+
+ void addLine (int y, int x1, int x2);
+ kpColor pixelColor (int x, int y, bool *beenHere = 0) const;
+ bool shouldGoTo (int x, int y) const;
+ void findAndAddLines (const FillLine &fillLine, int dy);
+ int findMinX (int y, int x) const;
+ int findMaxX (int y, int x) const;
+
+ QValueList <FillLine> m_fillLines;
+
+ // Init info
+ QImage m_image;
+ QValueVector < QValueList <FillLine> > m_fillLinesCache;
+ kpColor m_colorToChange;
+};
+
+#endif // __kpfloodfill_h__
diff --git a/kolourpaint/pixmapfx/kppixmapfx.cpp b/kolourpaint/pixmapfx/kppixmapfx.cpp
new file mode 100644
index 00000000..1bd0b173
--- /dev/null
+++ b/kolourpaint/pixmapfx/kppixmapfx.cpp
@@ -0,0 +1,1677 @@
+
+/*
+ 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_PIXMAP_FX 0
+
+
+#include <kppixmapfx.h>
+
+#include <math.h>
+
+#include <qapplication.h>
+#include <qbitmap.h>
+#include <qdatetime.h>
+#include <qimage.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qpointarray.h>
+#include <qrect.h>
+
+#include <kconfig.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+#include <kpcolor.h>
+#include <kpdefs.h>
+#include <kpselection.h>
+#include <kptool.h>
+
+
+//
+// Overflow Resistant Arithmetic:
+//
+
+// public static
+int kpPixmapFX::addDimensions (int lhs, int rhs)
+{
+ if (lhs < 0 || rhs < 0 ||
+ lhs > INT_MAX - rhs)
+ {
+ return INT_MAX;
+ }
+
+ return lhs + rhs;
+}
+
+// public static
+int kpPixmapFX::multiplyDimensions (int lhs, int rhs)
+{
+ if (rhs == 0)
+ return 0;
+
+ if (lhs < 0 || rhs < 0 ||
+ lhs > INT_MAX / rhs)
+ {
+ return INT_MAX;
+ }
+
+ return lhs * rhs;
+}
+
+
+//
+// QPixmap Statistics
+//
+
+// public static
+int kpPixmapFX::pixmapArea (const QPixmap &pixmap)
+{
+ return kpPixmapFX::pixmapArea (pixmap.width (), pixmap.height ());
+}
+
+// public static
+int kpPixmapFX::pixmapArea (const QPixmap *pixmap)
+{
+ return (pixmap ? kpPixmapFX::pixmapArea (*pixmap) : 0);
+}
+
+// public static
+int kpPixmapFX::pixmapArea (int width, int height)
+{
+ return multiplyDimensions (width, height);
+}
+
+
+// public static
+int kpPixmapFX::pixmapSize (const QPixmap &pixmap)
+{
+ return kpPixmapFX::pixmapSize (pixmap.width (), pixmap.height (),
+ pixmap.depth ());
+}
+
+// public static
+int kpPixmapFX::pixmapSize (const QPixmap *pixmap)
+{
+ return (pixmap ? kpPixmapFX::pixmapSize (*pixmap) : 0);
+}
+
+// public static
+int kpPixmapFX::pixmapSize (int width, int height, int depth)
+{
+ // handle 15bpp
+ int roundedDepth = (depth > 8 ? (depth + 7) / 8 * 8 : depth);
+
+#if DEBUG_KP_PIXMAP_FX && 0
+ kdDebug () << "kpPixmapFX::pixmapSize() w=" << width
+ << " h=" << height
+ << " d=" << depth
+ << " roundedDepth=" << roundedDepth
+ << " ret="
+ << multiplyDimensions (kpPixmapFX::pixmapArea (width, height), roundedDepth) / 8
+ << endl;
+#endif
+ return multiplyDimensions (kpPixmapFX::pixmapArea (width, height), roundedDepth) / 8;
+}
+
+
+// public static
+int kpPixmapFX::imageSize (const QImage &image)
+{
+ return kpPixmapFX::imageSize (image.width (), image.height (), image.depth ());
+}
+
+// public static
+int kpPixmapFX::imageSize (const QImage *image)
+{
+ return (image ? kpPixmapFX::imageSize (*image) : 0);
+}
+
+// public static
+int kpPixmapFX::imageSize (int width, int height, int depth)
+{
+ // handle 15bpp
+ int roundedDepth = (depth > 8 ? (depth + 7) / 8 * 8 : depth);
+
+#if DEBUG_KP_PIXMAP_FX && 0
+ kdDebug () << "kpPixmapFX::imageSize() w=" << width
+ << " h=" << height
+ << " d=" << depth
+ << " roundedDepth=" << roundedDepth
+ << " ret="
+ << multiplyDimensions (multiplyDimensions (width, height), roundedDepth) / 8
+ << endl;
+#endif
+
+ return multiplyDimensions (multiplyDimensions (width, height), roundedDepth) / 8;
+}
+
+
+// public static
+int kpPixmapFX::selectionSize (const kpSelection &sel)
+{
+ return sel.size ();
+}
+
+// public static
+int kpPixmapFX::selectionSize (const kpSelection *sel)
+{
+ return (sel ? sel->size () : 0);
+}
+
+
+// public static
+int kpPixmapFX::stringSize (const QString &string)
+{
+#if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "kpPixmapFX::stringSize(" << string << ")"
+ << " len=" << string.length ()
+ << " sizeof(QChar)=" << sizeof (QChar)
+ << endl;
+#endif
+ return string.length () * sizeof (QChar);
+}
+
+
+// public static
+int kpPixmapFX::pointArraySize (const QPointArray &points)
+{
+#if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "kpPixmapFX::pointArraySize() points.size="
+ << points.size ()
+ << " sizeof(QPoint)=" << sizeof (QPoint)
+ << endl;
+#endif
+
+ return (points.size () * sizeof (QPoint));
+}
+
+
+//
+// QPixmap/QImage Conversion Functions
+//
+
+// public static
+QImage kpPixmapFX::convertToImage (const QPixmap &pixmap)
+{
+ if (pixmap.isNull ())
+ return QImage ();
+
+ return pixmap.convertToImage ();
+}
+
+
+// Returns true if <image> contains translucency (rather than just transparency)
+// QPixmap::hasAlphaChannel() appears to give incorrect results
+static bool imageHasAlphaChannel (const QImage &image)
+{
+ if (image.depth () < 32)
+ return false;
+
+ for (int y = 0; y < image.height (); y++)
+ {
+ for (int x = 0; x < image.width (); x++)
+ {
+ const QRgb rgb = image.pixel (x, y);
+
+ if (qAlpha (rgb) > 0 && qAlpha (rgb) < 255)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static int imageNumColorsUpTo (const QImage &image, int max)
+{
+ QMap <QRgb, bool> rgbMap;
+
+ if (image.depth () <= 8)
+ {
+ for (int i = 0; i < image.numColors () && (int) rgbMap.size () < max; i++)
+ {
+ rgbMap.insert (image.color (i), true);
+ }
+ }
+ else
+ {
+ for (int y = 0; y < image.height () && (int) rgbMap.size () < max; y++)
+ {
+ for (int x = 0; x < image.width () && (int) rgbMap.size () < max; x++)
+ {
+ rgbMap.insert (image.pixel (x, y), true);
+ }
+ }
+ }
+
+ return rgbMap.size ();
+}
+
+static void convertToPixmapWarnAboutLoss (const QImage &image,
+ const kpPixmapFX::WarnAboutLossInfo &wali)
+{
+ if (!wali.isValid ())
+ return;
+
+
+ const QString colorDepthTranslucencyDontAskAgain =
+ wali.m_dontAskAgainPrefix + "_ColorDepthTranslucency";
+ const QString colorDepthDontAskAgain =
+ wali.m_dontAskAgainPrefix + "_ColorDepth";
+ const QString translucencyDontAskAgain =
+ wali.m_dontAskAgainPrefix + "_Translucency";
+
+#if DEBUG_KP_PIXMAP_FX && 1
+ QTime timer;
+ timer.start ();
+#endif
+
+ bool hasAlphaChannel =
+ (KMessageBox::shouldBeShownContinue (translucencyDontAskAgain) &&
+ imageHasAlphaChannel (image));
+
+#if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\twarnAboutLoss - check hasAlphaChannel took "
+ << timer.restart () << "msec" << endl;
+#endif
+
+ bool moreColorsThanDisplay =
+ (KMessageBox::shouldBeShownContinue (colorDepthDontAskAgain) &&
+ image.depth () > QColor::numBitPlanes () &&
+ QColor::numBitPlanes () < 24); // 32 indicates alpha channel
+
+ int screenDepthNeeded = 0;
+
+ if (moreColorsThanDisplay)
+ screenDepthNeeded = QMIN (24, image.depth ());
+
+#if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\ttranslucencyShouldBeShown="
+ << KMessageBox::shouldBeShownContinue (translucencyDontAskAgain)
+ << endl
+ << "\thasAlphaChannel=" << hasAlphaChannel
+ << endl
+ << "\tcolorDepthShownBeShown="
+ << KMessageBox::shouldBeShownContinue (colorDepthDontAskAgain)
+ << endl
+ << "\timage.depth()=" << image.depth ()
+ << endl
+ << "\tscreenDepth=" << QColor::numBitPlanes ()
+ << endl
+ << "\tmoreColorsThanDisplay=" << moreColorsThanDisplay
+ << endl
+ << "\tneedDepth=" << screenDepthNeeded
+ << endl;
+#endif
+
+
+ QApplication::setOverrideCursor (Qt::arrowCursor);
+
+ if (moreColorsThanDisplay && hasAlphaChannel)
+ {
+ KMessageBox::information (wali.m_parent,
+ wali.m_moreColorsThanDisplayAndHasAlphaChannelMessage
+ .arg (screenDepthNeeded),
+ QString::null, // or would you prefer "Low Screen Depth and Image Contains Transparency"? :)
+ colorDepthTranslucencyDontAskAgain);
+
+ if (!KMessageBox::shouldBeShownContinue (colorDepthTranslucencyDontAskAgain))
+ {
+ KMessageBox::saveDontShowAgainContinue (colorDepthDontAskAgain);
+ KMessageBox::saveDontShowAgainContinue (translucencyDontAskAgain);
+ }
+ }
+ else if (moreColorsThanDisplay)
+ {
+ KMessageBox::information (wali.m_parent,
+ wali.m_moreColorsThanDisplayMessage
+ .arg (screenDepthNeeded),
+ i18n ("Low Screen Depth"),
+ colorDepthDontAskAgain);
+ }
+ else if (hasAlphaChannel)
+ {
+ KMessageBox::information (wali.m_parent,
+ wali.m_hasAlphaChannelMessage,
+ i18n ("Image Contains Translucency"),
+ translucencyDontAskAgain);
+ }
+
+ QApplication::restoreOverrideCursor ();
+}
+
+// public static
+QPixmap kpPixmapFX::convertToPixmap (const QImage &image, bool pretty,
+ const WarnAboutLossInfo &wali)
+{
+#if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "kpPixmapFX::convertToPixmap(image,pretty=" << pretty
+ << ",warnAboutLossInfo.isValid=" << wali.isValid ()
+ << ")" << endl;
+ QTime timer;
+ timer.start ();
+#endif
+
+ if (image.isNull ())
+ return QPixmap ();
+
+
+ QPixmap destPixmap;
+
+ if (!pretty)
+ {
+ destPixmap.convertFromImage (image,
+ Qt::ColorOnly/*always display depth*/ |
+ Qt::ThresholdDither/*no dither*/ |
+ Qt::ThresholdAlphaDither/*no dither alpha*/|
+ Qt::AvoidDither);
+ }
+ else
+ {
+ destPixmap.convertFromImage (image,
+ Qt::ColorOnly/*always display depth*/ |
+ Qt::DiffuseDither/*hi quality dither*/ |
+ Qt::ThresholdAlphaDither/*no dither alpha*/ |
+ Qt::PreferDither/*(dither even if <256 colours)*/);
+ }
+
+#if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\tconversion took " << timer.elapsed () << "msec" << endl;
+#endif
+
+ kpPixmapFX::ensureNoAlphaChannel (&destPixmap);
+
+
+ if (wali.isValid ())
+ convertToPixmapWarnAboutLoss (image, wali);
+
+
+ return destPixmap;
+}
+
+// TODO: don't dup convertToPixmap() code
+// public static
+QPixmap kpPixmapFX::convertToPixmapAsLosslessAsPossible (const QImage &image,
+ const WarnAboutLossInfo &wali)
+{
+#if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "kpPixmapFX::convertToPixmapAsLosslessAsPossible(image depth="
+ << image.depth ()
+ << ",warnAboutLossInfo.isValid=" << wali.isValid ()
+ << ") screenDepth=" << QPixmap::defaultDepth ()
+ << " imageNumColorsUpTo257=" << imageNumColorsUpTo (image, 257)
+ << endl;
+ QTime timer;
+ timer.start ();
+#endif
+
+ if (image.isNull ())
+ return QPixmap ();
+
+
+ const int screenDepth = (QPixmap::defaultDepth () >= 24 ?
+ 32 :
+ QPixmap::defaultDepth ());
+
+ QPixmap destPixmap;
+ int ditherFlags = 0;
+
+ if (image.depth () <= screenDepth)
+ {
+ #if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\timage depth <= screen depth - don't dither"
+ << " (AvoidDither | ThresholdDither)" << endl;
+ #endif
+
+ ditherFlags = (Qt::AvoidDither | Qt::ThresholdDither);
+ }
+ // PRE: image.depth() > screenDepth
+ // ASSERT: screenDepth < 32
+ else if (screenDepth <= 8)
+ {
+ const int screenNumColors = (1 << screenDepth);
+
+ #if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\tscreen depth <= 8; imageNumColorsUpTo"
+ << (screenNumColors + 1)
+ << "=" << imageNumColorsUpTo (image, screenNumColors + 1)
+ << endl;
+ #endif
+
+ if (imageNumColorsUpTo (image, screenNumColors + 1) <= screenNumColors)
+ {
+ #if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\t\tcolors fit on screen - don't dither"
+ << " (AvoidDither | ThresholdDither)" << endl;
+ #endif
+ ditherFlags = (Qt::AvoidDither | Qt::ThresholdDither);
+ }
+ else
+ {
+ #if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\t\tcolors don't fit on screen - dither"
+ << " (PreferDither | DiffuseDither)" << endl;
+ #endif
+ ditherFlags = (Qt::PreferDither | Qt::DiffuseDither);
+ }
+ }
+ // PRE: image.depth() > screenDepth &&
+ // screenDepth > 8
+ // ASSERT: screenDepth < 32
+ else
+ {
+ #if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\tscreen depth > 8 - read config" << endl;
+ #endif
+
+ int configDitherIfNumColorsGreaterThan = 323;
+
+ KConfigGroupSaver cfgGroupSaver (KGlobal::config (),
+ kpSettingsGroupGeneral);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ if (cfg->hasKey (kpSettingDitherOnOpen))
+ {
+ configDitherIfNumColorsGreaterThan = cfg->readNumEntry (kpSettingDitherOnOpen);
+ }
+ else
+ {
+ cfg->writeEntry (kpSettingDitherOnOpen, configDitherIfNumColorsGreaterThan);
+ cfg->sync ();
+ }
+
+ #if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\t\tcfg=" << configDitherIfNumColorsGreaterThan
+ << " image=" << imageNumColorsUpTo (image, configDitherIfNumColorsGreaterThan + 1)
+ << endl;
+ #endif
+
+ if (imageNumColorsUpTo (image, configDitherIfNumColorsGreaterThan + 1) >
+ configDitherIfNumColorsGreaterThan)
+ {
+ #if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\t\t\talways dither (PreferDither | DiffuseDither)"
+ << endl;
+ #endif
+ ditherFlags = (Qt::PreferDither | Qt::DiffuseDither);
+ }
+ else
+ {
+ #if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\t\t\tdon't dither (AvoidDither | ThresholdDither)"
+ << endl;
+ #endif
+ ditherFlags = (Qt::AvoidDither | Qt::ThresholdDither);
+ }
+ }
+
+
+ destPixmap.convertFromImage (image,
+ Qt::ColorOnly/*always display depth*/ |
+ Qt::ThresholdAlphaDither/*no dither alpha*/ |
+ ditherFlags);
+
+#if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\tconversion took " << timer.elapsed () << "msec" << endl;
+#endif
+
+ kpPixmapFX::ensureNoAlphaChannel (&destPixmap);
+
+
+ if (wali.isValid ())
+ convertToPixmapWarnAboutLoss (image, wali);
+
+
+ return destPixmap;
+}
+
+
+// public static
+QPixmap kpPixmapFX::pixmapWithDefinedTransparentPixels (const QPixmap &pixmap,
+ const QColor &transparentColor)
+{
+ if (!pixmap.mask ())
+ return pixmap;
+
+ QPixmap retPixmap (pixmap.width (), pixmap.height ());
+ retPixmap.fill (transparentColor);
+
+ QPainter p (&retPixmap);
+ p.drawPixmap (QPoint (0, 0), pixmap);
+ p.end ();
+
+ retPixmap.setMask (*pixmap.mask ());
+ return retPixmap;
+}
+
+
+//
+// Get/Set Parts of Pixmap
+//
+
+
+// public static
+QPixmap kpPixmapFX::getPixmapAt (const QPixmap &pm, const QRect &rect)
+{
+ QPixmap retPixmap (rect.width (), rect.height ());
+
+#if DEBUG_KP_PIXMAP_FX && 0
+ kdDebug () << "kpPixmapFX::getPixmapAt(pm.hasMask="
+ << (pm.mask () ? 1 : 0)
+ << ",rect="
+ << rect
+ << ")"
+ << endl;
+#endif
+
+ const QRect validSrcRect = pm.rect ().intersect (rect);
+ const bool wouldHaveUndefinedPixels = (validSrcRect != rect);
+
+ if (wouldHaveUndefinedPixels)
+ {
+ #if DEBUG_KP_PIXMAP_FX && 0
+ kdDebug () << "\tret would contain undefined pixels - setting them to transparent" << endl;
+ #endif
+ QBitmap transparentMask (rect.width (), rect.height ());
+ transparentMask.fill (Qt::color0/*transparent*/);
+ retPixmap.setMask (transparentMask);
+ }
+
+ if (validSrcRect.isEmpty ())
+ {
+ #if DEBUG_KP_PIXMAP_FX && 0
+ kdDebug () << "\tsilly case - completely invalid rect - ret transparent pixmap" << endl;
+ #endif
+ return retPixmap;
+ }
+
+
+ const QPoint destTopLeft = validSrcRect.topLeft () - rect.topLeft ();
+
+ // copy data _and_ mask (if avail)
+ copyBlt (&retPixmap, /* dest */
+ destTopLeft.x (), destTopLeft.y (), /* dest pt */
+ &pm, /* src */
+ validSrcRect.x (), validSrcRect.y (), /* src pt */
+ validSrcRect.width (), validSrcRect.height ());
+
+ if (wouldHaveUndefinedPixels && retPixmap.mask () && !pm.mask ())
+ {
+ #if DEBUG_KP_PIXMAP_FX && 0
+ kdDebug () << "\tensure opaque in valid region" << endl;
+ #endif
+ kpPixmapFX::ensureOpaqueAt (&retPixmap,
+ QRect (destTopLeft.x (), destTopLeft.y (),
+ validSrcRect.width (), validSrcRect.height ()));
+ }
+
+#if DEBUG_KP_PIXMAP_FX && 0
+ kdDebug () << "\tretPixmap.hasMask="
+ << (retPixmap.mask () ? 1 : 0)
+ << endl;
+#endif
+
+ return retPixmap;
+}
+
+
+// public static
+void kpPixmapFX::setPixmapAt (QPixmap *destPixmapPtr, const QRect &destRect,
+ const QPixmap &srcPixmap)
+{
+ if (!destPixmapPtr)
+ return;
+
+#if DEBUG_KP_PIXMAP_FX && 0
+ kdDebug () << "kpPixmapFX::setPixmapAt(destPixmap->rect="
+ << destPixmapPtr->rect ()
+ << ",destPixmap->hasMask="
+ << (destPixmapPtr->mask () ? 1 : 0)
+ << ",destRect="
+ << destRect
+ << ",srcPixmap.rect="
+ << srcPixmap.rect ()
+ << ",srcPixmap.hasMask="
+ << (srcPixmap.mask () ? 1 : 0)
+ << ")"
+ << endl;
+#endif
+
+#if DEBUG_KP_PIXMAP_FX && 0
+ if (destPixmapPtr->mask ())
+ {
+ QImage image = kpPixmapFX::convertToImage (*destPixmapPtr);
+ int numTrans = 0;
+
+ for (int y = 0; y < image.height (); y++)
+ {
+ for (int x = 0; x < image.width (); x++)
+ {
+ if (qAlpha (image.pixel (x, y)) == 0)
+ numTrans++;
+ }
+ }
+
+ kdDebug () << "\tdestPixmapPtr numTrans=" << numTrans << endl;
+ }
+#endif
+
+#if 0
+ // TODO: why does undo'ing a single pen dot on a transparent pixel,
+ // result in a opaque image, except for that single transparent pixel???
+ // Qt bug on boundary case?
+
+ // copy data _and_ mask
+ copyBlt (destPixmapPtr,
+ destAt.x (), destAt.y (),
+ &srcPixmap,
+ 0, 0,
+ destRect.width (), destRect.height ());
+#else
+ bitBlt (destPixmapPtr,
+ destRect.x (), destRect.y (),
+ &srcPixmap,
+ 0, 0,
+ destRect.width (), destRect.height (),
+ Qt::CopyROP,
+ true/*ignore mask*/);
+
+ if (srcPixmap.mask ())
+ {
+ QBitmap mask = getNonNullMask (*destPixmapPtr);
+ bitBlt (&mask,
+ destRect.x (), destRect.y (),
+ srcPixmap.mask (),
+ 0, 0,
+ destRect.width (), destRect.height (),
+ Qt::CopyROP,
+ true/*ignore mask*/);
+ destPixmapPtr->setMask (mask);
+ }
+#endif
+
+ if (destPixmapPtr->mask () && !srcPixmap.mask ())
+ {
+ #if DEBUG_KP_PIXMAP_FX && 0
+ kdDebug () << "\t\topaque'ing dest rect" << endl;
+ #endif
+ kpPixmapFX::ensureOpaqueAt (destPixmapPtr, destRect);
+ }
+
+#if DEBUG_KP_PIXMAP_FX && 0
+ kdDebug () << "\tdestPixmap->hasMask="
+ << (destPixmapPtr->mask () ? 1 : 0)
+ << endl;
+ if (destPixmapPtr->mask ())
+ {
+ QImage image = kpPixmapFX::convertToImage (*destPixmapPtr);
+ int numTrans = 0;
+
+ for (int y = 0; y < image.height (); y++)
+ {
+ for (int x = 0; x < image.width (); x++)
+ {
+ if (qAlpha (image.pixel (x, y)) == 0)
+ numTrans++;
+ }
+ }
+
+ kdDebug () << "\tdestPixmapPtr numTrans=" << numTrans << endl;
+ }
+#endif
+}
+
+// public static
+void kpPixmapFX::setPixmapAt (QPixmap *destPixmapPtr, const QPoint &destAt,
+ const QPixmap &srcPixmap)
+{
+ kpPixmapFX::setPixmapAt (destPixmapPtr,
+ QRect (destAt.x (), destAt.y (),
+ srcPixmap.width (), srcPixmap.height ()),
+ srcPixmap);
+}
+
+// public static
+void kpPixmapFX::setPixmapAt (QPixmap *destPixmapPtr, int destX, int destY,
+ const QPixmap &srcPixmap)
+{
+ kpPixmapFX::setPixmapAt (destPixmapPtr, QPoint (destX, destY), srcPixmap);
+}
+
+
+// public static
+void kpPixmapFX::paintPixmapAt (QPixmap *destPixmapPtr, const QPoint &destAt,
+ const QPixmap &srcPixmap)
+{
+ if (!destPixmapPtr)
+ return;
+
+ // Copy src (masked by src's mask) on top of dest.
+ bitBlt (destPixmapPtr, /* dest */
+ destAt.x (), destAt.y (), /* dest pt */
+ &srcPixmap, /* src */
+ 0, 0 /* src pt */);
+
+ kpPixmapFX::ensureOpaqueAt (destPixmapPtr, destAt, srcPixmap);
+}
+
+// public static
+void kpPixmapFX::paintPixmapAt (QPixmap *destPixmapPtr, int destX, int destY,
+ const QPixmap &srcPixmap)
+{
+ kpPixmapFX::paintPixmapAt (destPixmapPtr, QPoint (destX, destY), srcPixmap);
+}
+
+
+// public static
+kpColor kpPixmapFX::getColorAtPixel (const QPixmap &pm, const QPoint &at)
+{
+#if DEBUG_KP_PIXMAP_FX && 0
+ kdDebug () << "kpToolColorPicker::colorAtPixel" << p << endl;
+#endif
+
+ if (at.x () < 0 || at.x () >= pm.width () ||
+ at.y () < 0 || at.y () >= pm.height ())
+ {
+ return kpColor::invalid;
+ }
+
+ QPixmap pixmap = getPixmapAt (pm, QRect (at, at));
+ QImage image = kpPixmapFX::convertToImage (pixmap);
+ if (image.isNull ())
+ {
+ kdError () << "kpPixmapFX::getColorAtPixel(QPixmap) could not convert to QImage" << endl;
+ return kpColor::invalid;
+ }
+
+ return getColorAtPixel (image, QPoint (0, 0));
+}
+
+// public static
+kpColor kpPixmapFX::getColorAtPixel (const QPixmap &pm, int x, int y)
+{
+ return kpPixmapFX::getColorAtPixel (pm, QPoint (x, y));
+}
+
+// public static
+kpColor kpPixmapFX::getColorAtPixel (const QImage &img, const QPoint &at)
+{
+ if (!img.valid (at.x (), at.y ()))
+ return kpColor::invalid;
+
+ QRgb rgba = img.pixel (at.x (), at.y ());
+ return kpColor (rgba);
+}
+
+// public static
+kpColor kpPixmapFX::getColorAtPixel (const QImage &img, int x, int y)
+{
+ return kpPixmapFX::getColorAtPixel (img, QPoint (x, y));
+}
+
+
+//
+// Mask Operations
+//
+
+
+// public static
+void kpPixmapFX::ensureNoAlphaChannel (QPixmap *destPixmapPtr)
+{
+ if (destPixmapPtr->hasAlphaChannel ())
+ destPixmapPtr->setMask (kpPixmapFX::getNonNullMask/*just in case*/ (*destPixmapPtr));
+}
+
+
+// public static
+QBitmap kpPixmapFX::getNonNullMask (const QPixmap &pm)
+{
+ if (pm.mask ())
+ return *pm.mask ();
+ else
+ {
+ QBitmap maskBitmap (pm.width (), pm.height ());
+ maskBitmap.fill (Qt::color1/*opaque*/);
+
+ return maskBitmap;
+ }
+}
+
+
+// public static
+void kpPixmapFX::ensureTransparentAt (QPixmap *destPixmapPtr, const QRect &destRect)
+{
+ if (!destPixmapPtr)
+ return;
+
+ QBitmap maskBitmap = getNonNullMask (*destPixmapPtr);
+
+ QPainter p (&maskBitmap);
+
+ p.setPen (Qt::color0/*transparent*/);
+ p.setBrush (Qt::color0/*transparent*/);
+
+ p.drawRect (destRect);
+
+ p.end ();
+
+ destPixmapPtr->setMask (maskBitmap);
+}
+
+
+// public static
+void kpPixmapFX::paintMaskTransparentWithBrush (QPixmap *destPixmapPtr, const QPoint &destAt,
+ const QPixmap &brushBitmap)
+{
+ if (!destPixmapPtr)
+ return;
+
+ if (brushBitmap.depth () > 1)
+ {
+ kdError () << "kpPixmapFX::paintMaskTransparentWidthBrush() passed brushPixmap with depth > 1" << endl;
+ return;
+ }
+
+ QBitmap destMaskBitmap = kpPixmapFX::getNonNullMask (*destPixmapPtr);
+
+ // Src
+ // Dest Mask Brush Bitmap = Result
+ // -------------------------------------
+ // 0 0 0
+ // 0 1 0
+ // 1 0 1
+ // 1 1 0
+ //
+ // Brush Bitmap value of 1 means "make transparent"
+ // 0 means "leave it as it is"
+
+ bitBlt (&destMaskBitmap,
+ destAt.x (), destAt.y (),
+ &brushBitmap,
+ 0, 0,
+ brushBitmap.width (), brushBitmap.height (),
+ Qt::NotAndROP);
+
+ destPixmapPtr->setMask (destMaskBitmap);
+}
+
+// public static
+void kpPixmapFX::paintMaskTransparentWithBrush (QPixmap *destPixmapPtr, int destX, int destY,
+ const QPixmap &brushBitmap)
+{
+ kpPixmapFX::paintMaskTransparentWithBrush (destPixmapPtr,
+ QPoint (destX, destY),
+ brushBitmap);
+}
+
+
+// public static
+void kpPixmapFX::ensureOpaqueAt (QPixmap *destPixmapPtr, const QRect &destRect)
+{
+ if (!destPixmapPtr || !destPixmapPtr->mask ()/*already opaque*/)
+ return;
+
+ QBitmap maskBitmap = *destPixmapPtr->mask ();
+
+ QPainter p (&maskBitmap);
+
+ p.setPen (Qt::color1/*opaque*/);
+ p.setBrush (Qt::color1/*opaque*/);
+
+ p.drawRect (destRect);
+
+ p.end ();
+
+ destPixmapPtr->setMask (maskBitmap);
+}
+
+// public static
+void kpPixmapFX::ensureOpaqueAt (QPixmap *destPixmapPtr, const QPoint &destAt,
+ const QPixmap &srcPixmap)
+{
+ if (!destPixmapPtr || !destPixmapPtr->mask ()/*already opaque*/)
+ return;
+
+ QBitmap destMask = *destPixmapPtr->mask ();
+
+ if (srcPixmap.mask ())
+ {
+ bitBlt (&destMask, /* dest */
+ destAt, /* dest pt */
+ srcPixmap.mask (), /* src */
+ QRect (0, 0, srcPixmap.width (), srcPixmap.height ()), /* src rect */
+ Qt::OrROP/*if either is opaque, it's opaque*/);
+ }
+ else
+ {
+ QPainter p (&destMask);
+
+ p.setPen (Qt::color1/*opaque*/);
+ p.setBrush (Qt::color1/*opaque*/);
+
+ p.drawRect (destAt.x (), destAt.y (),
+ srcPixmap.width (), srcPixmap.height ());
+
+ p.end ();
+ }
+
+ destPixmapPtr->setMask (destMask);
+}
+
+// public static
+void kpPixmapFX::ensureOpaqueAt (QPixmap *destPixmapPtr, int destX, int destY,
+ const QPixmap &srcPixmap)
+{
+ kpPixmapFX::ensureOpaqueAt (destPixmapPtr, QPoint (destX, destY), srcPixmap);
+}
+
+
+//
+// Effects
+//
+
+// public static
+void kpPixmapFX::convertToGrayscale (QPixmap *destPixmapPtr)
+{
+ QImage image = kpPixmapFX::convertToImage (*destPixmapPtr);
+ kpPixmapFX::convertToGrayscale (&image);
+ *destPixmapPtr = kpPixmapFX::convertToPixmap (image);
+}
+
+// public static
+QPixmap kpPixmapFX::convertToGrayscale (const QPixmap &pm)
+{
+ QImage image = kpPixmapFX::convertToImage (pm);
+ kpPixmapFX::convertToGrayscale (&image);
+ return kpPixmapFX::convertToPixmap (image);
+}
+
+static QRgb toGray (QRgb rgb)
+{
+ // naive way that doesn't preserve brightness
+ // int gray = (qRed (rgb) + qGreen (rgb) + qBlue (rgb)) / 3;
+
+ // over-exaggerates red & blue
+ // int gray = qGray (rgb);
+
+ int gray = (212671 * qRed (rgb) + 715160 * qGreen (rgb) + 72169 * qBlue (rgb)) / 1000000;
+ return qRgba (gray, gray, gray, qAlpha (rgb));
+}
+
+// public static
+void kpPixmapFX::convertToGrayscale (QImage *destImagePtr)
+{
+ if (destImagePtr->depth () > 8)
+ {
+ // hmm, why not just write to the pixmap directly???
+
+ for (int y = 0; y < destImagePtr->height (); y++)
+ {
+ for (int x = 0; x < destImagePtr->width (); x++)
+ {
+ destImagePtr->setPixel (x, y, toGray (destImagePtr->pixel (x, y)));
+ }
+ }
+ }
+ else
+ {
+ // 1- & 8- bit images use a color table
+ for (int i = 0; i < destImagePtr->numColors (); i++)
+ destImagePtr->setColor (i, toGray (destImagePtr->color (i)));
+ }
+}
+
+// public static
+QImage kpPixmapFX::convertToGrayscale (const QImage &img)
+{
+ QImage retImage = img;
+ kpPixmapFX::convertToGrayscale (&retImage);
+ return retImage;
+}
+
+
+// public static
+void kpPixmapFX::fill (QPixmap *destPixmapPtr, const kpColor &color)
+{
+ if (!destPixmapPtr)
+ return;
+
+ if (color.isOpaque ())
+ {
+ destPixmapPtr->setMask (QBitmap ()); // no mask = opaque
+ destPixmapPtr->fill (color.toQColor ());
+ }
+ else
+ {
+ kpPixmapFX::ensureTransparentAt (destPixmapPtr, destPixmapPtr->rect ());
+ }
+}
+
+// public static
+QPixmap kpPixmapFX::fill (const QPixmap &pm, const kpColor &color)
+{
+ QPixmap ret = pm;
+ kpPixmapFX::fill (&ret, color);
+ return ret;
+}
+
+
+// public static
+void kpPixmapFX::resize (QPixmap *destPixmapPtr, int w, int h,
+ const kpColor &backgroundColor, bool fillNewAreas)
+{
+#if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "kpPixmapFX::resize()" << endl;
+#endif
+
+ if (!destPixmapPtr)
+ return;
+
+ int oldWidth = destPixmapPtr->width ();
+ int oldHeight = destPixmapPtr->height ();
+
+ if (w == oldWidth && h == oldHeight)
+ return;
+
+
+ destPixmapPtr->resize (w, h);
+
+ if (fillNewAreas && (w > oldWidth || h > oldHeight))
+ {
+ #if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\tfilling in new areas" << endl;
+ #endif
+ QBitmap maskBitmap;
+ QPainter painter, maskPainter;
+
+ if (backgroundColor.isOpaque ())
+ {
+ painter.begin (destPixmapPtr);
+ painter.setPen (backgroundColor.toQColor ());
+ painter.setBrush (backgroundColor.toQColor ());
+ }
+
+ if (backgroundColor.isTransparent () || destPixmapPtr->mask ())
+ {
+ maskBitmap = kpPixmapFX::getNonNullMask (*destPixmapPtr);
+ maskPainter.begin (&maskBitmap);
+ maskPainter.setPen (backgroundColor.maskColor ());
+ maskPainter.setBrush (backgroundColor.maskColor ());
+ }
+
+ #define PAINTER_CALL(cmd) \
+ { \
+ if (painter.isActive ()) \
+ painter . cmd ; \
+ \
+ if (maskPainter.isActive ()) \
+ maskPainter . cmd ; \
+ }
+ if (w > oldWidth)
+ PAINTER_CALL (drawRect (oldWidth, 0, w - oldWidth, oldHeight));
+
+ if (h > oldHeight)
+ PAINTER_CALL (drawRect (0, oldHeight, w, h - oldHeight));
+ #undef PAINTER_CALL
+
+ if (maskPainter.isActive ())
+ maskPainter.end ();
+
+ if (painter.isActive ())
+ painter.end ();
+
+ if (!maskBitmap.isNull ())
+ destPixmapPtr->setMask (maskBitmap);
+ }
+}
+
+// public static
+QPixmap kpPixmapFX::resize (const QPixmap &pm, int w, int h,
+ const kpColor &backgroundColor, bool fillNewAreas)
+{
+ QPixmap ret = pm;
+ kpPixmapFX::resize (&ret, w, h, backgroundColor, fillNewAreas);
+ return ret;
+}
+
+
+// public static
+void kpPixmapFX::scale (QPixmap *destPixmapPtr, int w, int h, bool pretty)
+{
+ if (!destPixmapPtr)
+ return;
+
+ *destPixmapPtr = kpPixmapFX::scale (*destPixmapPtr, w, h, pretty);
+}
+
+// public static
+QPixmap kpPixmapFX::scale (const QPixmap &pm, int w, int h, bool pretty)
+{
+#if DEBUG_KP_PIXMAP_FX && 0
+ kdDebug () << "kpPixmapFX::scale(oldRect=" << pm.rect ()
+ << ",w=" << w
+ << ",h=" << h
+ << ",pretty=" << pretty
+ << ")"
+ << endl;
+#endif
+
+ if (w == pm.width () && h == pm.height ())
+ return pm;
+
+ if (pretty)
+ {
+ QImage image = kpPixmapFX::convertToImage (pm);
+
+ #if DEBUG_KP_PIXMAP_FX && 0
+ kdDebug () << "\tBefore smooth scale:" << endl;
+ for (int y = 0; y < image.height (); y++)
+ {
+ for (int x = 0; x < image.width (); x++)
+ {
+ fprintf (stderr, " %08X", image.pixel (x, y));
+ }
+ fprintf (stderr, "\n");
+ }
+ #endif
+
+ image = image.smoothScale (w, h);
+
+ #if DEBUG_KP_PIXMAP_FX && 0
+ kdDebug () << "\tAfter smooth scale:" << endl;
+ for (int y = 0; y < image.height (); y++)
+ {
+ for (int x = 0; x < image.width (); x++)
+ {
+ fprintf (stderr, " %08X", image.pixel (x, y));
+ }
+ fprintf (stderr, "\n");
+ }
+ #endif
+
+ return kpPixmapFX::convertToPixmap (image, false/*let's not smooth it again*/);
+ }
+ else
+ {
+ QWMatrix matrix;
+
+ matrix.scale (double (w) / double (pm.width ()),
+ double (h) / double (pm.height ()));
+
+ return pm.xForm (matrix);
+ }
+}
+
+
+// public static
+double kpPixmapFX::AngleInDegreesEpsilon =
+ KP_RADIANS_TO_DEGREES (atan (1.0 / 10000.0))
+ / (2.0/*max error allowed*/ * 2.0/*for good measure*/);
+
+
+static QWMatrix matrixWithZeroOrigin (const QWMatrix &matrix, int width, int height)
+{
+#if DEBUG_KP_PIXMAP_FX
+ kdDebug () << "matrixWithZeroOrigin(w=" << width << ",h=" << height << ")" << endl;
+ kdDebug () << "\tmatrix: m11=" << matrix.m11 ()
+ << " m12=" << matrix.m12 ()
+ << " m21=" << matrix.m21 ()
+ << " m22=" << matrix.m22 ()
+ << " dx=" << matrix.dx ()
+ << " dy=" << matrix.dy ()
+ << endl;
+#endif
+ // TODO: Should we be using QWMatrix::Areas?
+ QRect newRect = matrix.mapRect (QRect (0, 0, width, height));
+#if DEBUG_KP_PIXMAP_FX
+ kdDebug () << "\tnewRect=" << newRect << endl;
+#endif
+
+ QWMatrix translatedMatrix (matrix.m11 (), matrix.m12 (), matrix.m21 (), matrix.m22 (),
+ matrix.dx () - newRect.left (), matrix.dy () - newRect.top ());
+
+ return translatedMatrix;
+}
+
+static QPixmap xForm (const QPixmap &pm, const QWMatrix &transformMatrix_,
+ const kpColor &backgroundColor,
+ int targetWidth, int targetHeight)
+{
+ QWMatrix transformMatrix = transformMatrix_;
+
+#if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "kppixmapfx.cpp: xForm(pm.size=" << pm.size ()
+ << ",targetWidth=" << targetWidth
+ << ",targetHeight=" << targetHeight
+ << ")"
+ << endl;
+#endif
+ // TODO: Should we be using QWMatrix::Areas?
+ QRect newRect = transformMatrix.map (pm.rect ());
+#if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\tmappedRect=" << newRect << endl;
+
+#endif
+
+ QWMatrix scaleMatrix;
+ if (targetWidth > 0 && targetWidth != newRect.width ())
+ {
+ #if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\tadjusting for targetWidth" << endl;
+ #endif
+ scaleMatrix.scale (double (targetWidth) / double (newRect.width ()), 1);
+ }
+
+ if (targetHeight > 0 && targetHeight != newRect.height ())
+ {
+ #if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\tadjusting for targetHeight" << endl;
+ #endif
+ scaleMatrix.scale (1, double (targetHeight) / double (newRect.height ()));
+ }
+
+ if (!scaleMatrix.isIdentity ())
+ {
+ #if DEBUG_KP_PIXMAP_FX && 1
+ // TODO: What is going on here??? Why isn't matrix * working properly?
+ QWMatrix wrongMatrix = transformMatrix * scaleMatrix;
+ QWMatrix oldHat = transformMatrix;
+ if (targetWidth > 0 && targetWidth != newRect.width ())
+ oldHat.scale (double (targetWidth) / double (newRect.width ()), 1);
+ if (targetHeight > 0 && targetHeight != newRect.height ())
+ oldHat.scale (1, double (targetHeight) / double (newRect.height ()));
+ QWMatrix altHat = transformMatrix;
+ altHat.scale ((targetWidth > 0 && targetWidth != newRect.width ()) ? double (targetWidth) / double (newRect.width ()) : 1,
+ (targetHeight > 0 && targetHeight != newRect.height ()) ? double (targetHeight) / double (newRect.height ()) : 1);
+ QWMatrix correctMatrix = scaleMatrix * transformMatrix;
+
+ kdDebug () << "\tsupposedlyWrongMatrix: m11=" << wrongMatrix.m11 () // <<<---- this is the correct matrix???
+ << " m12=" << wrongMatrix.m12 ()
+ << " m21=" << wrongMatrix.m21 ()
+ << " m22=" << wrongMatrix.m22 ()
+ << " dx=" << wrongMatrix.dx ()
+ << " dy=" << wrongMatrix.dy ()
+ << " rect=" << wrongMatrix.map (pm.rect ())
+ << endl
+ << "\ti_used_to_use_thisMatrix: m11=" << oldHat.m11 ()
+ << " m12=" << oldHat.m12 ()
+ << " m21=" << oldHat.m21 ()
+ << " m22=" << oldHat.m22 ()
+ << " dx=" << oldHat.dx ()
+ << " dy=" << oldHat.dy ()
+ << " rect=" << oldHat.map (pm.rect ())
+ << endl
+ << "\tabove but scaled at the same time: m11=" << altHat.m11 ()
+ << " m12=" << altHat.m12 ()
+ << " m21=" << altHat.m21 ()
+ << " m22=" << altHat.m22 ()
+ << " dx=" << altHat.dx ()
+ << " dy=" << altHat.dy ()
+ << " rect=" << altHat.map (pm.rect ())
+ << endl
+ << "\tsupposedlyCorrectMatrix: m11=" << correctMatrix.m11 ()
+ << " m12=" << correctMatrix.m12 ()
+ << " m21=" << correctMatrix.m21 ()
+ << " m22=" << correctMatrix.m22 ()
+ << " dx=" << correctMatrix.dx ()
+ << " dy=" << correctMatrix.dy ()
+ << " rect=" << correctMatrix.map (pm.rect ())
+ << endl;
+ #endif
+
+ transformMatrix = transformMatrix * scaleMatrix;
+
+ // TODO: Should we be using QWMatrix::Areas?
+ newRect = transformMatrix.map (pm.rect ());
+ #if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\tnewRect after targetWidth,targetHeight adjust=" << newRect << endl;
+ #endif
+ }
+
+
+ QPixmap newPixmap (targetWidth > 0 ? targetWidth : newRect.width (),
+ targetHeight > 0 ? targetHeight : newRect.height ());
+ if ((targetWidth > 0 && targetWidth != newRect.width ()) ||
+ (targetHeight > 0 && targetHeight != newRect.height ()))
+ {
+ #if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "kppixmapfx.cpp: xForm(pm.size=" << pm.size ()
+ << ",targetWidth=" << targetWidth
+ << ",targetHeight=" << targetHeight
+ << ") newRect=" << newRect
+ << " (you are a victim of rounding error)"
+ << endl;
+ #endif
+ }
+
+ QBitmap newBitmapMask;
+
+ if (backgroundColor.isOpaque ())
+ newPixmap.fill (backgroundColor.toQColor ());
+
+ if (backgroundColor.isTransparent () || pm.mask ())
+ {
+ newBitmapMask.resize (newPixmap.width (), newPixmap.height ());
+ newBitmapMask.fill (backgroundColor.maskColor ());
+ }
+
+ QPainter painter (&newPixmap);
+#if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\tmatrix: m11=" << transformMatrix.m11 ()
+ << " m12=" << transformMatrix.m12 ()
+ << " m21=" << transformMatrix.m21 ()
+ << " m22=" << transformMatrix.m22 ()
+ << " dx=" << transformMatrix.dx ()
+ << " dy=" << transformMatrix.dy ()
+ << endl;
+ const QWMatrix trueMatrix = QPixmap::trueMatrix (transformMatrix,
+ pm.width (), pm.height ());
+ kdDebug () << "\ttrue matrix: m11=" << trueMatrix.m11 ()
+ << " m12=" << trueMatrix.m12 ()
+ << " m21=" << trueMatrix.m21 ()
+ << " m22=" << trueMatrix.m22 ()
+ << " dx=" << trueMatrix.dx ()
+ << " dy=" << trueMatrix.dy ()
+ << endl;
+#endif
+ painter.setWorldMatrix (transformMatrix);
+#if DEBUG_KP_PIXMAP_FX && 0
+ kdDebug () << "\ttranslate top=" << painter.xForm (QPoint (0, 0)) << endl;
+ kdDebug () << "\tmatrix: m11=" << painter.worldMatrix ().m11 ()
+ << " m12=" << painter.worldMatrix ().m12 ()
+ << " m21=" << painter.worldMatrix ().m21 ()
+ << " m22=" << painter.worldMatrix ().m22 ()
+ << " dx=" << painter.worldMatrix ().dx ()
+ << " dy=" << painter.worldMatrix ().dy ()
+ << endl;
+#endif
+ painter.drawPixmap (QPoint (0, 0), pm);
+ painter.end ();
+
+ if (!newBitmapMask.isNull ())
+ {
+ QPainter maskPainter (&newBitmapMask);
+ maskPainter.setWorldMatrix (transformMatrix);
+ maskPainter.drawPixmap (QPoint (0, 0), kpPixmapFX::getNonNullMask (pm));
+ maskPainter.end ();
+ newPixmap.setMask (newBitmapMask);
+ }
+
+ return newPixmap;
+}
+
+// public static
+QWMatrix kpPixmapFX::skewMatrix (int width, int height, double hangle, double vangle)
+{
+ if (fabs (hangle - 0) < kpPixmapFX::AngleInDegreesEpsilon &&
+ fabs (vangle - 0) < kpPixmapFX::AngleInDegreesEpsilon)
+ {
+ return QWMatrix ();
+ }
+
+
+ /* Diagram for completeness :)
+ *
+ * |---------- w ----------|
+ * (0,0)
+ * _ _______________________ (w,0)
+ * | |\~_ va |
+ * | | \ ~_ |
+ * | |ha\ ~__ |
+ * | \ ~__ | dy
+ * h | \ ~___ |
+ * | \ ~___ |
+ * | | \ ~___| (w,w*tan(va)=dy)
+ * | | \ * \
+ * _ |________\________|_____|\ vertical shear factor
+ * (0,h) dx ^~_ | \ |
+ * | ~_ \________\________ General Point (x,y) V
+ * | ~__ \ Skewed Point (x + y*tan(ha),y + x*tan(va))
+ * (h*tan(ha)=dx,h) ~__ \ ^
+ * ~___ \ |
+ * ~___ \ horizontal shear factor
+ * Key: ~___\
+ * ha = hangle (w + h*tan(ha)=w+dx,h + w*tan(va)=w+dy)
+ * va = vangle
+ *
+ * Skewing really just twists a rectangle into a parallelogram.
+ *
+ */
+
+ //QWMatrix matrix (1, tan (KP_DEGREES_TO_RADIANS (vangle)), tan (KP_DEGREES_TO_RADIANS (hangle)), 1, 0, 0);
+ // I think this is clearer than above :)
+ QWMatrix matrix;
+ matrix.shear (tan (KP_DEGREES_TO_RADIANS (hangle)),
+ tan (KP_DEGREES_TO_RADIANS (vangle)));
+
+ return matrixWithZeroOrigin (matrix, width, height);
+}
+
+// public static
+QWMatrix kpPixmapFX::skewMatrix (const QPixmap &pixmap, double hangle, double vangle)
+{
+ return kpPixmapFX::skewMatrix (pixmap.width (), pixmap.height (), hangle, vangle);
+}
+
+
+// public static
+void kpPixmapFX::skew (QPixmap *destPixmapPtr, double hangle, double vangle,
+ const kpColor &backgroundColor,
+ int targetWidth, int targetHeight)
+{
+ if (!destPixmapPtr)
+ return;
+
+ *destPixmapPtr = kpPixmapFX::skew (*destPixmapPtr, hangle, vangle,
+ backgroundColor,
+ targetWidth, targetHeight);
+}
+
+// public static
+QPixmap kpPixmapFX::skew (const QPixmap &pm, double hangle, double vangle,
+ const kpColor &backgroundColor,
+ int targetWidth, int targetHeight)
+{
+#if DEBUG_KP_PIXMAP_FX
+ kdDebug () << "kpPixmapFX::skew() pm.width=" << pm.width ()
+ << " pm.height=" << pm.height ()
+ << " hangle=" << hangle
+ << " vangle=" << vangle
+ << " targetWidth=" << targetWidth
+ << " targetHeight=" << targetHeight
+ << endl;
+#endif
+
+ if (fabs (hangle - 0) < kpPixmapFX::AngleInDegreesEpsilon &&
+ fabs (vangle - 0) < kpPixmapFX::AngleInDegreesEpsilon &&
+ (targetWidth <= 0 && targetHeight <= 0)/*don't want to scale?*/)
+ {
+ return pm;
+ }
+
+ if (fabs (hangle) > 90 - kpPixmapFX::AngleInDegreesEpsilon ||
+ fabs (vangle) > 90 - kpPixmapFX::AngleInDegreesEpsilon)
+ {
+ kdError () << "kpPixmapFX::skew() passed hangle and/or vangle out of range (-90 < x < 90)" << endl;
+ return pm;
+ }
+
+
+ QWMatrix matrix = skewMatrix (pm, hangle, vangle);
+
+ return ::xForm (pm, matrix, backgroundColor, targetWidth, targetHeight);
+}
+
+
+// public static
+QWMatrix kpPixmapFX::rotateMatrix (int width, int height, double angle)
+{
+ if (fabs (angle - 0) < kpPixmapFX::AngleInDegreesEpsilon)
+ {
+ return QWMatrix ();
+ }
+
+ QWMatrix matrix;
+ matrix.translate (width / 2, height / 2);
+ matrix.rotate (angle);
+
+ return matrixWithZeroOrigin (matrix, width, height);
+}
+
+// public static
+QWMatrix kpPixmapFX::rotateMatrix (const QPixmap &pixmap, double angle)
+{
+ return kpPixmapFX::rotateMatrix (pixmap.width (), pixmap.height (), angle);
+}
+
+
+// public static
+bool kpPixmapFX::isLosslessRotation (double angle)
+{
+ const double angleIn = angle;
+
+ // Reflect angle into positive if negative
+ if (angle < 0)
+ angle = -angle;
+
+ // Remove multiples of 90 to make sure 0 <= angle <= 90
+ angle -= ((int) angle) / 90 * 90;
+
+ // "Impossible" situation?
+ if (angle < 0 || angle > 90)
+ {
+ kdError () << "kpPixmapFX::isLosslessRotation(" << angleIn
+ << ") result=" << angle
+ << endl;
+ return false; // better safe than sorry
+ }
+
+ const bool ret = (angle < kpPixmapFX::AngleInDegreesEpsilon ||
+ 90 - angle < kpPixmapFX::AngleInDegreesEpsilon);
+#if DEBUG_KP_PIXMAP_FX
+ kdDebug () << "kpPixmapFX::isLosslessRotation(" << angleIn << ")"
+ << " residual angle=" << angle
+ << " returning " << ret
+ << endl;
+#endif
+ return ret;
+}
+
+
+// public static
+void kpPixmapFX::rotate (QPixmap *destPixmapPtr, double angle,
+ const kpColor &backgroundColor,
+ int targetWidth, int targetHeight)
+{
+ if (!destPixmapPtr)
+ return;
+
+ *destPixmapPtr = kpPixmapFX::rotate (*destPixmapPtr, angle,
+ backgroundColor,
+ targetWidth, targetHeight);
+}
+
+// public static
+QPixmap kpPixmapFX::rotate (const QPixmap &pm, double angle,
+ const kpColor &backgroundColor,
+ int targetWidth, int targetHeight)
+{
+ if (fabs (angle - 0) < kpPixmapFX::AngleInDegreesEpsilon &&
+ (targetWidth <= 0 && targetHeight <= 0)/*don't want to scale?*/)
+ {
+ return pm;
+ }
+
+
+ QWMatrix matrix = rotateMatrix (pm, angle);
+
+ return ::xForm (pm, matrix, backgroundColor, targetWidth, targetHeight);
+}
+
+
+// public static
+QWMatrix kpPixmapFX::flipMatrix (int width, int height, bool horz, bool vert)
+{
+ if (width <= 0 || height <= 0)
+ {
+ kdError () << "kpPixmapFX::flipMatrix() passed invalid dimensions" << endl;
+ return QWMatrix ();
+ }
+
+ return QWMatrix (horz ? -1 : +1, // m11
+ 0, // m12
+ 0, // m21
+ vert ? -1 : +1, // m22
+ horz ? (width - 1) : 0, // dx
+ vert ? (height - 1) : 0); // dy
+}
+
+// public static
+QWMatrix kpPixmapFX::flipMatrix (const QPixmap &pixmap, bool horz, bool vert)
+{
+ return kpPixmapFX::flipMatrix (pixmap.width (), pixmap.height (),
+ horz, vert);
+}
+
+
+// public static
+void kpPixmapFX::flip (QPixmap *destPixmapPtr, bool horz, bool vert)
+{
+ if (!horz && !vert)
+ return;
+
+ *destPixmapPtr = kpPixmapFX::flip (*destPixmapPtr, horz, vert);
+}
+
+// public static
+QPixmap kpPixmapFX::flip (const QPixmap &pm, bool horz, bool vert)
+{
+ if (!horz && !vert)
+ return pm;
+
+ return pm.xForm (flipMatrix (pm, horz, vert));
+}
+
+// public static
+void kpPixmapFX::flip (QImage *destImagePtr, bool horz, bool vert)
+{
+ if (!horz && !vert)
+ return;
+
+ *destImagePtr = kpPixmapFX::flip (*destImagePtr, horz, vert);
+}
+
+// public static
+QImage kpPixmapFX::flip (const QImage &img, bool horz, bool vert)
+{
+ if (!horz && !vert)
+ return img;
+
+ return img.mirror (horz, vert);
+}
diff --git a/kolourpaint/pixmapfx/kppixmapfx.h b/kolourpaint/pixmapfx/kppixmapfx.h
new file mode 100644
index 00000000..c083ee43
--- /dev/null
+++ b/kolourpaint/pixmapfx/kppixmapfx.h
@@ -0,0 +1,450 @@
+
+/*
+ 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.
+*/
+
+
+#ifndef KP_PIXMAP_FX_H
+#define KP_PIXMAP_FX_H
+
+
+#include <qstring.h>
+
+
+class QBitmap;
+class QColor;
+class QImage;
+class QPointArray;
+class QPixmap;
+class QPoint;
+class QRect;
+class QString;
+class QWidget;
+class QWMatrix;
+
+class kpColor;
+class kpSelection;
+
+
+class kpPixmapFX
+{
+public:
+
+ //
+ // Overflow Resistant Arithmetic:
+ //
+ // Returns INT_MAX if <lhs> or <rhs> < 0 or if would overflow.
+ static int addDimensions (int lhs, int rhs);
+ static int multiplyDimensions (int lhs, int rhs);
+
+
+ //
+ // QPixmap Statistics
+ //
+
+ // Returns the width * height.
+ static int pixmapArea (const QPixmap &pixmap);
+ static int pixmapArea (const QPixmap *pixmap);
+ static int pixmapArea (int width, int height);
+
+ // Returns the estimated size of <pixmap> in pixmap memory.
+ static int pixmapSize (const QPixmap &pixmap);
+ static int pixmapSize (const QPixmap *pixmap);
+ static int pixmapSize (int width, int height, int depth);
+
+ static int imageSize (const QImage &image);
+ static int imageSize (const QImage *image);
+ static int imageSize (int width, int height, int depth);
+
+ static int selectionSize (const kpSelection &sel);
+ static int selectionSize (const kpSelection *sel);
+
+ static int stringSize (const QString &string);
+
+ static int pointArraySize (const QPointArray &points);
+
+
+ //
+ // QPixmap/QImage Conversion Functions
+ //
+
+ //
+ // Converts <pixmap> to a QImage and returns it.
+ //
+ // WARNING: On an 8-bit screen:
+ //
+ // QPixmap result = convertToPixmap (convertToImage (pixmap));
+ //
+ // <result> is slightly differently colored to <pixmap>.
+ //
+ // KolourPaint needs to convert to QImage occasionally as
+ // QImage allows KolourPaint to read pixels and because the QImage
+ // methods give reliable results and pixel-identical results on
+ // all platforms. The QPixmap paint engine has no such guarantee
+ // and even depends on the quality of the video driver.
+ //
+ // As a result, KolourPaint should not be used on an 8-bit screen.
+ // HITODO: Add warning on startup, like in KolourPaint/KDE4.
+ //
+ // This bug will be fixed when KolourPaint gets a proper image library,
+ // where QPixmap -> QImage -> QPixmap transitions will be not be needed.
+ static QImage convertToImage (const QPixmap &pixmap);
+
+ //
+ // Dialog info for warning about data loss with convertToPixmap().
+ //
+ struct WarnAboutLossInfo
+ {
+ // <moreColorsThanDisplayAndHasAlphaChannelMessage>:
+ //
+ // i18n ("The (image \"example.jpg\"|image from the clipboard)"
+ // " may have more colors than the current screen mode."
+ // " In order to display it, some colors may be changed."
+ // " Try increasing your screen depth to at least %1bpp."
+ //
+ // "\nIt also"
+ //
+ // " contains translucency which is not fully"
+ // " supported. The translucency data will be"
+ // " approximated with a 1-bit transparency mask.")
+ //
+ // <moreColorsThanDisplayMessage>:
+ // i18n ("The (image \"example.jpg\"|image from the clipboard)"
+ // " may have more colors than the current screen mode."
+ // " In order to display it, some colors may be changed."
+ // " Try increasing your screen depth to at least %1bpp.")
+ //
+ // <hasAlphaChannelMessage>:
+ // i18n ("The (image \"example.jpg\"|image from the clipboard)"
+ // " contains translucency which is not fully"
+ // " supported. The translucency data will be"
+ // " approximated with a 1-bit transparency mask.")
+ //
+ // <dontAskAgainPrefix>:
+ //
+ // Don'tAskAgain ID for dialog.
+ //
+ // <parent>:
+ //
+ // Dialog parent
+ //
+ WarnAboutLossInfo (const QString &moreColorsThanDisplayAndHasAlphaChannelMessage,
+ const QString &moreColorsThanDisplayMessage,
+ const QString &hasAlphaChannelMessage,
+ const QString &dontAskAgainPrefix,
+ QWidget *parent)
+ :
+ m_moreColorsThanDisplayAndHasAlphaChannelMessage (
+ moreColorsThanDisplayAndHasAlphaChannelMessage),
+ m_moreColorsThanDisplayMessage (
+ moreColorsThanDisplayMessage),
+ m_hasAlphaChannelMessage (
+ hasAlphaChannelMessage),
+ m_dontAskAgainPrefix (
+ dontAskAgainPrefix),
+ m_parent (parent),
+ m_isValid (true)
+ {
+ }
+
+ WarnAboutLossInfo ()
+ : m_parent (0),
+ m_isValid (false)
+ {
+ }
+
+ ~WarnAboutLossInfo ()
+ {
+ }
+
+
+ bool isValid () const { return m_isValid; }
+
+
+ QString m_moreColorsThanDisplayAndHasAlphaChannelMessage,
+ m_moreColorsThanDisplayMessage,
+ m_hasAlphaChannelMessage;
+ QString m_dontAskAgainPrefix;
+ QWidget *m_parent;
+ bool m_isValid;
+ };
+
+ //
+ // Converts <image> to a QPixmap of the current display's depth and
+ // returns it.
+ //
+ // If the flag <pretty> is set, it will dither the image making the
+ // returned pixmap look better but if the image has few colours
+ // (less than the screen can handle), this will be at the expense of
+ // exactness of conversion.
+ //
+ // This will automatically call ensureNoAlphaChannel().
+ //
+ // Never use a foreign QPixmap that is offered to you - always get the
+ // foreign QImage and use this function to convert it to a sane QPixmap.
+ //
+ // <wali>, if specified, describes parameters for the dialog that comes
+ // up warning the user of data loss if the <image> contains translucency
+ // and/or more colors than the current display.
+ //
+ static QPixmap convertToPixmap (const QImage &image, bool pretty = false,
+ const WarnAboutLossInfo &wali = WarnAboutLossInfo ());
+
+ // Same as convertToPixmap() but tries as hard as possible to make the
+ // pixmap look like the original <image> - when in doubt, reads the
+ // config to see whether or not to dither (default: on).
+ //
+ // If you know for sure that <image> can be displayed losslessly on
+ // the screen, you should call convertToPixmap() with <pretty> = false
+ // instead. If you know for sure that <image> cannot be displayed
+ // losslessly, then call convertToPixmap() with <pretty> = true.
+ //
+ static QPixmap convertToPixmapAsLosslessAsPossible (const QImage &image,
+ const WarnAboutLossInfo &wali = WarnAboutLossInfo ());
+
+
+ // Sets the RGB values of the pixels where <pixmap> is transparent to
+ // <transparentColor>. This has visually no effect on the <pixmap>
+ // unless the mask is lost.
+ static QPixmap pixmapWithDefinedTransparentPixels (const QPixmap &pixmap,
+ const QColor &transparentColor);
+
+
+ //
+ // Get/Set Parts of Pixmap
+ //
+
+
+ //
+ // Returns the pixel and mask data found at the <rect> in <pm>.
+ //
+ static QPixmap getPixmapAt (const QPixmap &pm, const QRect &rect);
+
+ //
+ // Sets the pixel and mask data at <destRect> in <*destPixmapPtr>
+ // to <srcPixmap>.
+ //
+ static void setPixmapAt (QPixmap *destPixmapPtr, const QRect &destRect,
+ const QPixmap &srcPixmap);
+
+ //
+ // Sets the pixel and mask data at the rectangle in <*destPixmapPtr>,
+ // with the top-left <destAt> and dimensions <srcPixmap.rect()>,
+ // to <srcPixmap>.
+ //
+ static void setPixmapAt (QPixmap *destPixmapPtr, const QPoint &destAt,
+ const QPixmap &srcPixmap);
+ static void setPixmapAt (QPixmap *destPixmapPtr, int destX, int destY,
+ const QPixmap &srcPixmap);
+
+ //
+ // Draws <srcPixmap> on top of <*destPixmapPtr> at <destAt>.
+ // The mask of <*destPixmapPtr> is adjusted so that all opaque
+ // pixels in <srcPixmap> will be opaque in <*destPixmapPtr>.
+ //
+ static void paintPixmapAt (QPixmap *destPixmapPtr, const QPoint &destAt,
+ const QPixmap &srcPixmap);
+ static void paintPixmapAt (QPixmap *destPixmapPtr, int destX, int destY,
+ const QPixmap &srcPixmap);
+
+ //
+ // Returns the colour of the pixel at <at> in <pm>.
+ // If the pixel is transparent, a value is returned such that
+ // kpTool::isColorTransparent(<return_value>) will return true.
+ //
+ static kpColor getColorAtPixel (const QPixmap &pm, const QPoint &at);
+ static kpColor getColorAtPixel (const QPixmap &pm, int x, int y);
+
+ //
+ // Returns the color of the pixel at <at> in <img>.
+ // If the pixel is transparent, a value is returned such that
+ // kpTool::isColorTransparent(<return_value>) will return true.
+ //
+ static kpColor getColorAtPixel (const QImage &img, const QPoint &at);
+ static kpColor getColorAtPixel (const QImage &img, int x, int y);
+
+
+ //
+ // Mask Operations
+ //
+
+
+ //
+ // Removes <*destPixmapPtr>'s Alpha Channel and attempts to convert it
+ // to a mask. KolourPaint - and QPixmap to a great extent - does not
+ // support Alpha Channels - only masks. Call this whenever you get
+ // a pixmap from a foreign source; else all KolourPaint code will
+ // exhibit "undefined behaviour".
+ //
+ static void ensureNoAlphaChannel (QPixmap *destPixmapPtr);
+
+ //
+ // Returns <pm>'s mask or a fully opaque mask (with <pm>'s dimensions)
+ // if <pm> does not have a mask.
+ //
+ static QBitmap getNonNullMask (const QPixmap &pm);
+
+ //
+ // Ensures that <*destPixmapPtr> is transparent at <rect>.
+ //
+ static void ensureTransparentAt (QPixmap *destPixmapPtr, const QRect &destRect);
+
+ //
+ // Sets the mask of <*destPixmapPtr> at the rectangle, with the
+ // top-left <destAt> and dimensions <srcMaskBitmap.rect()>,
+ // to transparent where <brushBitmap> is opaque.
+ //
+ // <brushPixmap> must be a QPixmap of depth 1 (or a QBitmap).
+ //
+ static void paintMaskTransparentWithBrush (QPixmap *destPixmapPtr, const QPoint &destAt,
+ const QPixmap &brushBitmap);
+ static void paintMaskTransparentWithBrush (QPixmap *destPixmapPtr, int destX, int destY,
+ const QPixmap &brushBitmap);
+
+ //
+ // Ensures that <*destPixmapPtr> is opaque at <rect>.
+ //
+ static void ensureOpaqueAt (QPixmap *destPixmapPtr, const QRect &destRect);
+
+ //
+ // Ensures that <srcPixmap>'s opaque pixels will be opaque if
+ // painted onto <*destPixmapPtr> at <destAt>.
+ //
+ static void ensureOpaqueAt (QPixmap *destPixmapPtr, const QPoint &destAt,
+ const QPixmap &srcPixmap);
+ static void ensureOpaqueAt (QPixmap *destPixmapPtr, int destX, int destY,
+ const QPixmap &srcPixmap);
+
+
+ //
+ // Effects
+ //
+
+
+ //
+ // Converts the image to grayscale.
+ //
+ static void convertToGrayscale (QPixmap *destPixmapPtr);
+ static QPixmap convertToGrayscale (const QPixmap &pm);
+ static void convertToGrayscale (QImage *destImagePtr);
+ static QImage convertToGrayscale (const QImage &img);
+
+ //
+ // Fills an image in the given color.
+ //
+ static void fill (QPixmap *destPixmapPtr, const kpColor &color);
+ static QPixmap fill (const QPixmap &pm, const kpColor &color);
+
+ //
+ // Resizes an image to the given width and height,
+ // filling any new areas with <backgroundColor> if <fillNewAreas> is set.
+ //
+ static void resize (QPixmap *destPixmapPtr, int w, int h,
+ const kpColor &backgroundColor, bool fillNewAreas = true);
+ static QPixmap resize (const QPixmap &pm, int w, int h,
+ const kpColor &backgroundColor, bool fillNewAreas = true);
+
+ //
+ // Scales an image to the given width and height.
+ // If <pretty> is true, a smooth scale will be used.
+ //
+ static void scale (QPixmap *destPixmapPtr, int w, int h, bool pretty = false);
+ static QPixmap scale (const QPixmap &pm, int w, int h, bool pretty = false);
+
+
+ // The minimum difference between 2 angles (in degrees) such that they are
+ // considered different. This gives you at least enough precision to
+ // rotate an image whose width <= 10000 such that its height increases
+ // by just 1 (and similarly with height <= 10000 and width).
+ //
+ // Currently used for skew & rotate operations.
+ static double AngleInDegreesEpsilon;
+
+
+ //
+ // Skews an image.
+ //
+ // <hangle> horizontal angle clockwise (-90 < x < 90)
+ // <vangle> vertical angle clockwise (-90 < x < 90)
+ // <backgroundColor> color to fill new areas with
+ // <targetWidth> if > 0, the desired width of the resultant pixmap
+ // <targetHeight> if > 0, the desired height of the resultant pixmap
+ //
+ // Using <targetWidth> & <targetHeight> to generate preview pixmaps is
+ // significantly more efficient than skewing and then scaling yourself.
+ //
+ static QWMatrix skewMatrix (int width, int height, double hangle, double vangle);
+ static QWMatrix skewMatrix (const QPixmap &pixmap, double hangle, double vangle);
+
+ static void skew (QPixmap *destPixmapPtr, double hangle, double vangle,
+ const kpColor &backgroundColor,
+ int targetWidth = -1, int targetHeight = -1);
+ static QPixmap skew (const QPixmap &pm, double hangle, double vangle,
+ const kpColor &backgroundColor,
+ int targetWidth = -1, int targetHeight = -1);
+
+ //
+ // Rotates an image.
+ //
+ // <angle> clockwise angle to rotate by
+ // <backgroundColor> color to fill new areas with
+ // <targetWidth> if > 0, the desired width of the resultant pixmap
+ // <targetHeight> if > 0, the desired height of the resultant pixmap
+ //
+ // Using <targetWidth> & <targetHeight> to generate preview pixmaps is
+ // significantly more efficient than rotating and then scaling yourself.
+ //
+ static QWMatrix rotateMatrix (int width, int height, double angle);
+ static QWMatrix rotateMatrix (const QPixmap &pixmap, double angle);
+
+ static bool isLosslessRotation (double angle);
+
+ static void rotate (QPixmap *destPixmapPtr, double angle,
+ const kpColor &backgroundColor,
+ int targetWidth = -1, int targetHeight = -1);
+ static QPixmap rotate (const QPixmap &pm, double angle,
+ const kpColor &backgroundColor,
+ int targetWidth = -1, int targetHeight = -1);
+
+
+ //
+ // Flips an image in the given directions.
+ //
+ static QWMatrix flipMatrix (int width, int height, bool horz, bool vert);
+ static QWMatrix flipMatrix (const QPixmap &pixmap, bool horz, bool vert);
+
+ // TODO: this kind of overloading is error prone
+ // e.g. QPixmap pixmap;
+ // kpPixmapFX::flip (pixmap, false, true);
+ // looks like it will flip vertically but does absolutely nothing!
+ // (should be &pixmap)
+ static void flip (QPixmap *destPixmapPtr, bool horz, bool vert);
+ static QPixmap flip (const QPixmap &pm, bool horz, bool vert);
+ static void flip (QImage *destImagePtr, bool horz, bool vert);
+ static QImage flip (const QImage &img, bool horz, bool vert);
+};
+
+
+#endif // KP_PIXMAP_FX_H