summaryrefslogtreecommitdiffstats
path: root/kdefx
diff options
context:
space:
mode:
Diffstat (limited to 'kdefx')
-rw-r--r--kdefx/Mainpage.dox32
-rw-r--r--kdefx/Makefile.am45
-rw-r--r--kdefx/configure.in.in63
-rw-r--r--kdefx/kcpuinfo.cpp176
-rw-r--r--kdefx/kcpuinfo.h70
-rw-r--r--kdefx/kdrawutil.cpp264
-rw-r--r--kdefx/kdrawutil.h180
-rw-r--r--kdefx/kimageeffect.cpp4927
-rw-r--r--kdefx/kimageeffect.h807
-rw-r--r--kdefx/kpixmap.cpp389
-rw-r--r--kdefx/kpixmap.h213
-rw-r--r--kdefx/kpixmapeffect.cpp325
-rw-r--r--kdefx/kpixmapeffect.h218
-rw-r--r--kdefx/kpixmapsplitter.cpp95
-rw-r--r--kdefx/kpixmapsplitter.h123
-rw-r--r--kdefx/kstyle.cpp2164
-rw-r--r--kdefx/kstyle.h344
-rw-r--r--kdefx/libkdefx.nmcheck12
-rw-r--r--kdefx/libkdefx_weak.nmcheck3
19 files changed, 10450 insertions, 0 deletions
diff --git a/kdefx/Mainpage.dox b/kdefx/Mainpage.dox
new file mode 100644
index 000000000..1cd8258fa
--- /dev/null
+++ b/kdefx/Mainpage.dox
@@ -0,0 +1,32 @@
+/**
+ * \mainpage The KDEfx Library
+ *
+ * This library provides various classes related to image and pixmap
+ * manipulation, a class that provides information about CPU support
+ * for architecture specific features, as well as the base class for
+ * the %KDE widget styles.
+ *
+ * The two graphical effects classes, KImageEffect and KPixmapEffect,
+ * provide static methods for applying graphical effects to images and
+ * pixmaps respectively. KImageEffect also provides highly optimized
+ * methods for compositing images.
+ *
+ * A class that's related to those two is KPixmapSplitter, which is
+ * used for calculating the positions of items in pixmaps with
+ * multiple items arranged in rows and columns.
+ * Another is KPixmap, which extends QPixmap with the capability to
+ * ensure that a 256 color pixmap uses a specific system wide
+ * palette.
+ *
+ * The KCPUInfo class provides a means for applications to obtain
+ * information at runtime about processor support for certain
+ * architecture extensions that are useful when processing images,
+ * such as MMX, SSE, 3DNow! and AltiVec.
+ *
+ * KStyle is the base class for the %KDE widget styles. It simplifies
+ * and extends the QStyle API in order to make style coding easier.
+ * It also provides an internal menu transparency and drop shadow
+ * engine, which means that all styles inheriting this class will
+ * automatically support those features.
+ */
+
diff --git a/kdefx/Makefile.am b/kdefx/Makefile.am
new file mode 100644
index 000000000..d584ed2cb
--- /dev/null
+++ b/kdefx/Makefile.am
@@ -0,0 +1,45 @@
+
+# This file is part of the KDE libraries
+# Copyright (C) 1997 Matthias Kalle Dalheimer (kalle@kde.org)
+# (C) 1997 Stephan Kulow (coolo@kde.org)
+
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Library General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this library; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+
+#SUBDIRS = . tests
+
+INCLUDES= $(all_includes)
+
+lib_LTLIBRARIES = libkdefx.la
+libkdefx_la_LDFLAGS = $(KDE_RPATH) $(KDE_MT_LDFLAGS) $(all_libraries) -no-undefined -version-info 6:0:2
+libkdefx_la_LIBADD = $(LIB_QT) $(LIB_XRENDER)
+libkdefx_la_NMCHECK = $(srcdir)/libkdefx.nmcheck
+libkdefx_la_NMCHECKWEAK = $(srcdir)/libkdefx_weak.nmcheck $(top_srcdir)/kdecore/libqt-mt_weak.nmcheck \
+ $(top_srcdir)/kdecore/standard_weak.nmcheck
+
+include_HEADERS = kpixmap.h kpixmapsplitter.h \
+ kpixmapeffect.h kimageeffect.h kdrawutil.h kstyle.h kcpuinfo.h
+
+libkdefx_la_SOURCES = kpixmap.cpp kpixmapsplitter.cpp \
+ kpixmapeffect.cpp kimageeffect.cpp kdrawutil.cpp kstyle.cpp \
+ kcpuinfo.cpp
+
+METASOURCES = AUTO
+
+EXTRA_DIST = Mainpage.dox
+
+DOXYGEN_REFERENCES = kdecore
+include ../admin/Doxyfile.am
+
diff --git a/kdefx/configure.in.in b/kdefx/configure.in.in
new file mode 100644
index 000000000..9afed6f96
--- /dev/null
+++ b/kdefx/configure.in.in
@@ -0,0 +1,63 @@
+
+dnl -----------------------------------------------------
+dnl XRender check
+dnl -----------------------------------------------------
+LIB_XRENDER=
+if test "$kde_use_qt_emb" = "no" && test "$kde_use_qt_mac" = "no"; then
+ KDE_CHECK_HEADER(X11/extensions/Xrender.h, [xrender_h=yes], [xrender_h=no])
+ if test "$xrender_h" = yes; then
+ KDE_CHECK_LIB(Xrender, XRenderComposite, [
+ LIB_XRENDER=-lXrender
+ AC_DEFINE_UNQUOTED(HAVE_XRENDER, 1, [Defined if your system has XRender support])
+ ], [], -lXext -lX11 $X_EXTRA_LIBS)
+ fi
+fi
+AC_SUBST(LIB_XRENDER)
+
+dnl -----------------------------------------------------
+dnl IA32 checks
+dnl -----------------------------------------------------
+case $host_cpu in
+ i*86 )
+ AC_MSG_CHECKING(for assembler support for IA32 extensions)
+
+ dnl MMX check
+ AC_TRY_COMPILE(, [ __asm__("pxor %mm0, %mm0") ],
+ [
+ echo $ECHO_N "MMX yes$ECHO_C"
+ AC_DEFINE_UNQUOTED(HAVE_X86_MMX, 1, [Define to 1 if the assembler supports MMX instructions.])
+ ], [ echo $ECHO_N "MMX no$ECHO_C" ])
+
+ dnl SSE check
+ AC_TRY_COMPILE(,[ __asm__("xorps %xmm0, %xmm0") ],
+ [
+ echo $ECHO_N ", SSE yes$ECHO_C"
+ AC_DEFINE_UNQUOTED(HAVE_X86_SSE, 1, [Define to 1 if the assembler supports SSE instructions.])
+ ], [ echo $ECHO_N ", SSE no$ECHO_C" ])
+
+ dnl SSE2 check
+ AC_TRY_COMPILE(, [ __asm__("xorpd %xmm0, %xmm0") ],
+ [
+ echo $ECHO_N ", SSE2 yes$ECHO_C"
+ AC_DEFINE_UNQUOTED(HAVE_X86_SSE2, 1, [Define to 1 if the assembler supports SSE2 instructions.])
+ ], [ echo $ECHO_N ", SSE2 no$ECHO_C" ])
+
+ dnl 3DNOW check
+ AC_TRY_COMPILE(, [ __asm__("femms") ],
+ [
+ echo $ECHO_N ", 3DNOW yes$ECHO_C"
+ AC_DEFINE_UNQUOTED(HAVE_X86_3DNOW, 1, [Define to 1 if the assembler supports 3DNOW instructions.])
+ ], [ echo $ECHO_N ", 3DNOW no$ECHO_C" ])
+ echo
+ ;;
+ powerpc )
+ AC_MSG_CHECKING(for assembler support for AltiVec instructions)
+ dnl AltiVec check
+ AC_TRY_COMPILE(, [ __asm__("mtspr 256, %0\n\t" "vand %%v0, %%v0, %%v0" : : "r"(-1) ) ],
+ [
+ echo $ECHO_N " yes$ECHO_C"
+ AC_DEFINE_UNQUOTED(HAVE_PPC_ALTIVEC, 1, [Define to 1 if the assembler supports AltiVec instructions.])
+ ], [ echo $ECHO_N ", AltiVec no$ECHO_C" ])
+ echo
+ ;;
+esac
diff --git a/kdefx/kcpuinfo.cpp b/kdefx/kcpuinfo.cpp
new file mode 100644
index 000000000..97c99f8d9
--- /dev/null
+++ b/kdefx/kcpuinfo.cpp
@@ -0,0 +1,176 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 2003 Fredrik Höglund <fredrik@kde.org>
+ *
+ * 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 <csignal>
+#include <csetjmp>
+
+#include <config.h>
+#include "kcpuinfo.h"
+
+
+#if defined(__GNUC__) || defined(__INTEL_COMPILER)
+# define HAVE_GNU_INLINE_ASM
+#endif
+
+typedef void (*kde_sighandler_t) (int);
+
+#ifdef __i386__
+static jmp_buf env;
+
+// Sighandler for the SSE OS support check
+static void sighandler( int )
+{
+ std::longjmp( env, 1 );
+}
+#endif
+
+#ifdef __PPC__
+static sigjmp_buf KDE_NO_EXPORT jmpbuf;
+static sig_atomic_t KDE_NO_EXPORT canjump = 0;
+
+static void KDE_NO_EXPORT sigill_handler( int sig )
+{
+ if ( !canjump ) {
+ signal( sig, SIG_DFL );
+ raise( sig );
+ }
+ canjump = 0;
+ siglongjmp( jmpbuf, 1 );
+}
+#endif
+
+static int getCpuFeatures()
+{
+ volatile int features = 0;
+
+#if defined( HAVE_GNU_INLINE_ASM )
+#if defined( __i386__ )
+ bool haveCPUID = false;
+ bool have3DNOW = false;
+ int result = 0;
+
+ // First check if the CPU supports the CPUID instruction
+ __asm__ __volatile__(
+ // Try to toggle the CPUID bit in the EFLAGS register
+ "pushf \n\t" // Push the EFLAGS register onto the stack
+ "popl %%ecx \n\t" // Pop the value into ECX
+ "movl %%ecx, %%edx \n\t" // Copy ECX to EDX
+ "xorl $0x00200000, %%ecx \n\t" // Toggle bit 21 (CPUID) in ECX
+ "pushl %%ecx \n\t" // Push the modified value onto the stack
+ "popf \n\t" // Pop it back into EFLAGS
+
+ // Check if the CPUID bit was successfully toggled
+ "pushf \n\t" // Push EFLAGS back onto the stack
+ "popl %%ecx \n\t" // Pop the value into ECX
+ "xorl %%eax, %%eax \n\t" // Zero out the EAX register
+ "cmpl %%ecx, %%edx \n\t" // Compare ECX with EDX
+ "je .Lno_cpuid_support%= \n\t" // Jump if they're identical
+ "movl $1, %%eax \n\t" // Set EAX to true
+ ".Lno_cpuid_support%=: \n\t"
+ : "=a"(haveCPUID) : : "%ecx", "%edx" );
+
+ // If we don't have CPUID we won't have the other extensions either
+ if ( ! haveCPUID )
+ return 0L;
+
+ // Execute CPUID with the feature request bit set
+ __asm__ __volatile__(
+ "pushl %%ebx \n\t" // Save EBX
+ "movl $1, %%eax \n\t" // Set EAX to 1 (features request)
+ "cpuid \n\t" // Call CPUID
+ "popl %%ebx \n\t" // Restore EBX
+ : "=d"(result) : : "%eax", "%ecx" );
+
+ // Test bit 23 (MMX support)
+ if ( result & 0x00800000 )
+ features |= KCPUInfo::IntelMMX;
+
+ __asm__ __volatile__(
+ "pushl %%ebx \n\t"
+ "movl $0x80000000, %%eax \n\t"
+ "cpuid \n\t"
+ "cmpl $0x80000000, %%eax \n\t"
+ "jbe .Lno_extended%= \n\t"
+ "movl $0x80000001, %%eax \n\t"
+ "cpuid \n\t"
+ "test $0x80000000, %%edx \n\t"
+ "jz .Lno_extended%= \n\t"
+ "movl $1, %%eax \n\t" // // Set EAX to true
+ ".Lno_extended%=: \n\t"
+ "popl %%ebx \n\t" // Restore EBX
+ : "=a"(have3DNOW) : );
+
+ if ( have3DNOW )
+ features |= KCPUInfo::AMD3DNOW;
+
+#ifdef HAVE_X86_SSE
+ // Test bit 25 (SSE support)
+ if ( result & 0x00200000 ) {
+ features |= KCPUInfo::IntelSSE;
+
+ // OS support test for SSE.
+ // Install our own sighandler for SIGILL.
+ kde_sighandler_t oldhandler = std::signal( SIGILL, sighandler );
+
+ // Try executing an SSE insn to see if we get a SIGILL
+ if ( setjmp( env ) )
+ features ^= KCPUInfo::IntelSSE; // The OS support test failed
+ else
+ __asm__ __volatile__("xorps %xmm0, %xmm0");
+
+ // Restore the default sighandler
+ std::signal( SIGILL, oldhandler );
+
+ // Test bit 26 (SSE2 support)
+ if ( (result & 0x00400000) && (features & KCPUInfo::IntelSSE) )
+ features |= KCPUInfo::IntelSSE2;
+
+ // Note: The OS requirements for SSE2 are the same as for SSE
+ // so we don't have to do any additional tests for that.
+ }
+#endif // HAVE_X86_SSE
+#elif defined __PPC__ && defined HAVE_PPC_ALTIVEC
+ signal( SIGILL, sigill_handler );
+ if ( sigsetjmp( jmpbuf, 1 ) ) {
+ signal( SIGILL, SIG_DFL );
+ } else {
+ canjump = 1;
+ __asm__ __volatile__( "mtspr 256, %0\n\t"
+ "vand %%v0, %%v0, %%v0"
+ : /* none */
+ : "r" (-1) );
+ signal( SIGILL, SIG_DFL );
+ features |= KCPUInfo::AltiVec;
+ }
+#endif // __i386__
+#endif //HAVE_GNU_INLINE_ASM
+
+ return features;
+}
+
+unsigned int KCPUInfo::s_features = getCpuFeatures();
+
+
diff --git a/kdefx/kcpuinfo.h b/kdefx/kcpuinfo.h
new file mode 100644
index 000000000..ce39ded82
--- /dev/null
+++ b/kdefx/kcpuinfo.h
@@ -0,0 +1,70 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 2003 Fredrik Höglund <fredrik@kde.org>
+ *
+ * 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 __KCPUINFO_H
+#define __KCPUINFO_H
+
+#include <kdelibs_export.h>
+
+/**
+ * This class provides a means for applications to obtain information at
+ * runtime about processor support for certain architecture extensions,
+ * such as MMX, SSE, 3DNow and AltiVec.
+ *
+ * @since 3.2
+ */
+class KDEFX_EXPORT KCPUInfo
+{
+ public:
+ /**
+ * This enum contains the list of architecture extensions you
+ * can query.
+ */
+ enum Extensions {
+ IntelMMX = 1 << 0, //!< Intel's MMX instructions.
+ IntelSSE = 1 << 1, //!< Intel's SSE instructions.
+ IntelSSE2 = 1 << 2, //!< Intel's SSE2 instructions.
+ AMD3DNOW = 1 << 3, //!< AMD 3DNOW instructions
+ AltiVec = 1 << 4 //!< Motorola AltiVec instructions
+ };
+
+ /**
+ * Returns true if the processor supports @p extension,
+ * and false otherwise.
+ *
+ * @param extension the feature to query.
+ * @return If true, the processor supports @p extension.
+ * @see Extensions
+ */
+ static bool haveExtension( unsigned int extension )
+ { return (s_features & extension) != 0; }
+
+ private:
+ static unsigned int s_features;
+};
+
+#endif
+
diff --git a/kdefx/kdrawutil.cpp b/kdefx/kdrawutil.cpp
new file mode 100644
index 000000000..7ba703886
--- /dev/null
+++ b/kdefx/kdrawutil.cpp
@@ -0,0 +1,264 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 1999 Daniel M. Duley <mosfet@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#include "kdrawutil.h"
+#include <qdrawutil.h>
+
+KDEFX_EXPORT void kDrawNextButton(QPainter *p, int x, int y, int w, int h,
+ const QColorGroup &g, bool sunken,
+ const QBrush *fill)
+{
+ QPen oldPen = p->pen();
+ int x2 = x+w-1;
+ int y2 = y+h-1;
+ p->fillRect(x+1, y+1, w-2, h-2,
+ fill ? *fill : g.brush(QColorGroup::Button));
+ p->setPen(sunken ? Qt::black : g.light());
+ p->drawLine(x, y, x2-1, y);
+ p->drawLine(x, y, x, y2-1);
+ p->setPen(sunken ? g.midlight() : g.mid());
+ p->drawLine(x+1, y2-1, x2-1, y2-1);
+ p->drawLine(x2-1, y+1, x2-1, y2-1);
+ p->setPen(sunken ? g.light() : Qt::black);
+ p->drawLine(x, y2, x2, y2);
+ p->drawLine(x2, y, x2, y2);
+ p->setPen(oldPen);
+}
+
+
+KDEFX_EXPORT void kDrawNextButton(QPainter *p, const QRect &r, const QColorGroup &g,
+ bool sunken, const QBrush *fill)
+{
+ kDrawNextButton(p, r.x(), r.y(), r.width(), r.height(), g, sunken, fill);
+}
+
+KDEFX_EXPORT void kDrawBeButton(QPainter *p, int x, int y, int w, int h,
+ const QColorGroup &g, bool sunken, const QBrush *fill)
+{
+ QPen oldPen = p->pen();
+ int x2 = x+w-1;
+ int y2 = y+h-1;
+ p->setPen(g.dark());
+ p->drawLine(x+1, y, x2-1, y);
+ p->drawLine(x, y+1, x, y2-1);
+ p->drawLine(x+1, y2, x2-1, y2);
+ p->drawLine(x2, y+1, x2, y2-1);
+
+
+ if(!sunken){
+ p->setPen(g.light());
+ p->drawLine(x+2, y+2, x2-1, y+2);
+ p->drawLine(x+2, y+3, x2-2, y+3);
+ p->drawLine(x+2, y+4, x+2, y2-1);
+ p->drawLine(x+3, y+4, x+3, y2-2);
+ }
+ else{
+ p->setPen(g.mid());
+ p->drawLine(x+2, y+2, x2-1, y+2);
+ p->drawLine(x+2, y+3, x2-2, y+3);
+ p->drawLine(x+2, y+4, x+2, y2-1);
+ p->drawLine(x+3, y+4, x+3, y2-2);
+ }
+
+
+ p->setPen(sunken? g.light() : g.mid());
+ p->drawLine(x2-1, y+2, x2-1, y2-1);
+ p->drawLine(x+2, y2-1, x2-1, y2-1);
+
+ p->setPen(g.mid());
+ p->drawLine(x+1, y+1, x2-1, y+1);
+ p->drawLine(x+1, y+2, x+1, y2-1);
+ p->drawLine(x2-2, y+3, x2-2, y2-2);
+
+ if(fill)
+ p->fillRect(x+4, y+4, w-6, h-6, *fill);
+
+ p->setPen(oldPen);
+}
+
+KDEFX_EXPORT void kDrawBeButton(QPainter *p, QRect &r, const QColorGroup &g, bool sunken,
+ const QBrush *fill)
+{
+ kDrawBeButton(p, r.x(), r.y(), r.width(), r.height(), g, sunken, fill);
+}
+
+KDEFX_EXPORT void kDrawRoundButton(QPainter *p, const QRect &r, const QColorGroup &g,
+ bool sunken)
+{
+ int x, y, x2, y2;
+ r.coords(&x, &y, &x2, &y2);
+ if(r.width() > 16 && r.height() > 16){
+ QPen oldPen = p->pen();
+ QPointArray hPntArray, lPntArray;
+ hPntArray.putPoints(0, 12, x+4,y+1, x+5,y+1, // top left
+ x+3,y+2, x+2,y+3, x+1,y+4, x+1,y+5,
+ x+1,y2-5, x+1,y2-4, x+2,y2-3, // half corners
+ x2-5,y+1, x2-4,y+1, x2-3,y+2);
+
+ lPntArray.putPoints(0, 17, x2-5,y2-1, x2-4,y2-1, // btm right
+ x2-3,y2-2, x2-2,y2-3, x2-1,y2-5, x2-1,y2-4,
+
+ x+3,y2-2, x+4,y2-1, x+5,y2-1, //half corners
+ x2-2,y+3, x2-1,y+4, x2-1,y+5,
+
+ x2-5,y2-2, x2-4,y2-2, // testing
+ x2-3,y2-3,
+ x2-2,y2-5, x2-2,y2-4);
+
+ p->setPen(sunken ? g.dark() : g.light());
+ p->drawLine(x+6, y, x2-6, y);
+ p->drawLine(0, y+6, 0, y2-6);
+ p->drawPoints(hPntArray);
+
+ p->setPen(sunken ? g.light() : g.dark());
+ p->drawLine(x+6, y2, x2-6, y2);
+ p->drawLine(x+6, y2-1, x2-6, y2-1);
+ p->drawLine(x2, y+6, x2, y2-6);
+ p->drawLine(x2-1, y+6, x2-1, y2-6);
+ p->drawPoints(lPntArray);
+ p->setPen(oldPen);
+ }
+ else
+ qDrawWinPanel(p, x, y, r.width(), r.height(), g, sunken);
+}
+
+KDEFX_EXPORT void kDrawRoundButton(QPainter *p, int x, int y, int w, int h,
+ const QColorGroup &g, bool sunken)
+{
+ QRect r(x, y, w, h);
+ kDrawRoundButton(p, r, g, sunken);
+}
+
+#define QCOORDARRLEN(x) sizeof(x)/(sizeof(QCOORD)*2)
+
+KDEFX_EXPORT void kDrawRoundMask(QPainter *p, int x, int y, int w, int h, bool clear)
+{
+ // round edge fills
+ static const QCOORD btm_left_fill[]={ 0,0,1,0,2,0,3,0,4,0,0,1,1,1,2,1,3,1,4,1,
+ 1,2,2,2,3,2,4,2,2,3,3,3,4,3,3,4,4,4 };
+
+ static const QCOORD btm_right_fill[]={ 0,0,1,0,2,0,3,0,4,0,0,1,1,1,2,1,3,1,4,
+ 1,0,2,1,2,2,2,3,2,0,3,1,3,2,3,0,4,1,4 };
+
+ static const QCOORD top_left_fill[]={ 3,0,4,0,2,1,3,1,4,1,1,2,2,2,3,2,4,2,0,3,
+ 1,3,2,3,3,3,4,3,0,4,1,4,2,4,3,4,4,4 };
+
+ static const QCOORD top_right_fill[]={ 0,0,1,0,0,1,1,1,2,1,0,2,1,2,2,2,3,2,0,
+ 3,1,3,2,3,3,3,4,3,0,4,1,4,2,4,3,4,4,4 };
+
+ if(clear)
+ p->fillRect(x, y, w, h, QBrush(Qt::color0, Qt::SolidPattern));
+
+ QBrush fillBrush(Qt::color1, Qt::SolidPattern);
+ p->setPen(Qt::color1);
+ if(w > 16 && h > 16){
+ int x2 = x+w-1;
+ int y2 = y+h-1;
+ QPointArray a(QCOORDARRLEN(top_left_fill), top_left_fill);
+ a.translate(1, 1);
+ p->drawPoints(a);
+ a.setPoints(QCOORDARRLEN(btm_left_fill), btm_left_fill);
+ a.translate(1, h-6);
+ p->drawPoints(a);
+ a.setPoints(QCOORDARRLEN(top_right_fill), top_right_fill);
+ a.translate(w-6, 1);
+ p->drawPoints(a);
+ a.setPoints(QCOORDARRLEN(btm_right_fill), btm_right_fill);
+ a.translate(w-6, h-6);
+ p->drawPoints(a);
+
+ p->fillRect(x+6, y, w-12, h, fillBrush);
+ p->fillRect(x, y+6, x+6, h-12, fillBrush);
+ p->fillRect(x2-6, y+6, x2, h-12, fillBrush);
+ p->drawLine(x+6, y, x2-6, y);
+ p->drawLine(x+6, y2, x2-6, y2);
+ p->drawLine(x, y+6, x, y2-6);
+ p->drawLine(x2, y+6, x2, y2-6);
+
+ }
+ else
+ p->fillRect(x, y, w, h, fillBrush);
+}
+
+KDEFX_EXPORT void kRoundMaskRegion(QRegion &r, int x, int y, int w, int h)
+{
+ // using a bunch of QRect lines seems much more efficient than bitmaps or
+ // point arrays, even tho it uses more statements
+ r += QRect(x+6, y+0, w-12, h);
+ r += QRect(x+5, y+1, 1, h-2); // left
+ r += QRect(x+4, y+1, 1, h-2);
+ r += QRect(x+3, y+2, 1, h-4);
+ r += QRect(x+2, y+3, 1, h-6);
+ r += QRect(x+1, y+4, 1, h-8);
+ r += QRect(x, y+6, 1, h-12);
+ int x2 = x+w-1;
+ r += QRect(x2-5, y+1, 1, h-2); // right
+ r += QRect(x2-4, y+1, 1, h-2);
+ r += QRect(x2-3, y+2, 1, h-4);
+ r += QRect(x2-2, y+3, 1, h-6);
+ r += QRect(x2-1, y+4, 1, h-8);
+ r += QRect(x2, y+6, 1, h-12);
+}
+
+KDEFX_EXPORT void kColorBitmaps(QPainter *p, const QColorGroup &g, int x, int y,
+ QBitmap *lightColor, QBitmap *midColor,
+ QBitmap *midlightColor, QBitmap *darkColor,
+ QBitmap *blackColor, QBitmap *whiteColor)
+{
+ QBitmap *bitmaps[]={lightColor, midColor, midlightColor, darkColor,
+ blackColor, whiteColor};
+
+ QColor colors[]={g.light(), g.mid(), g.midlight(), g.dark(),
+ Qt::black, Qt::white};
+
+ int i;
+ for(i=0; i < 6; ++i){
+ if(bitmaps[i]){
+ if(!bitmaps[i]->mask())
+ bitmaps[i]->setMask(*bitmaps[i]);
+ p->setPen(colors[i]);
+ p->drawPixmap(x, y, *bitmaps[i]);
+ }
+ }
+}
+
+KDEFX_EXPORT void kColorBitmaps(QPainter *p, const QColorGroup &g, int x, int y, int w,
+ int h, bool isXBitmaps, const uchar *lightColor,
+ const uchar *midColor, const uchar *midlightColor,
+ const uchar *darkColor, const uchar *blackColor,
+ const uchar *whiteColor)
+{
+ const uchar *data[]={lightColor, midColor, midlightColor, darkColor,
+ blackColor, whiteColor};
+
+ QColor colors[]={g.light(), g.mid(), g.midlight(), g.dark(),
+ Qt::black, Qt::white};
+
+ int i;
+ QBitmap b;
+ for(i=0; i < 6; ++i){
+ if(data[i]){
+ b = QBitmap(w, h, data[i], isXBitmaps);
+ b.setMask(b);
+ p->setPen(colors[i]);
+ p->drawPixmap(x, y, b);
+ }
+ }
+}
+
+
+
diff --git a/kdefx/kdrawutil.h b/kdefx/kdrawutil.h
new file mode 100644
index 000000000..f16a7ab02
--- /dev/null
+++ b/kdefx/kdrawutil.h
@@ -0,0 +1,180 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 1999 Daniel M. Duley <mosfet@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef __KDRAWUTIL_H
+#define __KDRAWUTIL_H
+
+#include <qnamespace.h>
+#include <qpainter.h>
+#include <qbitmap.h>
+#include <qpalette.h>
+
+#include <kdelibs_export.h>
+
+/*
+ * Various drawing routines. Also see Qt's qdrawutil.h for some more routines
+ * contained in Qt.
+ *
+ * (C) Daniel M. Duley <mosfet@kde.org>
+ */
+
+/**
+ * @relates KStyle
+ * @c \#include @c <kdrawutil.h>
+ *
+ * Draws a Next-style button (solid black shadow with light and midlight highlight).
+ *
+ * @param p The painter to use for drawing the button.
+ * @param r Specifies the rect in which to draw the button.
+ * @param g Specifies the shading colors.
+ * @param sunken Whether to draw the button as sunken (pressed) or not.
+ * @param fill The brush to use for filling the interior of the button.
+ * Pass @a null to prevent the button from being filled.
+ */
+KDEFX_EXPORT void kDrawNextButton(QPainter *p, const QRect &r, const QColorGroup &g,
+ bool sunken=false, const QBrush *fill=0);
+
+/**
+ * @relates KStyle
+ * @overload
+ */
+KDEFX_EXPORT void kDrawNextButton(QPainter *p, int x, int y, int w, int h,
+ const QColorGroup &g, bool sunken=false,
+ const QBrush *fill=0);
+
+/**
+ * @relates KStyle
+ * @c \#include @c <kdrawutil.h>
+ *
+ * Draws a Be-style button.
+ *
+ * @param p The painter to use for drawing the button.
+ * @param r Specifies the rect in which to draw the button.
+ * @param g Specifies the shading colors.
+ * @param sunken Whether to draw the button as sunken (pressed) or not.
+ * @param fill The brush to use for filling the interior of the button.
+ * Pass @a null to prevent the button from being filled.
+ */
+KDEFX_EXPORT void kDrawBeButton(QPainter *p, QRect &r, const QColorGroup &g,
+ bool sunken=false, const QBrush *fill=0);
+
+/**
+ * @relates KStyle
+ * @c \#include @c <kdrawutil.h>
+ * @overload
+ */
+KDEFX_EXPORT void kDrawBeButton(QPainter *p, int x, int y, int w, int h,
+ const QColorGroup &g, bool sunken=false,
+ const QBrush *fill=0);
+
+/**
+ * @relates KStyle
+ * @c \#include @c <kdrawutil.h>
+ *
+ * Draws a rounded oval button. This function doesn't fill the button.
+ * See kRoundMaskRegion() for setting masks for fills.
+ *
+ * @param p The painter to use for drawing the button.
+ * @param r Specifies the rect in which to draw the button.
+ * @param g Specifies the shading colors.
+ * @param sunken Whether to draw the button as sunken (pressed) or not.
+ */
+KDEFX_EXPORT void kDrawRoundButton(QPainter *p, const QRect &r, const QColorGroup &g,
+ bool sunken=false);
+
+/**
+ * @relates KStyle
+ * @overload
+ */
+KDEFX_EXPORT void kDrawRoundButton(QPainter *p, int x, int y, int w, int h,
+ const QColorGroup &g, bool sunken=false);
+
+/**
+ * @relates KStyle
+ * @c \#include @c <kdrawutil.h>
+ *
+ * Sets a region to the pixels covered by a round button of the given
+ * size. You can use this to set clipping regions.
+ *
+ * @param r Reference to the region to set.
+ * @param x The X coordinate of the button.
+ * @param y The Y coordinate of the button.
+ * @param w The width of the button.
+ * @param h The height of the button.
+ *
+ * @see kDrawRoundButton() and kDrawRoundMask()
+ */
+KDEFX_EXPORT void kRoundMaskRegion(QRegion &r, int x, int y, int w, int h);
+
+/**
+ * @relates KStyle
+ * @c \#include @c <kdrawutil.h>
+ *
+ * Paints the pixels covered by a round button of the given size with
+ * Qt::color1. This function is useful in QStyle::drawControlMask().
+ *
+ * @param p The painter to use for drawing the button.
+ * @param x The X coordinate of the button.
+ * @param y The Y coordinate of the button.
+ * @param w The width of the button.
+ * @param h The height of the button.
+ * @param clear Whether to clear the rectangle specified by @p (x, y, w, h) to
+ * Qt::color0 before drawing the mask.
+ */
+KDEFX_EXPORT void kDrawRoundMask(QPainter *p, int x, int y, int w, int h, bool clear=false);
+
+/**
+ * @relates KStyle
+ * @c \#include @c <kdrawutil.h>
+ *
+ * Paints the provided bitmaps in the painter, using the supplied colorgroup for
+ * the foreground colors. There's one bitmap for each color. If you want to skip
+ * a color, pass @a null for the corresponding bitmap.
+ *
+ * @note The bitmaps will be self-masked automatically if not masked
+ * prior to calling this routine.
+ *
+ * @param p The painter to use for drawing the bitmaps.
+ * @param g Specifies the shading colors.
+ * @param x The X coordinate at which to draw the bitmaps.
+ * @param y The Y coordinate at which to draw the bitmaps.
+ * @param lightColor The bitmap to use for the light part.
+ * @param midColor The bitmap to use for the mid part.
+ * @param midlightColor The bitmap to use for the midlight part.
+ * @param darkColor The bitmap to use for the dark part.
+ * @param blackColor The bitmap to use for the black part.
+ * @param whiteColor The bitmap to use for the white part.
+ *
+ * @see QColorGroup
+ */
+KDEFX_EXPORT void kColorBitmaps(QPainter *p, const QColorGroup &g, int x, int y,
+ QBitmap *lightColor=0, QBitmap *midColor=0,
+ QBitmap *midlightColor=0, QBitmap *darkColor=0,
+ QBitmap *blackColor=0, QBitmap *whiteColor=0);
+
+/**
+ * @relates KStyle
+ * @c \#include @c <kdrawutil.h>
+ * @overload
+ */
+ KDEFX_EXPORT void kColorBitmaps(QPainter *p, const QColorGroup &g, int x, int y, int w,
+ int h, bool isXBitmaps=true, const uchar *lightColor = 0,
+ const uchar *midColor=0, const uchar *midlightColor=0,
+ const uchar *darkColor=0, const uchar *blackColor=0,
+ const uchar *whiteColor=0);
+
+#endif
diff --git a/kdefx/kimageeffect.cpp b/kdefx/kimageeffect.cpp
new file mode 100644
index 000000000..b2a859563
--- /dev/null
+++ b/kdefx/kimageeffect.cpp
@@ -0,0 +1,4927 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 1998, 1999, 2001, 2002 Daniel M. Duley <mosfet@kde.org>
+ (C) 1998, 1999 Christian Tibirna <ctibirna@total.net>
+ (C) 1998, 1999 Dirk Mueller <mueller@kde.org>
+ (C) 1999 Geert Jansen <g.t.jansen@stud.tue.nl>
+ (C) 2000 Josef Weidendorfer <weidendo@in.tum.de>
+ (C) 2004 Zack Rusin <zack@kde.org>
+
+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.
+
+*/
+
+// $Id$
+
+#include <math.h>
+#include <assert.h>
+
+#include <qimage.h>
+#include <stdlib.h>
+#include <iostream>
+
+#include "kimageeffect.h"
+#include "kcpuinfo.h"
+
+#include <config.h>
+
+#if 0
+//disabled until #74478 fixed.
+
+#if defined(__i386__) && ( defined(__GNUC__) || defined(__INTEL_COMPILER) )
+# if defined( HAVE_X86_MMX )
+# define USE_MMX_INLINE_ASM
+# endif
+# if defined( HAVE_X86_SSE2 )
+# define USE_SSE2_INLINE_ASM
+# endif
+#endif
+
+#endif
+//======================================================================
+//
+// Utility stuff for effects ported from ImageMagick to QImage
+//
+//======================================================================
+#define MaxRGB 255L
+#define DegreesToRadians(x) ((x)*M_PI/180.0)
+#define MagickSQ2PI 2.50662827463100024161235523934010416269302368164062
+#define MagickEpsilon 1.0e-12
+#define MagickPI 3.14159265358979323846264338327950288419716939937510
+#define MOD(x, y) ((x) < 0 ? ((y) - 1 - ((y) - 1 - (x)) % (y)) : (x) % (y))
+
+/**
+ * \relates KGlobal
+ * A typesafe function that returns x if it's between low and high values.
+ * low if x is smaller than then low and high if x is bigger than high.
+ */
+#define FXCLAMP(x,low,high) fxClamp(x,low,high)
+template<class T>
+inline const T& fxClamp( const T& x, const T& low, const T& high )
+{
+ if ( x < low ) return low;
+ else if ( x > high ) return high;
+ else return x;
+}
+
+static inline unsigned int intensityValue(unsigned int color)
+{
+ return((unsigned int)((0.299*qRed(color) +
+ 0.587*qGreen(color) +
+ 0.1140000000000001*qBlue(color))));
+}
+
+template<typename T>
+static inline void liberateMemory(T **memory)
+{
+ assert(memory != NULL);
+ if(*memory == NULL) return;
+ free((char*)*memory);
+ *memory=NULL;
+}
+
+struct double_packet
+{
+ double red;
+ double green;
+ double blue;
+ double alpha;
+};
+
+struct short_packet
+{
+ unsigned short int red;
+ unsigned short int green;
+ unsigned short int blue;
+ unsigned short int alpha;
+};
+
+
+//======================================================================
+//
+// Gradient effects
+//
+//======================================================================
+
+QImage KImageEffect::gradient(const QSize &size, const QColor &ca,
+ const QColor &cb, GradientType eff, int ncols)
+{
+ int rDiff, gDiff, bDiff;
+ int rca, gca, bca, rcb, gcb, bcb;
+
+ QImage image(size, 32);
+
+ if (size.width() == 0 || size.height() == 0) {
+#ifndef NDEBUG
+ std::cerr << "WARNING: KImageEffect::gradient: invalid image" << std::endl;
+#endif
+ return image;
+ }
+
+ register int x, y;
+
+ rDiff = (rcb = cb.red()) - (rca = ca.red());
+ gDiff = (gcb = cb.green()) - (gca = ca.green());
+ bDiff = (bcb = cb.blue()) - (bca = ca.blue());
+
+ if( eff == VerticalGradient || eff == HorizontalGradient ){
+
+ uint *p;
+ uint rgb;
+
+ register int rl = rca << 16;
+ register int gl = gca << 16;
+ register int bl = bca << 16;
+
+ if( eff == VerticalGradient ) {
+
+ int rcdelta = ((1<<16) / size.height()) * rDiff;
+ int gcdelta = ((1<<16) / size.height()) * gDiff;
+ int bcdelta = ((1<<16) / size.height()) * bDiff;
+
+ for ( y = 0; y < size.height(); y++ ) {
+ p = (uint *) image.scanLine(y);
+
+ rl += rcdelta;
+ gl += gcdelta;
+ bl += bcdelta;
+
+ rgb = qRgb( (rl>>16), (gl>>16), (bl>>16) );
+
+ for( x = 0; x < size.width(); x++ ) {
+ *p = rgb;
+ p++;
+ }
+ }
+
+ }
+ else { // must be HorizontalGradient
+
+ unsigned int *o_src = (unsigned int *)image.scanLine(0);
+ unsigned int *src = o_src;
+
+ int rcdelta = ((1<<16) / size.width()) * rDiff;
+ int gcdelta = ((1<<16) / size.width()) * gDiff;
+ int bcdelta = ((1<<16) / size.width()) * bDiff;
+
+ for( x = 0; x < size.width(); x++) {
+
+ rl += rcdelta;
+ gl += gcdelta;
+ bl += bcdelta;
+
+ *src++ = qRgb( (rl>>16), (gl>>16), (bl>>16));
+ }
+
+ src = o_src;
+
+ // Believe it or not, manually copying in a for loop is faster
+ // than calling memcpy for each scanline (on the order of ms...).
+ // I think this is due to the function call overhead (mosfet).
+
+ for (y = 1; y < size.height(); ++y) {
+
+ p = (unsigned int *)image.scanLine(y);
+ src = o_src;
+ for(x=0; x < size.width(); ++x)
+ *p++ = *src++;
+ }
+ }
+ }
+
+ else {
+
+ float rfd, gfd, bfd;
+ float rd = rca, gd = gca, bd = bca;
+
+ unsigned char *xtable[3];
+ unsigned char *ytable[3];
+
+ unsigned int w = size.width(), h = size.height();
+ xtable[0] = new unsigned char[w];
+ xtable[1] = new unsigned char[w];
+ xtable[2] = new unsigned char[w];
+ ytable[0] = new unsigned char[h];
+ ytable[1] = new unsigned char[h];
+ ytable[2] = new unsigned char[h];
+ w*=2, h*=2;
+
+ if ( eff == DiagonalGradient || eff == CrossDiagonalGradient) {
+ // Diagonal dgradient code inspired by BlackBox (mosfet)
+ // BlackBox dgradient is (C) Brad Hughes, <bhughes@tcac.net> and
+ // Mike Cole <mike@mydot.com>.
+
+ rfd = (float)rDiff/w;
+ gfd = (float)gDiff/w;
+ bfd = (float)bDiff/w;
+
+ int dir;
+ for (x = 0; x < size.width(); x++, rd+=rfd, gd+=gfd, bd+=bfd) {
+ dir = eff == DiagonalGradient? x : size.width() - x - 1;
+ xtable[0][dir] = (unsigned char) rd;
+ xtable[1][dir] = (unsigned char) gd;
+ xtable[2][dir] = (unsigned char) bd;
+ }
+ rfd = (float)rDiff/h;
+ gfd = (float)gDiff/h;
+ bfd = (float)bDiff/h;
+ rd = gd = bd = 0;
+ for (y = 0; y < size.height(); y++, rd+=rfd, gd+=gfd, bd+=bfd) {
+ ytable[0][y] = (unsigned char) rd;
+ ytable[1][y] = (unsigned char) gd;
+ ytable[2][y] = (unsigned char) bd;
+ }
+
+ for (y = 0; y < size.height(); y++) {
+ unsigned int *scanline = (unsigned int *)image.scanLine(y);
+ for (x = 0; x < size.width(); x++) {
+ scanline[x] = qRgb(xtable[0][x] + ytable[0][y],
+ xtable[1][x] + ytable[1][y],
+ xtable[2][x] + ytable[2][y]);
+ }
+ }
+ }
+
+ else if (eff == RectangleGradient ||
+ eff == PyramidGradient ||
+ eff == PipeCrossGradient ||
+ eff == EllipticGradient)
+ {
+ int rSign = rDiff>0? 1: -1;
+ int gSign = gDiff>0? 1: -1;
+ int bSign = bDiff>0? 1: -1;
+
+ rfd = (float)rDiff / size.width();
+ gfd = (float)gDiff / size.width();
+ bfd = (float)bDiff / size.width();
+
+ rd = (float)rDiff/2;
+ gd = (float)gDiff/2;
+ bd = (float)bDiff/2;
+
+ for (x = 0; x < size.width(); x++, rd-=rfd, gd-=gfd, bd-=bfd)
+ {
+ xtable[0][x] = (unsigned char) abs((int)rd);
+ xtable[1][x] = (unsigned char) abs((int)gd);
+ xtable[2][x] = (unsigned char) abs((int)bd);
+ }
+
+ rfd = (float)rDiff/size.height();
+ gfd = (float)gDiff/size.height();
+ bfd = (float)bDiff/size.height();
+
+ rd = (float)rDiff/2;
+ gd = (float)gDiff/2;
+ bd = (float)bDiff/2;
+
+ for (y = 0; y < size.height(); y++, rd-=rfd, gd-=gfd, bd-=bfd)
+ {
+ ytable[0][y] = (unsigned char) abs((int)rd);
+ ytable[1][y] = (unsigned char) abs((int)gd);
+ ytable[2][y] = (unsigned char) abs((int)bd);
+ }
+
+ int h = (size.height()+1)>>1;
+ for (y = 0; y < h; y++) {
+ unsigned int *sl1 = (unsigned int *)image.scanLine(y);
+ unsigned int *sl2 = (unsigned int *)image.scanLine(QMAX(size.height()-y-1, y));
+
+ int w = (size.width()+1)>>1;
+ int x2 = size.width()-1;
+
+ for (x = 0; x < w; x++, x2--) {
+ unsigned int rgb = 0;
+ if (eff == PyramidGradient) {
+ rgb = qRgb(rcb-rSign*(xtable[0][x]+ytable[0][y]),
+ gcb-gSign*(xtable[1][x]+ytable[1][y]),
+ bcb-bSign*(xtable[2][x]+ytable[2][y]));
+ }
+ if (eff == RectangleGradient) {
+ rgb = qRgb(rcb - rSign *
+ QMAX(xtable[0][x], ytable[0][y]) * 2,
+ gcb - gSign *
+ QMAX(xtable[1][x], ytable[1][y]) * 2,
+ bcb - bSign *
+ QMAX(xtable[2][x], ytable[2][y]) * 2);
+ }
+ if (eff == PipeCrossGradient) {
+ rgb = qRgb(rcb - rSign *
+ QMIN(xtable[0][x], ytable[0][y]) * 2,
+ gcb - gSign *
+ QMIN(xtable[1][x], ytable[1][y]) * 2,
+ bcb - bSign *
+ QMIN(xtable[2][x], ytable[2][y]) * 2);
+ }
+ if (eff == EllipticGradient) {
+ rgb = qRgb(rcb - rSign *
+ (int)sqrt((xtable[0][x]*xtable[0][x] +
+ ytable[0][y]*ytable[0][y])*2.0),
+ gcb - gSign *
+ (int)sqrt((xtable[1][x]*xtable[1][x] +
+ ytable[1][y]*ytable[1][y])*2.0),
+ bcb - bSign *
+ (int)sqrt((xtable[2][x]*xtable[2][x] +
+ ytable[2][y]*ytable[2][y])*2.0));
+ }
+
+ sl1[x] = sl2[x] = rgb;
+ sl1[x2] = sl2[x2] = rgb;
+ }
+ }
+ }
+
+ delete [] xtable[0];
+ delete [] xtable[1];
+ delete [] xtable[2];
+ delete [] ytable[0];
+ delete [] ytable[1];
+ delete [] ytable[2];
+ }
+
+ // dither if necessary
+ if (ncols && (QPixmap::defaultDepth() < 15 )) {
+ if ( ncols < 2 || ncols > 256 )
+ ncols = 3;
+ QColor *dPal = new QColor[ncols];
+ for (int i=0; i<ncols; i++) {
+ dPal[i].setRgb ( rca + rDiff * i / ( ncols - 1 ),
+ gca + gDiff * i / ( ncols - 1 ),
+ bca + bDiff * i / ( ncols - 1 ) );
+ }
+ dither(image, dPal, ncols);
+ delete [] dPal;
+ }
+
+ return image;
+}
+
+
+// -----------------------------------------------------------------------------
+
+//CT this was (before Dirk A. Mueller's speedup changes)
+// merely the same code as in the above method, but it's supposedly
+// way less performant since it introduces a lot of supplementary tests
+// and simple math operations for the calculus of the balance.
+// (surprizingly, it isn't less performant, in the contrary :-)
+// Yes, I could have merged them, but then the excellent performance of
+// the balanced code would suffer with no other gain than a mere
+// source code and byte code size economy.
+
+QImage KImageEffect::unbalancedGradient(const QSize &size, const QColor &ca,
+ const QColor &cb, GradientType eff, int xfactor, int yfactor,
+ int ncols)
+{
+ int dir; // general parameter used for direction switches
+
+ bool _xanti = false , _yanti = false;
+
+ if (xfactor < 0) _xanti = true; // negative on X direction
+ if (yfactor < 0) _yanti = true; // negative on Y direction
+
+ xfactor = abs(xfactor);
+ yfactor = abs(yfactor);
+
+ if (!xfactor) xfactor = 1;
+ if (!yfactor) yfactor = 1;
+
+ if (xfactor > 200 ) xfactor = 200;
+ if (yfactor > 200 ) yfactor = 200;
+
+
+ // float xbal = xfactor/5000.;
+ // float ybal = yfactor/5000.;
+ float xbal = xfactor/30./size.width();
+ float ybal = yfactor/30./size.height();
+ float rat;
+
+ int rDiff, gDiff, bDiff;
+ int rca, gca, bca, rcb, gcb, bcb;
+
+ QImage image(size, 32);
+
+ if (size.width() == 0 || size.height() == 0) {
+#ifndef NDEBUG
+ std::cerr << "WARNING: KImageEffect::unbalancedGradient : invalid image\n";
+#endif
+ return image;
+ }
+
+ register int x, y;
+ unsigned int *scanline;
+
+ rDiff = (rcb = cb.red()) - (rca = ca.red());
+ gDiff = (gcb = cb.green()) - (gca = ca.green());
+ bDiff = (bcb = cb.blue()) - (bca = ca.blue());
+
+ if( eff == VerticalGradient || eff == HorizontalGradient){
+ QColor cRow;
+
+ uint *p;
+ uint rgbRow;
+
+ if( eff == VerticalGradient) {
+ for ( y = 0; y < size.height(); y++ ) {
+ dir = _yanti ? y : size.height() - 1 - y;
+ p = (uint *) image.scanLine(dir);
+ rat = 1 - exp( - (float)y * ybal );
+
+ cRow.setRgb( rcb - (int) ( rDiff * rat ),
+ gcb - (int) ( gDiff * rat ),
+ bcb - (int) ( bDiff * rat ) );
+
+ rgbRow = cRow.rgb();
+
+ for( x = 0; x < size.width(); x++ ) {
+ *p = rgbRow;
+ p++;
+ }
+ }
+ }
+ else {
+
+ unsigned int *src = (unsigned int *)image.scanLine(0);
+ for(x = 0; x < size.width(); x++ )
+ {
+ dir = _xanti ? x : size.width() - 1 - x;
+ rat = 1 - exp( - (float)x * xbal );
+
+ src[dir] = qRgb(rcb - (int) ( rDiff * rat ),
+ gcb - (int) ( gDiff * rat ),
+ bcb - (int) ( bDiff * rat ));
+ }
+
+ // Believe it or not, manually copying in a for loop is faster
+ // than calling memcpy for each scanline (on the order of ms...).
+ // I think this is due to the function call overhead (mosfet).
+
+ for(y = 1; y < size.height(); ++y)
+ {
+ scanline = (unsigned int *)image.scanLine(y);
+ for(x=0; x < size.width(); ++x)
+ scanline[x] = src[x];
+ }
+ }
+ }
+
+ else {
+ int w=size.width(), h=size.height();
+
+ unsigned char *xtable[3];
+ unsigned char *ytable[3];
+ xtable[0] = new unsigned char[w];
+ xtable[1] = new unsigned char[w];
+ xtable[2] = new unsigned char[w];
+ ytable[0] = new unsigned char[h];
+ ytable[1] = new unsigned char[h];
+ ytable[2] = new unsigned char[h];
+
+ if ( eff == DiagonalGradient || eff == CrossDiagonalGradient)
+ {
+ for (x = 0; x < w; x++) {
+ dir = _xanti ? x : w - 1 - x;
+ rat = 1 - exp( - (float)x * xbal );
+
+ xtable[0][dir] = (unsigned char) ( rDiff/2 * rat );
+ xtable[1][dir] = (unsigned char) ( gDiff/2 * rat );
+ xtable[2][dir] = (unsigned char) ( bDiff/2 * rat );
+ }
+
+ for (y = 0; y < h; y++) {
+ dir = _yanti ? y : h - 1 - y;
+ rat = 1 - exp( - (float)y * ybal );
+
+ ytable[0][dir] = (unsigned char) ( rDiff/2 * rat );
+ ytable[1][dir] = (unsigned char) ( gDiff/2 * rat );
+ ytable[2][dir] = (unsigned char) ( bDiff/2 * rat );
+ }
+
+ for (y = 0; y < h; y++) {
+ unsigned int *scanline = (unsigned int *)image.scanLine(y);
+ for (x = 0; x < w; x++) {
+ scanline[x] = qRgb(rcb - (xtable[0][x] + ytable[0][y]),
+ gcb - (xtable[1][x] + ytable[1][y]),
+ bcb - (xtable[2][x] + ytable[2][y]));
+ }
+ }
+ }
+
+ else if (eff == RectangleGradient ||
+ eff == PyramidGradient ||
+ eff == PipeCrossGradient ||
+ eff == EllipticGradient)
+ {
+ int rSign = rDiff>0? 1: -1;
+ int gSign = gDiff>0? 1: -1;
+ int bSign = bDiff>0? 1: -1;
+
+ for (x = 0; x < w; x++)
+ {
+ dir = _xanti ? x : w - 1 - x;
+ rat = 1 - exp( - (float)x * xbal );
+
+ xtable[0][dir] = (unsigned char) abs((int)(rDiff*(0.5-rat)));
+ xtable[1][dir] = (unsigned char) abs((int)(gDiff*(0.5-rat)));
+ xtable[2][dir] = (unsigned char) abs((int)(bDiff*(0.5-rat)));
+ }
+
+ for (y = 0; y < h; y++)
+ {
+ dir = _yanti ? y : h - 1 - y;
+
+ rat = 1 - exp( - (float)y * ybal );
+
+ ytable[0][dir] = (unsigned char) abs((int)(rDiff*(0.5-rat)));
+ ytable[1][dir] = (unsigned char) abs((int)(gDiff*(0.5-rat)));
+ ytable[2][dir] = (unsigned char) abs((int)(bDiff*(0.5-rat)));
+ }
+
+ for (y = 0; y < h; y++) {
+ unsigned int *scanline = (unsigned int *)image.scanLine(y);
+ for (x = 0; x < w; x++) {
+ if (eff == PyramidGradient)
+ {
+ scanline[x] = qRgb(rcb-rSign*(xtable[0][x]+ytable[0][y]),
+ gcb-gSign*(xtable[1][x]+ytable[1][y]),
+ bcb-bSign*(xtable[2][x]+ytable[2][y]));
+ }
+ else if (eff == RectangleGradient)
+ {
+ scanline[x] = qRgb(rcb - rSign *
+ QMAX(xtable[0][x], ytable[0][y]) * 2,
+ gcb - gSign *
+ QMAX(xtable[1][x], ytable[1][y]) * 2,
+ bcb - bSign *
+ QMAX(xtable[2][x], ytable[2][y]) * 2);
+ }
+ else if (eff == PipeCrossGradient)
+ {
+ scanline[x] = qRgb(rcb - rSign *
+ QMIN(xtable[0][x], ytable[0][y]) * 2,
+ gcb - gSign *
+ QMIN(xtable[1][x], ytable[1][y]) * 2,
+ bcb - bSign *
+ QMIN(xtable[2][x], ytable[2][y]) * 2);
+ }
+ else if (eff == EllipticGradient)
+ {
+ scanline[x] = qRgb(rcb - rSign *
+ (int)sqrt((xtable[0][x]*xtable[0][x] +
+ ytable[0][y]*ytable[0][y])*2.0),
+ gcb - gSign *
+ (int)sqrt((xtable[1][x]*xtable[1][x] +
+ ytable[1][y]*ytable[1][y])*2.0),
+ bcb - bSign *
+ (int)sqrt((xtable[2][x]*xtable[2][x] +
+ ytable[2][y]*ytable[2][y])*2.0));
+ }
+ }
+ }
+ }
+
+ if (ncols && (QPixmap::defaultDepth() < 15 )) {
+ if ( ncols < 2 || ncols > 256 )
+ ncols = 3;
+ QColor *dPal = new QColor[ncols];
+ for (int i=0; i<ncols; i++) {
+ dPal[i].setRgb ( rca + rDiff * i / ( ncols - 1 ),
+ gca + gDiff * i / ( ncols - 1 ),
+ bca + bDiff * i / ( ncols - 1 ) );
+ }
+ dither(image, dPal, ncols);
+ delete [] dPal;
+ }
+
+ delete [] xtable[0];
+ delete [] xtable[1];
+ delete [] xtable[2];
+ delete [] ytable[0];
+ delete [] ytable[1];
+ delete [] ytable[2];
+
+ }
+
+ return image;
+}
+
+/**
+Types for MMX and SSE packing of colors, for safe constraints
+*/
+namespace {
+
+struct KIE4Pack
+{
+ Q_UINT16 data[4];
+};
+
+struct KIE8Pack
+{
+ Q_UINT16 data[8];
+};
+
+}
+
+//======================================================================
+//
+// Intensity effects
+//
+//======================================================================
+
+
+/* This builds a 256 byte unsigned char lookup table with all
+ * the possible percent values prior to applying the effect, then uses
+ * integer math for the pixels. For any image larger than 9x9 this will be
+ * less expensive than doing a float operation on the 3 color components of
+ * each pixel. (mosfet)
+ */
+QImage& KImageEffect::intensity(QImage &image, float percent)
+{
+ if (image.width() == 0 || image.height() == 0) {
+#ifndef NDEBUG
+ std::cerr << "WARNING: KImageEffect::intensity : invalid image\n";
+#endif
+ return image;
+ }
+
+ int segColors = image.depth() > 8 ? 256 : image.numColors();
+ int pixels = image.depth() > 8 ? image.width()*image.height() :
+ image.numColors();
+ unsigned int *data = image.depth() > 8 ? (unsigned int *)image.bits() :
+ (unsigned int *)image.colorTable();
+
+ bool brighten = (percent >= 0);
+ if(percent < 0)
+ percent = -percent;
+
+#ifdef USE_MMX_INLINE_ASM
+ bool haveMMX = KCPUInfo::haveExtension( KCPUInfo::IntelMMX );
+
+ if(haveMMX)
+ {
+ Q_UINT16 p = Q_UINT16(256.0f*(percent));
+ KIE4Pack mult = {{p,p,p,0}};
+
+ __asm__ __volatile__(
+ "pxor %%mm7, %%mm7\n\t" // zero mm7 for unpacking
+ "movq (%0), %%mm6\n\t" // copy intensity change to mm6
+ : : "r"(&mult), "m"(mult));
+
+ unsigned int rem = pixels % 4;
+ pixels -= rem;
+ Q_UINT32 *end = ( data + pixels );
+
+ if (brighten)
+ {
+ while ( data != end ) {
+ __asm__ __volatile__(
+ "movq (%0), %%mm0\n\t"
+ "movq 8(%0), %%mm4\n\t" // copy 4 pixels of data to mm0 and mm4
+ "movq %%mm0, %%mm1\n\t"
+ "movq %%mm0, %%mm3\n\t"
+ "movq %%mm4, %%mm5\n\t" // copy to registers for unpacking
+ "punpcklbw %%mm7, %%mm0\n\t"
+ "punpckhbw %%mm7, %%mm1\n\t" // unpack the two pixels from mm0
+ "pmullw %%mm6, %%mm0\n\t"
+ "punpcklbw %%mm7, %%mm4\n\t"
+ "pmullw %%mm6, %%mm1\n\t" // multiply by intensity*256
+ "psrlw $8, %%mm0\n\t" // divide by 256
+ "pmullw %%mm6, %%mm4\n\t"
+ "psrlw $8, %%mm1\n\t"
+ "psrlw $8, %%mm4\n\t"
+ "packuswb %%mm1, %%mm0\n\t" // pack solution into mm0. saturates at 255
+ "movq %%mm5, %%mm1\n\t"
+
+ "punpckhbw %%mm7, %%mm1\n\t" // unpack 4th pixel in mm1
+
+ "pmullw %%mm6, %%mm1\n\t"
+ "paddusb %%mm3, %%mm0\n\t" // add intesity result to original of mm0
+ "psrlw $8, %%mm1\n\t"
+ "packuswb %%mm1, %%mm4\n\t" // pack upper two pixels into mm4
+
+ "movq %%mm0, (%0)\n\t" // rewrite to memory lower two pixels
+ "paddusb %%mm5, %%mm4\n\t"
+ "movq %%mm4, 8(%0)\n\t" // rewrite upper two pixels
+ : : "r"(data) );
+ data += 4;
+ }
+
+ end += rem;
+ while ( data != end ) {
+ __asm__ __volatile__(
+ "movd (%0), %%mm0\n\t" // repeat above but for
+ "punpcklbw %%mm7, %%mm0\n\t" // one pixel at a time
+ "movq %%mm0, %%mm3\n\t"
+ "pmullw %%mm6, %%mm0\n\t"
+ "psrlw $8, %%mm0\n\t"
+ "paddw %%mm3, %%mm0\n\t"
+ "packuswb %%mm0, %%mm0\n\t"
+ "movd %%mm0, (%0)\n\t"
+ : : "r"(data) );
+ data++;
+ }
+ }
+ else
+ {
+ while ( data != end ) {
+ __asm__ __volatile__(
+ "movq (%0), %%mm0\n\t"
+ "movq 8(%0), %%mm4\n\t"
+ "movq %%mm0, %%mm1\n\t"
+ "movq %%mm0, %%mm3\n\t"
+
+ "movq %%mm4, %%mm5\n\t"
+
+ "punpcklbw %%mm7, %%mm0\n\t"
+ "punpckhbw %%mm7, %%mm1\n\t"
+ "pmullw %%mm6, %%mm0\n\t"
+ "punpcklbw %%mm7, %%mm4\n\t"
+ "pmullw %%mm6, %%mm1\n\t"
+ "psrlw $8, %%mm0\n\t"
+ "pmullw %%mm6, %%mm4\n\t"
+ "psrlw $8, %%mm1\n\t"
+ "psrlw $8, %%mm4\n\t"
+ "packuswb %%mm1, %%mm0\n\t"
+ "movq %%mm5, %%mm1\n\t"
+
+ "punpckhbw %%mm7, %%mm1\n\t"
+
+ "pmullw %%mm6, %%mm1\n\t"
+ "psubusb %%mm0, %%mm3\n\t" // subtract darkening amount
+ "psrlw $8, %%mm1\n\t"
+ "packuswb %%mm1, %%mm4\n\t"
+
+ "movq %%mm3, (%0)\n\t"
+ "psubusb %%mm4, %%mm5\n\t" // only change for this version is
+ "movq %%mm5, 8(%0)\n\t" // subtraction here as we are darkening image
+ : : "r"(data) );
+ data += 4;
+ }
+
+ end += rem;
+ while ( data != end ) {
+ __asm__ __volatile__(
+ "movd (%0), %%mm0\n\t"
+ "punpcklbw %%mm7, %%mm0\n\t"
+ "movq %%mm0, %%mm3\n\t"
+ "pmullw %%mm6, %%mm0\n\t"
+ "psrlw $8, %%mm0\n\t"
+ "psubusw %%mm0, %%mm3\n\t"
+ "packuswb %%mm3, %%mm3\n\t"
+ "movd %%mm3, (%0)\n\t"
+ : : "r"(data) );
+ data++;
+ }
+ }
+ __asm__ __volatile__("emms"); // clear mmx state
+ }
+ else
+#endif // USE_MMX_INLINE_ASM
+ {
+ unsigned char *segTbl = new unsigned char[segColors];
+ int tmp;
+ if(brighten){ // keep overflow check out of loops
+ for(int i=0; i < segColors; ++i){
+ tmp = (int)(i*percent);
+ if(tmp > 255)
+ tmp = 255;
+ segTbl[i] = tmp;
+ }
+ }
+ else{
+ for(int i=0; i < segColors; ++i){
+ tmp = (int)(i*percent);
+ if(tmp < 0)
+ tmp = 0;
+ segTbl[i] = tmp;
+ }
+ }
+
+ if(brighten){ // same here
+ for(int i=0; i < pixels; ++i){
+ int r = qRed(data[i]);
+ int g = qGreen(data[i]);
+ int b = qBlue(data[i]);
+ int a = qAlpha(data[i]);
+ r = r + segTbl[r] > 255 ? 255 : r + segTbl[r];
+ g = g + segTbl[g] > 255 ? 255 : g + segTbl[g];
+ b = b + segTbl[b] > 255 ? 255 : b + segTbl[b];
+ data[i] = qRgba(r, g, b,a);
+ }
+ }
+ else{
+ for(int i=0; i < pixels; ++i){
+ int r = qRed(data[i]);
+ int g = qGreen(data[i]);
+ int b = qBlue(data[i]);
+ int a = qAlpha(data[i]);
+ r = r - segTbl[r] < 0 ? 0 : r - segTbl[r];
+ g = g - segTbl[g] < 0 ? 0 : g - segTbl[g];
+ b = b - segTbl[b] < 0 ? 0 : b - segTbl[b];
+ data[i] = qRgba(r, g, b, a);
+ }
+ }
+ delete [] segTbl;
+ }
+
+ return image;
+}
+
+QImage& KImageEffect::channelIntensity(QImage &image, float percent,
+ RGBComponent channel)
+{
+ if (image.width() == 0 || image.height() == 0) {
+#ifndef NDEBUG
+ std::cerr << "WARNING: KImageEffect::channelIntensity : invalid image\n";
+#endif
+ return image;
+ }
+
+ int segColors = image.depth() > 8 ? 256 : image.numColors();
+ unsigned char *segTbl = new unsigned char[segColors];
+ int pixels = image.depth() > 8 ? image.width()*image.height() :
+ image.numColors();
+ unsigned int *data = image.depth() > 8 ? (unsigned int *)image.bits() :
+ (unsigned int *)image.colorTable();
+ bool brighten = (percent >= 0);
+ if(percent < 0)
+ percent = -percent;
+
+ if(brighten){ // keep overflow check out of loops
+ for(int i=0; i < segColors; ++i){
+ int tmp = (int)(i*percent);
+ if(tmp > 255)
+ tmp = 255;
+ segTbl[i] = tmp;
+ }
+ }
+ else{
+ for(int i=0; i < segColors; ++i){
+ int tmp = (int)(i*percent);
+ if(tmp < 0)
+ tmp = 0;
+ segTbl[i] = tmp;
+ }
+ }
+
+ if(brighten){ // same here
+ if(channel == Red){ // and here ;-)
+ for(int i=0; i < pixels; ++i){
+ int c = qRed(data[i]);
+ c = c + segTbl[c] > 255 ? 255 : c + segTbl[c];
+ data[i] = qRgba(c, qGreen(data[i]), qBlue(data[i]), qAlpha(data[i]));
+ }
+ }
+ else if(channel == Green){
+ for(int i=0; i < pixels; ++i){
+ int c = qGreen(data[i]);
+ c = c + segTbl[c] > 255 ? 255 : c + segTbl[c];
+ data[i] = qRgba(qRed(data[i]), c, qBlue(data[i]), qAlpha(data[i]));
+ }
+ }
+ else{
+ for(int i=0; i < pixels; ++i){
+ int c = qBlue(data[i]);
+ c = c + segTbl[c] > 255 ? 255 : c + segTbl[c];
+ data[i] = qRgba(qRed(data[i]), qGreen(data[i]), c, qAlpha(data[i]));
+ }
+ }
+
+ }
+ else{
+ if(channel == Red){
+ for(int i=0; i < pixels; ++i){
+ int c = qRed(data[i]);
+ c = c - segTbl[c] < 0 ? 0 : c - segTbl[c];
+ data[i] = qRgba(c, qGreen(data[i]), qBlue(data[i]), qAlpha(data[i]));
+ }
+ }
+ else if(channel == Green){
+ for(int i=0; i < pixels; ++i){
+ int c = qGreen(data[i]);
+ c = c - segTbl[c] < 0 ? 0 : c - segTbl[c];
+ data[i] = qRgba(qRed(data[i]), c, qBlue(data[i]), qAlpha(data[i]));
+ }
+ }
+ else{
+ for(int i=0; i < pixels; ++i){
+ int c = qBlue(data[i]);
+ c = c - segTbl[c] < 0 ? 0 : c - segTbl[c];
+ data[i] = qRgba(qRed(data[i]), qGreen(data[i]), c, qAlpha(data[i]));
+ }
+ }
+ }
+ delete [] segTbl;
+
+ return image;
+}
+
+// Modulate an image with an RBG channel of another image
+//
+QImage& KImageEffect::modulate(QImage &image, QImage &modImage, bool reverse,
+ ModulationType type, int factor, RGBComponent channel)
+{
+ if (image.width() == 0 || image.height() == 0 ||
+ modImage.width() == 0 || modImage.height() == 0) {
+#ifndef NDEBUG
+ std::cerr << "WARNING: KImageEffect::modulate : invalid image\n";
+#endif
+ return image;
+ }
+
+ int r, g, b, h, s, v, a;
+ QColor clr;
+ int mod=0;
+ unsigned int x1, x2, y1, y2;
+ register int x, y;
+
+ // for image, we handle only depth 32
+ if (image.depth()<32) image = image.convertDepth(32);
+
+ // for modImage, we handle depth 8 and 32
+ if (modImage.depth()<8) modImage = modImage.convertDepth(8);
+
+ unsigned int *colorTable2 = (modImage.depth()==8) ?
+ modImage.colorTable():0;
+ unsigned int *data1, *data2;
+ unsigned char *data2b;
+ unsigned int color1, color2;
+
+ x1 = image.width(); y1 = image.height();
+ x2 = modImage.width(); y2 = modImage.height();
+
+ for (y = 0; y < (int)y1; y++) {
+ data1 = (unsigned int *) image.scanLine(y);
+ data2 = (unsigned int *) modImage.scanLine( y%y2 );
+ data2b = (unsigned char *) modImage.scanLine( y%y2 );
+
+ x=0;
+ while(x < (int)x1) {
+ color2 = (colorTable2) ? colorTable2[*data2b] : *data2;
+ if (reverse) {
+ color1 = color2;
+ color2 = *data1;
+ }
+ else
+ color1 = *data1;
+
+ if (type == Intensity || type == Contrast) {
+ r = qRed(color1);
+ g = qGreen(color1);
+ b = qBlue(color1);
+ if (channel != All) {
+ mod = (channel == Red) ? qRed(color2) :
+ (channel == Green) ? qGreen(color2) :
+ (channel == Blue) ? qBlue(color2) :
+ (channel == Gray) ? qGray(color2) : 0;
+ mod = mod*factor/50;
+ }
+
+ if (type == Intensity) {
+ if (channel == All) {
+ r += r * factor/50 * qRed(color2)/256;
+ g += g * factor/50 * qGreen(color2)/256;
+ b += b * factor/50 * qBlue(color2)/256;
+ }
+ else {
+ r += r * mod/256;
+ g += g * mod/256;
+ b += b * mod/256;
+ }
+ }
+ else { // Contrast
+ if (channel == All) {
+ r += (r-128) * factor/50 * qRed(color2)/128;
+ g += (g-128) * factor/50 * qGreen(color2)/128;
+ b += (b-128) * factor/50 * qBlue(color2)/128;
+ }
+ else {
+ r += (r-128) * mod/128;
+ g += (g-128) * mod/128;
+ b += (b-128) * mod/128;
+ }
+ }
+
+ if (r<0) r=0; if (r>255) r=255;
+ if (g<0) g=0; if (g>255) g=255;
+ if (b<0) b=0; if (b>255) b=255;
+ a = qAlpha(*data1);
+ *data1 = qRgba(r, g, b, a);
+ }
+ else if (type == Saturation || type == HueShift) {
+ clr.setRgb(color1);
+ clr.hsv(&h, &s, &v);
+ mod = (channel == Red) ? qRed(color2) :
+ (channel == Green) ? qGreen(color2) :
+ (channel == Blue) ? qBlue(color2) :
+ (channel == Gray) ? qGray(color2) : 0;
+ mod = mod*factor/50;
+
+ if (type == Saturation) {
+ s -= s * mod/256;
+ if (s<0) s=0; if (s>255) s=255;
+ }
+ else { // HueShift
+ h += mod;
+ while(h<0) h+=360;
+ h %= 360;
+ }
+
+ clr.setHsv(h, s, v);
+ a = qAlpha(*data1);
+ *data1 = clr.rgb() | ((uint)(a & 0xff) << 24);
+ }
+ data1++; data2++; data2b++; x++;
+ if ( (x%x2) ==0) { data2 -= x2; data2b -= x2; }
+ }
+ }
+ return image;
+}
+
+
+
+//======================================================================
+//
+// Blend effects
+//
+//======================================================================
+
+
+// Nice and fast direct pixel manipulation
+QImage& KImageEffect::blend(const QColor& clr, QImage& dst, float opacity)
+{
+ if (dst.width() <= 0 || dst.height() <= 0)
+ return dst;
+
+ if (opacity < 0.0 || opacity > 1.0) {
+#ifndef NDEBUG
+ std::cerr << "WARNING: KImageEffect::blend : invalid opacity. Range [0, 1]\n";
+#endif
+ return dst;
+ }
+
+ if (dst.depth() != 32)
+ dst = dst.convertDepth(32);
+
+ int pixels = dst.width() * dst.height();
+
+#ifdef USE_SSE2_INLINE_ASM
+ if ( KCPUInfo::haveExtension( KCPUInfo::IntelSSE2 ) && pixels > 16 ) {
+ Q_UINT16 alpha = Q_UINT16( ( 1.0 - opacity ) * 256.0 );
+
+ KIE8Pack packedalpha = { { alpha, alpha, alpha, 256,
+ alpha, alpha, alpha, 256 } };
+
+ Q_UINT16 red = Q_UINT16( clr.red() * 256 * opacity );
+ Q_UINT16 green = Q_UINT16( clr.green() * 256 * opacity );
+ Q_UINT16 blue = Q_UINT16( clr.blue() * 256 * opacity );
+
+ KIE8Pack packedcolor = { { blue, green, red, 0,
+ blue, green, red, 0 } };
+
+ // Prepare the XMM5, XMM6 and XMM7 registers for unpacking and blending
+ __asm__ __volatile__(
+ "pxor %%xmm7, %%xmm7\n\t" // Zero out XMM7 for unpacking
+ "movdqu (%0), %%xmm6\n\t" // Set up (1 - alpha) * 256 in XMM6
+ "movdqu (%1), %%xmm5\n\t" // Set up color * alpha * 256 in XMM5
+ : : "r"(&packedalpha), "r"(&packedcolor),
+ "m"(packedcolor), "m"(packedalpha) );
+
+ Q_UINT32 *data = reinterpret_cast<Q_UINT32*>( dst.bits() );
+
+ // Check how many pixels we need to process to achieve 16 byte alignment
+ int offset = (16 - (Q_UINT32( data ) & 0x0f)) / 4;
+
+ // The main loop processes 8 pixels / iteration
+ int remainder = (pixels - offset) % 8;
+ pixels -= remainder;
+
+ // Alignment loop
+ for ( int i = 0; i < offset; i++ ) {
+ __asm__ __volatile__(
+ "movd (%0,%1,4), %%xmm0\n\t" // Load one pixel to XMM1
+ "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the pixel
+ "pmullw %%xmm6, %%xmm0\n\t" // Multiply the pixel with (1 - alpha) * 256
+ "paddw %%xmm5, %%xmm0\n\t" // Add color * alpha * 256 to the result
+ "psrlw $8, %%xmm0\n\t" // Divide by 256
+ "packuswb %%xmm1, %%xmm0\n\t" // Pack the pixel to a dword
+ "movd %%xmm0, (%0,%1,4)\n\t" // Write the pixel to the image
+ : : "r"(data), "r"(i) );
+ }
+
+ // Main loop
+ for ( int i = offset; i < pixels; i += 8 ) {
+ __asm__ __volatile(
+ // Load 8 pixels to XMM registers 1 - 4
+ "movq (%0,%1,4), %%xmm0\n\t" // Load pixels 1 and 2 to XMM1
+ "movq 8(%0,%1,4), %%xmm1\n\t" // Load pixels 3 and 4 to XMM2
+ "movq 16(%0,%1,4), %%xmm2\n\t" // Load pixels 5 and 6 to XMM3
+ "movq 24(%0,%1,4), %%xmm3\n\t" // Load pixels 7 and 8 to XMM4
+
+ // Prefetch the pixels for next iteration
+ "prefetchnta 32(%0,%1,4) \n\t"
+
+ // Blend pixels 1 and 2
+ "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the pixels
+ "pmullw %%xmm6, %%xmm0\n\t" // Multiply the pixels with (1 - alpha) * 256
+ "paddw %%xmm5, %%xmm0\n\t" // Add color * alpha * 256 to the result
+ "psrlw $8, %%xmm0\n\t" // Divide by 256
+
+ // Blend pixels 3 and 4
+ "punpcklbw %%xmm7, %%xmm1\n\t" // Unpack the pixels
+ "pmullw %%xmm6, %%xmm1\n\t" // Multiply the pixels with (1 - alpha) * 256
+ "paddw %%xmm5, %%xmm1\n\t" // Add color * alpha * 256 to the result
+ "psrlw $8, %%xmm1\n\t" // Divide by 256
+
+ // Blend pixels 5 and 6
+ "punpcklbw %%xmm7, %%xmm2\n\t" // Unpack the pixels
+ "pmullw %%xmm6, %%xmm2\n\t" // Multiply the pixels with (1 - alpha) * 256
+ "paddw %%xmm5, %%xmm2\n\t" // Add color * alpha * 256 to the result
+ "psrlw $8, %%xmm2\n\t" // Divide by 256
+
+ // Blend pixels 7 and 8
+ "punpcklbw %%xmm7, %%xmm3\n\t" // Unpack the pixels
+ "pmullw %%xmm6, %%xmm3\n\t" // Multiply the pixels with (1 - alpha) * 256
+ "paddw %%xmm5, %%xmm3\n\t" // Add color * alpha * 256 to the result
+ "psrlw $8, %%xmm3\n\t" // Divide by 256
+
+ // Pack the pixels into 2 double quadwords
+ "packuswb %%xmm1, %%xmm0\n\t" // Pack pixels 1 - 4 to a double qword
+ "packuswb %%xmm3, %%xmm2\n\t" // Pack pixles 5 - 8 to a double qword
+
+ // Write the pixels back to the image
+ "movdqa %%xmm0, (%0,%1,4)\n\t" // Store pixels 1 - 4
+ "movdqa %%xmm2, 16(%0,%1,4)\n\t" // Store pixels 5 - 8
+ : : "r"(data), "r"(i) );
+ }
+
+ // Cleanup loop
+ for ( int i = pixels; i < pixels + remainder; i++ ) {
+ __asm__ __volatile__(
+ "movd (%0,%1,4), %%xmm0\n\t" // Load one pixel to XMM1
+ "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the pixel
+ "pmullw %%xmm6, %%xmm0\n\t" // Multiply the pixel with (1 - alpha) * 256
+ "paddw %%xmm5, %%xmm0\n\t" // Add color * alpha * 256 to the result
+ "psrlw $8, %%xmm0\n\t" // Divide by 256
+ "packuswb %%xmm1, %%xmm0\n\t" // Pack the pixel to a dword
+ "movd %%xmm0, (%0,%1,4)\n\t" // Write the pixel to the image
+ : : "r"(data), "r"(i) );
+ }
+ } else
+#endif
+
+#ifdef USE_MMX_INLINE_ASM
+ if ( KCPUInfo::haveExtension( KCPUInfo::IntelMMX ) && pixels > 1 ) {
+ Q_UINT16 alpha = Q_UINT16( ( 1.0 - opacity ) * 256.0 );
+ KIE4Pack packedalpha = { { alpha, alpha, alpha, 256 } };
+
+ Q_UINT16 red = Q_UINT16( clr.red() * 256 * opacity );
+ Q_UINT16 green = Q_UINT16( clr.green() * 256 * opacity );
+ Q_UINT16 blue = Q_UINT16( clr.blue() * 256 * opacity );
+
+ KIE4Pack packedcolor = { { blue, green, red, 0 } };
+
+ __asm__ __volatile__(
+ "pxor %%mm7, %%mm7\n\t" // Zero out MM7 for unpacking
+ "movq (%0), %%mm6\n\t" // Set up (1 - alpha) * 256 in MM6
+ "movq (%1), %%mm5\n\t" // Set up color * alpha * 256 in MM5
+ : : "r"(&packedalpha), "r"(&packedcolor), "m"(packedcolor), "m"(packedalpha) );
+
+ Q_UINT32 *data = reinterpret_cast<Q_UINT32*>( dst.bits() );
+
+ // The main loop processes 4 pixels / iteration
+ int remainder = pixels % 4;
+ pixels -= remainder;
+
+ // Main loop
+ for ( int i = 0; i < pixels; i += 4 ) {
+ __asm__ __volatile__(
+ // Load 4 pixels to MM registers 1 - 4
+ "movd (%0,%1,4), %%mm0\n\t" // Load the 1st pixel to MM0
+ "movd 4(%0,%1,4), %%mm1\n\t" // Load the 2nd pixel to MM1
+ "movd 8(%0,%1,4), %%mm2\n\t" // Load the 3rd pixel to MM2
+ "movd 12(%0,%1,4), %%mm3\n\t" // Load the 4th pixel to MM3
+
+ // Blend the first pixel
+ "punpcklbw %%mm7, %%mm0\n\t" // Unpack the pixel
+ "pmullw %%mm6, %%mm0\n\t" // Multiply the pixel with (1 - alpha) * 256
+ "paddw %%mm5, %%mm0\n\t" // Add color * alpha * 256 to the result
+ "psrlw $8, %%mm0\n\t" // Divide by 256
+
+ // Blend the second pixel
+ "punpcklbw %%mm7, %%mm1\n\t" // Unpack the pixel
+ "pmullw %%mm6, %%mm1\n\t" // Multiply the pixel with (1 - alpha) * 256
+ "paddw %%mm5, %%mm1\n\t" // Add color * alpha * 256 to the result
+ "psrlw $8, %%mm1\n\t" // Divide by 256
+
+ // Blend the third pixel
+ "punpcklbw %%mm7, %%mm2\n\t" // Unpack the pixel
+ "pmullw %%mm6, %%mm2\n\t" // Multiply the pixel with (1 - alpha) * 256
+ "paddw %%mm5, %%mm2\n\t" // Add color * alpha * 256 to the result
+ "psrlw $8, %%mm2\n\t" // Divide by 256
+
+ // Blend the fourth pixel
+ "punpcklbw %%mm7, %%mm3\n\t" // Unpack the pixel
+ "pmullw %%mm6, %%mm3\n\t" // Multiply the pixel with (1 - alpha) * 256
+ "paddw %%mm5, %%mm3\n\t" // Add color * alpha * 256 to the result
+ "psrlw $8, %%mm3\n\t" // Divide by 256
+
+ // Pack the pixels into 2 quadwords
+ "packuswb %%mm1, %%mm0\n\t" // Pack pixels 1 and 2 to a qword
+ "packuswb %%mm3, %%mm2\n\t" // Pack pixels 3 and 4 to a qword
+
+ // Write the pixels back to the image
+ "movq %%mm0, (%0,%1,4)\n\t" // Store pixels 1 and 2
+ "movq %%mm2, 8(%0,%1,4)\n\t" // Store pixels 3 and 4
+ : : "r"(data), "r"(i) );
+ }
+
+ // Cleanup loop
+ for ( int i = pixels; i < pixels + remainder; i++ ) {
+ __asm__ __volatile__(
+ "movd (%0,%1,4), %%mm0\n\t" // Load one pixel to MM1
+ "punpcklbw %%mm7, %%mm0\n\t" // Unpack the pixel
+ "pmullw %%mm6, %%mm0\n\t" // Multiply the pixel with 1 - alpha * 256
+ "paddw %%mm5, %%mm0\n\t" // Add color * alpha * 256 to the result
+ "psrlw $8, %%mm0\n\t" // Divide by 256
+ "packuswb %%mm0, %%mm0\n\t" // Pack the pixel to a dword
+ "movd %%mm0, (%0,%1,4)\n\t" // Write the pixel to the image
+ : : "r"(data), "r"(i) );
+ }
+
+ // Empty the MMX state
+ __asm__ __volatile__("emms");
+ } else
+#endif // USE_MMX_INLINE_ASM
+
+ {
+ int rcol, gcol, bcol;
+ clr.rgb(&rcol, &gcol, &bcol);
+
+#ifdef WORDS_BIGENDIAN // ARGB (skip alpha)
+ register unsigned char *data = (unsigned char *)dst.bits() + 1;
+#else // BGRA
+ register unsigned char *data = (unsigned char *)dst.bits();
+#endif
+
+ for (register int i=0; i<pixels; i++)
+ {
+#ifdef WORDS_BIGENDIAN
+ *data += (unsigned char)((rcol - *data) * opacity);
+ data++;
+ *data += (unsigned char)((gcol - *data) * opacity);
+ data++;
+ *data += (unsigned char)((bcol - *data) * opacity);
+ data++;
+#else
+ *data += (unsigned char)((bcol - *data) * opacity);
+ data++;
+ *data += (unsigned char)((gcol - *data) * opacity);
+ data++;
+ *data += (unsigned char)((rcol - *data) * opacity);
+ data++;
+#endif
+ data++; // skip alpha
+ }
+ }
+
+ return dst;
+}
+
+// Nice and fast direct pixel manipulation
+QImage& KImageEffect::blend(QImage& src, QImage& dst, float opacity)
+{
+ if (src.width() <= 0 || src.height() <= 0)
+ return dst;
+ if (dst.width() <= 0 || dst.height() <= 0)
+ return dst;
+
+ if (src.width() != dst.width() || src.height() != dst.height()) {
+#ifndef NDEBUG
+ std::cerr << "WARNING: KImageEffect::blend : src and destination images are not the same size\n";
+#endif
+ return dst;
+ }
+
+ if (opacity < 0.0 || opacity > 1.0) {
+#ifndef NDEBUG
+ std::cerr << "WARNING: KImageEffect::blend : invalid opacity. Range [0, 1]\n";
+#endif
+ return dst;
+ }
+
+ if (src.depth() != 32) src = src.convertDepth(32);
+ if (dst.depth() != 32) dst = dst.convertDepth(32);
+
+ int pixels = src.width() * src.height();
+
+#ifdef USE_SSE2_INLINE_ASM
+ if ( KCPUInfo::haveExtension( KCPUInfo::IntelSSE2 ) && pixels > 16 ) {
+ Q_UINT16 alpha = Q_UINT16( opacity * 256.0 );
+ KIE8Pack packedalpha = { { alpha, alpha, alpha, 0,
+ alpha, alpha, alpha, 0 } };
+
+ // Prepare the XMM6 and XMM7 registers for unpacking and blending
+ __asm__ __volatile__(
+ "pxor %%xmm7, %%xmm7\n\t" // Zero out XMM7 for unpacking
+ "movdqu (%0), %%xmm6\n\t" // Set up alpha * 256 in XMM6
+ : : "r"(&packedalpha), "m"(packedalpha) );
+
+ Q_UINT32 *data1 = reinterpret_cast<Q_UINT32*>( src.bits() );
+ Q_UINT32 *data2 = reinterpret_cast<Q_UINT32*>( dst.bits() );
+
+ // Check how many pixels we need to process to achieve 16 byte alignment
+ int offset = (16 - (Q_UINT32( data2 ) & 0x0f)) / 4;
+
+ // The main loop processes 4 pixels / iteration
+ int remainder = (pixels - offset) % 4;
+ pixels -= remainder;
+
+ // Alignment loop
+ for ( int i = 0; i < offset; i++ ) {
+ __asm__ __volatile__(
+ "movd (%1,%2,4), %%xmm1\n\t" // Load one dst pixel to XMM1
+ "punpcklbw %%xmm7, %%xmm1\n\t" // Unpack the pixel
+ "movd (%0,%2,4), %%xmm0\n\t" // Load one src pixel to XMM0
+ "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the pixel
+ "psubw %%xmm1, %%xmm0\n\t" // Subtract dst from src
+ "pmullw %%xmm6, %%xmm0\n\t" // Multiply the result with alpha * 256
+ "psllw $8, %%xmm1\n\t" // Multiply dst with 256
+ "paddw %%xmm1, %%xmm0\n\t" // Add dst to result
+ "psrlw $8, %%xmm0\n\t" // Divide by 256
+ "packuswb %%xmm1, %%xmm0\n\t" // Pack the pixel to a dword
+ "movd %%xmm0, (%1,%2,4)\n\t" // Write the pixel to the image
+ : : "r"(data1), "r"(data2), "r"(i) );
+ }
+
+ // Main loop
+ for ( int i = offset; i < pixels; i += 4 ) {
+ __asm__ __volatile__(
+ // Load 4 src pixels to XMM0 and XMM2 and 4 dst pixels to XMM1 and XMM3
+ "movq (%0,%2,4), %%xmm0\n\t" // Load two src pixels to XMM0
+ "movq (%1,%2,4), %%xmm1\n\t" // Load two dst pixels to XMM1
+ "movq 8(%0,%2,4), %%xmm2\n\t" // Load two src pixels to XMM2
+ "movq 8(%1,%2,4), %%xmm3\n\t" // Load two dst pixels to XMM3
+
+ // Prefetch the pixels for the iteration after the next one
+ "prefetchnta 32(%0,%2,4) \n\t"
+ "prefetchnta 32(%1,%2,4) \n\t"
+
+ // Blend the first two pixels
+ "punpcklbw %%xmm7, %%xmm1\n\t" // Unpack the dst pixels
+ "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the src pixels
+ "psubw %%xmm1, %%xmm0\n\t" // Subtract dst from src
+ "pmullw %%xmm6, %%xmm0\n\t" // Multiply the result with alpha * 256
+ "psllw $8, %%xmm1\n\t" // Multiply dst with 256
+ "paddw %%xmm1, %%xmm0\n\t" // Add dst to the result
+ "psrlw $8, %%xmm0\n\t" // Divide by 256
+
+ // Blend the next two pixels
+ "punpcklbw %%xmm7, %%xmm3\n\t" // Unpack the dst pixels
+ "punpcklbw %%xmm7, %%xmm2\n\t" // Unpack the src pixels
+ "psubw %%xmm3, %%xmm2\n\t" // Subtract dst from src
+ "pmullw %%xmm6, %%xmm2\n\t" // Multiply the result with alpha * 256
+ "psllw $8, %%xmm3\n\t" // Multiply dst with 256
+ "paddw %%xmm3, %%xmm2\n\t" // Add dst to the result
+ "psrlw $8, %%xmm2\n\t" // Divide by 256
+
+ // Write the pixels back to the image
+ "packuswb %%xmm2, %%xmm0\n\t" // Pack the pixels to a double qword
+ "movdqa %%xmm0, (%1,%2,4)\n\t" // Store the pixels
+ : : "r"(data1), "r"(data2), "r"(i) );
+ }
+
+ // Cleanup loop
+ for ( int i = pixels; i < pixels + remainder; i++ ) {
+ __asm__ __volatile__(
+ "movd (%1,%2,4), %%xmm1\n\t" // Load one dst pixel to XMM1
+ "punpcklbw %%xmm7, %%xmm1\n\t" // Unpack the pixel
+ "movd (%0,%2,4), %%xmm0\n\t" // Load one src pixel to XMM0
+ "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the pixel
+ "psubw %%xmm1, %%xmm0\n\t" // Subtract dst from src
+ "pmullw %%xmm6, %%xmm0\n\t" // Multiply the result with alpha * 256
+ "psllw $8, %%xmm1\n\t" // Multiply dst with 256
+ "paddw %%xmm1, %%xmm0\n\t" // Add dst to result
+ "psrlw $8, %%xmm0\n\t" // Divide by 256
+ "packuswb %%xmm1, %%xmm0\n\t" // Pack the pixel to a dword
+ "movd %%xmm0, (%1,%2,4)\n\t" // Write the pixel to the image
+ : : "r"(data1), "r"(data2), "r"(i) );
+ }
+ } else
+#endif // USE_SSE2_INLINE_ASM
+
+#ifdef USE_MMX_INLINE_ASM
+ if ( KCPUInfo::haveExtension( KCPUInfo::IntelMMX ) && pixels > 1 ) {
+ Q_UINT16 alpha = Q_UINT16( opacity * 256.0 );
+ KIE4Pack packedalpha = { { alpha, alpha, alpha, 0 } };
+
+ // Prepare the MM6 and MM7 registers for blending and unpacking
+ __asm__ __volatile__(
+ "pxor %%mm7, %%mm7\n\t" // Zero out MM7 for unpacking
+ "movq (%0), %%mm6\n\t" // Set up alpha * 256 in MM6
+ : : "r"(&packedalpha), "m"(packedalpha) );
+
+ Q_UINT32 *data1 = reinterpret_cast<Q_UINT32*>( src.bits() );
+ Q_UINT32 *data2 = reinterpret_cast<Q_UINT32*>( dst.bits() );
+
+ // The main loop processes 2 pixels / iteration
+ int remainder = pixels % 2;
+ pixels -= remainder;
+
+ // Main loop
+ for ( int i = 0; i < pixels; i += 2 ) {
+ __asm__ __volatile__(
+ // Load 2 src pixels to MM0 and MM2 and 2 dst pixels to MM1 and MM3
+ "movd (%0,%2,4), %%mm0\n\t" // Load the 1st src pixel to MM0
+ "movd (%1,%2,4), %%mm1\n\t" // Load the 1st dst pixel to MM1
+ "movd 4(%0,%2,4), %%mm2\n\t" // Load the 2nd src pixel to MM2
+ "movd 4(%1,%2,4), %%mm3\n\t" // Load the 2nd dst pixel to MM3
+
+ // Blend the first pixel
+ "punpcklbw %%mm7, %%mm0\n\t" // Unpack the src pixel
+ "punpcklbw %%mm7, %%mm1\n\t" // Unpack the dst pixel
+ "psubw %%mm1, %%mm0\n\t" // Subtract dst from src
+ "pmullw %%mm6, %%mm0\n\t" // Multiply the result with alpha * 256
+ "psllw $8, %%mm1\n\t" // Multiply dst with 256
+ "paddw %%mm1, %%mm0\n\t" // Add dst to the result
+ "psrlw $8, %%mm0\n\t" // Divide by 256
+
+ // Blend the second pixel
+ "punpcklbw %%mm7, %%mm2\n\t" // Unpack the src pixel
+ "punpcklbw %%mm7, %%mm3\n\t" // Unpack the dst pixel
+ "psubw %%mm3, %%mm2\n\t" // Subtract dst from src
+ "pmullw %%mm6, %%mm2\n\t" // Multiply the result with alpha * 256
+ "psllw $8, %%mm3\n\t" // Multiply dst with 256
+ "paddw %%mm3, %%mm2\n\t" // Add dst to the result
+ "psrlw $8, %%mm2\n\t" // Divide by 256
+
+ // Write the pixels back to the image
+ "packuswb %%mm2, %%mm0\n\t" // Pack the pixels to a qword
+ "movq %%mm0, (%1,%2,4)\n\t" // Store the pixels
+ : : "r"(data1), "r"(data2), "r"(i) );
+ }
+
+ // Blend the remaining pixel (if there is one)
+ if ( remainder ) {
+ __asm__ __volatile__(
+ "movd (%0), %%mm0\n\t" // Load one src pixel to MM0
+ "punpcklbw %%mm7, %%mm0\n\t" // Unpack the src pixel
+ "movd (%1), %%mm1\n\t" // Load one dst pixel to MM1
+ "punpcklbw %%mm7, %%mm1\n\t" // Unpack the dst pixel
+ "psubw %%mm1, %%mm0\n\t" // Subtract dst from src
+ "pmullw %%mm6, %%mm0\n\t" // Multiply the result with alpha * 256
+ "psllw $8, %%mm1\n\t" // Multiply dst with 256
+ "paddw %%mm1, %%mm0\n\t" // Add dst to result
+ "psrlw $8, %%mm0\n\t" // Divide by 256
+ "packuswb %%mm0, %%mm0\n\t" // Pack the pixel to a dword
+ "movd %%mm0, (%1)\n\t" // Write the pixel to the image
+ : : "r"(data1 + pixels), "r"(data2 + pixels) );
+ }
+
+ // Empty the MMX state
+ __asm__ __volatile__("emms");
+ } else
+#endif // USE_MMX_INLINE_ASM
+
+ {
+#ifdef WORDS_BIGENDIAN // ARGB (skip alpha)
+ register unsigned char *data1 = (unsigned char *)dst.bits() + 1;
+ register unsigned char *data2 = (unsigned char *)src.bits() + 1;
+#else // BGRA
+ register unsigned char *data1 = (unsigned char *)dst.bits();
+ register unsigned char *data2 = (unsigned char *)src.bits();
+#endif
+
+ for (register int i=0; i<pixels; i++)
+ {
+#ifdef WORDS_BIGENDIAN
+ *data1 += (unsigned char)((*(data2++) - *data1) * opacity);
+ data1++;
+ *data1 += (unsigned char)((*(data2++) - *data1) * opacity);
+ data1++;
+ *data1 += (unsigned char)((*(data2++) - *data1) * opacity);
+ data1++;
+#else
+ *data1 += (unsigned char)((*(data2++) - *data1) * opacity);
+ data1++;
+ *data1 += (unsigned char)((*(data2++) - *data1) * opacity);
+ data1++;
+ *data1 += (unsigned char)((*(data2++) - *data1) * opacity);
+ data1++;
+#endif
+ data1++; // skip alpha
+ data2++;
+ }
+ }
+
+ return dst;
+}
+
+
+QImage& KImageEffect::blend(QImage &image, float initial_intensity,
+ const QColor &bgnd, GradientType eff,
+ bool anti_dir)
+{
+ if (image.width() == 0 || image.height() == 0 || image.depth()!=32 ) {
+#ifndef NDEBUG
+ std::cerr << "WARNING: KImageEffect::blend : invalid image\n";
+#endif
+ return image;
+ }
+
+ int r_bgnd = bgnd.red(), g_bgnd = bgnd.green(), b_bgnd = bgnd.blue();
+ int r, g, b;
+ int ind;
+
+ unsigned int xi, xf, yi, yf;
+ unsigned int a;
+
+ // check the boundaries of the initial intesity param
+ float unaffected = 1;
+ if (initial_intensity > 1) initial_intensity = 1;
+ if (initial_intensity < -1) initial_intensity = -1;
+ if (initial_intensity < 0) {
+ unaffected = 1. + initial_intensity;
+ initial_intensity = 0;
+ }
+
+
+ float intensity = initial_intensity;
+ float var = 1. - initial_intensity;
+
+ if (anti_dir) {
+ initial_intensity = intensity = 1.;
+ var = -var;
+ }
+
+ register int x, y;
+
+ unsigned int *data = (unsigned int *)image.bits();
+
+ int image_width = image.width(); //Those can't change
+ int image_height = image.height();
+
+
+ if( eff == VerticalGradient || eff == HorizontalGradient ) {
+
+ // set the image domain to apply the effect to
+ xi = 0, xf = image_width;
+ yi = 0, yf = image_height;
+ if (eff == VerticalGradient) {
+ if (anti_dir) yf = (int)(image_height * unaffected);
+ else yi = (int)(image_height * (1 - unaffected));
+ }
+ else {
+ if (anti_dir) xf = (int)(image_width * unaffected);
+ else xi = (int)(image_height * (1 - unaffected));
+ }
+
+ var /= (eff == VerticalGradient?yf-yi:xf-xi);
+
+ int ind_base;
+ for (y = yi; y < (int)yf; y++) {
+ intensity = eff == VerticalGradient? intensity + var :
+ initial_intensity;
+ ind_base = image_width * y ;
+ for (x = xi; x < (int)xf ; x++) {
+ if (eff == HorizontalGradient) intensity += var;
+ ind = x + ind_base;
+ r = qRed (data[ind]) + (int)(intensity *
+ (r_bgnd - qRed (data[ind])));
+ g = qGreen(data[ind]) + (int)(intensity *
+ (g_bgnd - qGreen(data[ind])));
+ b = qBlue (data[ind]) + (int)(intensity *
+ (b_bgnd - qBlue (data[ind])));
+ if (r > 255) r = 255; if (r < 0 ) r = 0;
+ if (g > 255) g = 255; if (g < 0 ) g = 0;
+ if (b > 255) b = 255; if (b < 0 ) b = 0;
+ a = qAlpha(data[ind]);
+ data[ind] = qRgba(r, g, b, a);
+ }
+ }
+ }
+ else if (eff == DiagonalGradient || eff == CrossDiagonalGradient) {
+ float xvar = var / 2 / image_width; // / unaffected;
+ float yvar = var / 2 / image_height; // / unaffected;
+ float tmp;
+
+ for (x = 0; x < image_width ; x++) {
+ tmp = xvar * (eff == DiagonalGradient? x : image.width()-x-1);
+ ind = x;
+ for (y = 0; y < image_height ; y++) {
+ intensity = initial_intensity + tmp + yvar * y;
+
+ r = qRed (data[ind]) + (int)(intensity *
+ (r_bgnd - qRed (data[ind])));
+ g = qGreen(data[ind]) + (int)(intensity *
+ (g_bgnd - qGreen(data[ind])));
+ b = qBlue (data[ind]) + (int)(intensity *
+ (b_bgnd - qBlue (data[ind])));
+ if (r > 255) r = 255; if (r < 0 ) r = 0;
+ if (g > 255) g = 255; if (g < 0 ) g = 0;
+ if (b > 255) b = 255; if (b < 0 ) b = 0;
+ a = qAlpha(data[ind]);
+ data[ind] = qRgba(r, g, b, a);
+
+ ind += image_width;
+ }
+ }
+ }
+
+ else if (eff == RectangleGradient || eff == EllipticGradient) {
+ float xvar;
+ float yvar;
+
+ for (x = 0; x < image_width / 2 + image_width % 2; x++) {
+ xvar = var / image_width * (image_width - x*2/unaffected-1);
+ for (y = 0; y < image_height / 2 + image_height % 2; y++) {
+ yvar = var / image_height * (image_height - y*2/unaffected -1);
+
+ if (eff == RectangleGradient)
+ intensity = initial_intensity + QMAX(xvar, yvar);
+ else
+ intensity = initial_intensity + sqrt(xvar * xvar + yvar * yvar);
+ if (intensity > 1) intensity = 1;
+ if (intensity < 0) intensity = 0;
+
+ //NW
+ ind = x + image_width * y ;
+ r = qRed (data[ind]) + (int)(intensity *
+ (r_bgnd - qRed (data[ind])));
+ g = qGreen(data[ind]) + (int)(intensity *
+ (g_bgnd - qGreen(data[ind])));
+ b = qBlue (data[ind]) + (int)(intensity *
+ (b_bgnd - qBlue (data[ind])));
+ if (r > 255) r = 255; if (r < 0 ) r = 0;
+ if (g > 255) g = 255; if (g < 0 ) g = 0;
+ if (b > 255) b = 255; if (b < 0 ) b = 0;
+ a = qAlpha(data[ind]);
+ data[ind] = qRgba(r, g, b, a);
+
+ //NE
+ ind = image_width - x - 1 + image_width * y ;
+ r = qRed (data[ind]) + (int)(intensity *
+ (r_bgnd - qRed (data[ind])));
+ g = qGreen(data[ind]) + (int)(intensity *
+ (g_bgnd - qGreen(data[ind])));
+ b = qBlue (data[ind]) + (int)(intensity *
+ (b_bgnd - qBlue (data[ind])));
+ if (r > 255) r = 255; if (r < 0 ) r = 0;
+ if (g > 255) g = 255; if (g < 0 ) g = 0;
+ if (b > 255) b = 255; if (b < 0 ) b = 0;
+ a = qAlpha(data[ind]);
+ data[ind] = qRgba(r, g, b, a);
+ }
+ }
+
+ //CT loop is doubled because of stupid central row/column issue.
+ // other solution?
+ for (x = 0; x < image_width / 2; x++) {
+ xvar = var / image_width * (image_width - x*2/unaffected-1);
+ for (y = 0; y < image_height / 2; y++) {
+ yvar = var / image_height * (image_height - y*2/unaffected -1);
+
+ if (eff == RectangleGradient)
+ intensity = initial_intensity + QMAX(xvar, yvar);
+ else
+ intensity = initial_intensity + sqrt(xvar * xvar + yvar * yvar);
+ if (intensity > 1) intensity = 1;
+ if (intensity < 0) intensity = 0;
+
+ //SW
+ ind = x + image_width * (image_height - y -1) ;
+ r = qRed (data[ind]) + (int)(intensity *
+ (r_bgnd - qRed (data[ind])));
+ g = qGreen(data[ind]) + (int)(intensity *
+ (g_bgnd - qGreen(data[ind])));
+ b = qBlue (data[ind]) + (int)(intensity *
+ (b_bgnd - qBlue (data[ind])));
+ if (r > 255) r = 255; if (r < 0 ) r = 0;
+ if (g > 255) g = 255; if (g < 0 ) g = 0;
+ if (b > 255) b = 255; if (b < 0 ) b = 0;
+ a = qAlpha(data[ind]);
+ data[ind] = qRgba(r, g, b, a);
+
+ //SE
+ ind = image_width-x-1 + image_width * (image_height - y - 1) ;
+ r = qRed (data[ind]) + (int)(intensity *
+ (r_bgnd - qRed (data[ind])));
+ g = qGreen(data[ind]) + (int)(intensity *
+ (g_bgnd - qGreen(data[ind])));
+ b = qBlue (data[ind]) + (int)(intensity *
+ (b_bgnd - qBlue (data[ind])));
+ if (r > 255) r = 255; if (r < 0 ) r = 0;
+ if (g > 255) g = 255; if (g < 0 ) g = 0;
+ if (b > 255) b = 255; if (b < 0 ) b = 0;
+ a = qAlpha(data[ind]);
+ data[ind] = qRgba(r, g, b, a);
+ }
+ }
+ }
+#ifndef NDEBUG
+ else std::cerr << "KImageEffect::blend effect not implemented" << std::endl;
+#endif
+ return image;
+}
+
+// Not very efficient as we create a third big image...
+//
+QImage& KImageEffect::blend(QImage &image1, QImage &image2,
+ GradientType gt, int xf, int yf)
+{
+ if (image1.width() == 0 || image1.height() == 0 ||
+ image2.width() == 0 || image2.height() == 0)
+ return image1;
+
+ QImage image3;
+
+ image3 = KImageEffect::unbalancedGradient(image1.size(),
+ QColor(0,0,0), QColor(255,255,255),
+ gt, xf, yf, 0);
+
+ return blend(image1,image2,image3, Red); // Channel to use is arbitrary
+}
+
+// Blend image2 into image1, using an RBG channel of blendImage
+//
+QImage& KImageEffect::blend(QImage &image1, QImage &image2,
+ QImage &blendImage, RGBComponent channel)
+{
+ if (image1.width() == 0 || image1.height() == 0 ||
+ image2.width() == 0 || image2.height() == 0 ||
+ blendImage.width() == 0 || blendImage.height() == 0) {
+#ifndef NDEBUG
+ std::cerr << "KImageEffect::blend effect invalid image" << std::endl;
+#endif
+ return image1;
+ }
+
+ int r, g, b;
+ int ind1, ind2, ind3;
+
+ unsigned int x1, x2, x3, y1, y2, y3;
+ unsigned int a;
+
+ register int x, y;
+
+ // for image1 and image2, we only handle depth 32
+ if (image1.depth()<32) image1 = image1.convertDepth(32);
+ if (image2.depth()<32) image2 = image2.convertDepth(32);
+
+ // for blendImage, we handle depth 8 and 32
+ if (blendImage.depth()<8) blendImage = blendImage.convertDepth(8);
+
+ unsigned int *colorTable3 = (blendImage.depth()==8) ?
+ blendImage.colorTable():0;
+
+ unsigned int *data1 = (unsigned int *)image1.bits();
+ unsigned int *data2 = (unsigned int *)image2.bits();
+ unsigned int *data3 = (unsigned int *)blendImage.bits();
+ unsigned char *data3b = (unsigned char *)blendImage.bits();
+ unsigned int color3;
+
+ x1 = image1.width(); y1 = image1.height();
+ x2 = image2.width(); y2 = image2.height();
+ x3 = blendImage.width(); y3 = blendImage.height();
+
+ for (y = 0; y < (int)y1; y++) {
+ ind1 = x1*y;
+ ind2 = x2*(y%y2);
+ ind3 = x3*(y%y3);
+
+ x=0;
+ while(x < (int)x1) {
+ color3 = (colorTable3) ? colorTable3[data3b[ind3]] : data3[ind3];
+
+ a = (channel == Red) ? qRed(color3) :
+ (channel == Green) ? qGreen(color3) :
+ (channel == Blue) ? qBlue(color3) : qGray(color3);
+
+ r = (a*qRed(data1[ind1]) + (256-a)*qRed(data2[ind2]))/256;
+ g = (a*qGreen(data1[ind1]) + (256-a)*qGreen(data2[ind2]))/256;
+ b = (a*qBlue(data1[ind1]) + (256-a)*qBlue(data2[ind2]))/256;
+
+ a = qAlpha(data1[ind1]);
+ data1[ind1] = qRgba(r, g, b, a);
+
+ ind1++; ind2++; ind3++; x++;
+ if ( (x%x2) ==0) ind2 -= x2;
+ if ( (x%x3) ==0) ind3 -= x3;
+ }
+ }
+ return image1;
+}
+
+
+//======================================================================
+//
+// Hash effects
+//
+//======================================================================
+
+unsigned int KImageEffect::lHash(unsigned int c)
+{
+ unsigned char r = qRed(c), g = qGreen(c), b = qBlue(c), a = qAlpha(c);
+ unsigned char nr, ng, nb;
+ nr =(r >> 1) + (r >> 2); nr = nr > r ? 0 : nr;
+ ng =(g >> 1) + (g >> 2); ng = ng > g ? 0 : ng;
+ nb =(b >> 1) + (b >> 2); nb = nb > b ? 0 : nb;
+
+ return qRgba(nr, ng, nb, a);
+}
+
+
+// -----------------------------------------------------------------------------
+
+unsigned int KImageEffect::uHash(unsigned int c)
+{
+ unsigned char r = qRed(c), g = qGreen(c), b = qBlue(c), a = qAlpha(c);
+ unsigned char nr, ng, nb;
+ nr = r + (r >> 3); nr = nr < r ? ~0 : nr;
+ ng = g + (g >> 3); ng = ng < g ? ~0 : ng;
+ nb = b + (b >> 3); nb = nb < b ? ~0 : nb;
+
+ return qRgba(nr, ng, nb, a);
+}
+
+
+// -----------------------------------------------------------------------------
+
+QImage& KImageEffect::hash(QImage &image, Lighting lite, unsigned int spacing)
+{
+ if (image.width() == 0 || image.height() == 0) {
+#ifndef NDEBUG
+ std::cerr << "KImageEffect::hash effect invalid image" << std::endl;
+#endif
+ return image;
+ }
+
+ register int x, y;
+ unsigned int *data = (unsigned int *)image.bits();
+ unsigned int ind;
+
+ //CT no need to do it if not enough space
+ if ((lite == NorthLite ||
+ lite == SouthLite)&&
+ (unsigned)image.height() < 2+spacing) return image;
+ if ((lite == EastLite ||
+ lite == WestLite)&&
+ (unsigned)image.height() < 2+spacing) return image;
+
+ if (lite == NorthLite || lite == SouthLite) {
+ for (y = 0 ; y < image.height(); y = y + 2 + spacing) {
+ for (x = 0; x < image.width(); x++) {
+ ind = x + image.width() * y;
+ data[ind] = lite==NorthLite?uHash(data[ind]):lHash(data[ind]);
+
+ ind = ind + image.width();
+ data[ind] = lite==NorthLite?lHash(data[ind]):uHash(data[ind]);
+ }
+ }
+ }
+
+ else if (lite == EastLite || lite == WestLite) {
+ for (y = 0 ; y < image.height(); y++) {
+ for (x = 0; x < image.width(); x = x + 2 + spacing) {
+ ind = x + image.width() * y;
+ data[ind] = lite==EastLite?uHash(data[ind]):lHash(data[ind]);
+
+ ind++;
+ data[ind] = lite==EastLite?lHash(data[ind]):uHash(data[ind]);
+ }
+ }
+ }
+
+ else if (lite == NWLite || lite == SELite) {
+ for (y = 0 ; y < image.height(); y++) {
+ for (x = 0;
+ x < (int)(image.width() - ((y & 1)? 1 : 0) * spacing);
+ x = x + 2 + spacing) {
+ ind = x + image.width() * y + ((y & 1)? 1 : 0);
+ data[ind] = lite==NWLite?uHash(data[ind]):lHash(data[ind]);
+
+ ind++;
+ data[ind] = lite==NWLite?lHash(data[ind]):uHash(data[ind]);
+ }
+ }
+ }
+
+ else if (lite == SWLite || lite == NELite) {
+ for (y = 0 ; y < image.height(); y++) {
+ for (x = 0 + ((y & 1)? 1 : 0); x < image.width(); x = x + 2 + spacing) {
+ ind = x + image.width() * y - ((y & 1)? 1 : 0);
+ data[ind] = lite==SWLite?uHash(data[ind]):lHash(data[ind]);
+
+ ind++;
+ data[ind] = lite==SWLite?lHash(data[ind]):uHash(data[ind]);
+ }
+ }
+ }
+
+ return image;
+}
+
+
+//======================================================================
+//
+// Flatten effects
+//
+//======================================================================
+
+QImage& KImageEffect::flatten(QImage &img, const QColor &ca,
+ const QColor &cb, int ncols)
+{
+ if (img.width() == 0 || img.height() == 0)
+ return img;
+
+ // a bitmap is easy...
+ if (img.depth() == 1) {
+ img.setColor(0, ca.rgb());
+ img.setColor(1, cb.rgb());
+ return img;
+ }
+
+ int r1 = ca.red(); int r2 = cb.red();
+ int g1 = ca.green(); int g2 = cb.green();
+ int b1 = ca.blue(); int b2 = cb.blue();
+ int min = 0, max = 255;
+
+ QRgb col;
+
+ // Get minimum and maximum greylevel.
+ if (img.numColors()) {
+ // pseudocolor
+ for (int i = 0; i < img.numColors(); i++) {
+ col = img.color(i);
+ int mean = (qRed(col) + qGreen(col) + qBlue(col)) / 3;
+ min = QMIN(min, mean);
+ max = QMAX(max, mean);
+ }
+ } else {
+ // truecolor
+ for (int y=0; y < img.height(); y++)
+ for (int x=0; x < img.width(); x++) {
+ col = img.pixel(x, y);
+ int mean = (qRed(col) + qGreen(col) + qBlue(col)) / 3;
+ min = QMIN(min, mean);
+ max = QMAX(max, mean);
+ }
+ }
+
+ // Conversion factors
+ float sr = ((float) r2 - r1) / (max - min);
+ float sg = ((float) g2 - g1) / (max - min);
+ float sb = ((float) b2 - b1) / (max - min);
+
+
+ // Repaint the image
+ if (img.numColors()) {
+ for (int i=0; i < img.numColors(); i++) {
+ col = img.color(i);
+ int mean = (qRed(col) + qGreen(col) + qBlue(col)) / 3;
+ int r = (int) (sr * (mean - min) + r1 + 0.5);
+ int g = (int) (sg * (mean - min) + g1 + 0.5);
+ int b = (int) (sb * (mean - min) + b1 + 0.5);
+ img.setColor(i, qRgba(r, g, b, qAlpha(col)));
+ }
+ } else {
+ for (int y=0; y < img.height(); y++)
+ for (int x=0; x < img.width(); x++) {
+ col = img.pixel(x, y);
+ int mean = (qRed(col) + qGreen(col) + qBlue(col)) / 3;
+ int r = (int) (sr * (mean - min) + r1 + 0.5);
+ int g = (int) (sg * (mean - min) + g1 + 0.5);
+ int b = (int) (sb * (mean - min) + b1 + 0.5);
+ img.setPixel(x, y, qRgba(r, g, b, qAlpha(col)));
+ }
+ }
+
+
+ // Dither if necessary
+ if ( (ncols <= 0) || ((img.numColors() != 0) && (img.numColors() <= ncols)))
+ return img;
+
+ if (ncols == 1) ncols++;
+ if (ncols > 256) ncols = 256;
+
+ QColor *pal = new QColor[ncols];
+ sr = ((float) r2 - r1) / (ncols - 1);
+ sg = ((float) g2 - g1) / (ncols - 1);
+ sb = ((float) b2 - b1) / (ncols - 1);
+
+ for (int i=0; i<ncols; i++)
+ pal[i] = QColor(r1 + int(sr*i), g1 + int(sg*i), b1 + int(sb*i));
+
+ dither(img, pal, ncols);
+
+ delete[] pal;
+ return img;
+}
+
+
+//======================================================================
+//
+// Fade effects
+//
+//======================================================================
+
+QImage& KImageEffect::fade(QImage &img, float val, const QColor &color)
+{
+ if (img.width() == 0 || img.height() == 0)
+ return img;
+
+ // We don't handle bitmaps
+ if (img.depth() == 1)
+ return img;
+
+ unsigned char tbl[256];
+ for (int i=0; i<256; i++)
+ tbl[i] = (int) (val * i + 0.5);
+
+ int red = color.red();
+ int green = color.green();
+ int blue = color.blue();
+
+ QRgb col;
+ int r, g, b, cr, cg, cb;
+
+ if (img.depth() <= 8) {
+ // pseudo color
+ for (int i=0; i<img.numColors(); i++) {
+ col = img.color(i);
+ cr = qRed(col); cg = qGreen(col); cb = qBlue(col);
+ if (cr > red)
+ r = cr - tbl[cr - red];
+ else
+ r = cr + tbl[red - cr];
+ if (cg > green)
+ g = cg - tbl[cg - green];
+ else
+ g = cg + tbl[green - cg];
+ if (cb > blue)
+ b = cb - tbl[cb - blue];
+ else
+ b = cb + tbl[blue - cb];
+ img.setColor(i, qRgba(r, g, b, qAlpha(col)));
+ }
+
+ } else {
+ // truecolor
+ for (int y=0; y<img.height(); y++) {
+ QRgb *data = (QRgb *) img.scanLine(y);
+ for (int x=0; x<img.width(); x++) {
+ col = *data;
+ cr = qRed(col); cg = qGreen(col); cb = qBlue(col);
+ if (cr > red)
+ r = cr - tbl[cr - red];
+ else
+ r = cr + tbl[red - cr];
+ if (cg > green)
+ g = cg - tbl[cg - green];
+ else
+ g = cg + tbl[green - cg];
+ if (cb > blue)
+ b = cb - tbl[cb - blue];
+ else
+ b = cb + tbl[blue - cb];
+ *data++ = qRgba(r, g, b, qAlpha(col));
+ }
+ }
+ }
+
+ return img;
+}
+
+//======================================================================
+//
+// Color effects
+//
+//======================================================================
+
+// This code is adapted from code (C) Rik Hemsley <rik@kde.org>
+//
+// The formula used (r + b + g) /3 is different from the qGray formula
+// used by Qt. This is because our formula is much much faster. If,
+// however, it turns out that this is producing sub-optimal images,
+// then it will have to change (kurt)
+//
+// It does produce lower quality grayscale ;-) Use fast == true for the fast
+// algorithm, false for the higher quality one (mosfet).
+QImage& KImageEffect::toGray(QImage &img, bool fast)
+{
+ if (img.width() == 0 || img.height() == 0)
+ return img;
+
+ if(fast){
+ if (img.depth() == 32) {
+ register uchar * r(img.bits());
+ register uchar * g(img.bits() + 1);
+ register uchar * b(img.bits() + 2);
+
+ uchar * end(img.bits() + img.numBytes());
+
+ while (r != end) {
+
+ *r = *g = *b = (((*r + *g) >> 1) + *b) >> 1; // (r + b + g) / 3
+
+ r += 4;
+ g += 4;
+ b += 4;
+ }
+ }
+ else
+ {
+ for (int i = 0; i < img.numColors(); i++)
+ {
+ register uint r = qRed(img.color(i));
+ register uint g = qGreen(img.color(i));
+ register uint b = qBlue(img.color(i));
+
+ register uint gray = (((r + g) >> 1) + b) >> 1;
+ img.setColor(i, qRgba(gray, gray, gray, qAlpha(img.color(i))));
+ }
+ }
+ }
+ else{
+ int pixels = img.depth() > 8 ? img.width()*img.height() :
+ img.numColors();
+ unsigned int *data = img.depth() > 8 ? (unsigned int *)img.bits() :
+ (unsigned int *)img.colorTable();
+ int val, i;
+ for(i=0; i < pixels; ++i){
+ val = qGray(data[i]);
+ data[i] = qRgba(val, val, val, qAlpha(data[i]));
+ }
+ }
+ return img;
+}
+
+// CT 29Jan2000 - desaturation algorithms
+QImage& KImageEffect::desaturate(QImage &img, float desat)
+{
+ if (img.width() == 0 || img.height() == 0)
+ return img;
+
+ if (desat < 0) desat = 0.;
+ if (desat > 1) desat = 1.;
+ int pixels = img.depth() > 8 ? img.width()*img.height() :
+ img.numColors();
+ unsigned int *data = img.depth() > 8 ? (unsigned int *)img.bits() :
+ (unsigned int *)img.colorTable();
+ int h, s, v, i;
+ QColor clr; // keep constructor out of loop (mosfet)
+ for(i=0; i < pixels; ++i){
+ clr.setRgb(data[i]);
+ clr.hsv(&h, &s, &v);
+ clr.setHsv(h, (int)(s * (1. - desat)), v);
+ data[i] = clr.rgb();
+ }
+ return img;
+}
+
+// Contrast stuff (mosfet)
+QImage& KImageEffect::contrast(QImage &img, int c)
+{
+ if (img.width() == 0 || img.height() == 0)
+ return img;
+
+ if(c > 255)
+ c = 255;
+ if(c < -255)
+ c = -255;
+ int pixels = img.depth() > 8 ? img.width()*img.height() :
+ img.numColors();
+ unsigned int *data = img.depth() > 8 ? (unsigned int *)img.bits() :
+ (unsigned int *)img.colorTable();
+ int i, r, g, b;
+ for(i=0; i < pixels; ++i){
+ r = qRed(data[i]);
+ g = qGreen(data[i]);
+ b = qBlue(data[i]);
+ if(qGray(data[i]) <= 127){
+ if(r - c > 0)
+ r -= c;
+ else
+ r = 0;
+ if(g - c > 0)
+ g -= c;
+ else
+ g = 0;
+ if(b - c > 0)
+ b -= c;
+ else
+ b = 0;
+ }
+ else{
+ if(r + c <= 255)
+ r += c;
+ else
+ r = 255;
+ if(g + c <= 255)
+ g += c;
+ else
+ g = 255;
+ if(b + c <= 255)
+ b += c;
+ else
+ b = 255;
+ }
+ data[i] = qRgba(r, g, b, qAlpha(data[i]));
+ }
+ return(img);
+}
+
+//======================================================================
+//
+// Dithering effects
+//
+//======================================================================
+
+// adapted from kFSDither (C) 1997 Martin Jones (mjones@kde.org)
+//
+// Floyd-Steinberg dithering
+// Ref: Bitmapped Graphics Programming in C++
+// Marv Luse, Addison-Wesley Publishing, 1993.
+QImage& KImageEffect::dither(QImage &img, const QColor *palette, int size)
+{
+ if (img.width() == 0 || img.height() == 0 ||
+ palette == 0 || img.depth() <= 8)
+ return img;
+
+ QImage dImage( img.width(), img.height(), 8, size );
+ int i;
+
+ dImage.setNumColors( size );
+ for ( i = 0; i < size; i++ )
+ dImage.setColor( i, palette[ i ].rgb() );
+
+ int *rerr1 = new int [ img.width() * 2 ];
+ int *gerr1 = new int [ img.width() * 2 ];
+ int *berr1 = new int [ img.width() * 2 ];
+
+ memset( rerr1, 0, sizeof( int ) * img.width() * 2 );
+ memset( gerr1, 0, sizeof( int ) * img.width() * 2 );
+ memset( berr1, 0, sizeof( int ) * img.width() * 2 );
+
+ int *rerr2 = rerr1 + img.width();
+ int *gerr2 = gerr1 + img.width();
+ int *berr2 = berr1 + img.width();
+
+ for ( int j = 0; j < img.height(); j++ )
+ {
+ uint *ip = (uint * )img.scanLine( j );
+ uchar *dp = dImage.scanLine( j );
+
+ for ( i = 0; i < img.width(); i++ )
+ {
+ rerr1[i] = rerr2[i] + qRed( *ip );
+ rerr2[i] = 0;
+ gerr1[i] = gerr2[i] + qGreen( *ip );
+ gerr2[i] = 0;
+ berr1[i] = berr2[i] + qBlue( *ip );
+ berr2[i] = 0;
+ ip++;
+ }
+
+ *dp++ = nearestColor( rerr1[0], gerr1[0], berr1[0], palette, size );
+
+ for ( i = 1; i < img.width()-1; i++ )
+ {
+ int indx = nearestColor( rerr1[i], gerr1[i], berr1[i], palette, size );
+ *dp = indx;
+
+ int rerr = rerr1[i];
+ rerr -= palette[indx].red();
+ int gerr = gerr1[i];
+ gerr -= palette[indx].green();
+ int berr = berr1[i];
+ berr -= palette[indx].blue();
+
+ // diffuse red error
+ rerr1[ i+1 ] += ( rerr * 7 ) >> 4;
+ rerr2[ i-1 ] += ( rerr * 3 ) >> 4;
+ rerr2[ i ] += ( rerr * 5 ) >> 4;
+ rerr2[ i+1 ] += ( rerr ) >> 4;
+
+ // diffuse green error
+ gerr1[ i+1 ] += ( gerr * 7 ) >> 4;
+ gerr2[ i-1 ] += ( gerr * 3 ) >> 4;
+ gerr2[ i ] += ( gerr * 5 ) >> 4;
+ gerr2[ i+1 ] += ( gerr ) >> 4;
+
+ // diffuse red error
+ berr1[ i+1 ] += ( berr * 7 ) >> 4;
+ berr2[ i-1 ] += ( berr * 3 ) >> 4;
+ berr2[ i ] += ( berr * 5 ) >> 4;
+ berr2[ i+1 ] += ( berr ) >> 4;
+
+ dp++;
+ }
+
+ *dp = nearestColor( rerr1[i], gerr1[i], berr1[i], palette, size );
+ }
+
+ delete [] rerr1;
+ delete [] gerr1;
+ delete [] berr1;
+
+ img = dImage;
+ return img;
+}
+
+int KImageEffect::nearestColor( int r, int g, int b, const QColor *palette, int size )
+{
+ if (palette == 0)
+ return 0;
+
+ int dr = palette[0].red() - r;
+ int dg = palette[0].green() - g;
+ int db = palette[0].blue() - b;
+
+ int minDist = dr*dr + dg*dg + db*db;
+ int nearest = 0;
+
+ for (int i = 1; i < size; i++ )
+ {
+ dr = palette[i].red() - r;
+ dg = palette[i].green() - g;
+ db = palette[i].blue() - b;
+
+ int dist = dr*dr + dg*dg + db*db;
+
+ if ( dist < minDist )
+ {
+ minDist = dist;
+ nearest = i;
+ }
+ }
+
+ return nearest;
+}
+
+bool KImageEffect::blend(
+ const QImage & upper,
+ const QImage & lower,
+ QImage & output
+)
+{
+ if (
+ upper.width() > lower.width() ||
+ upper.height() > lower.height() ||
+ upper.depth() != 32 ||
+ lower.depth() != 32
+ )
+ {
+#ifndef NDEBUG
+ std::cerr << "KImageEffect::blend : Sizes not correct\n" ;
+#endif
+ return false;
+ }
+
+ output = lower.copy();
+
+ register uchar *i, *o;
+ register int a;
+ register int col;
+ register int w = upper.width();
+ int row(upper.height() - 1);
+
+ do {
+
+ i = upper.scanLine(row);
+ o = output.scanLine(row);
+
+ col = w << 2;
+ --col;
+
+ do {
+
+ while (!(a = i[col]) && (col != 3)) {
+ --col; --col; --col; --col;
+ }
+
+ --col;
+ o[col] += ((i[col] - o[col]) * a) >> 8;
+
+ --col;
+ o[col] += ((i[col] - o[col]) * a) >> 8;
+
+ --col;
+ o[col] += ((i[col] - o[col]) * a) >> 8;
+
+ } while (col--);
+
+ } while (row--);
+
+ return true;
+}
+
+#if 0
+// Not yet...
+bool KImageEffect::blend(
+ const QImage & upper,
+ const QImage & lower,
+ QImage & output,
+ const QRect & destRect
+)
+{
+ output = lower.copy();
+ return output;
+}
+
+#endif
+
+bool KImageEffect::blend(
+ int &x, int &y,
+ const QImage & upper,
+ const QImage & lower,
+ QImage & output
+)
+{
+ int cx=0, cy=0, cw=upper.width(), ch=upper.height();
+
+ if ( upper.width() + x > lower.width() ||
+ upper.height() + y > lower.height() ||
+ x < 0 || y < 0 ||
+ upper.depth() != 32 || lower.depth() != 32 )
+ {
+ if ( x > lower.width() || y > lower.height() ) return false;
+ if ( upper.width()<=0 || upper.height() <= 0 ) return false;
+ if ( lower.width()<=0 || lower.height() <= 0 ) return false;
+
+ if (x<0) {cx=-x; cw+=x; x=0; };
+ if (cw + x > lower.width()) { cw=lower.width()-x; };
+ if (y<0) {cy=-y; ch+=y; y=0; };
+ if (ch + y > lower.height()) { ch=lower.height()-y; };
+
+ if ( cx >= upper.width() || cy >= upper.height() ) return true;
+ if ( cw <= 0 || ch <= 0 ) return true;
+ }
+
+ output.create(cw,ch,32);
+// output.setAlphaBuffer(true); // I should do some benchmarks to see if
+ // this is worth the effort
+
+ register QRgb *i, *o, *b;
+
+ register int a;
+ register int j,k;
+ for (j=0; j<ch; j++)
+ {
+ b=reinterpret_cast<QRgb *>(&lower.scanLine(y+j) [ (x+cw) << 2 ]);
+ i=reinterpret_cast<QRgb *>(&upper.scanLine(cy+j)[ (cx+cw) << 2 ]);
+ o=reinterpret_cast<QRgb *>(&output.scanLine(j) [ cw << 2 ]);
+
+ k=cw-1;
+ --b; --i; --o;
+ do
+ {
+ while ( !(a=qAlpha(*i)) && k>0 )
+ {
+ i--;
+// *o=0;
+ *o=*b;
+ --o; --b;
+ k--;
+ };
+// *o=0xFF;
+ *o = qRgb(qRed(*b) + (((qRed(*i) - qRed(*b)) * a) >> 8),
+ qGreen(*b) + (((qGreen(*i) - qGreen(*b)) * a) >> 8),
+ qBlue(*b) + (((qBlue(*i) - qBlue(*b)) * a) >> 8));
+ --i; --o; --b;
+ } while (k--);
+ }
+
+ return true;
+}
+
+bool KImageEffect::blendOnLower(
+ int x, int y,
+ const QImage & upper,
+ const QImage & lower
+)
+{
+ int cx=0, cy=0, cw=upper.width(), ch=upper.height();
+
+ if ( upper.depth() != 32 || lower.depth() != 32 ) return false;
+ if ( x + cw > lower.width() ||
+ y + ch > lower.height() ||
+ x < 0 || y < 0 )
+ {
+ if ( x > lower.width() || y > lower.height() ) return true;
+ if ( upper.width()<=0 || upper.height() <= 0 ) return true;
+ if ( lower.width()<=0 || lower.height() <= 0 ) return true;
+
+ if (x<0) {cx=-x; cw+=x; x=0; };
+ if (cw + x > lower.width()) { cw=lower.width()-x; };
+ if (y<0) {cy=-y; ch+=y; y=0; };
+ if (ch + y > lower.height()) { ch=lower.height()-y; };
+
+ if ( cx >= upper.width() || cy >= upper.height() ) return true;
+ if ( cw <= 0 || ch <= 0 ) return true;
+ }
+
+ register uchar *i, *b;
+ register int a;
+ register int k;
+
+ for (int j=0; j<ch; j++)
+ {
+ b=&lower.scanLine(y+j) [ (x+cw) << 2 ];
+ i=&upper.scanLine(cy+j)[ (cx+cw) << 2 ];
+
+ k=cw-1;
+ --b; --i;
+ do
+ {
+#ifndef WORDS_BIGENDIAN
+ while ( !(a=*i) && k>0 )
+#else
+ while ( !(a=*(i-3)) && k>0 )
+#endif
+ {
+ i-=4; b-=4; k--;
+ };
+
+#ifndef WORDS_BIGENDIAN
+ --i; --b;
+ *b += ( ((*i - *b) * a) >> 8 );
+ --i; --b;
+ *b += ( ((*i - *b) * a) >> 8 );
+ --i; --b;
+ *b += ( ((*i - *b) * a) >> 8 );
+ --i; --b;
+#else
+ *b += ( ((*i - *b) * a) >> 8 );
+ --i; --b;
+ *b += ( ((*i - *b) * a) >> 8 );
+ --i; --b;
+ *b += ( ((*i - *b) * a) >> 8 );
+ i -= 2; b -= 2;
+#endif
+ } while (k--);
+ }
+
+ return true;
+}
+
+void KImageEffect::blendOnLower(const QImage &upper, const QPoint &upperOffset,
+ QImage &lower, const QRect &lowerRect)
+{
+ // clip rect
+ QRect lr = lowerRect & lower.rect();
+ lr.setWidth( QMIN(lr.width(), upper.width()-upperOffset.x()) );
+ lr.setHeight( QMIN(lr.height(), upper.height()-upperOffset.y()) );
+ if ( !lr.isValid() ) return;
+
+ // blend
+ for (int y = 0; y < lr.height(); y++) {
+ for (int x = 0; x < lr.width(); x++) {
+ QRgb *b = reinterpret_cast<QRgb*>(lower.scanLine(lr.y() + y)+ (lr.x() + x) * sizeof(QRgb));
+ QRgb *d = reinterpret_cast<QRgb*>(upper.scanLine(upperOffset.y() + y) + (upperOffset.x() + x) * sizeof(QRgb));
+ int a = qAlpha(*d);
+ *b = qRgb(qRed(*b) - (((qRed(*b) - qRed(*d)) * a) >> 8),
+ qGreen(*b) - (((qGreen(*b) - qGreen(*d)) * a) >> 8),
+ qBlue(*b) - (((qBlue(*b) - qBlue(*d)) * a) >> 8));
+ }
+ }
+}
+
+void KImageEffect::blendOnLower(const QImage &upper, const QPoint &upperOffset,
+ QImage &lower, const QRect &lowerRect, float opacity)
+{
+ // clip rect
+ QRect lr = lowerRect & lower.rect();
+ lr.setWidth( QMIN(lr.width(), upper.width()-upperOffset.x()) );
+ lr.setHeight( QMIN(lr.height(), upper.height()-upperOffset.y()) );
+ if ( !lr.isValid() ) return;
+
+ // blend
+ for (int y = 0; y < lr.height(); y++) {
+ for (int x = 0; x < lr.width(); x++) {
+ QRgb *b = reinterpret_cast<QRgb*>(lower.scanLine(lr.y() + y)+ (lr.x() + x) * sizeof(QRgb));
+ QRgb *d = reinterpret_cast<QRgb*>(upper.scanLine(upperOffset.y() + y) + (upperOffset.x() + x) * sizeof(QRgb));
+ int a = qRound(opacity * qAlpha(*d));
+ *b = qRgb(qRed(*b) - (((qRed(*b) - qRed(*d)) * a) >> 8),
+ qGreen(*b) - (((qGreen(*b) - qGreen(*d)) * a) >> 8),
+ qBlue(*b) - (((qBlue(*b) - qBlue(*d)) * a) >> 8));
+ }
+ }
+}
+
+QRect KImageEffect::computeDestinationRect(const QSize &lowerSize,
+ Disposition disposition, QImage &upper)
+{
+ int w = lowerSize.width();
+ int h = lowerSize.height();
+ int ww = upper.width();
+ int wh = upper.height();
+ QRect d;
+
+ switch (disposition) {
+ case NoImage:
+ break;
+ case Centered:
+ d.setRect((w - ww) / 2, (h - wh) / 2, ww, wh);
+ break;
+ case Tiled:
+ d.setRect(0, 0, w, h);
+ break;
+ case CenterTiled:
+ d.setCoords(-ww + ((w - ww) / 2) % ww, -wh + ((h - wh) / 2) % wh,
+ w-1, h-1);
+ break;
+ case Scaled:
+ upper = upper.smoothScale(w, h);
+ d.setRect(0, 0, w, h);
+ break;
+ case CenteredAutoFit:
+ if( ww <= w && wh <= h ) {
+ d.setRect((w - ww) / 2, (h - wh) / 2, ww, wh); // like Centered
+ break;
+ }
+ // fall through
+ case CenteredMaxpect: {
+ double sx = (double) w / ww;
+ double sy = (double) h / wh;
+ if (sx > sy) {
+ ww = (int)(sy * ww);
+ wh = h;
+ } else {
+ wh = (int)(sx * wh);
+ ww = w;
+ }
+ upper = upper.smoothScale(ww, wh);
+ d.setRect((w - ww) / 2, (h - wh) / 2, ww, wh);
+ break;
+ }
+ case TiledMaxpect: {
+ double sx = (double) w / ww;
+ double sy = (double) h / wh;
+ if (sx > sy) {
+ ww = (int)(sy * ww);
+ wh = h;
+ } else {
+ wh = (int)(sx * wh);
+ ww = w;
+ }
+ upper = upper.smoothScale(ww, wh);
+ d.setRect(0, 0, w, h);
+ break;
+ }
+ }
+
+ return d;
+}
+
+void KImageEffect::blendOnLower(QImage &upper, QImage &lower,
+ Disposition disposition, float opacity)
+{
+ QRect r = computeDestinationRect(lower.size(), disposition, upper);
+ for (int y = r.top(); y<r.bottom(); y += upper.height())
+ for (int x = r.left(); x<r.right(); x += upper.width())
+ blendOnLower(upper, QPoint(-QMIN(x, 0), -QMIN(y, 0)),
+ lower, QRect(x, y, upper.width(), upper.height()), opacity);
+}
+
+
+// For selected icons
+QImage& KImageEffect::selectedImage( QImage &img, const QColor &col )
+{
+ return blend( col, img, 0.5);
+}
+
+//
+// ===================================================================
+// Effects originally ported from ImageMagick for PixiePlus, plus a few
+// new ones. (mosfet 05/26/2003)
+// ===================================================================
+//
+/*
+ Portions of this software are based on ImageMagick. Such portions are clearly
+marked as being ported from ImageMagick. ImageMagick is copyrighted under the
+following conditions:
+
+Copyright (C) 2003 ImageMagick Studio, a non-profit organization dedicated to
+making software imaging solutions freely available.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files ("ImageMagick"), to deal
+in ImageMagick without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of ImageMagick, and to permit persons to whom the ImageMagick is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of ImageMagick.
+
+The software is provided "as is", without warranty of any kind, express or
+implied, including but not limited to the warranties of merchantability,
+fitness for a particular purpose and noninfringement. In no event shall
+ImageMagick Studio be liable for any claim, damages or other liability,
+whether in an action of contract, tort or otherwise, arising from, out of or
+in connection with ImageMagick or the use or other dealings in ImageMagick.
+
+Except as contained in this notice, the name of the ImageMagick Studio shall
+not be used in advertising or otherwise to promote the sale, use or other
+dealings in ImageMagick without prior written authorization from the
+ImageMagick Studio.
+*/
+
+QImage KImageEffect::sample(QImage &src, int w, int h)
+{
+ if(w == src.width() && h == src.height())
+ return(src);
+
+ int depth = src.depth();
+ QImage dest(w, h, depth, depth <= 8 ? src.numColors() : 0,
+ depth == 1 ? QImage::LittleEndian : QImage::IgnoreEndian);
+ int *x_offset = (int *)malloc(w*sizeof(int));
+ int *y_offset = (int *)malloc(h*sizeof(int));
+ if(!x_offset || !y_offset){
+#ifndef NDEBUG
+ qWarning("KImageEffect::sample(): Unable to allocate pixel buffer");
+#endif
+ free(x_offset);
+ free(y_offset);
+ return(src);
+ }
+
+ // init pixel offsets
+ for(int x=0; x < w; ++x)
+ x_offset[x] = (int)(x*src.width()/((double)w));
+ for(int y=0; y < h; ++y)
+ y_offset[y] = (int)(y*src.height()/((double)h));
+
+ if(depth > 8){ // DirectClass source image
+ for(int y=0; y < h; ++y){
+ unsigned int *destData = (unsigned int *)dest.scanLine(y);
+ unsigned int *srcData = (unsigned int *)src.scanLine(y_offset[y]);
+ for(int x=0; x < w; ++x)
+ destData[x] = srcData[x_offset[x]];
+ }
+ }
+ else if(depth == 1) {
+ int r = src.bitOrder() == QImage::LittleEndian;
+ memcpy(dest.colorTable(), src.colorTable(), src.numColors()*sizeof(QRgb));
+ for(int y=0; y < h; ++y){
+ unsigned char *destData = dest.scanLine(y);
+ unsigned char *srcData = src.scanLine(y_offset[y]);
+ for(int x=0; x < w; ++x){
+ int k = x_offset[x];
+ int l = r ? (k & 7) : (7 - (k&7));
+ if(srcData[k >> 3] & (1 << l))
+ destData[x >> 3] |= 1 << (x & 7);
+ else
+ destData[x >> 3] &= ~(1 << (x & 7));
+ }
+ }
+ }
+ else{ // PseudoClass source image
+ memcpy(dest.colorTable(), src.colorTable(), src.numColors()*sizeof(QRgb));
+ for(int y=0; y < h; ++y){
+ unsigned char *destData = dest.scanLine(y);
+ unsigned char *srcData = src.scanLine(y_offset[y]);
+ for(int x=0; x < w; ++x)
+ destData[x] = srcData[x_offset[x]];
+ }
+ }
+ free(x_offset);
+ free(y_offset);
+ return(dest);
+}
+
+void KImageEffect::threshold(QImage &img, unsigned int threshold)
+{
+ int i, count;
+ unsigned int *data;
+ if(img.depth() > 8){ // DirectClass
+ count = img.width()*img.height();
+ data = (unsigned int *)img.bits();
+ }
+ else{ // PsudeoClass
+ count = img.numColors();
+ data = (unsigned int *)img.colorTable();
+ }
+ for(i=0; i < count; ++i)
+ data[i] = intensityValue(data[i]) < threshold ? Qt::black.rgb() : Qt::white.rgb();
+}
+
+void KImageEffect::hull(const int x_offset, const int y_offset,
+ const int polarity, const int columns,
+ const int rows,
+ unsigned int *f, unsigned int *g)
+{
+ int x, y;
+
+ unsigned int *p, *q, *r, *s;
+ unsigned int v;
+ if(f == NULL || g == NULL)
+ return;
+ p=f+(columns+2);
+ q=g+(columns+2);
+ r=p+(y_offset*(columns+2)+x_offset);
+ for (y=0; y < rows; y++){
+ p++;
+ q++;
+ r++;
+ if(polarity > 0)
+ for (x=0; x < columns; x++){
+ v=(*p);
+ if (*r > v)
+ v++;
+ *q=v;
+ p++;
+ q++;
+ r++;
+ }
+ else
+ for(x=0; x < columns; x++){
+ v=(*p);
+ if (v > (unsigned int) (*r+1))
+ v--;
+ *q=v;
+ p++;
+ q++;
+ r++;
+ }
+ p++;
+ q++;
+ r++;
+ }
+ p=f+(columns+2);
+ q=g+(columns+2);
+ r=q+(y_offset*(columns+2)+x_offset);
+ s=q-(y_offset*(columns+2)+x_offset);
+ for(y=0; y < rows; y++){
+ p++;
+ q++;
+ r++;
+ s++;
+ if(polarity > 0)
+ for(x=0; x < (int) columns; x++){
+ v=(*q);
+ if (((unsigned int) (*s+1) > v) && (*r > v))
+ v++;
+ *p=v;
+ p++;
+ q++;
+ r++;
+ s++;
+ }
+ else
+ for (x=0; x < columns; x++){
+ v=(*q);
+ if (((unsigned int) (*s+1) < v) && (*r < v))
+ v--;
+ *p=v;
+ p++;
+ q++;
+ r++;
+ s++;
+ }
+ p++;
+ q++;
+ r++;
+ s++;
+ }
+}
+
+QImage KImageEffect::despeckle(QImage &src)
+{
+ int i, j, x, y;
+ unsigned int *blue_channel, *red_channel, *green_channel, *buffer,
+ *alpha_channel;
+ int packets;
+ static const int
+ X[4]= {0, 1, 1,-1},
+ Y[4]= {1, 0, 1, 1};
+
+ unsigned int *destData;
+ QImage dest(src.width(), src.height(), 32);
+
+ packets = (src.width()+2)*(src.height()+2);
+ red_channel = (unsigned int *)calloc(packets, sizeof(unsigned int));
+ green_channel = (unsigned int *)calloc(packets, sizeof(unsigned int));
+ blue_channel = (unsigned int *)calloc(packets, sizeof(unsigned int));
+ alpha_channel = (unsigned int *)calloc(packets, sizeof(unsigned int));
+ buffer = (unsigned int *)calloc(packets, sizeof(unsigned int));
+ if(!red_channel || ! green_channel || ! blue_channel || ! alpha_channel ||
+ !buffer){
+ free(red_channel);
+ free(green_channel);
+ free(blue_channel);
+ free(alpha_channel);
+ free(buffer);
+ return(src);
+ }
+
+ // copy image pixels to color component buffers
+ j = src.width()+2;
+ if(src.depth() > 8){ // DirectClass source image
+ unsigned int *srcData;
+ for(y=0; y < src.height(); ++y){
+ srcData = (unsigned int *)src.scanLine(y);
+ ++j;
+ for(x=0; x < src.width(); ++x){
+ red_channel[j] = qRed(srcData[x]);
+ green_channel[j] = qGreen(srcData[x]);
+ blue_channel[j] = qBlue(srcData[x]);
+ alpha_channel[j] = qAlpha(srcData[x]);
+ ++j;
+ }
+ ++j;
+ }
+ }
+ else{ // PsudeoClass source image
+ unsigned char *srcData;
+ unsigned int *cTable = src.colorTable();
+ unsigned int pixel;
+ for(y=0; y < src.height(); ++y){
+ srcData = (unsigned char *)src.scanLine(y);
+ ++j;
+ for(x=0; x < src.width(); ++x){
+ pixel = *(cTable+srcData[x]);
+ red_channel[j] = qRed(pixel);
+ green_channel[j] = qGreen(pixel);
+ blue_channel[j] = qBlue(pixel);
+ alpha_channel[j] = qAlpha(pixel);
+ ++j;
+ }
+ ++j;
+ }
+ }
+ // reduce speckle in red channel
+ for(i=0; i < 4; i++){
+ hull(X[i],Y[i],1,src.width(),src.height(),red_channel,buffer);
+ hull(-X[i],-Y[i],1,src.width(),src.height(),red_channel,buffer);
+ hull(-X[i],-Y[i],-1,src.width(),src.height(),red_channel,buffer);
+ hull(X[i],Y[i],-1,src.width(),src.height(),red_channel,buffer);
+ }
+ // reduce speckle in green channel
+ for (i=0; i < packets; i++)
+ buffer[i]=0;
+ for (i=0; i < 4; i++){
+ hull(X[i],Y[i],1,src.width(),src.height(),green_channel,buffer);
+ hull(-X[i],-Y[i],1,src.width(),src.height(),green_channel,buffer);
+ hull(-X[i],-Y[i],-1,src.width(),src.height(),green_channel,buffer);
+ hull(X[i],Y[i],-1,src.width(),src.height(),green_channel,buffer);
+ }
+ // reduce speckle in blue channel
+ for (i=0; i < packets; i++)
+ buffer[i]=0;
+ for (i=0; i < 4; i++){
+ hull(X[i],Y[i],1,src.width(),src.height(),blue_channel,buffer);
+ hull(-X[i],-Y[i],1,src.width(),src.height(),blue_channel,buffer);
+ hull(-X[i],-Y[i],-1,src.width(),src.height(),blue_channel,buffer);
+ hull(X[i],Y[i],-1,src.width(),src.height(),blue_channel,buffer);
+ }
+ // copy color component buffers to despeckled image
+ j = dest.width()+2;
+ for(y=0; y < dest.height(); ++y)
+ {
+ destData = (unsigned int *)dest.scanLine(y);
+ ++j;
+ for (x=0; x < dest.width(); ++x)
+ {
+ destData[x] = qRgba(red_channel[j], green_channel[j],
+ blue_channel[j], alpha_channel[j]);
+ ++j;
+ }
+ ++j;
+ }
+ free(buffer);
+ free(red_channel);
+ free(green_channel);
+ free(blue_channel);
+ free(alpha_channel);
+ return(dest);
+}
+
+unsigned int KImageEffect::generateNoise(unsigned int pixel,
+ NoiseType noise_type)
+{
+#define NoiseEpsilon 1.0e-5
+#define NoiseMask 0x7fff
+#define SigmaUniform 4.0
+#define SigmaGaussian 4.0
+#define SigmaImpulse 0.10
+#define SigmaLaplacian 10.0
+#define SigmaMultiplicativeGaussian 0.5
+#define SigmaPoisson 0.05
+#define TauGaussian 20.0
+
+ double alpha, beta, sigma, value;
+ alpha=(double) (rand() & NoiseMask)/NoiseMask;
+ if (alpha == 0.0)
+ alpha=1.0;
+ switch(noise_type){
+ case UniformNoise:
+ default:
+ {
+ value=(double) pixel+SigmaUniform*(alpha-0.5);
+ break;
+ }
+ case GaussianNoise:
+ {
+ double tau;
+
+ beta=(double) (rand() & NoiseMask)/NoiseMask;
+ sigma=sqrt(-2.0*log(alpha))*cos(2.0*M_PI*beta);
+ tau=sqrt(-2.0*log(alpha))*sin(2.0*M_PI*beta);
+ value=(double) pixel+
+ (sqrt((double) pixel)*SigmaGaussian*sigma)+(TauGaussian*tau);
+ break;
+ }
+ case MultiplicativeGaussianNoise:
+ {
+ if (alpha <= NoiseEpsilon)
+ sigma=MaxRGB;
+ else
+ sigma=sqrt(-2.0*log(alpha));
+ beta=(rand() & NoiseMask)/NoiseMask;
+ value=(double) pixel+
+ pixel*SigmaMultiplicativeGaussian*sigma*cos(2.0*M_PI*beta);
+ break;
+ }
+ case ImpulseNoise:
+ {
+ if (alpha < (SigmaImpulse/2.0))
+ value=0;
+ else
+ if (alpha >= (1.0-(SigmaImpulse/2.0)))
+ value=MaxRGB;
+ else
+ value=pixel;
+ break;
+ }
+ case LaplacianNoise:
+ {
+ if (alpha <= 0.5)
+ {
+ if (alpha <= NoiseEpsilon)
+ value=(double) pixel-MaxRGB;
+ else
+ value=(double) pixel+SigmaLaplacian*log(2.0*alpha);
+ break;
+ }
+ beta=1.0-alpha;
+ if (beta <= (0.5*NoiseEpsilon))
+ value=(double) pixel+MaxRGB;
+ else
+ value=(double) pixel-SigmaLaplacian*log(2.0*beta);
+ break;
+ }
+ case PoissonNoise:
+ {
+ register int
+ i;
+
+ for (i=0; alpha > exp(-SigmaPoisson*pixel); i++)
+ {
+ beta=(double) (rand() & NoiseMask)/NoiseMask;
+ alpha=alpha*beta;
+ }
+ value=i/SigmaPoisson;
+ break;
+ }
+ }
+ if(value < 0.0)
+ return(0);
+ if(value > MaxRGB)
+ return(MaxRGB);
+ return((unsigned int) (value+0.5));
+}
+
+QImage KImageEffect::addNoise(QImage &src, NoiseType noise_type)
+{
+ int x, y;
+ QImage dest(src.width(), src.height(), 32);
+ unsigned int *destData;
+
+ if(src.depth() > 8){ // DirectClass source image
+ unsigned int *srcData;
+ for(y=0; y < src.height(); ++y){
+ srcData = (unsigned int *)src.scanLine(y);
+ destData = (unsigned int *)dest.scanLine(y);
+ for(x=0; x < src.width(); ++x){
+ destData[x] = qRgba(generateNoise(qRed(srcData[x]), noise_type),
+ generateNoise(qGreen(srcData[x]), noise_type),
+ generateNoise(qBlue(srcData[x]), noise_type),
+ qAlpha(srcData[x]));
+ }
+ }
+ }
+ else{ // PsudeoClass source image
+ unsigned char *srcData;
+ unsigned int *cTable = src.colorTable();
+ unsigned int pixel;
+ for(y=0; y < src.height(); ++y){
+ srcData = (unsigned char *)src.scanLine(y);
+ destData = (unsigned int *)dest.scanLine(y);
+ for(x=0; x < src.width(); ++x){
+ pixel = *(cTable+srcData[x]);
+ destData[x] = qRgba(generateNoise(qRed(pixel), noise_type),
+ generateNoise(qGreen(pixel), noise_type),
+ generateNoise(qBlue(pixel), noise_type),
+ qAlpha(pixel));
+ }
+ }
+
+ }
+ return(dest);
+}
+
+unsigned int KImageEffect::interpolateColor(QImage *image, double x_offset,
+ double y_offset,
+ unsigned int background)
+{
+ double alpha, beta;
+ unsigned int p, q, r, s;
+ int x, y;
+
+ x = (int)x_offset;
+ y = (int)y_offset;
+ if((x < -1) || (x >= image->width()) || (y < -1) || (y >= image->height()))
+ return(background);
+ if(image->depth() > 8){
+ if((x >= 0) && (y >= 0) && (x < (image->width()-1)) && (y < (image->height()-1))) {
+ unsigned int *t = (unsigned int *)image->scanLine(y);
+ p = t[x];
+ q = t[x+1];
+ r = t[x+image->width()];
+ s = t[x+image->width()+1];
+ }
+ else{
+ unsigned int *t = (unsigned int *)image->scanLine(y);
+ p = background;
+ if((x >= 0) && (y >= 0)){
+ p = t[x];
+ }
+ q = background;
+ if(((x+1) < image->width()) && (y >= 0)){
+ q = t[x+1];
+ }
+ r = background;
+ if((x >= 0) && ((y+1) < image->height())){
+ t = (unsigned int *)image->scanLine(y+1);
+ r = t[x+image->width()];
+ }
+ s = background;
+ if(((x+1) < image->width()) && ((y+1) < image->height())){
+ t = (unsigned int *)image->scanLine(y+1);
+ s = t[x+image->width()+1];
+ }
+
+ }
+ }
+ else{
+ unsigned int *colorTable = (unsigned int *)image->colorTable();
+ if((x >= 0) && (y >= 0) && (x < (image->width()-1)) && (y < (image->height()-1))) {
+ unsigned char *t;
+ t = (unsigned char *)image->scanLine(y);
+ p = *(colorTable+t[x]);
+ q = *(colorTable+t[x+1]);
+ t = (unsigned char *)image->scanLine(y+1);
+ r = *(colorTable+t[x]);
+ s = *(colorTable+t[x+1]);
+ }
+ else{
+ unsigned char *t;
+ p = background;
+ if((x >= 0) && (y >= 0)){
+ t = (unsigned char *)image->scanLine(y);
+ p = *(colorTable+t[x]);
+ }
+ q = background;
+ if(((x+1) < image->width()) && (y >= 0)){
+ t = (unsigned char *)image->scanLine(y);
+ q = *(colorTable+t[x+1]);
+ }
+ r = background;
+ if((x >= 0) && ((y+1) < image->height())){
+ t = (unsigned char *)image->scanLine(y+1);
+ r = *(colorTable+t[x]);
+ }
+ s = background;
+ if(((x+1) < image->width()) && ((y+1) < image->height())){
+ t = (unsigned char *)image->scanLine(y+1);
+ s = *(colorTable+t[x+1]);
+ }
+
+ }
+
+ }
+ x_offset -= floor(x_offset);
+ y_offset -= floor(y_offset);
+ alpha = 1.0-x_offset;
+ beta = 1.0-y_offset;
+
+ return(qRgba((unsigned char)(beta*(alpha*qRed(p)+x_offset*qRed(q))+y_offset*(alpha*qRed(r)+x_offset*qRed(s))),
+ (unsigned char)(beta*(alpha*qGreen(p)+x_offset*qGreen(q))+y_offset*(alpha*qGreen(r)+x_offset*qGreen(s))),
+ (unsigned char)(beta*(alpha*qBlue(p)+x_offset*qBlue(q))+y_offset*(alpha*qBlue(r)+x_offset*qBlue(s))),
+ (unsigned char)(beta*(alpha*qAlpha(p)+x_offset*qAlpha(q))+y_offset*(alpha*qAlpha(r)+x_offset*qAlpha(s)))));
+}
+
+QImage KImageEffect::implode(QImage &src, double factor,
+ unsigned int background)
+{
+ double amount, distance, radius;
+ double x_center, x_distance, x_scale;
+ double y_center, y_distance, y_scale;
+ unsigned int *destData;
+ int x, y;
+
+ QImage dest(src.width(), src.height(), 32);
+
+ // compute scaling factor
+ x_scale = 1.0;
+ y_scale = 1.0;
+ x_center = (double)0.5*src.width();
+ y_center = (double)0.5*src.height();
+ radius=x_center;
+ if(src.width() > src.height())
+ y_scale = (double)src.width()/src.height();
+ else if(src.width() < src.height()){
+ x_scale = (double) src.height()/src.width();
+ radius = y_center;
+ }
+ amount=factor/10.0;
+ if(amount >= 0)
+ amount/=10.0;
+ if(src.depth() > 8){ // DirectClass source image
+ unsigned int *srcData;
+ for(y=0; y < src.height(); ++y){
+ srcData = (unsigned int *)src.scanLine(y);
+ destData = (unsigned int *)dest.scanLine(y);
+ y_distance=y_scale*(y-y_center);
+ for(x=0; x < src.width(); ++x){
+ destData[x] = srcData[x];
+ x_distance = x_scale*(x-x_center);
+ distance= x_distance*x_distance+y_distance*y_distance;
+ if(distance < (radius*radius)){
+ double factor;
+ // Implode the pixel.
+ factor=1.0;
+ if(distance > 0.0)
+ factor=
+ pow(sin(0.5000000000000001*M_PI*sqrt(distance)/radius),-amount);
+ destData[x] = interpolateColor(&src, factor*x_distance/x_scale+x_center,
+ factor*y_distance/y_scale+y_center,
+ background);
+ }
+ }
+ }
+ }
+ else{ // PsudeoClass source image
+ unsigned char *srcData;
+ unsigned char idx;
+ unsigned int *cTable = src.colorTable();
+ for(y=0; y < src.height(); ++y){
+ srcData = (unsigned char *)src.scanLine(y);
+ destData = (unsigned int *)dest.scanLine(y);
+ y_distance=y_scale*(y-y_center);
+ for(x=0; x < src.width(); ++x){
+ idx = srcData[x];
+ destData[x] = cTable[idx];
+ x_distance = x_scale*(x-x_center);
+ distance= x_distance*x_distance+y_distance*y_distance;
+ if(distance < (radius*radius)){
+ double factor;
+ // Implode the pixel.
+ factor=1.0;
+ if(distance > 0.0)
+ factor=
+ pow(sin(0.5000000000000001*M_PI*sqrt(distance)/radius),-amount);
+ destData[x] = interpolateColor(&src, factor*x_distance/x_scale+x_center,
+ factor*y_distance/y_scale+y_center,
+ background);
+ }
+ }
+ }
+
+ }
+ return(dest);
+}
+
+QImage KImageEffect::rotate(QImage &img, RotateDirection r)
+{
+ QImage dest;
+ int x, y;
+ if(img.depth() > 8){
+ unsigned int *srcData, *destData;
+ switch(r){
+ case Rotate90:
+ dest.create(img.height(), img.width(), img.depth());
+ for(y=0; y < img.height(); ++y){
+ srcData = (unsigned int *)img.scanLine(y);
+ for(x=0; x < img.width(); ++x){
+ destData = (unsigned int *)dest.scanLine(x);
+ destData[img.height()-y-1] = srcData[x];
+ }
+ }
+ break;
+ case Rotate180:
+ dest.create(img.width(), img.height(), img.depth());
+ for(y=0; y < img.height(); ++y){
+ srcData = (unsigned int *)img.scanLine(y);
+ destData = (unsigned int *)dest.scanLine(img.height()-y-1);
+ for(x=0; x < img.width(); ++x)
+ destData[img.width()-x-1] = srcData[x];
+ }
+ break;
+ case Rotate270:
+ dest.create(img.height(), img.width(), img.depth());
+ for(y=0; y < img.height(); ++y){
+ srcData = (unsigned int *)img.scanLine(y);
+ for(x=0; x < img.width(); ++x){
+ destData = (unsigned int *)dest.scanLine(img.width()-x-1);
+ destData[y] = srcData[x];
+ }
+ }
+ break;
+ default:
+ dest = img;
+ break;
+ }
+ }
+ else{
+ unsigned char *srcData, *destData;
+ unsigned int *srcTable, *destTable;
+ switch(r){
+ case Rotate90:
+ dest.create(img.height(), img.width(), img.depth());
+ dest.setNumColors(img.numColors());
+ srcTable = (unsigned int *)img.colorTable();
+ destTable = (unsigned int *)dest.colorTable();
+ for(x=0; x < img.numColors(); ++x)
+ destTable[x] = srcTable[x];
+ for(y=0; y < img.height(); ++y){
+ srcData = (unsigned char *)img.scanLine(y);
+ for(x=0; x < img.width(); ++x){
+ destData = (unsigned char *)dest.scanLine(x);
+ destData[img.height()-y-1] = srcData[x];
+ }
+ }
+ break;
+ case Rotate180:
+ dest.create(img.width(), img.height(), img.depth());
+ dest.setNumColors(img.numColors());
+ srcTable = (unsigned int *)img.colorTable();
+ destTable = (unsigned int *)dest.colorTable();
+ for(x=0; x < img.numColors(); ++x)
+ destTable[x] = srcTable[x];
+ for(y=0; y < img.height(); ++y){
+ srcData = (unsigned char *)img.scanLine(y);
+ destData = (unsigned char *)dest.scanLine(img.height()-y-1);
+ for(x=0; x < img.width(); ++x)
+ destData[img.width()-x-1] = srcData[x];
+ }
+ break;
+ case Rotate270:
+ dest.create(img.height(), img.width(), img.depth());
+ dest.setNumColors(img.numColors());
+ srcTable = (unsigned int *)img.colorTable();
+ destTable = (unsigned int *)dest.colorTable();
+ for(x=0; x < img.numColors(); ++x)
+ destTable[x] = srcTable[x];
+ for(y=0; y < img.height(); ++y){
+ srcData = (unsigned char *)img.scanLine(y);
+ for(x=0; x < img.width(); ++x){
+ destData = (unsigned char *)dest.scanLine(img.width()-x-1);
+ destData[y] = srcData[x];
+ }
+ }
+ break;
+ default:
+ dest = img;
+ break;
+ }
+
+ }
+ return(dest);
+}
+
+void KImageEffect::solarize(QImage &img, double factor)
+{
+ int i, count;
+ int threshold;
+ unsigned int *data;
+
+ threshold = (int)(factor*(MaxRGB+1)/100.0);
+ if(img.depth() < 32){
+ data = (unsigned int *)img.colorTable();
+ count = img.numColors();
+ }
+ else{
+ data = (unsigned int *)img.bits();
+ count = img.width()*img.height();
+ }
+ for(i=0; i < count; ++i){
+ data[i] = qRgba(qRed(data[i]) > threshold ? MaxRGB-qRed(data[i]) : qRed(data[i]),
+ qGreen(data[i]) > threshold ? MaxRGB-qGreen(data[i]) : qGreen(data[i]),
+ qBlue(data[i]) > threshold ? MaxRGB-qBlue(data[i]) : qBlue(data[i]),
+ qAlpha(data[i]));
+ }
+}
+
+QImage KImageEffect::spread(QImage &src, unsigned int amount)
+{
+ int quantum, x, y;
+ int x_distance, y_distance;
+ if(src.width() < 3 || src.height() < 3)
+ return(src);
+ QImage dest(src);
+ dest.detach();
+ quantum=(amount+1) >> 1;
+ if(src.depth() > 8){ // DirectClass source image
+ unsigned int *p, *q;
+ for(y=0; y < src.height(); y++){
+ q = (unsigned int *)dest.scanLine(y);
+ for(x=0; x < src.width(); x++){
+ x_distance = x + ((rand() & (amount+1))-quantum);
+ y_distance = y + ((rand() & (amount+1))-quantum);
+ x_distance = QMIN(x_distance, src.width()-1);
+ y_distance = QMIN(y_distance, src.height()-1);
+ if(x_distance < 0)
+ x_distance = 0;
+ if(y_distance < 0)
+ y_distance = 0;
+ p = (unsigned int *)src.scanLine(y_distance);
+ p += x_distance;
+ *q++=(*p);
+ }
+ }
+ }
+ else{ // PsudeoClass source image
+ // just do colortable values
+ unsigned char *p, *q;
+ for(y=0; y < src.height(); y++){
+ q = (unsigned char *)dest.scanLine(y);
+ for(x=0; x < src.width(); x++){
+ x_distance = x + ((rand() & (amount+1))-quantum);
+ y_distance = y + ((rand() & (amount+1))-quantum);
+ x_distance = QMIN(x_distance, src.width()-1);
+ y_distance = QMIN(y_distance, src.height()-1);
+ if(x_distance < 0)
+ x_distance = 0;
+ if(y_distance < 0)
+ y_distance = 0;
+ p = (unsigned char *)src.scanLine(y_distance);
+ p += x_distance;
+ *q++=(*p);
+ }
+ }
+ }
+ return(dest);
+}
+
+QImage KImageEffect::swirl(QImage &src, double degrees,
+ unsigned int background)
+{
+ double cosine, distance, factor, radius, sine, x_center, x_distance,
+ x_scale, y_center, y_distance, y_scale;
+ int x, y;
+ unsigned int *q;
+ QImage dest(src.width(), src.height(), 32);
+
+ // compute scaling factor
+ x_center = src.width()/2.0;
+ y_center = src.height()/2.0;
+ radius = QMAX(x_center,y_center);
+ x_scale=1.0;
+ y_scale=1.0;
+ if(src.width() > src.height())
+ y_scale=(double)src.width()/src.height();
+ else if(src.width() < src.height())
+ x_scale=(double)src.height()/src.width();
+ degrees=DegreesToRadians(degrees);
+ // swirl each row
+ if(src.depth() > 8){ // DirectClass source image
+ unsigned int *p;
+ for(y=0; y < src.height(); y++){
+ p = (unsigned int *)src.scanLine(y);
+ q = (unsigned int *)dest.scanLine(y);
+ y_distance = y_scale*(y-y_center);
+ for(x=0; x < src.width(); x++){
+ // determine if the pixel is within an ellipse
+ *q=(*p);
+ x_distance = x_scale*(x-x_center);
+ distance = x_distance*x_distance+y_distance*y_distance;
+ if (distance < (radius*radius)){
+ // swirl
+ factor = 1.0-sqrt(distance)/radius;
+ sine = sin(degrees*factor*factor);
+ cosine = cos(degrees*factor*factor);
+ *q = interpolateColor(&src,
+ (cosine*x_distance-sine*y_distance)/x_scale+x_center,
+ (sine*x_distance+cosine*y_distance)/y_scale+y_center,
+ background);
+ }
+ p++;
+ q++;
+ }
+ }
+ }
+ else{ // PsudeoClass source image
+ unsigned char *p;
+ unsigned int *cTable = (unsigned int *)src.colorTable();
+ for(y=0; y < src.height(); y++){
+ p = (unsigned char *)src.scanLine(y);
+ q = (unsigned int *)dest.scanLine(y);
+ y_distance = y_scale*(y-y_center);
+ for(x=0; x < src.width(); x++){
+ // determine if the pixel is within an ellipse
+ *q = *(cTable+(*p));
+ x_distance = x_scale*(x-x_center);
+ distance = x_distance*x_distance+y_distance*y_distance;
+ if (distance < (radius*radius)){
+ // swirl
+ factor = 1.0-sqrt(distance)/radius;
+ sine = sin(degrees*factor*factor);
+ cosine = cos(degrees*factor*factor);
+ *q = interpolateColor(&src,
+ (cosine*x_distance-sine*y_distance)/x_scale+x_center,
+ (sine*x_distance+cosine*y_distance)/y_scale+y_center,
+ background);
+ }
+ p++;
+ q++;
+ }
+ }
+
+ }
+ return(dest);
+}
+
+QImage KImageEffect::wave(QImage &src, double amplitude, double wavelength,
+ unsigned int background)
+{
+ double *sine_map;
+ int x, y;
+ unsigned int *q;
+
+ QImage dest(src.width(), src.height() + (int)(2*fabs(amplitude)), 32);
+ // allocate sine map
+ sine_map = (double *)malloc(dest.width()*sizeof(double));
+ if(!sine_map)
+ return(src);
+ for(x=0; x < dest.width(); ++x)
+ sine_map[x]=fabs(amplitude)+amplitude*sin((2*M_PI*x)/wavelength);
+ // wave image
+ for(y=0; y < dest.height(); ++y){
+ q = (unsigned int *)dest.scanLine(y);
+ for (x=0; x < dest.width(); x++){
+ *q=interpolateColor(&src, x, (int)(y-sine_map[x]), background);
+ ++q;
+ }
+ }
+ free(sine_map);
+ return(dest);
+}
+
+//
+// The following methods work by computing a value from neighboring pixels
+// (mosfet 05/26/03)
+//
+
+// New algorithms based on ImageMagick 5.5.6 (05/26/03)
+
+QImage KImageEffect::oilPaint(QImage &src, int /*radius*/)
+{
+ /* binary compat method - remove me when possible! */
+ return(oilPaintConvolve(src, 0));
+}
+
+QImage KImageEffect::oilPaintConvolve(QImage &src, double radius)
+{
+ unsigned long count /*,*histogram*/;
+ unsigned long histogram[256];
+ unsigned int k;
+ int width;
+ int x, y, mx, my, sx, sy;
+ int mcx, mcy;
+ unsigned int *s=0, *q;
+
+ if(src.depth() < 32)
+ src.convertDepth(32);
+ QImage dest(src);
+ dest.detach();
+
+ width = getOptimalKernelWidth(radius, 0.5);
+ if(src.width() < width){
+ qWarning("KImageEffect::oilPaintConvolve(): Image is smaller than radius!");
+ return(dest);
+ }
+ /*
+ histogram = (unsigned long *)malloc(256*sizeof(unsigned long));
+ if(!histogram){
+ qWarning("KImageEffect::oilPaintColvolve(): Unable to allocate memory!");
+ return(dest);
+ }
+ */
+ unsigned int **jumpTable = (unsigned int **)src.jumpTable();
+ for(y=0; y < dest.height(); ++y){
+ sy = y-(width/2);
+ q = (unsigned int *)dest.scanLine(y);
+ for(x=0; x < dest.width(); ++x){
+ count = 0;
+ memset(histogram, 0, 256*sizeof(unsigned long));
+ //memset(histogram, 0, 256);
+ sy = y-(width/2);
+ for(mcy=0; mcy < width; ++mcy, ++sy){
+ my = sy < 0 ? 0 : sy > src.height()-1 ?
+ src.height()-1 : sy;
+ sx = x+(-width/2);
+ for(mcx=0; mcx < width; ++mcx, ++sx){
+ mx = sx < 0 ? 0 : sx > src.width()-1 ?
+ src.width()-1 : sx;
+
+ k = intensityValue(jumpTable[my][mx]);
+ if(k > 255){
+ qWarning("KImageEffect::oilPaintConvolve(): k is %d",
+ k);
+ k = 255;
+ }
+ histogram[k]++;
+ if(histogram[k] > count){
+ count = histogram[k];
+ s = jumpTable[my]+mx;
+ }
+ }
+ }
+ if (s)
+ *q++ = (*s);
+ }
+ }
+ /* liberateMemory((histogram); */
+ return(dest);
+}
+
+QImage KImageEffect::charcoal(QImage &src, double /*factor*/)
+{
+ /* binary compat method - remove me when possible! */
+ return(charcoal(src, 0, 1));
+}
+
+QImage KImageEffect::charcoal(QImage &src, double radius, double sigma)
+{
+ QImage img(edge(src, radius));
+ img = blur(img, radius, sigma);
+ normalize(img);
+ img.invertPixels(false);
+ KImageEffect::toGray(img);
+ return(img);
+}
+
+void KImageEffect::normalize(QImage &image)
+{
+ struct double_packet high, low, intensity, *histogram;
+ struct short_packet *normalize_map;
+ Q_INT64 number_pixels;
+ int x, y;
+ unsigned int *p, *q;
+ register long i;
+ unsigned long threshold_intensity;
+ unsigned char r, g, b, a;
+
+ if(image.depth() < 32) // result will always be 32bpp
+ image = image.convertDepth(32);
+
+ histogram = (struct double_packet *)
+ malloc(256*sizeof(struct double_packet));
+ normalize_map = (struct short_packet *)
+ malloc(256*sizeof(struct short_packet));
+
+ if(!histogram || !normalize_map){
+ if(histogram)
+ liberateMemory(&histogram);
+ if(normalize_map)
+ liberateMemory(&normalize_map);
+ qWarning("KImageEffect::normalize(): Unable to allocate memory!");
+ return;
+ }
+
+ /*
+ Form histogram.
+ */
+ memset(histogram, 0, 256*sizeof(struct double_packet));
+ for(y=0; y < image.height(); ++y){
+ p = (unsigned int *)image.scanLine(y);
+ for(x=0; x < image.width(); ++x){
+ histogram[(unsigned char)(qRed(*p))].red++;
+ histogram[(unsigned char)(qGreen(*p))].green++;
+ histogram[(unsigned char)(qBlue(*p))].blue++;
+ histogram[(unsigned char)(qAlpha(*p))].alpha++;
+ p++;
+ }
+ }
+
+ /*
+ Find the histogram boundaries by locating the 0.1 percent levels.
+ */
+ number_pixels = (Q_INT64)image.width()*image.height();
+ threshold_intensity = number_pixels/1000;
+
+ /* red */
+ memset(&intensity, 0, sizeof(struct double_packet));
+ memset(&high, 0, sizeof(struct double_packet));
+ memset(&low, 0, sizeof(struct double_packet));
+ for(high.red=255; high.red != 0; high.red--){
+ intensity.red+=histogram[(unsigned char)high.red].red;
+ if(intensity.red > threshold_intensity)
+ break;
+ }
+ if(low.red == high.red){
+ threshold_intensity = 0;
+ memset(&intensity, 0, sizeof(struct double_packet));
+ for(low.red=0; low.red < 255; low.red++){
+ intensity.red+=histogram[(unsigned char)low.red].red;
+ if(intensity.red > threshold_intensity)
+ break;
+ }
+ memset(&intensity, 0, sizeof(struct double_packet));
+ for(high.red=255; high.red != 0; high.red--){
+ intensity.red+=histogram[(unsigned char)high.red].red;
+ if(intensity.red > threshold_intensity)
+ break;
+ }
+ }
+
+ /* green */
+ memset(&intensity, 0, sizeof(struct double_packet));
+ for(high.green=255; high.green != 0; high.green--){
+ intensity.green+=histogram[(unsigned char)high.green].green;
+ if(intensity.green > threshold_intensity)
+ break;
+ }
+ if(low.green == high.green){
+ threshold_intensity = 0;
+ memset(&intensity, 0, sizeof(struct double_packet));
+ for(low.green=0; low.green < 255; low.green++){
+ intensity.green+=histogram[(unsigned char)low.green].green;
+ if(intensity.green > threshold_intensity)
+ break;
+ }
+ memset(&intensity,0,sizeof(struct double_packet));
+ for(high.green=255; high.green != 0; high.green--){
+ intensity.green+=histogram[(unsigned char)high.green].green;
+ if(intensity.green > threshold_intensity)
+ break;
+ }
+ }
+
+ /* blue */
+ memset(&intensity, 0, sizeof(struct double_packet));
+ for(high.blue=255; high.blue != 0; high.blue--){
+ intensity.blue+=histogram[(unsigned char)high.blue].blue;
+ if(intensity.blue > threshold_intensity)
+ break;
+ }
+ if(low.blue == high.blue){
+ threshold_intensity = 0;
+ memset(&intensity, 0, sizeof(struct double_packet));
+ for(low.blue=0; low.blue < 255; low.blue++){
+ intensity.blue+=histogram[(unsigned char)low.blue].blue;
+ if(intensity.blue > threshold_intensity)
+ break;
+ }
+ memset(&intensity,0,sizeof(struct double_packet));
+ for(high.blue=255; high.blue != 0; high.blue--){
+ intensity.blue+=histogram[(unsigned char)high.blue].blue;
+ if(intensity.blue > threshold_intensity)
+ break;
+ }
+ }
+
+ /* alpha */
+ memset(&intensity, 0, sizeof(struct double_packet));
+ for(high.alpha=255; high.alpha != 0; high.alpha--){
+ intensity.alpha+=histogram[(unsigned char)high.alpha].alpha;
+ if(intensity.alpha > threshold_intensity)
+ break;
+ }
+ if(low.alpha == high.alpha){
+ threshold_intensity = 0;
+ memset(&intensity, 0, sizeof(struct double_packet));
+ for(low.alpha=0; low.alpha < 255; low.alpha++){
+ intensity.alpha+=histogram[(unsigned char)low.alpha].alpha;
+ if(intensity.alpha > threshold_intensity)
+ break;
+ }
+ memset(&intensity,0,sizeof(struct double_packet));
+ for(high.alpha=255; high.alpha != 0; high.alpha--){
+ intensity.alpha+=histogram[(unsigned char)high.alpha].alpha;
+ if(intensity.alpha > threshold_intensity)
+ break;
+ }
+ }
+ liberateMemory(&histogram);
+
+ /*
+ Stretch the histogram to create the normalized image mapping.
+ */
+
+ // should the maxes be 65535?
+ memset(normalize_map, 0 ,256*sizeof(struct short_packet));
+ for(i=0; i <= (long) 255; i++){
+ if(i < (long) low.red)
+ normalize_map[i].red=0;
+ else if (i > (long) high.red)
+ normalize_map[i].red=65535;
+ else if (low.red != high.red)
+ normalize_map[i].red =
+ (unsigned short)((65535*(i-low.red))/(high.red-low.red));
+
+ if(i < (long) low.green)
+ normalize_map[i].green=0;
+ else if (i > (long) high.green)
+ normalize_map[i].green=65535;
+ else if (low.green != high.green)
+ normalize_map[i].green =
+ (unsigned short)((65535*(i-low.green))/(high.green-low.green));
+
+ if(i < (long) low.blue)
+ normalize_map[i].blue=0;
+ else if (i > (long) high.blue)
+ normalize_map[i].blue=65535;
+ else if (low.blue != high.blue)
+ normalize_map[i].blue =
+ (unsigned short)((65535*(i-low.blue))/(high.blue-low.blue));
+
+ if(i < (long) low.alpha)
+ normalize_map[i].alpha=0;
+ else if (i > (long) high.alpha)
+ normalize_map[i].alpha=65535;
+ else if (low.alpha != high.alpha)
+ normalize_map[i].alpha =
+ (unsigned short)((65535*(i-low.alpha))/(high.alpha-low.alpha));
+
+ }
+
+ for(y=0; y < image.height(); ++y){
+ q = (unsigned int *)image.scanLine(y);
+ for(x=0; x < image.width(); ++x){
+ if(low.red != high.red)
+ r = (normalize_map[(unsigned short)(qRed(q[x]))].red)/257;
+ else
+ r = qRed(q[x]);
+ if(low.green != high.green)
+ g = (normalize_map[(unsigned short)(qGreen(q[x]))].green)/257;
+ else
+ g = qGreen(q[x]);
+ if(low.blue != high.blue)
+ b = (normalize_map[(unsigned short)(qBlue(q[x]))].blue)/257;
+ else
+ b = qBlue(q[x]);
+ if(low.alpha != high.alpha)
+ a = (normalize_map[(unsigned short)(qAlpha(q[x]))].alpha)/257;
+ else
+ a = qAlpha(q[x]);
+ q[x] = qRgba(r, g, b, a);
+ }
+ }
+ liberateMemory(&normalize_map);
+}
+
+void KImageEffect::equalize(QImage &image)
+{
+ struct double_packet high, low, intensity, *map, *histogram;
+ struct short_packet *equalize_map;
+ int x, y;
+ unsigned int *p, *q;
+ long i;
+ unsigned char r, g, b, a;
+
+ if(image.depth() < 32) // result will always be 32bpp
+ image = image.convertDepth(32);
+
+ histogram=(struct double_packet *) malloc(256*sizeof(struct double_packet));
+ map=(struct double_packet *) malloc(256*sizeof(struct double_packet));
+ equalize_map=(struct short_packet *)malloc(256*sizeof(struct short_packet));
+ if(!histogram || !map || !equalize_map){
+ if(histogram)
+ liberateMemory(&histogram);
+ if(map)
+ liberateMemory(&map);
+ if(equalize_map)
+ liberateMemory(&equalize_map);
+ qWarning("KImageEffect::equalize(): Unable to allocate memory!");
+ return;
+ }
+
+ /*
+ Form histogram.
+ */
+ memset(histogram, 0, 256*sizeof(struct double_packet));
+ for(y=0; y < image.height(); ++y){
+ p = (unsigned int *)image.scanLine(y);
+ for(x=0; x < image.width(); ++x){
+ histogram[(unsigned char)(qRed(*p))].red++;
+ histogram[(unsigned char)(qGreen(*p))].green++;
+ histogram[(unsigned char)(qBlue(*p))].blue++;
+ histogram[(unsigned char)(qAlpha(*p))].alpha++;
+ p++;
+ }
+ }
+ /*
+ Integrate the histogram to get the equalization map.
+ */
+ memset(&intensity, 0 ,sizeof(struct double_packet));
+ for(i=0; i <= 255; ++i){
+ intensity.red += histogram[i].red;
+ intensity.green += histogram[i].green;
+ intensity.blue += histogram[i].blue;
+ intensity.alpha += histogram[i].alpha;
+ map[i]=intensity;
+ }
+ low=map[0];
+ high=map[255];
+ memset(equalize_map, 0, 256*sizeof(short_packet));
+ for(i=0; i <= 255; ++i){
+ if(high.red != low.red)
+ equalize_map[i].red=(unsigned short)
+ ((65535*(map[i].red-low.red))/(high.red-low.red));
+ if(high.green != low.green)
+ equalize_map[i].green=(unsigned short)
+ ((65535*(map[i].green-low.green))/(high.green-low.green));
+ if(high.blue != low.blue)
+ equalize_map[i].blue=(unsigned short)
+ ((65535*(map[i].blue-low.blue))/(high.blue-low.blue));
+ if(high.alpha != low.alpha)
+ equalize_map[i].alpha=(unsigned short)
+ ((65535*(map[i].alpha-low.alpha))/(high.alpha-low.alpha));
+ }
+ liberateMemory(&histogram);
+ liberateMemory(&map);
+
+ /*
+ Stretch the histogram.
+ */
+ for(y=0; y < image.height(); ++y){
+ q = (unsigned int *)image.scanLine(y);
+ for(x=0; x < image.width(); ++x){
+ if(low.red != high.red)
+ r = (equalize_map[(unsigned short)(qRed(q[x]))].red/257);
+ else
+ r = qRed(q[x]);
+ if(low.green != high.green)
+ g = (equalize_map[(unsigned short)(qGreen(q[x]))].green/257);
+ else
+ g = qGreen(q[x]);
+ if(low.blue != high.blue)
+ b = (equalize_map[(unsigned short)(qBlue(q[x]))].blue/257);
+ else
+ b = qBlue(q[x]);
+ if(low.alpha != high.alpha)
+ a = (equalize_map[(unsigned short)(qAlpha(q[x]))].alpha/257);
+ else
+ a = qAlpha(q[x]);
+ q[x] = qRgba(r, g, b, a);
+ }
+ }
+ liberateMemory(&equalize_map);
+
+}
+
+QImage KImageEffect::edge(QImage &image, double radius)
+{
+ double *kernel;
+ int width;
+ register long i;
+ QImage dest;
+
+ if(radius == 50.0){
+ /* For binary compatability! Remove me when possible! This used to
+ * take a different parameter, a factor, and this was the default
+ * value */
+ radius = 0.0;
+ }
+
+ width = getOptimalKernelWidth(radius, 0.5);
+ if(image.width() < width || image.height() < width){
+ qWarning("KImageEffect::edge(): Image is smaller than radius!");
+ return(dest);
+ }
+ kernel= (double *)malloc(width*width*sizeof(double));
+ if(!kernel){
+ qWarning("KImageEffect::edge(): Unable to allocate memory!");
+ return(dest);
+ }
+ for(i=0; i < (width*width); i++)
+ kernel[i]=(-1.0);
+ kernel[i/2]=width*width-1.0;
+ convolveImage(&image, &dest, width, kernel);
+ free(kernel);
+ return(dest);
+}
+
+QImage KImageEffect::emboss(QImage &src)
+{
+ /* binary compat method - remove me when possible! */
+ return(emboss(src, 0, 1));
+}
+
+QImage KImageEffect::emboss(QImage &image, double radius, double sigma)
+{
+ double alpha, *kernel;
+ int j, width;
+ register long i, u, v;
+ QImage dest;
+
+ if(sigma == 0.0){
+ qWarning("KImageEffect::emboss(): Zero sigma is not permitted!");
+ return(dest);
+ }
+
+ width = getOptimalKernelWidth(radius, sigma);
+ if(image.width() < width || image.height() < width){
+ qWarning("KImageEffect::emboss(): Image is smaller than radius!");
+ return(dest);
+ }
+ kernel= (double *)malloc(width*width*sizeof(double));
+ if(!kernel){
+ qWarning("KImageEffect::emboss(): Unable to allocate memory!");
+ return(dest);
+ }
+ if(image.depth() < 32)
+ image = image.convertDepth(32);
+
+ i=0;
+ j=width/2;
+ for(v=(-width/2); v <= (width/2); v++){
+ for(u=(-width/2); u <= (width/2); u++){
+ alpha=exp(-((double) u*u+v*v)/(2.0*sigma*sigma));
+ kernel[i]=((u < 0) || (v < 0) ? -8.0 : 8.0)*alpha/
+ (2.0*MagickPI*sigma*sigma);
+ if (u == j)
+ kernel[i]=0.0;
+ i++;
+ }
+ j--;
+ }
+ convolveImage(&image, &dest, width, kernel);
+ liberateMemory(&kernel);
+
+ equalize(dest);
+ return(dest);
+}
+
+void KImageEffect::blurScanLine(double *kernel, int width,
+ unsigned int *src, unsigned int *dest,
+ int columns)
+{
+ register double *p;
+ unsigned int *q;
+ register int x;
+ register long i;
+ double red, green, blue, alpha;
+ double scale = 0.0;
+
+ if(width > columns){
+ for(x=0; x < columns; ++x){
+ scale = 0.0;
+ red = blue = green = alpha = 0.0;
+ p = kernel;
+ q = src;
+ for(i=0; i < columns; ++i){
+ if((i >= (x-width/2)) && (i <= (x+width/2))){
+ red += (*p)*(qRed(*q)*257);
+ green += (*p)*(qGreen(*q)*257);
+ blue += (*p)*(qBlue(*q)*257);
+ alpha += (*p)*(qAlpha(*q)*257);
+ }
+ if(((i+width/2-x) >= 0) && ((i+width/2-x) < width))
+ scale+=kernel[i+width/2-x];
+ p++;
+ q++;
+ }
+ scale = 1.0/scale;
+ red = scale*(red+0.5);
+ green = scale*(green+0.5);
+ blue = scale*(blue+0.5);
+ alpha = scale*(alpha+0.5);
+
+ red = red < 0 ? 0 : red > 65535 ? 65535 : red;
+ green = green < 0 ? 0 : green > 65535 ? 65535 : green;
+ blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue;
+ alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha;
+
+ dest[x] = qRgba((unsigned char)(red/257UL),
+ (unsigned char)(green/257UL),
+ (unsigned char)(blue/257UL),
+ (unsigned char)(alpha/257UL));
+ }
+ return;
+ }
+
+ for(x=0; x < width/2; ++x){
+ scale = 0.0;
+ red = blue = green = alpha = 0.0;
+ p = kernel+width/2-x;
+ q = src;
+ for(i=width/2-x; i < width; ++i){
+ red += (*p)*(qRed(*q)*257);
+ green += (*p)*(qGreen(*q)*257);
+ blue += (*p)*(qBlue(*q)*257);
+ alpha += (*p)*(qAlpha(*q)*257);
+ scale += (*p);
+ p++;
+ q++;
+ }
+ scale=1.0/scale;
+
+ red = scale*(red+0.5);
+ green = scale*(green+0.5);
+ blue = scale*(blue+0.5);
+ alpha = scale*(alpha+0.5);
+
+ red = red < 0 ? 0 : red > 65535 ? 65535 : red;
+ green = green < 0 ? 0 : green > 65535 ? 65535 : green;
+ blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue;
+ alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha;
+
+ dest[x] = qRgba((unsigned char)(red/257UL),
+ (unsigned char)(green/257UL),
+ (unsigned char)(blue/257UL),
+ (unsigned char)(alpha/257UL));
+ }
+
+ for(; x < columns-width/2; ++x){
+ red = blue = green = alpha = 0.0;
+ p = kernel;
+ q = src+(x-width/2);
+ for (i=0; i < (long) width; ++i){
+ red += (*p)*(qRed(*q)*257);
+ green += (*p)*(qGreen(*q)*257);
+ blue += (*p)*(qBlue(*q)*257);
+ alpha += (*p)*(qAlpha(*q)*257);
+ p++;
+ q++;
+ }
+ red = scale*(red+0.5);
+ green = scale*(green+0.5);
+ blue = scale*(blue+0.5);
+ alpha = scale*(alpha+0.5);
+
+ red = red < 0 ? 0 : red > 65535 ? 65535 : red;
+ green = green < 0 ? 0 : green > 65535 ? 65535 : green;
+ blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue;
+ alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha;
+
+ dest[x] = qRgba((unsigned char)(red/257UL),
+ (unsigned char)(green/257UL),
+ (unsigned char)(blue/257UL),
+ (unsigned char)(alpha/257UL));
+ }
+
+ for(; x < columns; ++x){
+ red = blue = green = alpha = 0.0;
+ scale=0;
+ p = kernel;
+ q = src+(x-width/2);
+ for(i=0; i < columns-x+width/2; ++i){
+ red += (*p)*(qRed(*q)*257);
+ green += (*p)*(qGreen(*q)*257);
+ blue += (*p)*(qBlue(*q)*257);
+ alpha += (*p)*(qAlpha(*q)*257);
+ scale += (*p);
+ p++;
+ q++;
+ }
+ scale=1.0/scale;
+ red = scale*(red+0.5);
+ green = scale*(green+0.5);
+ blue = scale*(blue+0.5);
+ alpha = scale*(alpha+0.5);
+
+ red = red < 0 ? 0 : red > 65535 ? 65535 : red;
+ green = green < 0 ? 0 : green > 65535 ? 65535 : green;
+ blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue;
+ alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha;
+
+ dest[x] = qRgba((unsigned char)(red/257UL),
+ (unsigned char)(green/257UL),
+ (unsigned char)(blue/257UL),
+ (unsigned char)(alpha/257UL));
+ }
+}
+
+int KImageEffect::getBlurKernel(int width, double sigma, double **kernel)
+{
+#define KernelRank 3
+ double alpha, normalize;
+ register long i;
+ int bias;
+
+ assert(sigma != 0.0);
+ if(width == 0)
+ width = 3;
+ *kernel=(double *)malloc(width*sizeof(double));
+ if(*kernel == (double *)NULL)
+ return(0);
+ memset(*kernel, 0, width*sizeof(double));
+ bias = KernelRank*width/2;
+ for(i=(-bias); i <= bias; i++){
+ alpha=exp(-((double) i*i)/(2.0*KernelRank*KernelRank*sigma*sigma));
+ (*kernel)[(i+bias)/KernelRank]+=alpha/(MagickSQ2PI*sigma);
+ }
+ normalize=0;
+ for(i=0; i < width; i++)
+ normalize+=(*kernel)[i];
+ for(i=0; i < width; i++)
+ (*kernel)[i]/=normalize;
+
+ return(width);
+}
+
+QImage KImageEffect::blur(QImage &src, double /*factor*/)
+{
+ /* binary compat method - remove me when possible! */
+ return(blur(src, 0, 1));
+}
+
+QImage KImageEffect::blur(QImage &src, double radius, double sigma)
+{
+ double *kernel;
+ QImage dest;
+ int width;
+ int x, y;
+ unsigned int *scanline, *temp;
+ unsigned int *p, *q;
+
+ if(sigma == 0.0){
+ qWarning("KImageEffect::blur(): Zero sigma is not permitted!");
+ return(dest);
+ }
+ if(src.depth() < 32)
+ src = src.convertDepth(32);
+
+ kernel=(double *) NULL;
+ if(radius > 0)
+ width=getBlurKernel((int) (2*ceil(radius)+1),sigma,&kernel);
+ else{
+ double *last_kernel;
+ last_kernel=(double *) NULL;
+ width=getBlurKernel(3,sigma,&kernel);
+
+ while ((long) (MaxRGB*kernel[0]) > 0){
+ if(last_kernel != (double *)NULL){
+ liberateMemory(&last_kernel);
+ }
+ last_kernel=kernel;
+ kernel = (double *)NULL;
+ width = getBlurKernel(width+2, sigma, &kernel);
+ }
+ if(last_kernel != (double *) NULL){
+ liberateMemory(&kernel);
+ width-=2;
+ kernel = last_kernel;
+ }
+ }
+
+ if(width < 3){
+ qWarning("KImageEffect::blur(): Kernel radius is too small!");
+ liberateMemory(&kernel);
+ return(dest);
+ }
+
+ dest.create(src.width(), src.height(), 32);
+
+ scanline = (unsigned int *)malloc(sizeof(unsigned int)*src.height());
+ temp = (unsigned int *)malloc(sizeof(unsigned int)*src.height());
+ for(y=0; y < src.height(); ++y){
+ p = (unsigned int *)src.scanLine(y);
+ q = (unsigned int *)dest.scanLine(y);
+ blurScanLine(kernel, width, p, q, src.width());
+ }
+
+ unsigned int **srcTable = (unsigned int **)src.jumpTable();
+ unsigned int **destTable = (unsigned int **)dest.jumpTable();
+ for(x=0; x < src.width(); ++x){
+ for(y=0; y < src.height(); ++y){
+ scanline[y] = srcTable[y][x];
+ }
+ blurScanLine(kernel, width, scanline, temp, src.height());
+ for(y=0; y < src.height(); ++y){
+ destTable[y][x] = temp[y];
+ }
+ }
+ free(scanline);
+ free(temp);
+ free(kernel);
+ return(dest);
+}
+
+bool KImageEffect::convolveImage(QImage *image, QImage *dest,
+ const unsigned int order,
+ const double *kernel)
+{
+ long width;
+ double red, green, blue, alpha;
+ double normalize, *normal_kernel;
+ register const double *k;
+ register unsigned int *q;
+ int x, y, mx, my, sx, sy;
+ long i;
+ int mcx, mcy;
+
+ width = order;
+ if((width % 2) == 0){
+ qWarning("KImageEffect: Kernel width must be an odd number!");
+ return(false);
+ }
+ normal_kernel = (double *)malloc(width*width*sizeof(double));
+ if(!normal_kernel){
+ qWarning("KImageEffect: Unable to allocate memory!");
+ return(false);
+ }
+ dest->reset();
+ dest->create(image->width(), image->height(), 32);
+ if(image->depth() < 32)
+ *image = image->convertDepth(32);
+
+ normalize=0.0;
+ for(i=0; i < (width*width); i++)
+ normalize += kernel[i];
+ if(fabs(normalize) <= MagickEpsilon)
+ normalize=1.0;
+ normalize=1.0/normalize;
+ for(i=0; i < (width*width); i++)
+ normal_kernel[i] = normalize*kernel[i];
+
+ unsigned int **jumpTable = (unsigned int **)image->jumpTable();
+ for(y=0; y < dest->height(); ++y){
+ sy = y-(width/2);
+ q = (unsigned int *)dest->scanLine(y);
+ for(x=0; x < dest->width(); ++x){
+ k = normal_kernel;
+ red = green = blue = alpha = 0;
+ sy = y-(width/2);
+ for(mcy=0; mcy < width; ++mcy, ++sy){
+ my = sy < 0 ? 0 : sy > image->height()-1 ?
+ image->height()-1 : sy;
+ sx = x+(-width/2);
+ for(mcx=0; mcx < width; ++mcx, ++sx){
+ mx = sx < 0 ? 0 : sx > image->width()-1 ?
+ image->width()-1 : sx;
+ red += (*k)*(qRed(jumpTable[my][mx])*257);
+ green += (*k)*(qGreen(jumpTable[my][mx])*257);
+ blue += (*k)*(qBlue(jumpTable[my][mx])*257);
+ alpha += (*k)*(qAlpha(jumpTable[my][mx])*257);
+ ++k;
+ }
+ }
+
+ red = red < 0 ? 0 : red > 65535 ? 65535 : red+0.5;
+ green = green < 0 ? 0 : green > 65535 ? 65535 : green+0.5;
+ blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue+0.5;
+ alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha+0.5;
+
+ *q++ = qRgba((unsigned char)(red/257UL),
+ (unsigned char)(green/257UL),
+ (unsigned char)(blue/257UL),
+ (unsigned char)(alpha/257UL));
+ }
+ }
+ free(normal_kernel);
+ return(true);
+
+}
+
+int KImageEffect::getOptimalKernelWidth(double radius, double sigma)
+{
+ double normalize, value;
+ long width;
+ register long u;
+
+ assert(sigma != 0.0);
+ if(radius > 0.0)
+ return((int)(2.0*ceil(radius)+1.0));
+ for(width=5; ;){
+ normalize=0.0;
+ for(u=(-width/2); u <= (width/2); u++)
+ normalize+=exp(-((double) u*u)/(2.0*sigma*sigma))/(MagickSQ2PI*sigma);
+ u=width/2;
+ value=exp(-((double) u*u)/(2.0*sigma*sigma))/(MagickSQ2PI*sigma)/normalize;
+ if((long)(65535*value) <= 0)
+ break;
+ width+=2;
+ }
+ return((int)width-2);
+}
+
+QImage KImageEffect::sharpen(QImage &src, double /*factor*/)
+{
+ /* binary compat method - remove me when possible! */
+ return(sharpen(src, 0, 1));
+}
+
+QImage KImageEffect::sharpen(QImage &image, double radius, double sigma)
+{
+ double alpha, normalize, *kernel;
+ int width;
+ register long i, u, v;
+ QImage dest;
+
+ if(sigma == 0.0){
+ qWarning("KImageEffect::sharpen(): Zero sigma is not permitted!");
+ return(dest);
+ }
+ width = getOptimalKernelWidth(radius, sigma);
+ if(image.width() < width){
+ qWarning("KImageEffect::sharpen(): Image is smaller than radius!");
+ return(dest);
+ }
+ kernel = (double *)malloc(width*width*sizeof(double));
+ if(!kernel){
+ qWarning("KImageEffect::sharpen(): Unable to allocate memory!");
+ return(dest);
+ }
+
+ i = 0;
+ normalize=0.0;
+ for(v=(-width/2); v <= (width/2); v++){
+ for(u=(-width/2); u <= (width/2); u++){
+ alpha=exp(-((double) u*u+v*v)/(2.0*sigma*sigma));
+ kernel[i]=alpha/(2.0*MagickPI*sigma*sigma);
+ normalize+=kernel[i];
+ i++;
+ }
+ }
+ kernel[i/2]=(-2.0)*normalize;
+ convolveImage(&image, &dest, width, kernel);
+ free(kernel);
+ return(dest);
+}
+
+// End of new algorithms
+
+QImage KImageEffect::shade(QImage &src, bool color_shading, double azimuth,
+ double elevation)
+{
+ struct PointInfo{
+ double x, y, z;
+ };
+
+ double distance, normal_distance, shade;
+ int x, y;
+
+ struct PointInfo light, normal;
+
+ unsigned int *q;
+
+ QImage dest(src.width(), src.height(), 32);
+
+ azimuth = DegreesToRadians(azimuth);
+ elevation = DegreesToRadians(elevation);
+ light.x = MaxRGB*cos(azimuth)*cos(elevation);
+ light.y = MaxRGB*sin(azimuth)*cos(elevation);
+ light.z = MaxRGB*sin(elevation);
+ normal.z= 2*MaxRGB; // constant Z of surface normal
+
+ if(src.depth() > 8){ // DirectClass source image
+ unsigned int *p, *s0, *s1, *s2;
+ for(y=0; y < src.height(); ++y){
+ p = (unsigned int *)src.scanLine(QMIN(QMAX(y-1,0),src.height()-3));
+ q = (unsigned int *)dest.scanLine(y);
+ // shade this row of pixels.
+ *q++=(*(p+src.width()));
+ p++;
+ s0 = p;
+ s1 = p + src.width();
+ s2 = p + 2*src.width();
+ for(x=1; x < src.width()-1; ++x){
+ // determine the surface normal and compute shading.
+ normal.x=intensityValue(*(s0-1))+intensityValue(*(s1-1))+intensityValue(*(s2-1))-
+ (double) intensityValue(*(s0+1))-(double) intensityValue(*(s1+1))-
+ (double) intensityValue(*(s2+1));
+ normal.y=intensityValue(*(s2-1))+intensityValue(*s2)+intensityValue(*(s2+1))-
+ (double) intensityValue(*(s0-1))-(double) intensityValue(*s0)-
+ (double) intensityValue(*(s0+1));
+ if((normal.x == 0) && (normal.y == 0))
+ shade=light.z;
+ else{
+ shade=0.0;
+ distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
+ if (distance > 0.0){
+ normal_distance=
+ normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
+ if(fabs(normal_distance) > 0.0000001)
+ shade=distance/sqrt(normal_distance);
+ }
+ }
+ if(!color_shading){
+ *q = qRgba((unsigned char)(shade),
+ (unsigned char)(shade),
+ (unsigned char)(shade),
+ qAlpha(*s1));
+ }
+ else{
+ *q = qRgba((unsigned char)((shade*qRed(*s1))/(MaxRGB+1)),
+ (unsigned char)((shade*qGreen(*s1))/(MaxRGB+1)),
+ (unsigned char)((shade*qBlue(*s1))/(MaxRGB+1)),
+ qAlpha(*s1));
+ }
+ ++s0;
+ ++s1;
+ ++s2;
+ q++;
+ }
+ *q++=(*s1);
+ }
+ }
+ else{ // PsudeoClass source image
+ unsigned char *p, *s0, *s1, *s2;
+ int scanLineIdx;
+ unsigned int *cTable = (unsigned int *)src.colorTable();
+ for(y=0; y < src.height(); ++y){
+ scanLineIdx = QMIN(QMAX(y-1,0),src.height()-3);
+ p = (unsigned char *)src.scanLine(scanLineIdx);
+ q = (unsigned int *)dest.scanLine(y);
+ // shade this row of pixels.
+ s0 = p;
+ s1 = (unsigned char *) src.scanLine(scanLineIdx+1);
+ s2 = (unsigned char *) src.scanLine(scanLineIdx+2);
+ *q++=(*(cTable+(*s1)));
+ ++p;
+ ++s0;
+ ++s1;
+ ++s2;
+ for(x=1; x < src.width()-1; ++x){
+ // determine the surface normal and compute shading.
+ normal.x=intensityValue(*(cTable+(*(s0-1))))+intensityValue(*(cTable+(*(s1-1))))+intensityValue(*(cTable+(*(s2-1))))-
+ (double) intensityValue(*(cTable+(*(s0+1))))-(double) intensityValue(*(cTable+(*(s1+1))))-
+ (double) intensityValue(*(cTable+(*(s2+1))));
+ normal.y=intensityValue(*(cTable+(*(s2-1))))+intensityValue(*(cTable+(*s2)))+intensityValue(*(cTable+(*(s2+1))))-
+ (double) intensityValue(*(cTable+(*(s0-1))))-(double) intensityValue(*(cTable+(*s0)))-
+ (double) intensityValue(*(cTable+(*(s0+1))));
+ if((normal.x == 0) && (normal.y == 0))
+ shade=light.z;
+ else{
+ shade=0.0;
+ distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
+ if (distance > 0.0){
+ normal_distance=
+ normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
+ if(fabs(normal_distance) > 0.0000001)
+ shade=distance/sqrt(normal_distance);
+ }
+ }
+ if(!color_shading){
+ *q = qRgba((unsigned char)(shade),
+ (unsigned char)(shade),
+ (unsigned char)(shade),
+ qAlpha(*(cTable+(*s1))));
+ }
+ else{
+ *q = qRgba((unsigned char)((shade*qRed(*(cTable+(*s1))))/(MaxRGB+1)),
+ (unsigned char)((shade*qGreen(*(cTable+(*s1))))/(MaxRGB+1)),
+ (unsigned char)((shade*qBlue(*(cTable+(*s1))))/(MaxRGB+1)),
+ qAlpha(*s1));
+ }
+ ++s0;
+ ++s1;
+ ++s2;
+ q++;
+ }
+ *q++=(*(cTable+(*s1)));
+ }
+ }
+ return(dest);
+}
+
+// High quality, expensive HSV contrast. You can do a faster one by just
+// taking a grayscale threshold (ie: 128) and incrementing RGB color
+// channels above it and decrementing those below it, but this gives much
+// better results. (mosfet 12/28/01)
+void KImageEffect::contrastHSV(QImage &img, bool sharpen)
+{
+ int i, sign;
+ unsigned int *data;
+ int count;
+ double brightness, scale, theta;
+ QColor c;
+ int h, s, v;
+
+ sign = sharpen ? 1 : -1;
+ scale=0.5000000000000001;
+ if(img.depth() > 8){
+ count = img.width()*img.height();
+ data = (unsigned int *)img.bits();
+ }
+ else{
+ count = img.numColors();
+ data = (unsigned int *)img.colorTable();
+ }
+ for(i=0; i < count; ++i){
+ c.setRgb(data[i]);
+ c.hsv(&h, &s, &v);
+ brightness = v/255.0;
+ theta=(brightness-0.5)*M_PI;
+ brightness+=scale*(((scale*((sin(theta)+1.0)))-brightness)*sign);
+ if (brightness > 1.0)
+ brightness=1.0;
+ else
+ if (brightness < 0)
+ brightness=0.0;
+ v = (int)(brightness*255);
+ c.setHsv(h, s, v);
+ data[i] = qRgba(c.red(), c.green(), c.blue(), qAlpha(data[i]));
+ }
+}
+
+
+struct BumpmapParams {
+ BumpmapParams( double bm_azimuth, double bm_elevation,
+ int bm_depth, KImageEffect::BumpmapType bm_type,
+ bool invert ) {
+ /* Convert to radians */
+ double azimuth = DegreesToRadians( bm_azimuth );
+ double elevation = DegreesToRadians( bm_elevation );
+
+ /* Calculate the light vector */
+ lx = (int)( cos(azimuth) * cos(elevation) * 255.0 );
+ ly = (int)( sin(azimuth) * cos(elevation) * 255.0 );
+ int lz = (int)( sin(elevation) * 255.0 );
+
+ /* Calculate constant Z component of surface normal */
+ int nz = (6 * 255) / bm_depth;
+ nz2 = nz * nz;
+ nzlz = nz * lz;
+
+ /* Optimize for vertical normals */
+ background = lz;
+
+ /* Calculate darkness compensation factor */
+ compensation = sin(elevation);
+
+ /* Create look-up table for map type */
+ for (int i = 0; i < 256; i++)
+ {
+ double n = 0;
+ switch (bm_type)
+ {
+ case KImageEffect::Spherical:
+ n = i / 255.0 - 1.0;
+ lut[i] = (int) (255.0 * sqrt(1.0 - n * n) + 0.5);
+ break;
+
+ case KImageEffect::Sinuosidal:
+ n = i / 255.0;
+ lut[i] = (int) (255.0 * (sin((-M_PI / 2.0) + M_PI * n) + 1.0) /
+ 2.0 + 0.5);
+ break;
+
+ case KImageEffect::Linear:
+ default:
+ lut[i] = i;
+ }
+
+ if (invert)
+ lut[i] = 255 - lut[i];
+ }
+ }
+ int lx, ly;
+ int nz2, nzlz;
+ int background;
+ double compensation;
+ uchar lut[256];
+};
+
+
+static void bumpmap_convert_row( uint *row,
+ int width,
+ int bpp,
+ int has_alpha,
+ uchar *lut,
+ int waterlevel )
+{
+ uint *p;
+
+ p = row;
+
+ has_alpha = has_alpha ? 1 : 0;
+
+ if (bpp >= 3)
+ for (; width; width--)
+ {
+ if (has_alpha) {
+ unsigned int idx = (unsigned int)(intensityValue( *row ) + 0.5);
+ *p++ = lut[(unsigned int) ( waterlevel +
+ ( ( idx -
+ waterlevel) * qBlue( *row )) / 255.0 )];
+ } else {
+ unsigned int idx = (unsigned int)(intensityValue( *row ) + 0.5);
+ *p++ = lut[idx];
+ }
+
+ ++row;
+ }
+}
+
+static void bumpmap_row( uint *src,
+ uint *dest,
+ int width,
+ int bpp,
+ int has_alpha,
+ uint *bm_row1,
+ uint *bm_row2,
+ uint *bm_row3,
+ int bm_width,
+ int bm_xofs,
+ bool tiled,
+ bool row_in_bumpmap,
+ int ambient,
+ bool compensate,
+ BumpmapParams *params )
+{
+ int xofs1, xofs2, xofs3;
+ int shade;
+ int ndotl;
+ int nx, ny;
+ int x;
+ int tmp;
+
+ tmp = bm_xofs;
+ xofs2 = MOD(tmp, bm_width);
+
+ for (x = 0; x < width; x++)
+ {
+ /* Calculate surface normal from bump map */
+
+ if (tiled || (row_in_bumpmap &&
+ x >= - tmp && x < - tmp + bm_width)) {
+ if (tiled) {
+ xofs1 = MOD(xofs2 - 1, bm_width);
+ xofs3 = MOD(xofs2 + 1, bm_width);
+ } else {
+ xofs1 = FXCLAMP(xofs2 - 1, 0, bm_width - 1);
+ xofs3 = FXCLAMP(xofs2 + 1, 0, bm_width - 1);
+ }
+ nx = (bm_row1[xofs1] + bm_row2[xofs1] + bm_row3[xofs1] -
+ bm_row1[xofs3] - bm_row2[xofs3] - bm_row3[xofs3]);
+ ny = (bm_row3[xofs1] + bm_row3[xofs2] + bm_row3[xofs3] -
+ bm_row1[xofs1] - bm_row1[xofs2] - bm_row1[xofs3]);
+ } else {
+ nx = ny = 0;
+ }
+
+ /* Shade */
+
+ if ((nx == 0) && (ny == 0))
+ shade = params->background;
+ else {
+ ndotl = nx * params->lx + ny * params->ly + params->nzlz;
+
+ if (ndotl < 0)
+ shade = (int)( params->compensation * ambient );
+ else {
+ shade = (int)( ndotl / sqrt(double(nx * nx + ny * ny + params->nz2)) );
+
+ shade = (int)( shade + QMAX(0.0, (255 * params->compensation - shade)) *
+ ambient / 255 );
+ }
+ }
+
+ /* Paint */
+
+ /**
+ * NOTE: if we want to work with non-32bit images the alpha handling would
+ * also change
+ */
+ if (compensate) {
+ int red = (int)((qRed( *src ) * shade) / (params->compensation * 255));
+ int green = (int)((qGreen( *src ) * shade) / (params->compensation * 255));
+ int blue = (int)((qBlue( *src ) * shade) / (params->compensation * 255));
+ int alpha = (int)((qAlpha( *src ) * shade) / (params->compensation * 255));
+ ++src;
+ *dest++ = qRgba( red, green, blue, alpha );
+ } else {
+ int red = qRed( *src ) * shade / 255;
+ int green = qGreen( *src ) * shade / 255;
+ int blue = qBlue( *src ) * shade / 255;
+ int alpha = qAlpha( *src ) * shade / 255;
+ ++src;
+ *dest++ = qRgba( red, green, blue, alpha );
+ }
+
+ /* Next pixel */
+
+ if (++xofs2 == bm_width)
+ xofs2 = 0;
+ }
+}
+
+/**
+ * A bumpmapping algorithm.
+ *
+ * @param img the image you want bumpmap
+ * @param map the map used
+ * @param azimuth azimuth
+ * @param elevation elevation
+ * @param depth depth (not the depth of the image, but of the map)
+ * @param xofs X offset
+ * @param yofs Y offset
+ * @param waterlevel level that full transparency should represent
+ * @param ambient ambient lighting factor
+ * @param compensate compensate for darkening
+ * @param invert invert bumpmap
+ * @param type type of the bumpmap
+ *
+ * @return The destination image (dst) containing the result.
+ * @author Zack Rusin <zack@kde.org>
+ */
+QImage KImageEffect::bumpmap(QImage &img, QImage &map, double azimuth, double elevation,
+ int depth, int xofs, int yofs, int waterlevel,
+ int ambient, bool compensate, bool invert,
+ BumpmapType type, bool tiled)
+{
+ QImage dst;
+
+ if ( img.depth() != 32 || img.depth() != 32 ) {
+ qWarning( "Bump-mapping effect works only with 32 bit images");
+ return dst;
+ }
+
+ dst.create( img.width(), img.height(), img.depth() );
+ int bm_width = map.width();
+ int bm_height = map.height();
+ int bm_bpp = map.depth();
+ int bm_has_alpha = map.hasAlphaBuffer();
+
+ int yofs1, yofs2, yofs3;
+
+ if ( tiled ) {
+ yofs2 = MOD( yofs, bm_height );
+ yofs1 = MOD( yofs2 - 1, bm_height);
+ yofs3 = MOD( yofs2 + 1, bm_height);
+ } else {
+ yofs1 = 0;
+ yofs2 = 0;
+ yofs3 = FXCLAMP( yofs2+1, 0, bm_height - 1 );
+ }
+
+ BumpmapParams params( azimuth, elevation, depth, type, invert );
+
+ uint* bm_row1 = (unsigned int*)map.scanLine( yofs1 );
+ uint* bm_row2 = (unsigned int*)map.scanLine( yofs2 );
+ uint* bm_row3 = (unsigned int*)map.scanLine( yofs3 );
+
+ bumpmap_convert_row( bm_row1, bm_width, bm_bpp, bm_has_alpha, params.lut, waterlevel );
+ bumpmap_convert_row( bm_row2, bm_width, bm_bpp, bm_has_alpha, params.lut, waterlevel );
+ bumpmap_convert_row( bm_row3, bm_width, bm_bpp, bm_has_alpha, params.lut, waterlevel );
+
+ for (int y = 0; y < img.height(); ++y)
+ {
+ int row_in_bumpmap = (y >= - yofs && y < - yofs + bm_height);
+
+ uint* src_row = (unsigned int*)img.scanLine( y );
+ uint* dest_row = (unsigned int*)dst.scanLine( y );
+
+ bumpmap_row( src_row, dest_row, img.width(), img.depth(), img.hasAlphaBuffer(),
+ bm_row1, bm_row2, bm_row3, bm_width, xofs,
+ tiled,
+ row_in_bumpmap, ambient, compensate,
+ &params );
+
+ /* Next line */
+
+ if (tiled || row_in_bumpmap)
+ {
+ uint* bm_tmprow = bm_row1;
+ bm_row1 = bm_row2;
+ bm_row2 = bm_row3;
+ bm_row3 = bm_tmprow;
+
+ if (++yofs2 == bm_height)
+ yofs2 = 0;
+
+ if (tiled)
+ yofs3 = MOD(yofs2 + 1, bm_height);
+ else
+ yofs3 = FXCLAMP(yofs2 + 1, 0, bm_height - 1);
+
+ bm_row3 = (unsigned int*)map.scanLine( yofs3 );
+ bumpmap_convert_row( bm_row3, bm_width, bm_bpp, bm_has_alpha,
+ params.lut, waterlevel );
+ }
+ }
+ return dst;
+}
diff --git a/kdefx/kimageeffect.h b/kdefx/kimageeffect.h
new file mode 100644
index 000000000..7bca73820
--- /dev/null
+++ b/kdefx/kimageeffect.h
@@ -0,0 +1,807 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 1998, 1999, 2001, 2002 Daniel M. Duley <mosfet@interaccess.com>
+ (C) 1998, 1999 Christian Tibirna <ctibirna@total.net>
+ (C) 1998, 1999 Dirk Mueller <mueller@kde.org>
+
+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.
+
+*/
+
+// $Id$
+
+#ifndef __KIMAGE_EFFECT_H
+#define __KIMAGE_EFFECT_H
+
+#include <kdelibs_export.h>
+
+class QImage;
+class QSize;
+class QColor;
+class QPoint;
+class QRect;
+
+/**
+ * This class includes various QImage based graphical effects.
+ *
+ * Everything is
+ * static, so there is no need to create an instance of this class. You can
+ * just call the static methods. They are encapsulated here merely to provide
+ * a common namespace.
+ */
+class KDEFX_EXPORT KImageEffect
+{
+public:
+ /**
+ * This enum provides a gradient type specification
+ * @see KImageEffect::blend(), KImageEffect::gradient(),
+ * KImageEffect::unbalancedGradient()
+ */
+ enum GradientType { VerticalGradient,
+ HorizontalGradient,
+ DiagonalGradient,
+ CrossDiagonalGradient,
+ PyramidGradient,
+ RectangleGradient,
+ PipeCrossGradient,
+ EllipticGradient
+ };
+
+ /**
+ * This enum provides a RGB channel specification
+ * @see KImageEffect::blend(), KImageEffect::channelIntensity(),
+ * KImageEffect::modulate()
+ */
+ enum RGBComponent { Red, //!< Red channel
+ Green, //!< Green channel
+ Blue, //!< Blue channel
+ Gray, //!< Grey channel
+ All //!< All channels
+ };
+
+ /**
+ * This enum provides a lighting direction specification
+ * @see KImageEffect::hash()
+ */
+ enum Lighting {NorthLite, //!< Lighting from the top of the image
+ NWLite, //!< Lighting from the top left of the image
+ WestLite, //!< Lighting from the left of the image
+ SWLite, //!< Lighting from the bottom left of the image
+ SouthLite, //!< Lighting from the bottom of the image
+ SELite, //!< Lighting from the bottom right of the image
+ EastLite, //!< Lighting from the right of the image
+ NELite //!< Lighting from the top right of the image
+ };
+
+ /**
+ * This enum provides a modulation type specification
+ * @see KImageEffect::modulate()
+ */
+ enum ModulationType { Intensity, //!< Modulate image intensity
+ Saturation, //!< Modulate image saturation
+ HueShift, //!< Modulate image hue
+ Contrast //!< Modulate image contrast
+ };
+
+ /**
+ * This enum provides a noise type specification
+ * @see KImageEffect::addNoise()
+ */
+ enum NoiseType { UniformNoise=0, //!< Uniform distribution
+ GaussianNoise, //!< Gaussian distribution
+ MultiplicativeGaussianNoise, //!< Multiplicative Gaussian distribution
+ ImpulseNoise, //!< Impulse distribution
+ LaplacianNoise, //!< Laplacian distribution
+ PoissonNoise //!< Poisson distribution
+ };
+
+ /**
+ * This enum provides a rotation specification.
+ * @see KImageEffect::rotate()
+ */
+ enum RotateDirection{ Rotate90, //!< Rotate 90 degrees to the right.
+ Rotate180, //!< Rotate 180 degrees.
+ Rotate270 //!< Rotate 90 degrees to the left.
+ };
+
+ /**
+ * This enum lists possible bumpmapping implementations.
+ * @see KImageEffect::bumpmap()
+ */
+ enum BumpmapType {
+ Linear,
+ Spherical,
+ Sinuosidal
+ };
+
+ /**
+ * Create a gradient from color a to color b of the specified type.
+ *
+ * @param size The desired size of the gradient.
+ * @param ca Color a
+ * @param cb Color b
+ * @param type The type of gradient.
+ * @param ncols The number of colors to use when not running on a
+ * truecolor display. The gradient will be dithered to this number of
+ * colors. Pass 0 to prevent dithering.
+ */
+ static QImage gradient(const QSize &size, const QColor &ca,
+ const QColor &cb, GradientType type, int ncols=3);
+
+ /**
+ * Create an unbalanced gradient.
+ *
+ * An unbalanced gradient is a gradient where the transition from
+ * color a to color b is not linear, but in this case, exponential.
+ *
+ * @param size The desired size of the gradient.
+ * @param ca Color a
+ * @param cb Color b
+ * @param type The type of gradient.
+ * @param xfactor The x decay length. Use a value between -200 and 200.
+ * @param yfactor The y decay length.
+ * @param ncols The number of colors. See KImageEffect:gradient.
+ */
+ static QImage unbalancedGradient(const QSize &size, const QColor &ca,
+ const QColor &cb, GradientType type, int xfactor = 100,
+ int yfactor = 100, int ncols = 3);
+
+ /**
+ * Blends a color into the destination image, using an opacity
+ * value for blending one into another. Very fast direct pixel
+ * manipulation is used.
+ *
+ * This function uses MMX and SSE2 instructions to blend the
+ * image on processors that support it.
+ *
+ * @param clr source color to be blended into the destination image.
+ * @param dst destination image in which the source will be blended into.
+ * @param opacity opacity (between 0.0 and 1.0) which determines how much
+ * the source color will be blended into the destination image.
+ * @return The destination image (dst) containing the result.
+ * @author Karol Szwed (gallium@kde.org)
+ * @author Fredrik H&ouml;glund (fredrik@kde.org)
+ */
+ static QImage& blend(const QColor& clr, QImage& dst, float opacity);
+
+ /**
+ * Blend the src image into the destination image, using an opacity
+ * value for blending one into another. Very fast direct pixel
+ * manipulation is used.
+ *
+ * This function uses MMX and SSE2 instructions to blend the
+ * images on processors that support it.
+ *
+ * @param src source image to be blended into the destination image.
+ * @param dst destination image in which the source will be blended into.
+ * @param opacity opacity (between 0.0 and 1.0) which determines how much
+ * the source image will be blended into the destination image.
+ * @return The destination image (dst) containing the result.
+ * @author Karol Szwed (gallium@kde.org)
+ * @author Fredrik H&ouml;glund (fredrik@kde.org)
+ */
+ static QImage& blend(QImage& src, QImage& dst, float opacity);
+
+ /**
+ * Blend the provided image into a background of the indicated color.
+ *
+ * @param initial_intensity this parameter takes values from -1 to 1:
+ * a) if positive: how much to fade the image in its
+ * less affected spot
+ * b) if negative: roughly indicates how much of the image
+ * remains unaffected
+ * @param bgnd indicates the color of the background to blend in
+ * @param eff lets you choose what kind of blending you like
+ * @param anti_dir blend in the opposite direction (makes no much sense
+ * with concentric blending effects)
+ * @param image must be 32bpp
+ */
+ static QImage& blend(QImage &image, float initial_intensity,
+ const QColor &bgnd, GradientType eff,
+ bool anti_dir=false);
+
+ /**
+ * Blend an image into another one, using a gradient type
+ * for blending from one to another.
+ *
+ * @param image1 source1 and result of blending
+ * @param image2 source2 of blending
+ * @param gt gradient type for blending between source1 and source2
+ * @param xf x decay length for unbalanced gradient tpye
+ * @param yf y decay length for unbalanced gradient tpye
+ */
+ static QImage& blend(QImage &image1,QImage &image2,
+ GradientType gt, int xf=100, int yf=100);
+
+ /**
+ * Blend an image into another one, using a color channel of a
+ * third image for the decision of blending from one to another.
+ *
+ * @param image1 Source 1 and result of blending
+ * @param image2 Source 2 of blending
+ * @param blendImage If the gray value of of pixel is 0, the result
+ * for this pixel is that of image1; for a gray value
+ * of 1, the pixel of image2 is used; for a value
+ * in between, a corresponding blending is used.
+ * @param channel The RBG channel to use for the blending decision.
+ */
+ static QImage& blend(QImage &image1, QImage &image2,
+ QImage &blendImage, RGBComponent channel);
+
+ /**
+ * Blend an image into another one, using alpha in the expected way.
+ * @param upper the "upper" image
+ * @param lower the "lower" image
+ * @param output the target image
+ * @author Rik Hemsley (rikkus) <rik@kde.org>
+ */
+ static bool blend(const QImage & upper, const QImage & lower, QImage & output);
+// Not yet... static bool blend(const QImage & image1, const QImage & image2, QImage & output, const QRect & destRect);
+
+ /**
+ * Blend an image into another one, using alpha in the expected way and
+ * over coordinates @p x and @p y with respect to the lower image.
+ * The output is a QImage which is the @p upper image already blended
+ * with the @p lower one, so its size will be (in general) the same than
+ * @p upper instead of the same size than @p lower like the method above.
+ * In fact, the size of @p output is like upper's one only when it can be
+ * painted on lower, if there has to be some clipping, output's size will
+ * be the clipped area and x and y will be set to the correct up-left corner
+ * where the clipped rectangle begins.
+ * @param x x-coordinate of lower image
+ * @param y y-coordinate of lower image
+ * @param upper the "upper" image
+ * @param lower the "lower" image
+ * @param output the target image
+ */
+ static bool blend(int &x, int &y, const QImage & upper, const QImage & lower, QImage & output);
+
+ /**
+ * Blend an image into another one, using alpha in the expected way and
+ * over coordinates @p x and @p y with respect to the lower image.
+ * The output is painted in the own @p lower image. This is an optimization
+ * of the blend method above provided by convenience.
+ * @param x x-coordinate of lower image
+ * @param y y-coordinate of lower image
+ * @param upper the "upper" image
+ * @param lower the "lower" image, which becomes the output image
+ */
+ static bool blendOnLower(int x, int y, const QImage & upper, const QImage & lower);
+
+ /**
+ * Blend part of an image into part of another, using the alpha channel in
+ * the expected way.
+ * Note that the destination rectangle will be correctly clipped.
+ *
+ * @param upper the "upper" image
+ * @param upperOffset Offset for the part of the upper image to be used.
+ * @param lower the "lower" image
+ * @param lowerRect Rectangle for the part of the lower image where the
+ * blending will occur.
+ * @since 3.2
+ */
+ static void blendOnLower(const QImage &upper, const QPoint &upperOffset,
+ QImage &lower, const QRect &lowerRect);
+
+ /**
+ * Blend part of an image into part of another, using the opacity value
+ * and the alpha channel in the expected way.
+ * Note that the destination rectangle will be correctly clipped.
+ *
+ * @param upper the "upper" image
+ * @param upperOffset Offset for the part of the upper image to be used.
+ * @param lower the "lower" image
+ * @param lowerRect Rectangle for the part of the lower image where the
+ * blending will occur.
+ * @param opacity Opacity (between 0.0 and 1.0) which determines how much
+ * the source image will be blended into the destination image.
+ * @since 3.2
+ */
+ static void blendOnLower(const QImage &upper, const QPoint &upperOffset,
+ QImage &lower, const QRect &lowerRect, float opacity);
+
+ /**
+ * Disposition of a source image on top of a destination image.
+ * @see KImageEffect::computeDestinationRect, KImageEffect::blendOnLower
+ * @since 3.2
+ */
+ enum Disposition { NoImage = 0, //!< Don't overlay
+ Centered, //!< Center top image on botton image
+ Tiled, //!< Tile top image on bottom image
+ CenterTiled, //!< Center and tile top image on bottom image
+ CenteredMaxpect, //!< Center and scale aspect
+ TiledMaxpect, //!< Tile and scale aspect
+ Scaled, //!< Scale
+ CenteredAutoFit //!< Center and scale or scale aspect
+ };
+
+ /**
+ * Compute the destination rectangle where to draw the upper image on top
+ * of another image using the given disposition. For tiled
+ * disposition, the rectangle should be duplicated on the whole area to
+ * obtained the wanted effect.
+ *
+ * @param lowerSize The size of the destination image.
+ * @param disposition The wanted disposition.
+ * @param upper The upper image. Note that this image may be scaled to
+ * adjust to the requested disposition.
+ *
+ * @return the computed rectangle. Its size may exceed @e lowerSize.
+ * @since 3.2
+ */
+ static QRect computeDestinationRect(const QSize &lowerSize,
+ Disposition disposition, QImage &upper);
+
+ /**
+ * Blend an image on top of another using a given disposition and a given
+ * opacity. The alpha channel of the upper image is used in the expected
+ * way. Beware the upper image may be modified.
+ * @since 3.2
+ */
+ static void blendOnLower(QImage &upper, QImage &lower,
+ Disposition disposition, float opacity);
+
+ /**
+ * Modifies the intensity of a pixmap's RGB channel component.
+ *
+ * @param image The QImage to process.
+ * @param percent Percent value. Use a negative value to dim.
+ * @param channel Which channel(s) should be modified
+ * @return The @p image, provided for convenience.
+ * @author Daniel M. Duley (mosfet)
+ */
+ static QImage& channelIntensity(QImage &image, float percent,
+ RGBComponent channel);
+
+ /**
+ * Fade an image to a certain background color.
+ *
+ * The number of colors will not be changed.
+ *
+ * @param image The QImage to process.
+ * @param val The strength of the effect. 0 <= val <= 1.
+ * @param color The background color.
+ * @return Returns the image(), provided for convenience.
+ */
+ static QImage& fade(QImage &image, float val, const QColor &color);
+
+
+ /**
+ * This recolors a pixmap. The most dark color will become color a,
+ * the most bright one color b, and in between.
+ *
+ * @param image A QImage to process.
+ * @param ca Color a
+ * @param cb Color b
+ * @param ncols The number of colors to dither the image to.
+ * Pass 0 to prevent dithering.
+ */
+ static QImage& flatten(QImage &image, const QColor &ca,
+ const QColor &cb, int ncols=0);
+
+ /**
+ * Build a hash on any given QImage
+ *
+ * @param image The QImage to process
+ * @param lite The hash faces the indicated lighting (cardinal poles).
+ * @param spacing How many unmodified pixels in between hashes.
+ * @return Returns the image(), provided for convenience.
+ */
+ static QImage& hash(QImage &image, Lighting lite=NorthLite,
+ unsigned int spacing=0);
+
+ /**
+ * Either brighten or dim the image by a specified percent.
+ * For example, .50 will modify the colors by 50%.
+ *
+ * This function uses MMX instructions to process the image
+ * on processors that support it.
+ *
+ * @param image The QImage to process.
+ * @param percent The percent value. Use a negative value to dim.
+ * @return Returns The image(), provided for convenience.
+ * @author Daniel M. Duley (mosfet)
+ * @author Benjamin Roe (ben@benroe.com)
+ */
+ static QImage& intensity(QImage &image, float percent);
+
+ /**
+ * Modulate the image with a color channel of another image.
+ *
+ * @param image The QImage to modulate and result.
+ * @param modImage The QImage to use for modulation.
+ * @param reverse Invert the meaning of image/modImage; result is image!
+ * @param type The modulation Type to use.
+ * @param factor The modulation amplitude; with 0 no effect [-200;200].
+ * @param channel The RBG channel of image2 to use for modulation.
+ * @return Returns the image(), provided for convenience.
+ */
+ static QImage& modulate(QImage &image, QImage &modImage, bool reverse,
+ ModulationType type, int factor, RGBComponent channel);
+
+ /**
+ * Convert an image to grayscale.
+ *
+ * @param image The QImage to process.
+ * @param fast Set to @p true in order to use a faster but non-photographic
+ * quality algorithm. Appropriate for things such as toolbar icons.
+ * @return Returns the image(), provided for convenience.
+ * @author Daniel M. Duley (mosfet)
+ */
+ static QImage& toGray(QImage &image, bool fast = false);
+
+ /**
+ * Desaturate an image evenly.
+ *
+ * @param image The QImage to process.
+ * @param desat A value between 0 and 1 setting the degree of desaturation
+ * @return Returns the image(), provided for convenience.
+ */
+ static QImage& desaturate(QImage &image, float desat = 0.3);
+
+ /**
+ * Fast, but low quality contrast of an image. Also see contrastHSV.
+ *
+ * @param image The QImage to process.
+ * @param c A contrast value between -255 to 255.
+ * @return The image(), provided for convenience.
+ * @author Daniel M. Duley (mosfet)
+ * ### KDE 4: remove
+ */
+ static QImage& contrast(QImage &image, int c);
+
+ /**
+ * Dither an image using Floyd-Steinberg dithering for low-color
+ * situations.
+ *
+ * @param image The QImage to process.
+ * @param palette The color palette to use
+ * @param size The size of the palette
+ * @return Returns the image(), provided for convenience.
+ */
+ static QImage& dither(QImage &image, const QColor *palette, int size);
+
+ /**
+ * Calculate the image for a selected image, for instance a selected icon
+ * on the desktop.
+ * @param img the QImage to select
+ * @param col the selected color, usually from QColorGroup::highlight().
+ */
+ static QImage& selectedImage( QImage &img, const QColor &col );
+
+ /**
+ * High quality, expensive HSV contrast. You can do a faster one by just
+ * taking a intensity threshold (ie: 128) and incrementing RGB color
+ * channels above it and decrementing those below it, but this gives much
+ * better results.
+ *
+ * @param img The QImage to process.
+ * @param sharpen If true sharpness is increase, (spiffed). Otherwise
+ * it is decreased, (dulled).
+ * @author Daniel M. Duley (mosfet)
+ */
+ static void contrastHSV(QImage &img, bool sharpen=true);
+
+ /**
+ * Normalises the pixel values to span the full range of color values.
+ * This is a contrast enhancement technique.
+ * @param img the image that is normalised
+ * @author Daniel M. Duley (mosfet)
+ */
+ static void normalize(QImage &img);
+
+ /**
+ * Performs histogram equalisation on the reference
+ * image.
+ * @param img the image that is equalised
+ * @author Daniel M. Duley (mosfet)
+ */
+ static void equalize(QImage &img);
+
+ /**
+ * Thresholds the reference image. You can also threshold images by using
+ * ThresholdDither in the various QPixmap/QImage convert methods, but this
+ * lets you specify a threshold value.
+ *
+ * @param img The QImage to process.
+ * @param value The threshold value.
+ * @author Daniel M. Duley (mosfet)
+ */
+ static void threshold(QImage &img, unsigned int value=128);
+
+ /**
+ * Produces a 'solarization' effect seen when exposing a photographic
+ * film to light during the development process.
+ *
+ * @param img The QImage to process.
+ * @param factor The extent of the solarization (0-99.9)
+ * @author Daniel M. Duley (mosfet)
+ */
+ static void solarize(QImage &img, double factor=50.0);
+
+ /**
+ * Embosses the source image. This involves highlighting the edges
+ * and applying various other enhancements in order to get a metal
+ * effect.
+ *
+ * @param src The QImage to process.
+ * @param radius The radius of the gaussian not counting the
+ * center pixel. Use 0 and a suitable radius will be automatically used.
+ * @param sigma The standard deviation of the gaussian. Use 1 if you're not
+ * sure.
+ * @return The embossed image. The original is not changed.
+ * @author Daniel M. Duley (mosfet)
+ */
+ static QImage emboss(QImage &src, double radius, double sigma);
+
+ /**
+ * Convenience method.
+ */
+ static QImage emboss(QImage &src);
+
+ /**
+ * Minimizes speckle noise in the source image using the 8 hull
+ * algorithm.
+ *
+ * @param src The QImage to process.
+ * @return The despeckled image. The original is not changed.
+ * @author Daniel M. Duley (mosfet)
+ */
+ static QImage despeckle(QImage &src);
+
+ /**
+ * Produces a neat little "charcoal" effect.
+ *
+ * @param src The QImage to process.
+ * @param radius The radius of the gaussian not counting the
+ * center pixel. Use 0 and a suitable radius will be automatically used.
+ * @param sigma The standard deviation of the gaussian. Use 1 if you're not
+ * sure.
+ * @return The charcoal image. The original is not changed.
+ * @author Daniel M. Duley (mosfet)
+ */
+ static QImage charcoal(QImage &src, double radius, double sigma);
+
+ /**
+ * This is provided for binary compatability only! Use the above method
+ * with a radius and sigma instead!
+ */
+ static QImage charcoal(QImage &src, double factor=50.0);
+
+ /**
+ * Rotates the image by the specified amount
+ *
+ * @param src The QImage to process.
+ * @param r The rotate direction.
+ * @return The rotated image. The original is not changed.
+ * @author Daniel M. Duley (mosfet)
+ */
+ static QImage rotate(QImage &src, RotateDirection r);
+
+ /**
+ * Scales an image using simple pixel sampling. This does not produce
+ * nearly as nice a result as QImage::smoothScale(), but has the
+ * advantage of being much faster - only a few milliseconds.
+ *
+ * @param src The QImage to process.
+ * @param w The new width.
+ * @param h The new height.
+ * @return The scaled image. The original is not changed.
+ * @author Daniel M. Duley (mosfet)
+ */
+ static QImage sample(QImage &src, int w, int h);
+
+ /**
+ * Adds noise to an image.
+ *
+ * @param src The QImage to process.
+ * @param type The algorithm used to generate the noise.
+ * @return The image with noise added. The original is not changed.
+ * @author Daniel M. Duley (mosfet)
+ */
+ static QImage addNoise(QImage &src, NoiseType type = GaussianNoise);
+
+ /**
+ * Blurs an image by convolving pixel neighborhoods.
+ *
+ * @param src The QImage to process.
+ * @param radius The radius of the gaussian not counting the
+ * center pixel. Use 0 and a suitable radius will be automatically used.
+ * @param sigma The standard deviation of the gaussian. Use 1 if you're not
+ * sure.
+ * @return The blurred image. The original is not changed.
+ * @author Daniel M. Duley (mosfet)
+ */
+ static QImage blur(QImage &src, double radius, double sigma);
+
+ /**
+ * This is provided for binary compatability only! Use the above method
+ * with a radius and sigma instead!
+ */
+ static QImage blur(QImage &src, double factor=50.0);
+
+ /**
+ * Detects edges in an image using pixel neighborhoods and an edge
+ * detection mask.
+ *
+ * @param src The QImage to process.
+ * @param radius The radius of the gaussian not counting the
+ * center pixel. Use 0 and a suitable radius will be automatically used.
+ * @return The image with edges detected. The original is not changed.
+ * @author Daniel M. Duley (mosfet)
+ */
+ static QImage edge(QImage &src, double radius);
+
+ /**
+ * Implodes an image by a specified percent.
+ *
+ * @param src The QImage to process.
+ * @param factor The extent of the implosion.
+ * @param background An RGBA value to use for the background. After the
+ * effect some pixels may be "empty". This value is used for those pixels.
+ * @return The imploded image. The original is not changed.
+ * @author Daniel M. Duley (mosfet)
+ */
+ static QImage implode(QImage &src, double factor=30.0,
+ unsigned int background = 0xFFFFFFFF);
+
+ /**
+ * Produces an oil painting effect.
+ *
+ * @param src The QImage to process.
+ * @param radius The radius of the gaussian not counting the
+ * center pixel. Use 0 and a suitable radius will be automatically used.
+ * @return The new image. The original is not changed.
+ * @author Daniel M. Duley (mosfet)
+ */
+ static QImage oilPaintConvolve(QImage &src, double radius);
+
+ /**
+ * This is provided for binary compatability only! Use the above method
+ * instead!
+ */
+ static QImage oilPaint(QImage &src, int radius=3);
+
+ /**
+ * Sharpens the pixels in the image using pixel neighborhoods.
+ *
+ * @param src The QImage to process.
+ * @param radius The radius of the gaussian not counting the
+ * center pixel. Use 0 and a suitable radius will be automatically used.
+ * @param sigma The standard deviation of the gaussian. Use 1 if you're not
+ * sure.
+ * @return The sharpened image. The original is not changed.
+ * @author Daniel M. Duley (mosfet)
+ */
+ static QImage sharpen(QImage &src, double radius, double sigma);
+
+ /**
+ * This is provided for binary compatability only! Use the above method
+ * instead!
+ */
+ static QImage sharpen(QImage &src, double factor=30.0);
+
+ /**
+ * Randomly displaces pixels.
+ *
+ * @param src The QImage to process.
+ * @param amount The vicinity for choosing a random pixel to swap.
+ * @return The image with pixels displaced. The original is not changed.
+ * @author Daniel M. Duley (mosfet)
+ */
+ static QImage spread(QImage &src, unsigned int amount=3);
+
+ /**
+ * Shades the image using a distance light source.
+ *
+ * @param src The QImage to process.
+ * @param color_shading If true do color shading, otherwise do grayscale.
+ * @param azimuth Determines the light source and direction.
+ * @param elevation Determines the light source and direction.
+ * @return The shaded image. The original is not changed.
+ * @author Daniel M. Duley (mosfet)
+ */
+ static QImage shade(QImage &src, bool color_shading=true, double azimuth=30.0,
+ double elevation=30.0);
+ /**
+ * Swirls the image by a specified amount
+ *
+ * @param src The QImage to process.
+ * @param degrees The tightness of the swirl.
+ * @param background An RGBA value to use for the background. After the
+ * effect some pixels may be "empty". This value is used for those pixels.
+ * @return The swirled image. The original is not changed.
+ * @author Daniel M. Duley (mosfet)
+ */
+ static QImage swirl(QImage &src, double degrees=50.0, unsigned int background =
+ 0xFFFFFFFF);
+
+ /**
+ * Modifies the pixels along a sine wave.
+ *
+ * @param src The QImage to process.
+ * @param amplitude The amplitude of the sine wave.
+ * @param frequency The frequency of the sine wave.
+ * @param background An RGBA value to use for the background. After the
+ * effect some pixels may be "empty". This value is used for those pixels.
+ * @return The new image. The original is not changed.
+ * @author Daniel M. Duley (mosfet)
+ */
+ static QImage wave(QImage &src, double amplitude=25.0, double frequency=150.0,
+ unsigned int background = 0xFFFFFFFF);
+
+ /**
+ * A bumpmapping algorithm.
+ *
+ * @param img the image you want bumpmap
+ * @param map the map used
+ * @param azimuth azimuth
+ * @param elevation elevation
+ * @param depth depth (not the depth of the image, but of the map)
+ * @param xofs X offset
+ * @param yofs Y offset
+ * @param waterlevel level that full transparency should represent
+ * @param ambient ambient lighting factor
+ * @param compensate compensate for darkening
+ * @param invert invert bumpmap
+ * @param type type of the bumpmap
+ * @param tiled tile the bumpmap over the image through the Y offset
+ *
+ * @return The destination image (dst) containing the result.
+ * @author Zack Rusin <zack@kde.org>
+ */
+ static QImage bumpmap(QImage &img, QImage &map, double azimuth, double elevation,
+ int depth, int xofs, int yofs, int waterlevel,
+ int ambient, bool compensate, bool invert,
+ BumpmapType type, bool tiled);
+
+private:
+
+ /**
+ * Helper function to fast calc some altered (lighten, shaded) colors
+ *
+ */
+ static unsigned int lHash(unsigned int c);
+ static unsigned int uHash(unsigned int c);
+
+ /**
+ * Helper function to find the nearest color to the RBG triplet
+ */
+ static int nearestColor( int r, int g, int b, const QColor *pal, int size );
+
+ static void hull(const int x_offset, const int y_offset, const int polarity,
+ const int width, const int height,
+ unsigned int *f, unsigned int *g);
+ static unsigned int generateNoise(unsigned int pixel, NoiseType type);
+ static unsigned int interpolateColor(QImage *image, double x, double y,
+ unsigned int background);
+ /* Various convolve routines */
+ static int getOptimalKernelWidth(double radius, double sigma);
+ static bool convolveImage(QImage *image, QImage *dest,
+ const unsigned int order,
+ const double *kernel);
+ static void blurScanLine(double *kernel, int width,
+ unsigned int *src, unsigned int *dest,
+ int columns);
+ static int getBlurKernel(int width, double sigma, double **kernel);
+};
+
+#endif
diff --git a/kdefx/kpixmap.cpp b/kdefx/kpixmap.cpp
new file mode 100644
index 000000000..9d7b186bd
--- /dev/null
+++ b/kdefx/kpixmap.cpp
@@ -0,0 +1,389 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1998 Mark Donohoe <donohoe@kde.org>
+ * Stephan Kulow <coolo@kde.org>
+ *
+ * $Id$
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <qpixmap.h>
+#include <qpainter.h>
+#include <qimage.h>
+#include <qbitmap.h>
+#include <qcolor.h>
+
+#include <stdlib.h>
+#include "kpixmap.h"
+
+// Fast diffuse dither to 3x3x3 color cube
+// Based on Qt's image conversion functions
+static bool kdither_32_to_8( const QImage *src, QImage *dst )
+{
+ // register QRgb *p;
+ uchar *b;
+ int y;
+
+ if ( !dst->create(src->width(), src->height(), 8, 256) ) {
+ qWarning("KPixmap: destination image not valid\n");
+ return false;
+ }
+
+ dst->setNumColors( 256 );
+
+#define MAX_R 2
+#define MAX_G 2
+#define MAX_B 2
+#define INDEXOF(r,g,b) (((r)*(MAX_G+1)+(g))*(MAX_B+1)+(b))
+
+ int rc, gc, bc;
+
+ for ( rc=0; rc<=MAX_R; rc++ ) // build 2x2x2 color cube
+ for ( gc=0; gc<=MAX_G; gc++ )
+ for ( bc=0; bc<=MAX_B; bc++ ) {
+ dst->setColor( INDEXOF(rc,gc,bc),
+ qRgb( rc*255/MAX_R, gc*255/MAX_G, bc*255/MAX_B ) );
+ }
+
+ int sw = src->width();
+ int* line1[3];
+ int* line2[3];
+ int* pv[3];
+
+ line1[0] = new int[src->width()];
+ line2[0] = new int[src->width()];
+ line1[1] = new int[src->width()];
+ line2[1] = new int[src->width()];
+ line1[2] = new int[src->width()];
+ line2[2] = new int[src->width()];
+ pv[0] = new int[sw];
+ pv[1] = new int[sw];
+ pv[2] = new int[sw];
+
+ for ( y=0; y < src->height(); y++ ) {
+ // p = (QRgb *)src->scanLine(y);
+ b = dst->scanLine(y);
+ int endian = (QImage::systemBitOrder() == QImage::BigEndian);
+ int x;
+ uchar* q = src->scanLine(y);
+ uchar* q2 = src->scanLine(y+1 < src->height() ? y + 1 : 0);
+
+ for (int chan = 0; chan < 3; chan++) {
+ b = dst->scanLine(y);
+ int *l1 = (y&1) ? line2[chan] : line1[chan];
+ int *l2 = (y&1) ? line1[chan] : line2[chan];
+ if ( y == 0 ) {
+ for (int i=0; i<sw; i++)
+ l1[i] = q[i*4+chan+endian];
+ }
+ if ( y+1 < src->height() ) {
+ for (int i=0; i<sw; i++)
+ l2[i] = q2[i*4+chan+endian];
+ }
+
+ // Bi-directional error diffusion
+ if ( y&1 ) {
+ for (x=0; x<sw; x++) {
+ int pix = QMAX(QMIN(2, (l1[x] * 2 + 128)/ 255), 0);
+ int err = l1[x] - pix * 255 / 2;
+ pv[chan][x] = pix;
+
+ // Spread the error around...
+ if ( x+1<sw ) {
+ l1[x+1] += (err*7)>>4;
+ l2[x+1] += err>>4;
+ }
+ l2[x]+=(err*5)>>4;
+ if (x>1)
+ l2[x-1]+=(err*3)>>4;
+ }
+ } else {
+ for (x=sw; x-->0; ) {
+ int pix = QMAX(QMIN(2, (l1[x] * 2 + 128)/ 255), 0);
+ int err = l1[x] - pix * 255 / 2;
+ pv[chan][x] = pix;
+
+ // Spread the error around...
+ if ( x > 0 ) {
+ l1[x-1] += (err*7)>>4;
+ l2[x-1] += err>>4;
+ }
+ l2[x]+=(err*5)>>4;
+ if (x+1 < sw)
+ l2[x+1]+=(err*3)>>4;
+ }
+ }
+ }
+
+ if (!endian) {
+ for (x=0; x<sw; x++)
+ *b++ = INDEXOF(pv[2][x],pv[1][x],pv[0][x]);
+ } else {
+ for (x=0; x<sw; x++)
+ *b++ = INDEXOF(pv[0][x],pv[1][x],pv[2][x]);
+ }
+
+ }
+
+ delete [] line1[0];
+ delete [] line2[0];
+ delete [] line1[1];
+ delete [] line2[1];
+ delete [] line1[2];
+ delete [] line2[2];
+ delete [] pv[0];
+ delete [] pv[1];
+ delete [] pv[2];
+
+#undef MAX_R
+#undef MAX_G
+#undef MAX_B
+#undef INDEXOF
+
+ return true;
+}
+
+KPixmap::~KPixmap()
+{
+}
+
+bool KPixmap::load( const QString& fileName, const char *format,
+ int conversion_flags )
+{
+ QImageIO io( fileName, format );
+
+ bool result = io.read();
+
+ if ( result ) {
+ detach();
+ result = convertFromImage( io.image(), conversion_flags );
+ }
+ return result;
+}
+
+bool KPixmap::load( const QString& fileName, const char *format,
+ ColorMode mode )
+{
+ int conversion_flags = 0;
+ switch (mode) {
+ case Color:
+ conversion_flags |= ColorOnly;
+ break;
+ case Mono:
+ conversion_flags |= MonoOnly;
+ break;
+ case LowColor:
+ conversion_flags |= LowOnly;
+ break;
+ case WebColor:
+ conversion_flags |= WebOnly;
+ break;
+ default:
+ break;// Nothing.
+ }
+ return load( fileName, format, conversion_flags );
+}
+
+bool KPixmap::convertFromImage( const QImage &img, ColorMode mode )
+{
+ int conversion_flags = 0;
+ switch (mode) {
+ case Color:
+ conversion_flags |= ColorOnly;
+ break;
+ case Mono:
+ conversion_flags |= MonoOnly;
+ break;
+ case LowColor:
+ conversion_flags |= LowOnly;
+ break;
+ case WebColor:
+ conversion_flags |= WebOnly;
+ break;
+ default:
+ break; // Nothing.
+ }
+ return convertFromImage( img, conversion_flags );
+}
+
+bool KPixmap::convertFromImage( const QImage &img, int conversion_flags )
+{
+ if ( img.isNull() ) {
+#if defined(CHECK_NULL)
+ qWarning( "KPixmap::convertFromImage: Cannot convert a null image" );
+#endif
+ return false;
+ }
+ detach(); // detach other references
+
+ int dd = defaultDepth();
+
+ // If color mode not one of KPixmaps extra modes nothing to do
+ if ( ( conversion_flags & KColorMode_Mask ) != LowOnly &&
+ ( conversion_flags & KColorMode_Mask ) != WebOnly ) {
+ return QPixmap::convertFromImage ( img, conversion_flags );
+ }
+
+ // If the default pixmap depth is not 8bpp, KPixmap color modes have no
+ // effect. Ignore them and use AutoColor instead.
+ if ( dd > 8 ) {
+ if ( ( conversion_flags & KColorMode_Mask ) == LowOnly ||
+ ( conversion_flags & KColorMode_Mask ) == WebOnly )
+ conversion_flags = (conversion_flags & ~KColorMode_Mask) | Auto;
+ return QPixmap::convertFromImage ( img, conversion_flags );
+ }
+
+ if ( ( conversion_flags & KColorMode_Mask ) == LowOnly ) {
+ // Here we skimp a little on the possible conversion modes
+ // Don't offer ordered or threshold dither of RGB channels or
+ // diffuse or ordered dither of alpha channel. It hardly seems
+ // worth the effort for this specialized mode.
+
+ // If image uses icon palette don't dither it.
+ if( img.numColors() > 0 && img.numColors() <=40 ) {
+ if ( checkColorTable( img ) )
+ return QPixmap::convertFromImage( img, QPixmap::Auto );
+ }
+
+ QBitmap mask;
+ bool isMask = false;
+
+ QImage image = img.convertDepth(32);
+ QImage tImage( image.width(), image.height(), 8, 256 );
+
+ if( img.hasAlphaBuffer() ) {
+ image.setAlphaBuffer( true );
+ tImage.setAlphaBuffer( true );
+ isMask = mask.convertFromImage( img.createAlphaMask() );
+ }
+
+ kdither_32_to_8( &image, &tImage );
+
+ if( QPixmap::convertFromImage( tImage ) ) {
+ if ( isMask ) QPixmap::setMask( mask );
+ return true;
+ } else
+ return false;
+ } else {
+ QImage image = img.convertDepth( 32 );
+ image.setAlphaBuffer( img.hasAlphaBuffer() );
+ conversion_flags = (conversion_flags & ~ColorMode_Mask) | Auto;
+ return QPixmap::convertFromImage ( image, conversion_flags );
+ }
+}
+
+static QColor* kpixmap_iconPalette = 0;
+
+bool KPixmap::checkColorTable( const QImage &image )
+{
+ int i = 0;
+
+ if (kpixmap_iconPalette == 0) {
+ kpixmap_iconPalette = new QColor[40];
+
+ // Standard palette
+ kpixmap_iconPalette[i++] = red;
+ kpixmap_iconPalette[i++] = green;
+ kpixmap_iconPalette[i++] = blue;
+ kpixmap_iconPalette[i++] = cyan;
+ kpixmap_iconPalette[i++] = magenta;
+ kpixmap_iconPalette[i++] = yellow;
+ kpixmap_iconPalette[i++] = darkRed;
+ kpixmap_iconPalette[i++] = darkGreen;
+ kpixmap_iconPalette[i++] = darkBlue;
+ kpixmap_iconPalette[i++] = darkCyan;
+ kpixmap_iconPalette[i++] = darkMagenta;
+ kpixmap_iconPalette[i++] = darkYellow;
+ kpixmap_iconPalette[i++] = white;
+ kpixmap_iconPalette[i++] = lightGray;
+ kpixmap_iconPalette[i++] = gray;
+ kpixmap_iconPalette[i++] = darkGray;
+ kpixmap_iconPalette[i++] = black;
+
+ // Pastels
+ kpixmap_iconPalette[i++] = QColor( 255, 192, 192 );
+ kpixmap_iconPalette[i++] = QColor( 192, 255, 192 );
+ kpixmap_iconPalette[i++] = QColor( 192, 192, 255 );
+ kpixmap_iconPalette[i++] = QColor( 255, 255, 192 );
+ kpixmap_iconPalette[i++] = QColor( 255, 192, 255 );
+ kpixmap_iconPalette[i++] = QColor( 192, 255, 255 );
+
+ // Reds
+ kpixmap_iconPalette[i++] = QColor( 64, 0, 0 );
+ kpixmap_iconPalette[i++] = QColor( 192, 0, 0 );
+
+ // Oranges
+ kpixmap_iconPalette[i++] = QColor( 255, 128, 0 );
+ kpixmap_iconPalette[i++] = QColor( 192, 88, 0 );
+ kpixmap_iconPalette[i++] = QColor( 255, 168, 88 );
+ kpixmap_iconPalette[i++] = QColor( 255, 220, 168 );
+
+ // Blues
+ kpixmap_iconPalette[i++] = QColor( 0, 0, 192 );
+
+ // Turquoise
+ kpixmap_iconPalette[i++] = QColor( 0, 64, 64 );
+ kpixmap_iconPalette[i++] = QColor( 0, 192, 192 );
+
+ // Yellows
+ kpixmap_iconPalette[i++] = QColor( 64, 64, 0 );
+ kpixmap_iconPalette[i++] = QColor( 192, 192, 0 );
+
+ // Greens
+ kpixmap_iconPalette[i++] = QColor( 0, 64, 0 );
+ kpixmap_iconPalette[i++] = QColor( 0, 192, 0 );
+
+ // Purples
+ kpixmap_iconPalette[i++] = QColor( 192, 0, 192 );
+
+ // Greys
+ kpixmap_iconPalette[i++] = QColor( 88, 88, 88 );
+ kpixmap_iconPalette[i++] = QColor( 48, 48, 48 );
+ kpixmap_iconPalette[i++] = QColor( 220, 220, 220 );
+
+ }
+
+ QRgb* ctable = image.colorTable();
+
+ int ncols = image.numColors();
+ int j;
+
+ // Allow one failure which could be transparent background
+ int failures = 0;
+
+ for ( i=0; i<ncols; i++ ) {
+ for ( j=0; j<40; j++ ) {
+ if ( kpixmap_iconPalette[j].red() == qRed( ctable[i] ) &&
+ kpixmap_iconPalette[j].green() == qGreen( ctable[i] ) &&
+ kpixmap_iconPalette[j].blue() == qBlue( ctable[i] ) ) {
+ break;
+ }
+ }
+
+ if ( j == 40 ) {
+ failures ++;
+ }
+ }
+
+ return ( failures <= 1 );
+
+}
+
+KPixmap::KPixmap(const QPixmap& p)
+ : QPixmap(p)
+{
+}
diff --git a/kdefx/kpixmap.h b/kdefx/kpixmap.h
new file mode 100644
index 000000000..9a1af03bb
--- /dev/null
+++ b/kdefx/kpixmap.h
@@ -0,0 +1,213 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1998 Mark Donohoe <donohoe@kde.org>
+ * Stephan Kulow <coolo@kde.org>
+ *
+ * $Id$
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __KPIXMAP_H__
+#define __KPIXMAP_H__
+
+#include <qpixmap.h>
+
+#include <kdelibs_export.h>
+
+const int KColorMode_Mask = 0x00000300;
+const int WebOnly = 0x00000200;
+const int LowOnly = 0x00000300;
+
+class KPixmapPrivate;
+
+/**
+ * Off-screen paint device with extended features.
+
+ * KPixmap has two new color modes, WebColor and LowColor, applicable
+ * to 8bpp displays.
+
+ * In WebColor mode all images are dithered to the Netscape palette,
+ * even when they have their own color table. WebColor is the default
+ * mode for KPixmap so that standard applications can share the Netscape
+ * palette across the desktop.
+
+ * In LowColor mode images are checked to see if their color table
+ * matches the KDE icon palette. If the color tables do not match, the
+ * images are dithered to a minimal 3x3x3 color cube. LowColor mode can
+ * be used to load icons, background images etc. so that components of
+ * the desktop which are always present use no more than 40 colors.
+
+ * @author Mark Donohoe (donohoe@kde.org)
+ * @version $Id$
+ */
+class KDEFX_EXPORT KPixmap : public QPixmap
+{
+public:
+ /**
+ * This enumeration provides a color pallete specification
+ * @see KPixmap::convertFromImage(), KPixmap::load()
+ */
+ enum ColorMode { Auto, //!< Convert to monochrome if possible
+ Color, //!< Native display depth
+ Mono, //!< Monochrome pixmap
+ LowColor, //!< 3x3x3 color cube (or monochrome)
+ WebColor //!< Netscape pallete (or monochrome)
+ };
+ /**
+ * This enumeration provides a gradient mode specification
+ */
+ enum GradientMode { Horizontal,
+ Vertical,
+ Diagonal,
+ CrossDiagonal
+ };
+
+ /**
+ * Constructs a null pixmap.
+ */
+ KPixmap() : QPixmap() {};
+
+ /**
+ * Destructs the pixmap.
+ * ### KDE 4: remove
+ */
+ ~KPixmap();
+
+ /**
+ * Copies the QPixmap @p pix.
+ */
+ KPixmap(const QPixmap& pix);
+
+ /**
+ * Converts an image and sets this pixmap.
+ *
+ * The conversion_flags argument is a bitwise-OR from the
+ * following choices. The options marked (default) are the
+ * choice if no other choice from the list is included (they
+ * are zero):
+ *
+ * Color/Mono preference
+ *
+ * @li WebColor - If the image has depth 1 and contains
+ * only black and white pixels then the pixmap becomes monochrome. If
+ * the pixmap has a depth of 8 bits per pixel then the Netscape
+ * palette is used for the pixmap color table.
+ * @li LowColor - If the image has depth 1 and contains only black and
+ * white pixels then the pixmap becomes monochrome. If the pixmap has a
+ * depth of 8 bits per pixel and the image does not posess a color table
+ * that matches the Icon palette a 3x3x3 color cube is used for the
+ * pixmap color table.
+ * @li AutoColor (default) - If the image has depth 1 and contains
+ * only black and white pixels, then the pixmap becomes
+ * monochrome.
+ * @li ColorOnly - The pixmap is dithered/converted to the native
+ * display depth.
+ * @li MonoOnly - The pixmap becomes monochrome. If necessary, it
+ * is dithered using the chosen dithering algorithm.
+ *
+ * Dithering mode preference, for RGB channels
+ *
+ * @li DiffuseDither (default) - A high quality dither.
+ * @li OrderedDither - A faster more ordered dither.
+ * @li ThresholdDither - No dithering, closest color is used.
+ *
+ * Dithering mode preference, for alpha channel
+ *
+ * @li DiffuseAlphaDither - A high quality dither.
+ * @li OrderedAlphaDither - A faster more ordered dither.
+ * @li ThresholdAlphaDither (default) - No dithering.
+ *
+ * Color matching versus dithering preference
+ *
+ * @li PreferDither - Always dither 32-bit images when the image
+ * is being converted to 8-bits. This is the default when
+ * converting to a pixmap.
+ * @li AvoidDither - Only dither 32-bit images if the image has
+ * more than 256 colors and it is being converted to 8-bits.
+ * This is the default when an image is converted for the
+ * purpose of saving to a file.
+ *
+ * Passing 0 for @p conversion_flags gives all the default
+ * options.
+ *
+ * @param img the image to convert
+ * @param conversion_flags bitmask, described above
+ * @return @p true if successful.
+ **/
+ bool convertFromImage( const QImage &img, int conversion_flags );
+
+ /**
+ * This is an overloaded member function, provided for
+ * convenience. It differs from the above function only in
+ * what argument(s) it accepts.
+ * @param img the image to convert
+ * @param mode a ColorMode to apply
+ * @return @p true if successful.
+ **/
+ bool convertFromImage( const QImage &img, ColorMode mode = WebColor );
+
+ /**
+ * Loads a pixmap from the file @p fileName.
+ *
+ * If format is specified, the loader attempts to read the
+ * pixmap using the specified format. If format is not
+ * specified (default), the loader reads a few bytes from the
+ * header to guess the file format.
+ *
+ * The QImageIO documentation lists the supported image
+ * formats and explains how to add extra formats.
+ *
+ * @param fileName the name of the file to load the image from
+ * @param format the format for the image
+ * @param conversion_flags a bitmask, as described in
+ * convertFromImage()
+ * @return @p true if successful, or false if the pixmap
+ * could not be loaded.
+ **/
+ bool load( const QString& fileName, const char *format,
+ int conversion_flags );
+
+ /**
+ * This is an overloaded member function, provided for
+ * convenience. It differs from the above function only in
+ * what argument(s) it accepts.
+ * @param fileName the name of the file to load the image from
+ * @param format the format for the image
+ * @param mode a ColorMode to apply
+ * @return @p true if successful, or false if the pixmap
+ * could not be loaded.
+ **/
+ bool load( const QString& fileName,
+ const char *format = 0,
+ ColorMode mode = WebColor );
+
+ /**
+ * Returns true if the image posesses a color table that
+ * matches the Icon palette or false otherwise.
+ *
+ * An image with one color not found in the Icon palette is
+ * considered to be a match, since this extra color may be a
+ * transparent background.
+ * @param image the image to test
+ **/
+ bool checkColorTable(const QImage &image);
+
+private:
+ KPixmapPrivate *d;
+};
+
+#endif
diff --git a/kdefx/kpixmapeffect.cpp b/kdefx/kpixmapeffect.cpp
new file mode 100644
index 000000000..5184c323e
--- /dev/null
+++ b/kdefx/kpixmapeffect.cpp
@@ -0,0 +1,325 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 1998, 1999 Christian Tibirna <ctibirna@total.net>
+ (C) 1998, 1999 Daniel M. Duley <mosfet@kde.org>
+ (C) 1998, 1999 Dirk A. Mueller <mueller@kde.org>
+
+*/
+
+// $Id$
+
+#include <qimage.h>
+#include <qpainter.h>
+
+#include "kpixmapeffect.h"
+#include "kpixmap.h"
+#include "kimageeffect.h"
+
+//======================================================================
+//
+// Gradient effects
+//
+//======================================================================
+
+
+KPixmap& KPixmapEffect::gradient(KPixmap &pixmap, const QColor &ca,
+ const QColor &cb, GradientType eff, int ncols)
+{
+ if(pixmap.depth() > 8 &&
+ (eff == VerticalGradient || eff == HorizontalGradient)) {
+
+ int rDiff, gDiff, bDiff;
+ int rca, gca, bca /*, rcb, gcb, bcb*/;
+
+ register int x, y;
+
+ rDiff = (/*rcb = */ cb.red()) - (rca = ca.red());
+ gDiff = (/*gcb = */ cb.green()) - (gca = ca.green());
+ bDiff = (/*bcb = */ cb.blue()) - (bca = ca.blue());
+
+ register int rl = rca << 16;
+ register int gl = gca << 16;
+ register int bl = bca << 16;
+
+ int rcdelta = ((1<<16) / (eff == VerticalGradient ? pixmap.height() : pixmap.width())) * rDiff;
+ int gcdelta = ((1<<16) / (eff == VerticalGradient ? pixmap.height() : pixmap.width())) * gDiff;
+ int bcdelta = ((1<<16) / (eff == VerticalGradient ? pixmap.height() : pixmap.width())) * bDiff;
+
+ QPainter p(&pixmap);
+
+ // these for-loops could be merged, but the if's in the inner loop
+ // would make it slow
+ switch(eff) {
+ case VerticalGradient:
+ for ( y = 0; y < pixmap.height(); y++ ) {
+ rl += rcdelta;
+ gl += gcdelta;
+ bl += bcdelta;
+
+ p.setPen(QColor(rl>>16, gl>>16, bl>>16));
+ p.drawLine(0, y, pixmap.width()-1, y);
+ }
+ break;
+ case HorizontalGradient:
+ for( x = 0; x < pixmap.width(); x++) {
+ rl += rcdelta;
+ gl += gcdelta;
+ bl += bcdelta;
+
+ p.setPen(QColor(rl>>16, gl>>16, bl>>16));
+ p.drawLine(x, 0, x, pixmap.height()-1);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ else {
+ QImage image = KImageEffect::gradient(pixmap.size(), ca, cb,
+ (KImageEffect::GradientType) eff, ncols);
+ pixmap.convertFromImage(image);
+ }
+
+ return pixmap;
+}
+
+
+// -----------------------------------------------------------------------------
+
+KPixmap& KPixmapEffect::unbalancedGradient(KPixmap &pixmap, const QColor &ca,
+ const QColor &cb, GradientType eff, int xfactor, int yfactor,
+ int ncols)
+{
+ QImage image = KImageEffect::unbalancedGradient(pixmap.size(), ca, cb,
+ (KImageEffect::GradientType) eff,
+ xfactor, yfactor, ncols);
+ pixmap.convertFromImage(image);
+
+ return pixmap;
+}
+
+
+//======================================================================
+//
+// Intensity effects
+//
+//======================================================================
+
+
+
+KPixmap& KPixmapEffect::intensity(KPixmap &pixmap, float percent)
+{
+ QImage image = pixmap.convertToImage();
+ KImageEffect::intensity(image, percent);
+ pixmap.convertFromImage(image);
+
+ return pixmap;
+}
+
+
+// -----------------------------------------------------------------------------
+
+KPixmap& KPixmapEffect::channelIntensity(KPixmap &pixmap, float percent,
+ RGBComponent channel)
+{
+ QImage image = pixmap.convertToImage();
+ KImageEffect::channelIntensity(image, percent,
+ (KImageEffect::RGBComponent) channel);
+ pixmap.convertFromImage(image);
+
+ return pixmap;
+}
+
+
+//======================================================================
+//
+// Blend effects
+//
+//======================================================================
+
+
+KPixmap& KPixmapEffect::blend(KPixmap &pixmap, float initial_intensity,
+ const QColor &bgnd, GradientType eff,
+ bool anti_dir, int ncols)
+{
+
+ QImage image = pixmap.convertToImage();
+ if (image.depth() <=8)
+ image = image.convertDepth(32); //Sloww..
+
+ KImageEffect::blend(image, initial_intensity, bgnd,
+ (KImageEffect::GradientType) eff, anti_dir);
+
+ unsigned int tmp;
+
+ if(pixmap.depth() <= 8 ) {
+ if ( ncols < 2 || ncols > 256 )
+ ncols = 3;
+ QColor *dPal = new QColor[ncols];
+ for (int i=0; i<ncols; i++) {
+ tmp = 0 + 255 * i / ( ncols - 1 );
+ dPal[i].setRgb ( tmp, tmp, tmp );
+ }
+ KImageEffect::dither(image, dPal, ncols);
+ pixmap.convertFromImage(image);
+ delete [] dPal;
+ }
+ else
+ pixmap.convertFromImage(image);
+
+ return pixmap;
+}
+
+
+//======================================================================
+//
+// Hash effects
+//
+//======================================================================
+
+KPixmap& KPixmapEffect::hash(KPixmap &pixmap, Lighting lite,
+ unsigned int spacing, int ncols)
+{
+ QImage image = pixmap.convertToImage();
+ KImageEffect::hash(image, (KImageEffect::Lighting) lite, spacing);
+
+ unsigned int tmp;
+
+ if(pixmap.depth() <= 8 ) {
+ if ( ncols < 2 || ncols > 256 )
+ ncols = 3;
+ QColor *dPal = new QColor[ncols];
+ for (int i=0; i<ncols; i++) {
+ tmp = 0 + 255 * i / ( ncols - 1 );
+ dPal[i].setRgb ( tmp, tmp, tmp );
+ }
+ KImageEffect::dither(image, dPal, ncols);
+ pixmap.convertFromImage(image);
+ delete [] dPal;
+ }
+ else
+ pixmap.convertFromImage(image);
+
+ return pixmap;
+}
+
+
+//======================================================================
+//
+// Pattern effects
+//
+//======================================================================
+
+#if 0
+void KPixmapEffect::pattern(KPixmap &pixmap, const QColor &ca,
+ const QColor &cb, unsigned pat[8])
+{
+ QImage img = pattern(pixmap.size(), ca, cb, pat);
+ pixmap.convertFromImage(img);
+}
+#endif
+
+// -----------------------------------------------------------------------------
+
+KPixmap KPixmapEffect::pattern(const KPixmap& pmtile, QSize size,
+ const QColor &ca, const QColor &cb, int ncols)
+{
+ if (pmtile.depth() > 8)
+ ncols = 0;
+
+ QImage img = pmtile.convertToImage();
+ KImageEffect::flatten(img, ca, cb, ncols);
+ KPixmap pixmap;
+ pixmap.convertFromImage(img);
+
+ return KPixmapEffect::createTiled(pixmap, size);
+}
+
+
+// -----------------------------------------------------------------------------
+
+KPixmap KPixmapEffect::createTiled(const KPixmap& pixmap, QSize size)
+{
+ KPixmap pix(size);
+
+ QPainter p(&pix);
+ p.drawTiledPixmap(0, 0, size.width(), size.height(), pixmap);
+
+ return pix;
+}
+
+
+//======================================================================
+//
+// Fade effects
+//
+//======================================================================
+
+KPixmap& KPixmapEffect::fade(KPixmap &pixmap, double val, const QColor &color)
+{
+ QImage img = pixmap.convertToImage();
+ KImageEffect::fade(img, val, color);
+ pixmap.convertFromImage(img);
+
+ return pixmap;
+}
+
+
+// -----------------------------------------------------------------------------
+KPixmap& KPixmapEffect::toGray(KPixmap &pixmap, bool fast)
+{
+ QImage img = pixmap.convertToImage();
+ KImageEffect::toGray(img, fast);
+ pixmap.convertFromImage(img);
+
+ return pixmap;
+}
+
+// -----------------------------------------------------------------------------
+KPixmap& KPixmapEffect::desaturate(KPixmap &pixmap, float desat)
+{
+ QImage img = pixmap.convertToImage();
+ KImageEffect::desaturate(img, desat);
+ pixmap.convertFromImage(img);
+
+ return pixmap;
+}
+// -----------------------------------------------------------------------------
+KPixmap& KPixmapEffect::contrast(KPixmap &pixmap, int c)
+{
+ QImage img = pixmap.convertToImage();
+ KImageEffect::contrast(img, c);
+ pixmap.convertFromImage(img);
+
+ return pixmap;
+}
+
+//======================================================================
+//
+// Dither effects
+//
+//======================================================================
+
+// -----------------------------------------------------------------------------
+KPixmap& KPixmapEffect::dither(KPixmap &pixmap, const QColor *palette, int size)
+{
+ QImage img = pixmap.convertToImage();
+ KImageEffect::dither(img, palette, size);
+ pixmap.convertFromImage(img);
+
+ return pixmap;
+}
+
+//======================================================================
+//
+// Other effects
+//
+//======================================================================
+
+KPixmap KPixmapEffect::selectedPixmap( const KPixmap &pix, const QColor &col )
+{
+ QImage img = pix.convertToImage();
+ KImageEffect::selectedImage(img, col);
+ KPixmap outPix;
+ outPix.convertFromImage(img);
+ return outPix;
+}
diff --git a/kdefx/kpixmapeffect.h b/kdefx/kpixmapeffect.h
new file mode 100644
index 000000000..a4776f0e6
--- /dev/null
+++ b/kdefx/kpixmapeffect.h
@@ -0,0 +1,218 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 1998, 1999 Christian Tibirna <ctibirna@total.net>
+ (C) 1998, 1999 Daniel M. Duley <mosfet@kde.org>
+ (C) 1998, 1999 Dirk Mueller <mueller@kde.org>
+
+*/
+
+// $Id$
+
+#ifndef __KPIXMAP_EFFECT_H
+#define __KPIXMAP_EFFECT_H
+
+#include <kdelibs_export.h>
+
+#include <qsize.h>
+class KPixmap;
+class QColor;
+
+/**
+ * This class includes various pixmap-based graphical effects.
+ *
+ * Everything is
+ * static, so there is no need to create an instance of this class. You can
+ * just call the static methods. They are encapsulated here merely to provide
+ * a common namespace.
+ */
+class KDEFX_EXPORT KPixmapEffect
+{
+public:
+ enum GradientType { VerticalGradient, HorizontalGradient,
+ DiagonalGradient, CrossDiagonalGradient,
+ PyramidGradient, RectangleGradient,
+ PipeCrossGradient, EllipticGradient };
+ enum RGBComponent { Red, Green, Blue };
+
+ enum Lighting {NorthLite, NWLite, WestLite, SWLite,
+ SouthLite, SELite, EastLite, NELite};
+
+ /**
+ * Creates a gradient from color a to color b of the specified type.
+ *
+ * @param pixmap The pixmap to process.
+ * @param ca Color a.
+ * @param cb Color b.
+ * @param type The type of gradient.
+ * @param ncols The number of colors to use when not running on a
+ * truecolor display. The gradient will be dithered to this number of
+ * colors. Pass 0 to prevent dithering.
+ * @return Returns the generated pixmap, for convenience.
+ */
+ static KPixmap& gradient(KPixmap& pixmap, const QColor &ca, const QColor &cb,
+ GradientType type, int ncols=3);
+
+ /**
+ * Creates an unbalanced gradient.
+ *
+ * An unbalanced gradient is a gradient where the transition from
+ * color a to color b is not linear, but in this case, exponential.
+ *
+ * @param pixmap The pixmap that should be written.
+ * @param ca Color a.
+ * @param cb Color b.
+ * @param type The type of gradient.
+ * @param xfactor The x decay length. Use a value between -200 and 200.
+ * @param yfactor The y decay length.
+ * @param ncols The number of colors. See #gradient.
+ * @return The generated pixmap, for convencience.
+ */
+ static KPixmap& unbalancedGradient(KPixmap& pixmap, const QColor &ca,
+ const QColor &cb, GradientType type, int xfactor = 100,
+ int yfactor = 100, int ncols=3);
+
+ /**
+ * Creates a pixmap of a given size with the given pixmap.
+ *
+ * if the
+ * given size is bigger than the size of the pixmap, the pixmap is
+ * tiled.
+ *
+ * @param pixmap This is the source pixmap
+ * @param size The size the new pixmap should have.
+ * @return The generated, tiled pixmap.
+ */
+ static KPixmap createTiled(const KPixmap& pixmap, QSize size);
+
+ /**
+ * Either brightens or dims a pixmap by a specified ratio.
+ *
+ * @param pixmap The pixmap to process.
+ * @param ratio The ratio to use. Use negative value to dim.
+ * @return Returns The pixmap(), provided for convenience.
+ */
+ static KPixmap& intensity(KPixmap& pixmap, float ratio);
+
+ /**
+ * Modifies the intensity of a pixmap's RGB channel component.
+ *
+ * @param pixmap The pixmap to process.
+ * @param ratio value. Use negative value to dim.
+ * @param channel Which channel(s) should be modified
+ * @return Returns the pixmap(), provided for convenience.
+ */
+ static KPixmap& channelIntensity(KPixmap& pixmap, float ratio,
+ RGBComponent channel);
+
+ /**
+ * Blends the provided pixmap into a background of the indicated color.
+ *
+ * @param pixmap The pixmap to process.
+ * @param initial_intensity this parameter takes values from -1 to 1:
+ * @li If positive, it tells how much to fade the image in its
+ * less affected spot.
+ * @li If negative, it tells roughly indicates how much of the image
+ * remains unaffected
+ * @param bgnd Indicates the color of the background to blend in.
+ * @param eff Lets you choose what kind of blending you like.
+ * @param anti_dir Blend in the opposite direction (makes no much sense
+ * with concentric blending effects).
+ * @param ncols The number of colors to dither the pixmap to. Only
+ * used for 8 bpp pixmaps.
+ * @return Returns the pixmap(), provided for convenience.
+ */
+ static KPixmap& blend(KPixmap& pixmap, float initial_intensity,
+ const QColor &bgnd, GradientType eff,
+ bool anti_dir=false, int ncols=3);
+
+ /**
+ * Builds a hash on any given pixmap.
+ *
+ * @param pixmap The pixmap to process.
+ * @param lite The hash faces the indicated lighting (cardinal poles)
+ * @param spacing How many unmodified pixels inbetween hashes.
+ * @param ncols The number of colors to dither the pixmap to.
+ * Only used for 8 bpp pixmaps.
+ * @return Returns The pixmap(), provided for convenience.
+ */
+ static KPixmap& hash(KPixmap& pixmap, Lighting lite=NorthLite,
+ unsigned int spacing=0, int ncols=3);
+
+ /**
+ * Creates a pattern from a pixmap.
+ *
+ * The given pixmap is "flattened"
+ * between color a to color b.
+ * Doesn't change the original pixmap.
+ *
+ * @param pixmap The pixmap to process.
+ * @param size The size of the returned pixmap. If @p size is larger than
+ * the original, the resulting pixmap will be tiled.
+ * @param ca Color a.
+ * @param cb Color b.
+ * @param ncols The number of colors to use. The image will be
+ * dithered to this depth. Pass zero to prevent dithering.
+ * @return The resulting pixmap.
+ */
+ static KPixmap pattern(const KPixmap& pixmap, QSize size,
+ const QColor &ca, const QColor &cb, int ncols=8);
+
+ /**
+ * Fades a pixmap to a certain color.
+ *
+ * @param pixmap The pixmap to process.
+ * @param val The strength of the effect. 0 <= val <= 1.
+ * @param color The color to blend to.
+ * @return Returns the pixmap(), provided for convenience.
+ */
+ static KPixmap& fade(KPixmap& pixmap, double val, const QColor &color);
+
+ /**
+ * Converts a pixmap to grayscale.
+ *
+ * @param pixmap The pixmap to process.
+ * @param fast Set to @p true in order to use a faster but non-photographic
+ * quality algorithm. Appropriate for things such as toolbar icons.
+ * @return Returns the pixmap(), provided for convenience.
+ */
+ static KPixmap& toGray(KPixmap& pixmap, bool fast=false);
+
+ /**
+ * Desaturates a pixmap.
+ *
+ * @param pixmap The pixmap to process.
+ * @param desat A value between 0 and 1 setting the degree of desaturation
+ * @return Returns The pixmap(), provided for convenience.
+ */
+ static KPixmap& desaturate(KPixmap& pixmap, float desat = 0.3);
+
+ /**
+ * Modifies the contrast of a pixmap.
+ *
+ * @param pixmap The pixmap to process.
+ * @param c A contrast value between -255 and 255.
+ * @return Returns the pixmap(), provided for convenience.
+ */
+ static KPixmap& contrast(KPixmap& pixmap, int c);
+
+ /**
+ * Dithers a pixmap using Floyd-Steinberg dithering for low-color
+ * situations.
+ *
+ * @param pixmap The pixmap to process.
+ * @param palette The color palette to use.
+ * @param size The size of the palette.
+ * @return Returns the pixmap(), provided for convenience.
+ */
+ static KPixmap& dither(KPixmap &pixmap, const QColor *palette, int size);
+
+ /**
+ * Calculate a 'selected' pixmap, for instance a selected icon
+ * on the desktop.
+ * @param pixmap the pixmap to select
+ * @param col the selected color, usually from QColorGroup::highlight().
+ */
+ static KPixmap selectedPixmap( const KPixmap &pixmap, const QColor &col );
+};
+
+
+#endif
diff --git a/kdefx/kpixmapsplitter.cpp b/kdefx/kpixmapsplitter.cpp
new file mode 100644
index 000000000..83fc3de5b
--- /dev/null
+++ b/kdefx/kpixmapsplitter.cpp
@@ -0,0 +1,95 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 Carsten Pfeiffer <pfeiffer@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kpixmapsplitter.h"
+
+KPixmapSplitter::KPixmapSplitter()
+ : m_itemSize( 4, 7 ),
+ m_vSpacing( 0 ),
+ m_hSpacing( 0 ),
+ m_numCols( 0 ),
+ m_numRows( 0 ),
+ m_dirty( false )
+{
+}
+
+KPixmapSplitter::~KPixmapSplitter()
+{
+}
+
+void KPixmapSplitter::setPixmap( const QPixmap& pixmap )
+{
+ m_pixmap = pixmap;
+ m_dirty = true;
+}
+
+void KPixmapSplitter::setItemSize( const QSize& size )
+{
+ if ( size != m_itemSize ) {
+ m_itemSize = size;
+ m_dirty = true;
+ }
+}
+
+void KPixmapSplitter::setVSpacing( int spacing )
+{
+ if ( spacing != m_vSpacing ) {
+ m_vSpacing = spacing;
+ m_dirty = true;
+ }
+}
+
+void KPixmapSplitter::setHSpacing( int spacing )
+{
+ if ( spacing != m_hSpacing ) {
+ m_hSpacing = spacing;
+ m_dirty = true;
+ }
+}
+
+
+QRect KPixmapSplitter::coordinates( int pos )
+{
+ if ( pos < 0 || m_pixmap.isNull() )
+ return QRect();
+
+ if ( m_dirty ) {
+ m_numCols = m_pixmap.width() / ( m_itemSize.width() + m_hSpacing );
+ m_numRows = m_pixmap.height() / ( m_itemSize.height() + m_vSpacing );
+ m_dirty = false;
+ // qDebug("cols: %i, rows: %i (pixmap: %i, %i)", m_numCols, m_numRows, m_pixmap.width(), m_pixmap.height());
+ }
+
+ if ( m_numCols == 0 || m_numRows == 0 )
+ return QRect();
+
+ int row = pos / m_numCols;
+ int col = pos - (row * m_numCols);
+
+ return QRect( col * (m_itemSize.width() + m_hSpacing),
+ row * (m_itemSize.height() + m_vSpacing),
+ m_itemSize.width(),
+ m_itemSize.height() );
+}
+
+QRect KPixmapSplitter::coordinates( const QChar& ch )
+{
+ return coordinates( (unsigned char) ch.latin1() );
+}
+
diff --git a/kdefx/kpixmapsplitter.h b/kdefx/kpixmapsplitter.h
new file mode 100644
index 000000000..6c525c172
--- /dev/null
+++ b/kdefx/kpixmapsplitter.h
@@ -0,0 +1,123 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 Carsten Pfeiffer <pfeiffer@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KPIXMAPSPLITTER_H
+#define KPIXMAPSPLITTER_H
+
+#include <qpixmap.h>
+#include <qrect.h>
+#include <qsize.h>
+#include <qstring.h>
+
+#include <kdelibs_export.h>
+
+class KPixmapSplitterPrivate;
+/**
+ * @short A class to split a pixmap into several items.
+ *
+ * If you have a pixmap containing several items (icons), you can use this
+ * class to get the coordinates of each item.
+ *
+ * For example, if you have a pixmap with 25 items and you want to get the
+ * 4th item as a pixmap (every item being 20x10 pixels):
+ * \code
+ * KPixmapSplitter splitter;
+ * splitter.setPixmap( somePixmap );
+ * splitter.setItemSize( QSize( 20, 10 ));
+ *
+ * QPixmap item( 20, 10 );
+ * item.fill( Qt::white );
+ * QRect rect = splitter.coordinates( 4 );
+ * if ( !rect.isEmpty() )
+ * bitBlt( &item, QPoint(0,0), &somePixmap, rect, CopyROP );
+ * \endcode
+ *
+ * @author Carsten Pfeiffer <pfeiffer@kde.org>
+ */
+class KDEFX_EXPORT KPixmapSplitter
+{
+public:
+ /**
+ * Constructor, does nothing but initialize some default-values.
+ */
+ KPixmapSplitter();
+ ~KPixmapSplitter();
+
+ /**
+ * Sets the pixmap to be split.
+ */
+ void setPixmap( const QPixmap& pixmap );
+
+ /**
+ * @returns the pixmap that has been set via setPixmap().
+ */
+ const QPixmap& pixmap() const { return m_pixmap; }
+
+ /**
+ * Sets the size of the items you want to get out of the given pixmap.
+ * The QRect of #coordinates(int) will have the width and height of exactly
+ * this @p size.
+ */
+ void setItemSize( const QSize& size );
+
+ /**
+ * @returns the set size of the items (coordinates) you want to get
+ * out of the given pixmap.
+ */
+ QSize itemSize() const { return m_itemSize; }
+
+ /**
+ * If there is space between rows in the given pixmap, you have to specify
+ * how many pixels there are.
+ */
+ void setVSpacing( int spacing );
+
+ /**
+ * If there is space between columns in the given pixmap, you have to
+ * specify how many pixels there are.
+ */
+ void setHSpacing( int spacing );
+
+ /**
+ * @returns the coordinates of the item at position pos in the given
+ * pixmap.
+ */
+ QRect coordinates( int pos );
+
+ /**
+ * Overloaded for convenience. Returns the item at the position of the
+ * given character (when using a latin1 font-pixmap)
+ */
+ QRect coordinates( const QChar& ch );
+
+private:
+ QPixmap m_pixmap;
+ QSize m_itemSize;
+
+ int m_vSpacing;
+ int m_hSpacing;
+
+ int m_numCols;
+ int m_numRows;
+
+ bool m_dirty;
+ KPixmapSplitterPrivate* d;
+};
+
+#endif // KPIXMAPSPLITTER_H
diff --git a/kdefx/kstyle.cpp b/kdefx/kstyle.cpp
new file mode 100644
index 000000000..5ce02012b
--- /dev/null
+++ b/kdefx/kstyle.cpp
@@ -0,0 +1,2164 @@
+/*
+ *
+ * KStyle
+ * Copyright (C) 2001-2002 Karol Szwed <gallium@kde.org>
+ *
+ * QWindowsStyle CC_ListView and style images were kindly donated by TrollTech,
+ * Copyright (C) 1998-2000 TrollTech AS.
+ *
+ * Many thanks to Bradley T. Hughes for the 3 button scrollbar code.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "kstyle.h"
+
+#include <qapplication.h>
+#include <qbitmap.h>
+#include <qcleanuphandler.h>
+#include <qmap.h>
+#include <qimage.h>
+#include <qlistview.h>
+#include <qmenubar.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qpopupmenu.h>
+#include <qprogressbar.h>
+#include <qscrollbar.h>
+#include <qsettings.h>
+#include <qslider.h>
+#include <qstylefactory.h>
+#include <qtabbar.h>
+#include <qtoolbar.h>
+
+#include <kpixmap.h>
+#include <kpixmapeffect.h>
+#include <kimageeffect.h>
+
+#ifdef Q_WS_X11
+# include <X11/Xlib.h>
+# ifdef HAVE_XRENDER
+# include <X11/extensions/Xrender.h> // schroder
+ extern bool qt_use_xrender;
+# endif
+#else
+#undef HAVE_XRENDER
+#endif
+
+
+#include <limits.h>
+
+namespace
+{
+ // INTERNAL
+ enum TransparencyEngine {
+ Disabled = 0,
+ SoftwareTint,
+ SoftwareBlend,
+ XRender
+ };
+
+ // Drop Shadow
+ struct ShadowElements {
+ QWidget* w1;
+ QWidget* w2;
+ };
+ typedef QMap<const QPopupMenu*,ShadowElements> ShadowMap;
+ static ShadowMap *_shadowMap = 0;
+ QSingleCleanupHandler<ShadowMap> cleanupShadowMap;
+ ShadowMap &shadowMap() {
+ if ( !_shadowMap ) {
+ _shadowMap = new ShadowMap;
+ cleanupShadowMap.set( &_shadowMap );
+ }
+ return *_shadowMap;
+ }
+
+
+ // DO NOT ASK ME HOW I MADE THESE TABLES!
+ // (I probably won't remember anyway ;)
+ const double top_right_corner[16] =
+ { 0.949, 0.965, 0.980, 0.992,
+ 0.851, 0.890, 0.945, 0.980,
+ 0.706, 0.780, 0.890, 0.960,
+ 0.608, 0.706, 0.851, 0.949 };
+
+ const double bottom_right_corner[16] =
+ { 0.608, 0.706, 0.851, 0.949,
+ 0.706, 0.780, 0.890, 0.960,
+ 0.851, 0.890, 0.945, 0.980,
+ 0.949, 0.965, 0.980, 0.992 };
+
+ const double bottom_left_corner[16] =
+ { 0.949, 0.851, 0.706, 0.608,
+ 0.965, 0.890, 0.780, 0.706,
+ 0.980, 0.945, 0.890, 0.851,
+ 0.992, 0.980, 0.960, 0.949 };
+
+ const double shadow_strip[4] =
+ { 0.565, 0.675, 0.835, 0.945 };
+}
+
+
+namespace
+{
+class TransparencyHandler : public QObject
+{
+ public:
+ TransparencyHandler(KStyle* style, TransparencyEngine tEngine,
+ float menuOpacity, bool useDropShadow);
+ ~TransparencyHandler();
+ bool eventFilter(QObject* object, QEvent* event);
+
+ protected:
+ void blendToColor(const QColor &col);
+ void blendToPixmap(const QColorGroup &cg, const QPopupMenu* p);
+#ifdef HAVE_XRENDER
+ void XRenderBlendToPixmap(const QPopupMenu* p);
+#endif
+ void createShadowWindows(const QPopupMenu* p);
+ void removeShadowWindows(const QPopupMenu* p);
+ void rightShadow(QImage& dst);
+ void bottomShadow(QImage& dst);
+ private:
+ bool dropShadow;
+ float opacity;
+ QPixmap pix;
+ KStyle* kstyle;
+ TransparencyEngine te;
+};
+} // namespace
+
+struct KStylePrivate
+{
+ bool highcolor : 1;
+ bool useFilledFrameWorkaround : 1;
+ bool etchDisabledText : 1;
+ bool scrollablePopupmenus : 1;
+ bool menuAltKeyNavigation : 1;
+ bool menuDropShadow : 1;
+ bool sloppySubMenus : 1;
+ int popupMenuDelay;
+ float menuOpacity;
+
+ TransparencyEngine transparencyEngine;
+ KStyle::KStyleScrollBarType scrollbarType;
+ TransparencyHandler* menuHandler;
+ KStyle::KStyleFlags flags;
+
+ //For KPE_ListViewBranch
+ QBitmap *verticalLine;
+ QBitmap *horizontalLine;
+};
+
+// -----------------------------------------------------------------------------
+
+
+KStyle::KStyle( KStyleFlags flags, KStyleScrollBarType sbtype )
+ : QCommonStyle(), d(new KStylePrivate)
+{
+ d->flags = flags;
+ bool useMenuTransparency = (flags & AllowMenuTransparency);
+ d->useFilledFrameWorkaround = (flags & FilledFrameWorkaround);
+ d->scrollbarType = sbtype;
+ d->highcolor = QPixmap::defaultDepth() > 8;
+
+ // Read style settings
+ QSettings settings;
+ d->popupMenuDelay = settings.readNumEntry ("/KStyle/Settings/PopupMenuDelay", 256);
+ d->sloppySubMenus = settings.readBoolEntry("/KStyle/Settings/SloppySubMenus", false);
+ d->etchDisabledText = settings.readBoolEntry("/KStyle/Settings/EtchDisabledText", true);
+ d->menuAltKeyNavigation = settings.readBoolEntry("/KStyle/Settings/MenuAltKeyNavigation", true);
+ d->scrollablePopupmenus = settings.readBoolEntry("/KStyle/Settings/ScrollablePopupMenus", false);
+ d->menuDropShadow = settings.readBoolEntry("/KStyle/Settings/MenuDropShadow", false);
+ d->menuHandler = NULL;
+
+ if (useMenuTransparency) {
+ QString effectEngine = settings.readEntry("/KStyle/Settings/MenuTransparencyEngine", "Disabled");
+
+#ifdef HAVE_XRENDER
+ if (effectEngine == "XRender")
+ d->transparencyEngine = XRender;
+#else
+ if (effectEngine == "XRender")
+ d->transparencyEngine = SoftwareBlend;
+#endif
+ else if (effectEngine == "SoftwareBlend")
+ d->transparencyEngine = SoftwareBlend;
+ else if (effectEngine == "SoftwareTint")
+ d->transparencyEngine = SoftwareTint;
+ else
+ d->transparencyEngine = Disabled;
+
+ if (d->transparencyEngine != Disabled) {
+ // Create an instance of the menu transparency handler
+ d->menuOpacity = settings.readDoubleEntry("/KStyle/Settings/MenuOpacity", 0.90);
+ d->menuHandler = new TransparencyHandler(this, d->transparencyEngine,
+ d->menuOpacity, d->menuDropShadow);
+ }
+ }
+
+ d->verticalLine = 0;
+ d->horizontalLine = 0;
+
+ // Create a transparency handler if only drop shadows are enabled.
+ if (!d->menuHandler && d->menuDropShadow)
+ d->menuHandler = new TransparencyHandler(this, Disabled, 1.0, d->menuDropShadow);
+}
+
+
+KStyle::~KStyle()
+{
+ delete d->verticalLine;
+ delete d->horizontalLine;
+
+ delete d->menuHandler;
+
+ d->menuHandler = NULL;
+ delete d;
+}
+
+
+QString KStyle::defaultStyle()
+{
+ if (QPixmap::defaultDepth() > 8)
+ return QString("plastik");
+ else
+ return QString("light, 3rd revision");
+}
+
+
+void KStyle::polish( QWidget* widget )
+{
+ if ( d->useFilledFrameWorkaround )
+ {
+ if ( QFrame *frame = ::qt_cast< QFrame* >( widget ) ) {
+ QFrame::Shape shape = frame->frameShape();
+ if (shape == QFrame::ToolBarPanel || shape == QFrame::MenuBarPanel)
+ widget->installEventFilter(this);
+ }
+ }
+}
+
+
+void KStyle::unPolish( QWidget* widget )
+{
+ if ( d->useFilledFrameWorkaround )
+ {
+ if ( QFrame *frame = ::qt_cast< QFrame* >( widget ) ) {
+ QFrame::Shape shape = frame->frameShape();
+ if (shape == QFrame::ToolBarPanel || shape == QFrame::MenuBarPanel)
+ widget->removeEventFilter(this);
+ }
+ }
+}
+
+
+// Style changes (should) always re-polish popups.
+void KStyle::polishPopupMenu( QPopupMenu* p )
+{
+ if (!p->testWState( WState_Polished ))
+ p->setCheckable(true);
+
+ // Install transparency handler if the effect is enabled.
+ if ( d->menuHandler &&
+ (strcmp(p->name(), "tear off menu") != 0))
+ p->installEventFilter(d->menuHandler);
+}
+
+
+// -----------------------------------------------------------------------------
+// KStyle extensions
+// -----------------------------------------------------------------------------
+
+void KStyle::setScrollBarType(KStyleScrollBarType sbtype)
+{
+ d->scrollbarType = sbtype;
+}
+
+KStyle::KStyleFlags KStyle::styleFlags() const
+{
+ return d->flags;
+}
+
+void KStyle::renderMenuBlendPixmap( KPixmap &pix, const QColorGroup &cg,
+ const QPopupMenu* /* popup */ ) const
+{
+ pix.fill(cg.button()); // Just tint as the default behavior
+}
+
+
+void KStyle::drawKStylePrimitive( KStylePrimitive kpe,
+ QPainter* p,
+ const QWidget* widget,
+ const QRect &r,
+ const QColorGroup &cg,
+ SFlags flags,
+ const QStyleOption& /* opt */ ) const
+{
+ switch( kpe )
+ {
+ // Dock / Toolbar / General handles.
+ // ---------------------------------
+
+ case KPE_DockWindowHandle: {
+
+ // Draws a nice DockWindow handle including the dock title.
+ QWidget* wid = const_cast<QWidget*>(widget);
+ bool horizontal = flags & Style_Horizontal;
+ int x,y,w,h,x2,y2;
+
+ r.rect( &x, &y, &w, &h );
+ if ((w <= 2) || (h <= 2)) {
+ p->fillRect(r, cg.highlight());
+ return;
+ }
+
+
+ x2 = x + w - 1;
+ y2 = y + h - 1;
+
+ QFont fnt;
+ fnt = QApplication::font(wid);
+ fnt.setPointSize( fnt.pointSize()-2 );
+
+ // Draw the item on an off-screen pixmap
+ // to preserve Xft antialiasing for
+ // vertically oriented handles.
+ QPixmap pix;
+ if (horizontal)
+ pix.resize( h-2, w-2 );
+ else
+ pix.resize( w-2, h-2 );
+
+ QString title = wid->parentWidget()->caption();
+ QPainter p2;
+ p2.begin(&pix);
+ p2.fillRect(pix.rect(), cg.brush(QColorGroup::Highlight));
+ p2.setPen(cg.highlightedText());
+ p2.setFont(fnt);
+ p2.drawText(pix.rect(), AlignCenter, title);
+ p2.end();
+
+ // Draw a sunken bevel
+ p->setPen(cg.dark());
+ p->drawLine(x, y, x2, y);
+ p->drawLine(x, y, x, y2);
+ p->setPen(cg.light());
+ p->drawLine(x+1, y2, x2, y2);
+ p->drawLine(x2, y+1, x2, y2);
+
+ if (horizontal) {
+ QWMatrix m;
+ m.rotate(-90.0);
+ QPixmap vpix = pix.xForm(m);
+ bitBlt(wid, r.x()+1, r.y()+1, &vpix);
+ } else
+ bitBlt(wid, r.x()+1, r.y()+1, &pix);
+
+ break;
+ }
+
+
+ /*
+ * KPE_ListViewExpander and KPE_ListViewBranch are based on code from
+ * QWindowStyle's CC_ListView, kindly donated by TrollTech.
+ * CC_ListView code is Copyright (C) 1998-2000 TrollTech AS.
+ */
+
+ case KPE_ListViewExpander: {
+ // Typical Windows style expand/collapse element.
+ int radius = (r.width() - 4) / 2;
+ int centerx = r.x() + r.width()/2;
+ int centery = r.y() + r.height()/2;
+
+ // Outer box
+ p->setPen( cg.mid() );
+ p->drawRect( r );
+
+ // plus or minus
+ p->setPen( cg.text() );
+ p->drawLine( centerx - radius, centery, centerx + radius, centery );
+ if ( flags & Style_On ) // Collapsed = On
+ p->drawLine( centerx, centery - radius, centerx, centery + radius );
+ break;
+ }
+
+ case KPE_ListViewBranch: {
+ // Typical Windows style listview branch element (dotted line).
+
+ // Create the dotline pixmaps if not already created
+ if ( !d->verticalLine )
+ {
+ // make 128*1 and 1*128 bitmaps that can be used for
+ // drawing the right sort of lines.
+ d->verticalLine = new QBitmap( 1, 129, true );
+ d->horizontalLine = new QBitmap( 128, 1, true );
+ QPointArray a( 64 );
+ QPainter p2;
+ p2.begin( d->verticalLine );
+
+ int i;
+ for( i=0; i < 64; i++ )
+ a.setPoint( i, 0, i*2+1 );
+ p2.setPen( color1 );
+ p2.drawPoints( a );
+ p2.end();
+ QApplication::flushX();
+ d->verticalLine->setMask( *d->verticalLine );
+
+ p2.begin( d->horizontalLine );
+ for( i=0; i < 64; i++ )
+ a.setPoint( i, i*2+1, 0 );
+ p2.setPen( color1 );
+ p2.drawPoints( a );
+ p2.end();
+ QApplication::flushX();
+ d->horizontalLine->setMask( *d->horizontalLine );
+ }
+
+ p->setPen( cg.text() ); // cg.dark() is bad for dark color schemes.
+
+ if (flags & Style_Horizontal)
+ {
+ int point = r.x();
+ int other = r.y();
+ int end = r.x()+r.width();
+ int thickness = r.height();
+
+ while( point < end )
+ {
+ int i = 128;
+ if ( i+point > end )
+ i = end-point;
+ p->drawPixmap( point, other, *d->horizontalLine, 0, 0, i, thickness );
+ point += i;
+ }
+
+ } else {
+ int point = r.y();
+ int other = r.x();
+ int end = r.y()+r.height();
+ int thickness = r.width();
+ int pixmapoffset = (flags & Style_NoChange) ? 0 : 1; // ### Hackish
+
+ while( point < end )
+ {
+ int i = 128;
+ if ( i+point > end )
+ i = end-point;
+ p->drawPixmap( other, point, *d->verticalLine, 0, pixmapoffset, thickness, i );
+ point += i;
+ }
+ }
+
+ break;
+ }
+
+ // Reimplement the other primitives in your styles.
+ // The current implementation just paints something visibly different.
+ case KPE_ToolBarHandle:
+ case KPE_GeneralHandle:
+ case KPE_SliderHandle:
+ p->fillRect(r, cg.light());
+ break;
+
+ case KPE_SliderGroove:
+ p->fillRect(r, cg.dark());
+ break;
+
+ default:
+ p->fillRect(r, Qt::yellow); // Something really bad happened - highlight.
+ break;
+ }
+}
+
+
+int KStyle::kPixelMetric( KStylePixelMetric kpm, const QWidget* /* widget */) const
+{
+ int value;
+ switch(kpm)
+ {
+ case KPM_ListViewBranchThickness:
+ value = 1;
+ break;
+
+ case KPM_MenuItemSeparatorHeight:
+ case KPM_MenuItemHMargin:
+ case KPM_MenuItemVMargin:
+ case KPM_MenuItemHFrame:
+ case KPM_MenuItemVFrame:
+ case KPM_MenuItemCheckMarkHMargin:
+ case KPM_MenuItemArrowHMargin:
+ case KPM_MenuItemTabSpacing:
+ default:
+ value = 0;
+ }
+
+ return value;
+}
+
+
+// -----------------------------------------------------------------------------
+
+void KStyle::drawPrimitive( PrimitiveElement pe,
+ QPainter* p,
+ const QRect &r,
+ const QColorGroup &cg,
+ SFlags flags,
+ const QStyleOption& opt ) const
+{
+ // TOOLBAR/DOCK WINDOW HANDLE
+ // ------------------------------------------------------------------------
+ if (pe == PE_DockWindowHandle)
+ {
+ // Wild workarounds are here. Beware.
+ QWidget *widget, *parent;
+
+ if (p && p->device()->devType() == QInternal::Widget) {
+ widget = static_cast<QWidget*>(p->device());
+ parent = widget->parentWidget();
+ } else
+ return; // Don't paint on non-widgets
+
+ // Check if we are a normal toolbar or a hidden dockwidget.
+ if ( parent &&
+ (parent->inherits("QToolBar") || // Normal toolbar
+ (parent->inherits("QMainWindow")) )) // Collapsed dock
+
+ // Draw a toolbar handle
+ drawKStylePrimitive( KPE_ToolBarHandle, p, widget, r, cg, flags, opt );
+
+ else if ( widget->inherits("QDockWindowHandle") )
+
+ // Draw a dock window handle
+ drawKStylePrimitive( KPE_DockWindowHandle, p, widget, r, cg, flags, opt );
+
+ else
+ // General handle, probably a kicker applet handle.
+ drawKStylePrimitive( KPE_GeneralHandle, p, widget, r, cg, flags, opt );
+
+ } else
+ QCommonStyle::drawPrimitive( pe, p, r, cg, flags, opt );
+}
+
+
+
+void KStyle::drawControl( ControlElement element,
+ QPainter* p,
+ const QWidget* widget,
+ const QRect &r,
+ const QColorGroup &cg,
+ SFlags flags,
+ const QStyleOption &opt ) const
+{
+ switch (element)
+ {
+ // TABS
+ // ------------------------------------------------------------------------
+ case CE_TabBarTab: {
+ const QTabBar* tb = (const QTabBar*) widget;
+ QTabBar::Shape tbs = tb->shape();
+ bool selected = flags & Style_Selected;
+ int x = r.x(), y=r.y(), bottom=r.bottom(), right=r.right();
+
+ switch (tbs) {
+
+ case QTabBar::RoundedAbove: {
+ if (!selected)
+ p->translate(0,1);
+ p->setPen(selected ? cg.light() : cg.shadow());
+ p->drawLine(x, y+4, x, bottom);
+ p->drawLine(x, y+4, x+4, y);
+ p->drawLine(x+4, y, right-1, y);
+ if (selected)
+ p->setPen(cg.shadow());
+ p->drawLine(right, y+1, right, bottom);
+
+ p->setPen(cg.midlight());
+ p->drawLine(x+1, y+4, x+1, bottom);
+ p->drawLine(x+1, y+4, x+4, y+1);
+ p->drawLine(x+5, y+1, right-2, y+1);
+
+ if (selected) {
+ p->setPen(cg.mid());
+ p->drawLine(right-1, y+1, right-1, bottom);
+ } else {
+ p->setPen(cg.mid());
+ p->drawPoint(right-1, y+1);
+ p->drawLine(x+4, y+2, right-1, y+2);
+ p->drawLine(x+3, y+3, right-1, y+3);
+ p->fillRect(x+2, y+4, r.width()-3, r.height()-6, cg.mid());
+
+ p->setPen(cg.light());
+ p->drawLine(x, bottom-1, right, bottom-1);
+ p->translate(0,-1);
+ }
+ break;
+ }
+
+ case QTabBar::RoundedBelow: {
+ if (!selected)
+ p->translate(0,-1);
+ p->setPen(selected ? cg.light() : cg.shadow());
+ p->drawLine(x, bottom-4, x, y);
+ if (selected)
+ p->setPen(cg.mid());
+ p->drawLine(x, bottom-4, x+4, bottom);
+ if (selected)
+ p->setPen(cg.shadow());
+ p->drawLine(x+4, bottom, right-1, bottom);
+ p->drawLine(right, bottom-1, right, y);
+
+ p->setPen(cg.midlight());
+ p->drawLine(x+1, bottom-4, x+1, y);
+ p->drawLine(x+1, bottom-4, x+4, bottom-1);
+ p->drawLine(x+5, bottom-1, right-2, bottom-1);
+
+ if (selected) {
+ p->setPen(cg.mid());
+ p->drawLine(right-1, y, right-1, bottom-1);
+ } else {
+ p->setPen(cg.mid());
+ p->drawPoint(right-1, bottom-1);
+ p->drawLine(x+4, bottom-2, right-1, bottom-2);
+ p->drawLine(x+3, bottom-3, right-1, bottom-3);
+ p->fillRect(x+2, y+2, r.width()-3, r.height()-6, cg.mid());
+ p->translate(0,1);
+ p->setPen(cg.dark());
+ p->drawLine(x, y, right, y);
+ }
+ break;
+ }
+
+ case QTabBar::TriangularAbove: {
+ if (!selected)
+ p->translate(0,1);
+ p->setPen(selected ? cg.light() : cg.shadow());
+ p->drawLine(x, bottom, x, y+6);
+ p->drawLine(x, y+6, x+6, y);
+ p->drawLine(x+6, y, right-6, y);
+ if (selected)
+ p->setPen(cg.mid());
+ p->drawLine(right-5, y+1, right-1, y+5);
+ p->setPen(cg.shadow());
+ p->drawLine(right, y+6, right, bottom);
+
+ p->setPen(cg.midlight());
+ p->drawLine(x+1, bottom, x+1, y+6);
+ p->drawLine(x+1, y+6, x+6, y+1);
+ p->drawLine(x+6, y+1, right-6, y+1);
+ p->drawLine(right-5, y+2, right-2, y+5);
+ p->setPen(cg.mid());
+ p->drawLine(right-1, y+6, right-1, bottom);
+
+ QPointArray a(6);
+ a.setPoint(0, x+2, bottom);
+ a.setPoint(1, x+2, y+7);
+ a.setPoint(2, x+7, y+2);
+ a.setPoint(3, right-7, y+2);
+ a.setPoint(4, right-2, y+7);
+ a.setPoint(5, right-2, bottom);
+ p->setPen (selected ? cg.background() : cg.mid());
+ p->setBrush(selected ? cg.background() : cg.mid());
+ p->drawPolygon(a);
+ p->setBrush(NoBrush);
+ if (!selected) {
+ p->translate(0,-1);
+ p->setPen(cg.light());
+ p->drawLine(x, bottom, right, bottom);
+ }
+ break;
+ }
+
+ default: { // QTabBar::TriangularBelow
+ if (!selected)
+ p->translate(0,-1);
+ p->setPen(selected ? cg.light() : cg.shadow());
+ p->drawLine(x, y, x, bottom-6);
+ if (selected)
+ p->setPen(cg.mid());
+ p->drawLine(x, bottom-6, x+6, bottom);
+ if (selected)
+ p->setPen(cg.shadow());
+ p->drawLine(x+6, bottom, right-6, bottom);
+ p->drawLine(right-5, bottom-1, right-1, bottom-5);
+ if (!selected)
+ p->setPen(cg.shadow());
+ p->drawLine(right, bottom-6, right, y);
+
+ p->setPen(cg.midlight());
+ p->drawLine(x+1, y, x+1, bottom-6);
+ p->drawLine(x+1, bottom-6, x+6, bottom-1);
+ p->drawLine(x+6, bottom-1, right-6, bottom-1);
+ p->drawLine(right-5, bottom-2, right-2, bottom-5);
+ p->setPen(cg.mid());
+ p->drawLine(right-1, bottom-6, right-1, y);
+
+ QPointArray a(6);
+ a.setPoint(0, x+2, y);
+ a.setPoint(1, x+2, bottom-7);
+ a.setPoint(2, x+7, bottom-2);
+ a.setPoint(3, right-7, bottom-2);
+ a.setPoint(4, right-2, bottom-7);
+ a.setPoint(5, right-2, y);
+ p->setPen (selected ? cg.background() : cg.mid());
+ p->setBrush(selected ? cg.background() : cg.mid());
+ p->drawPolygon(a);
+ p->setBrush(NoBrush);
+ if (!selected) {
+ p->translate(0,1);
+ p->setPen(cg.dark());
+ p->drawLine(x, y, right, y);
+ }
+ break;
+ }
+ };
+
+ break;
+ }
+
+ // Popup menu scroller
+ // ------------------------------------------------------------------------
+ case CE_PopupMenuScroller: {
+ p->fillRect(r, cg.background());
+ drawPrimitive(PE_ButtonTool, p, r, cg, Style_Enabled);
+ drawPrimitive((flags & Style_Up) ? PE_ArrowUp : PE_ArrowDown, p, r, cg, Style_Enabled);
+ break;
+ }
+
+
+ // PROGRESSBAR
+ // ------------------------------------------------------------------------
+ case CE_ProgressBarGroove: {
+ QRect fr = subRect(SR_ProgressBarGroove, widget);
+ drawPrimitive(PE_Panel, p, fr, cg, Style_Sunken, QStyleOption::Default);
+ break;
+ }
+
+ case CE_ProgressBarContents: {
+ // ### Take into account totalSteps() for busy indicator
+ const QProgressBar* pb = (const QProgressBar*)widget;
+ QRect cr = subRect(SR_ProgressBarContents, widget);
+ double progress = pb->progress();
+ bool reverse = QApplication::reverseLayout();
+ int steps = pb->totalSteps();
+
+ if (!cr.isValid())
+ return;
+
+ // Draw progress bar
+ if (progress > 0 || steps == 0) {
+ double pg = (steps == 0) ? 0.1 : progress / steps;
+ int width = QMIN(cr.width(), (int)(pg * cr.width()));
+ if (steps == 0) { //Busy indicator
+
+ if (width < 1) width = 1; //A busy indicator with width 0 is kind of useless
+
+ int remWidth = cr.width() - width; //Never disappear completely
+ if (remWidth <= 0) remWidth = 1; //Do something non-crashy when too small...
+
+ int pstep = int(progress) % ( 2 * remWidth );
+
+ if ( pstep > remWidth ) {
+ //Bounce about.. We're remWidth + some delta, we want to be remWidth - delta...
+ // - ( (remWidth + some delta) - 2* remWidth ) = - (some deleta - remWidth) = remWidth - some delta..
+ pstep = - (pstep - 2 * remWidth );
+ }
+
+ if (reverse)
+ p->fillRect(cr.x() + cr.width() - width - pstep, cr.y(), width, cr.height(),
+ cg.brush(QColorGroup::Highlight));
+ else
+ p->fillRect(cr.x() + pstep, cr.y(), width, cr.height(),
+ cg.brush(QColorGroup::Highlight));
+
+ return;
+ }
+
+
+ // Do fancy gradient for highcolor displays
+ if (d->highcolor) {
+ QColor c(cg.highlight());
+ KPixmap pix;
+ pix.resize(cr.width(), cr.height());
+ KPixmapEffect::gradient(pix, reverse ? c.light(150) : c.dark(150),
+ reverse ? c.dark(150) : c.light(150),
+ KPixmapEffect::HorizontalGradient);
+ if (reverse)
+ p->drawPixmap(cr.x()+(cr.width()-width), cr.y(), pix,
+ cr.width()-width, 0, width, cr.height());
+ else
+ p->drawPixmap(cr.x(), cr.y(), pix, 0, 0, width, cr.height());
+ } else
+ if (reverse)
+ p->fillRect(cr.x()+(cr.width()-width), cr.y(), width, cr.height(),
+ cg.brush(QColorGroup::Highlight));
+ else
+ p->fillRect(cr.x(), cr.y(), width, cr.height(),
+ cg.brush(QColorGroup::Highlight));
+ }
+ break;
+ }
+
+ case CE_ProgressBarLabel: {
+ const QProgressBar* pb = (const QProgressBar*)widget;
+ QRect cr = subRect(SR_ProgressBarContents, widget);
+ double progress = pb->progress();
+ bool reverse = QApplication::reverseLayout();
+ int steps = pb->totalSteps();
+
+ if (!cr.isValid())
+ return;
+
+ QFont font = p->font();
+ font.setBold(true);
+ p->setFont(font);
+
+ // Draw label
+ if (progress > 0 || steps == 0) {
+ double pg = (steps == 0) ? 1.0 : progress / steps;
+ int width = QMIN(cr.width(), (int)(pg * cr.width()));
+ QRect crect;
+ if (reverse)
+ crect.setRect(cr.x()+(cr.width()-width), cr.y(), cr.width(), cr.height());
+ else
+ crect.setRect(cr.x()+width, cr.y(), cr.width(), cr.height());
+
+ p->save();
+ p->setPen(pb->isEnabled() ? (reverse ? cg.text() : cg.highlightedText()) : cg.text());
+ p->drawText(r, AlignCenter, pb->progressString());
+ p->setClipRect(crect);
+ p->setPen(reverse ? cg.highlightedText() : cg.text());
+ p->drawText(r, AlignCenter, pb->progressString());
+ p->restore();
+
+ } else {
+ p->setPen(cg.text());
+ p->drawText(r, AlignCenter, pb->progressString());
+ }
+
+ break;
+ }
+
+ default:
+ QCommonStyle::drawControl(element, p, widget, r, cg, flags, opt);
+ }
+}
+
+
+QRect KStyle::subRect(SubRect r, const QWidget* widget) const
+{
+ switch(r)
+ {
+ // KDE2 look smooth progress bar
+ // ------------------------------------------------------------------------
+ case SR_ProgressBarGroove:
+ return widget->rect();
+
+ case SR_ProgressBarContents:
+ case SR_ProgressBarLabel: {
+ // ### take into account indicatorFollowsStyle()
+ QRect rt = widget->rect();
+ return QRect(rt.x()+2, rt.y()+2, rt.width()-4, rt.height()-4);
+ }
+
+ default:
+ return QCommonStyle::subRect(r, widget);
+ }
+}
+
+
+int KStyle::pixelMetric(PixelMetric m, const QWidget* widget) const
+{
+ switch(m)
+ {
+ // BUTTONS
+ // ------------------------------------------------------------------------
+ case PM_ButtonShiftHorizontal: // Offset by 1
+ case PM_ButtonShiftVertical: // ### Make configurable
+ return 1;
+
+ case PM_DockWindowHandleExtent:
+ {
+ QWidget* parent = 0;
+ // Check that we are not a normal toolbar or a hidden dockwidget,
+ // in which case we need to adjust the height for font size
+ if (widget && (parent = widget->parentWidget() )
+ && !parent->inherits("QToolBar")
+ && !parent->inherits("QMainWindow")
+ && widget->inherits("QDockWindowHandle") )
+ return widget->fontMetrics().lineSpacing();
+ else
+ return QCommonStyle::pixelMetric(m, widget);
+ }
+
+ // TABS
+ // ------------------------------------------------------------------------
+ case PM_TabBarTabHSpace:
+ return 24;
+
+ case PM_TabBarTabVSpace: {
+ const QTabBar * tb = (const QTabBar *) widget;
+ if ( tb->shape() == QTabBar::RoundedAbove ||
+ tb->shape() == QTabBar::RoundedBelow )
+ return 10;
+ else
+ return 4;
+ }
+
+ case PM_TabBarTabOverlap: {
+ const QTabBar* tb = (const QTabBar*)widget;
+ QTabBar::Shape tbs = tb->shape();
+
+ if ( (tbs == QTabBar::RoundedAbove) ||
+ (tbs == QTabBar::RoundedBelow) )
+ return 0;
+ else
+ return 2;
+ }
+
+ // SLIDER
+ // ------------------------------------------------------------------------
+ case PM_SliderLength:
+ return 18;
+
+ case PM_SliderThickness:
+ return 24;
+
+ // Determines how much space to leave for the actual non-tickmark
+ // portion of the slider.
+ case PM_SliderControlThickness: {
+ const QSlider* slider = (const QSlider*)widget;
+ QSlider::TickSetting ts = slider->tickmarks();
+ int thickness = (slider->orientation() == Horizontal) ?
+ slider->height() : slider->width();
+ switch (ts) {
+ case QSlider::NoMarks: // Use total area.
+ break;
+ case QSlider::Both:
+ thickness = (thickness/2) + 3; // Use approx. 1/2 of area.
+ break;
+ default: // Use approx. 2/3 of area
+ thickness = ((thickness*2)/3) + 3;
+ break;
+ };
+ return thickness;
+ }
+
+ // SPLITTER
+ // ------------------------------------------------------------------------
+ case PM_SplitterWidth:
+ if (widget && widget->inherits("QDockWindowResizeHandle"))
+ return 8; // ### why do we need 2pix extra?
+ else
+ return 6;
+
+ // FRAMES
+ // ------------------------------------------------------------------------
+ case PM_MenuBarFrameWidth:
+ return 1;
+
+ case PM_DockWindowFrameWidth:
+ return 1;
+
+ // GENERAL
+ // ------------------------------------------------------------------------
+ case PM_MaximumDragDistance:
+ return -1;
+
+ case PM_MenuBarItemSpacing:
+ return 5;
+
+ case PM_ToolBarItemSpacing:
+ return 0;
+
+ case PM_PopupMenuScrollerHeight:
+ return pixelMetric( PM_ScrollBarExtent, 0);
+
+ default:
+ return QCommonStyle::pixelMetric( m, widget );
+ }
+}
+
+//Helper to find the next sibling that's not hidden
+static QListViewItem* nextVisibleSibling(QListViewItem* item)
+{
+ QListViewItem* sibling = item;
+ do
+ {
+ sibling = sibling->nextSibling();
+ }
+ while (sibling && !sibling->isVisible());
+
+ return sibling;
+}
+
+void KStyle::drawComplexControl( ComplexControl control,
+ QPainter* p,
+ const QWidget* widget,
+ const QRect &r,
+ const QColorGroup &cg,
+ SFlags flags,
+ SCFlags controls,
+ SCFlags active,
+ const QStyleOption &opt ) const
+{
+ switch(control)
+ {
+ // 3 BUTTON SCROLLBAR
+ // ------------------------------------------------------------------------
+ case CC_ScrollBar: {
+ // Many thanks to Brad Hughes for contributing this code.
+ bool useThreeButtonScrollBar = (d->scrollbarType & ThreeButtonScrollBar);
+
+ const QScrollBar *sb = (const QScrollBar*)widget;
+ bool maxedOut = (sb->minValue() == sb->maxValue());
+ bool horizontal = (sb->orientation() == Qt::Horizontal);
+ SFlags sflags = ((horizontal ? Style_Horizontal : Style_Default) |
+ (maxedOut ? Style_Default : Style_Enabled));
+
+ QRect addline, subline, subline2, addpage, subpage, slider, first, last;
+ subline = querySubControlMetrics(control, widget, SC_ScrollBarSubLine, opt);
+ addline = querySubControlMetrics(control, widget, SC_ScrollBarAddLine, opt);
+ subpage = querySubControlMetrics(control, widget, SC_ScrollBarSubPage, opt);
+ addpage = querySubControlMetrics(control, widget, SC_ScrollBarAddPage, opt);
+ slider = querySubControlMetrics(control, widget, SC_ScrollBarSlider, opt);
+ first = querySubControlMetrics(control, widget, SC_ScrollBarFirst, opt);
+ last = querySubControlMetrics(control, widget, SC_ScrollBarLast, opt);
+ subline2 = addline;
+
+ if ( useThreeButtonScrollBar )
+ if (horizontal)
+ subline2.moveBy(-addline.width(), 0);
+ else
+ subline2.moveBy(0, -addline.height());
+
+ // Draw the up/left button set
+ if ((controls & SC_ScrollBarSubLine) && subline.isValid()) {
+ drawPrimitive(PE_ScrollBarSubLine, p, subline, cg,
+ sflags | (active == SC_ScrollBarSubLine ?
+ Style_Down : Style_Default));
+
+ if (useThreeButtonScrollBar && subline2.isValid())
+ drawPrimitive(PE_ScrollBarSubLine, p, subline2, cg,
+ sflags | (active == SC_ScrollBarSubLine ?
+ Style_Down : Style_Default));
+ }
+
+ if ((controls & SC_ScrollBarAddLine) && addline.isValid())
+ drawPrimitive(PE_ScrollBarAddLine, p, addline, cg,
+ sflags | ((active == SC_ScrollBarAddLine) ?
+ Style_Down : Style_Default));
+
+ if ((controls & SC_ScrollBarSubPage) && subpage.isValid())
+ drawPrimitive(PE_ScrollBarSubPage, p, subpage, cg,
+ sflags | ((active == SC_ScrollBarSubPage) ?
+ Style_Down : Style_Default));
+
+ if ((controls & SC_ScrollBarAddPage) && addpage.isValid())
+ drawPrimitive(PE_ScrollBarAddPage, p, addpage, cg,
+ sflags | ((active == SC_ScrollBarAddPage) ?
+ Style_Down : Style_Default));
+
+ if ((controls & SC_ScrollBarFirst) && first.isValid())
+ drawPrimitive(PE_ScrollBarFirst, p, first, cg,
+ sflags | ((active == SC_ScrollBarFirst) ?
+ Style_Down : Style_Default));
+
+ if ((controls & SC_ScrollBarLast) && last.isValid())
+ drawPrimitive(PE_ScrollBarLast, p, last, cg,
+ sflags | ((active == SC_ScrollBarLast) ?
+ Style_Down : Style_Default));
+
+ if ((controls & SC_ScrollBarSlider) && slider.isValid()) {
+ drawPrimitive(PE_ScrollBarSlider, p, slider, cg,
+ sflags | ((active == SC_ScrollBarSlider) ?
+ Style_Down : Style_Default));
+ // Draw focus rect
+ if (sb->hasFocus()) {
+ QRect fr(slider.x() + 2, slider.y() + 2,
+ slider.width() - 5, slider.height() - 5);
+ drawPrimitive(PE_FocusRect, p, fr, cg, Style_Default);
+ }
+ }
+ break;
+ }
+
+
+ // SLIDER
+ // -------------------------------------------------------------------
+ case CC_Slider: {
+ const QSlider* slider = (const QSlider*)widget;
+ QRect groove = querySubControlMetrics(CC_Slider, widget, SC_SliderGroove, opt);
+ QRect handle = querySubControlMetrics(CC_Slider, widget, SC_SliderHandle, opt);
+
+ // Double-buffer slider for no flicker
+ QPixmap pix(widget->size());
+ QPainter p2;
+ p2.begin(&pix);
+
+ if ( slider->parentWidget() &&
+ slider->parentWidget()->backgroundPixmap() &&
+ !slider->parentWidget()->backgroundPixmap()->isNull() ) {
+ QPixmap pixmap = *(slider->parentWidget()->backgroundPixmap());
+ p2.drawTiledPixmap(r, pixmap, slider->pos());
+ } else
+ pix.fill(cg.background());
+
+ // Draw slider groove
+ if ((controls & SC_SliderGroove) && groove.isValid()) {
+ drawKStylePrimitive( KPE_SliderGroove, &p2, widget, groove, cg, flags, opt );
+
+ // Draw the focus rect around the groove
+ if (slider->hasFocus())
+ drawPrimitive(PE_FocusRect, &p2, groove, cg);
+ }
+
+ // Draw the tickmarks
+ if (controls & SC_SliderTickmarks)
+ QCommonStyle::drawComplexControl(control, &p2, widget,
+ r, cg, flags, SC_SliderTickmarks, active, opt);
+
+ // Draw the slider handle
+ if ((controls & SC_SliderHandle) && handle.isValid()) {
+ if (active == SC_SliderHandle)
+ flags |= Style_Active;
+ drawKStylePrimitive( KPE_SliderHandle, &p2, widget, handle, cg, flags, opt );
+ }
+
+ p2.end();
+ bitBlt((QWidget*)widget, r.x(), r.y(), &pix);
+ break;
+ }
+
+ // LISTVIEW
+ // -------------------------------------------------------------------
+ case CC_ListView: {
+
+ /*
+ * Many thanks to TrollTech AS for donating CC_ListView from QWindowsStyle.
+ * CC_ListView code is Copyright (C) 1998-2000 TrollTech AS.
+ */
+
+ // Paint the icon and text.
+ if ( controls & SC_ListView )
+ QCommonStyle::drawComplexControl( control, p, widget, r, cg, flags, controls, active, opt );
+
+ // If we're have a branch or are expanded...
+ if ( controls & (SC_ListViewBranch | SC_ListViewExpand) )
+ {
+ // If no list view item was supplied, break
+ if (opt.isDefault())
+ break;
+
+ QListViewItem *item = opt.listViewItem();
+ QListViewItem *child = item->firstChild();
+
+ int y = r.y();
+ int c; // dotline vertice count
+ int dotoffset = 0;
+ QPointArray dotlines;
+
+ if ( active == SC_All && controls == SC_ListViewExpand ) {
+ // We only need to draw a vertical line
+ c = 2;
+ dotlines.resize(2);
+ dotlines[0] = QPoint( r.right(), r.top() );
+ dotlines[1] = QPoint( r.right(), r.bottom() );
+
+ } else {
+
+ int linetop = 0, linebot = 0;
+ // each branch needs at most two lines, ie. four end points
+ dotoffset = (item->itemPos() + item->height() - y) % 2;
+ dotlines.resize( item->childCount() * 4 );
+ c = 0;
+
+ // skip the stuff above the exposed rectangle
+ while ( child && y + child->height() <= 0 )
+ {
+ y += child->totalHeight();
+ child = nextVisibleSibling(child);
+ }
+
+ int bx = r.width() / 2;
+
+ // paint stuff in the magical area
+ QListView* v = item->listView();
+ int lh = QMAX( p->fontMetrics().height() + 2 * v->itemMargin(),
+ QApplication::globalStrut().height() );
+ if ( lh % 2 > 0 )
+ lh++;
+
+ // Draw all the expand/close boxes...
+ QRect boxrect;
+ QStyle::StyleFlags boxflags;
+ while ( child && y < r.height() )
+ {
+ linebot = y + lh/2;
+ if ( (child->isExpandable() || child->childCount()) &&
+ (child->height() > 0) )
+ {
+ // The primitive requires a rect.
+ boxrect = QRect( bx-4, linebot-4, 9, 9 );
+ boxflags = child->isOpen() ? QStyle::Style_Off : QStyle::Style_On;
+
+ // KStyle extension: Draw the box and expand/collapse indicator
+ drawKStylePrimitive( KPE_ListViewExpander, p, NULL, boxrect, cg, boxflags, opt );
+
+ // dotlinery
+ p->setPen( cg.mid() );
+ dotlines[c++] = QPoint( bx, linetop );
+ dotlines[c++] = QPoint( bx, linebot - 5 );
+ dotlines[c++] = QPoint( bx + 5, linebot );
+ dotlines[c++] = QPoint( r.width(), linebot );
+ linetop = linebot + 5;
+ } else {
+ // just dotlinery
+ dotlines[c++] = QPoint( bx+1, linebot );
+ dotlines[c++] = QPoint( r.width(), linebot );
+ }
+
+ y += child->totalHeight();
+ child = nextVisibleSibling(child);
+ }
+
+ if ( child ) // there's a child to draw, so move linebot to edge of rectangle
+ linebot = r.height();
+
+ if ( linetop < linebot )
+ {
+ dotlines[c++] = QPoint( bx, linetop );
+ dotlines[c++] = QPoint( bx, linebot );
+ }
+ }
+
+ // Draw all the branches...
+ static int thickness = kPixelMetric( KPM_ListViewBranchThickness );
+ int line; // index into dotlines
+ QRect branchrect;
+ QStyle::StyleFlags branchflags;
+ for( line = 0; line < c; line += 2 )
+ {
+ // assumptions here: lines are horizontal or vertical.
+ // lines always start with the numerically lowest
+ // coordinate.
+
+ // point ... relevant coordinate of current point
+ // end ..... same coordinate of the end of the current line
+ // other ... the other coordinate of the current point/line
+ if ( dotlines[line].y() == dotlines[line+1].y() )
+ {
+ // Horizontal branch
+ int end = dotlines[line+1].x();
+ int point = dotlines[line].x();
+ int other = dotlines[line].y();
+
+ branchrect = QRect( point, other-(thickness/2), end-point, thickness );
+ branchflags = QStyle::Style_Horizontal;
+
+ // KStyle extension: Draw the horizontal branch
+ drawKStylePrimitive( KPE_ListViewBranch, p, NULL, branchrect, cg, branchflags, opt );
+
+ } else {
+ // Vertical branch
+ int end = dotlines[line+1].y();
+ int point = dotlines[line].y();
+ int other = dotlines[line].x();
+ int pixmapoffset = ((point & 1) != dotoffset ) ? 1 : 0;
+
+ branchrect = QRect( other-(thickness/2), point, thickness, end-point );
+ if (!pixmapoffset) // ### Hackish - used to hint the offset
+ branchflags = QStyle::Style_NoChange;
+ else
+ branchflags = QStyle::Style_Default;
+
+ // KStyle extension: Draw the vertical branch
+ drawKStylePrimitive( KPE_ListViewBranch, p, NULL, branchrect, cg, branchflags, opt );
+ }
+ }
+ }
+ break;
+ }
+
+ default:
+ QCommonStyle::drawComplexControl( control, p, widget, r, cg,
+ flags, controls, active, opt );
+ break;
+ }
+}
+
+
+QStyle::SubControl KStyle::querySubControl( ComplexControl control,
+ const QWidget* widget,
+ const QPoint &pos,
+ const QStyleOption &opt ) const
+{
+ QStyle::SubControl ret = QCommonStyle::querySubControl(control, widget, pos, opt);
+
+ if (d->scrollbarType == ThreeButtonScrollBar) {
+ // Enable third button
+ if (control == CC_ScrollBar && ret == SC_None)
+ ret = SC_ScrollBarSubLine;
+ }
+ return ret;
+}
+
+
+QRect KStyle::querySubControlMetrics( ComplexControl control,
+ const QWidget* widget,
+ SubControl sc,
+ const QStyleOption &opt ) const
+{
+ QRect ret;
+
+ if (control == CC_ScrollBar)
+ {
+ bool threeButtonScrollBar = d->scrollbarType & ThreeButtonScrollBar;
+ bool platinumScrollBar = d->scrollbarType & PlatinumStyleScrollBar;
+ bool nextScrollBar = d->scrollbarType & NextStyleScrollBar;
+
+ const QScrollBar *sb = (const QScrollBar*)widget;
+ bool horizontal = sb->orientation() == Qt::Horizontal;
+ int sliderstart = sb->sliderStart();
+ int sbextent = pixelMetric(PM_ScrollBarExtent, widget);
+ int maxlen = (horizontal ? sb->width() : sb->height())
+ - (sbextent * (threeButtonScrollBar ? 3 : 2));
+ int sliderlen;
+
+ // calculate slider length
+ if (sb->maxValue() != sb->minValue())
+ {
+ uint range = sb->maxValue() - sb->minValue();
+ sliderlen = (sb->pageStep() * maxlen) / (range + sb->pageStep());
+
+ int slidermin = pixelMetric( PM_ScrollBarSliderMin, widget );
+ if ( sliderlen < slidermin || range > INT_MAX / 2 )
+ sliderlen = slidermin;
+ if ( sliderlen > maxlen )
+ sliderlen = maxlen;
+ } else
+ sliderlen = maxlen;
+
+ // Subcontrols
+ switch (sc)
+ {
+ case SC_ScrollBarSubLine: {
+ // top/left button
+ if (platinumScrollBar) {
+ if (horizontal)
+ ret.setRect(sb->width() - 2 * sbextent, 0, sbextent, sbextent);
+ else
+ ret.setRect(0, sb->height() - 2 * sbextent, sbextent, sbextent);
+ } else
+ ret.setRect(0, 0, sbextent, sbextent);
+ break;
+ }
+
+ case SC_ScrollBarAddLine: {
+ // bottom/right button
+ if (nextScrollBar) {
+ if (horizontal)
+ ret.setRect(sbextent, 0, sbextent, sbextent);
+ else
+ ret.setRect(0, sbextent, sbextent, sbextent);
+ } else {
+ if (horizontal)
+ ret.setRect(sb->width() - sbextent, 0, sbextent, sbextent);
+ else
+ ret.setRect(0, sb->height() - sbextent, sbextent, sbextent);
+ }
+ break;
+ }
+
+ case SC_ScrollBarSubPage: {
+ // between top/left button and slider
+ if (platinumScrollBar) {
+ if (horizontal)
+ ret.setRect(0, 0, sliderstart, sbextent);
+ else
+ ret.setRect(0, 0, sbextent, sliderstart);
+ } else if (nextScrollBar) {
+ if (horizontal)
+ ret.setRect(sbextent*2, 0, sliderstart-2*sbextent, sbextent);
+ else
+ ret.setRect(0, sbextent*2, sbextent, sliderstart-2*sbextent);
+ } else {
+ if (horizontal)
+ ret.setRect(sbextent, 0, sliderstart - sbextent, sbextent);
+ else
+ ret.setRect(0, sbextent, sbextent, sliderstart - sbextent);
+ }
+ break;
+ }
+
+ case SC_ScrollBarAddPage: {
+ // between bottom/right button and slider
+ int fudge;
+
+ if (platinumScrollBar)
+ fudge = 0;
+ else if (nextScrollBar)
+ fudge = 2*sbextent;
+ else
+ fudge = sbextent;
+
+ if (horizontal)
+ ret.setRect(sliderstart + sliderlen, 0,
+ maxlen - sliderstart - sliderlen + fudge, sbextent);
+ else
+ ret.setRect(0, sliderstart + sliderlen, sbextent,
+ maxlen - sliderstart - sliderlen + fudge);
+ break;
+ }
+
+ case SC_ScrollBarGroove: {
+ int multi = threeButtonScrollBar ? 3 : 2;
+ int fudge;
+
+ if (platinumScrollBar)
+ fudge = 0;
+ else if (nextScrollBar)
+ fudge = 2*sbextent;
+ else
+ fudge = sbextent;
+
+ if (horizontal)
+ ret.setRect(fudge, 0, sb->width() - sbextent * multi, sb->height());
+ else
+ ret.setRect(0, fudge, sb->width(), sb->height() - sbextent * multi);
+ break;
+ }
+
+ case SC_ScrollBarSlider: {
+ if (horizontal)
+ ret.setRect(sliderstart, 0, sliderlen, sbextent);
+ else
+ ret.setRect(0, sliderstart, sbextent, sliderlen);
+ break;
+ }
+
+ default:
+ ret = QCommonStyle::querySubControlMetrics(control, widget, sc, opt);
+ break;
+ }
+ } else
+ ret = QCommonStyle::querySubControlMetrics(control, widget, sc, opt);
+
+ return ret;
+}
+
+static const char * const kstyle_close_xpm[] = {
+"12 12 2 1",
+"# c #000000",
+". c None",
+"............",
+"............",
+"..##....##..",
+"...##..##...",
+"....####....",
+".....##.....",
+"....####....",
+"...##..##...",
+"..##....##..",
+"............",
+"............",
+"............"};
+
+static const char * const kstyle_maximize_xpm[]={
+"12 12 2 1",
+"# c #000000",
+". c None",
+"............",
+"............",
+".##########.",
+".##########.",
+".#........#.",
+".#........#.",
+".#........#.",
+".#........#.",
+".#........#.",
+".#........#.",
+".##########.",
+"............"};
+
+
+static const char * const kstyle_minimize_xpm[] = {
+"12 12 2 1",
+"# c #000000",
+". c None",
+"............",
+"............",
+"............",
+"............",
+"............",
+"............",
+"............",
+"...######...",
+"...######...",
+"............",
+"............",
+"............"};
+
+static const char * const kstyle_normalizeup_xpm[] = {
+"12 12 2 1",
+"# c #000000",
+". c None",
+"............",
+"...#######..",
+"...#######..",
+"...#.....#..",
+".#######.#..",
+".#######.#..",
+".#.....#.#..",
+".#.....###..",
+".#.....#....",
+".#.....#....",
+".#######....",
+"............"};
+
+
+static const char * const kstyle_shade_xpm[] = {
+"12 12 2 1",
+"# c #000000",
+". c None",
+"............",
+"............",
+"............",
+"............",
+"............",
+".....#......",
+"....###.....",
+"...#####....",
+"..#######...",
+"............",
+"............",
+"............"};
+
+static const char * const kstyle_unshade_xpm[] = {
+"12 12 2 1",
+"# c #000000",
+". c None",
+"............",
+"............",
+"............",
+"............",
+"..#######...",
+"...#####....",
+"....###.....",
+".....#......",
+"............",
+"............",
+"............",
+"............"};
+
+static const char * const dock_window_close_xpm[] = {
+"8 8 2 1",
+"# c #000000",
+". c None",
+"##....##",
+".##..##.",
+"..####..",
+"...##...",
+"..####..",
+".##..##.",
+"##....##",
+"........"};
+
+// Message box icons, from page 210 of the Windows style guide.
+
+// Hand-drawn to resemble Microsoft's icons, but in the Mac/Netscape
+// palette. The "question mark" icon, which Microsoft recommends not
+// using but a lot of people still use, is left out.
+
+/* XPM */
+static const char * const information_xpm[]={
+"32 32 5 1",
+". c None",
+"c c #000000",
+"* c #999999",
+"a c #ffffff",
+"b c #0000ff",
+"...........********.............",
+"........***aaaaaaaa***..........",
+"......**aaaaaaaaaaaaaa**........",
+".....*aaaaaaaaaaaaaaaaaa*.......",
+"....*aaaaaaaabbbbaaaaaaaac......",
+"...*aaaaaaaabbbbbbaaaaaaaac.....",
+"..*aaaaaaaaabbbbbbaaaaaaaaac....",
+".*aaaaaaaaaaabbbbaaaaaaaaaaac...",
+".*aaaaaaaaaaaaaaaaaaaaaaaaaac*..",
+"*aaaaaaaaaaaaaaaaaaaaaaaaaaaac*.",
+"*aaaaaaaaaabbbbbbbaaaaaaaaaaac*.",
+"*aaaaaaaaaaaabbbbbaaaaaaaaaaac**",
+"*aaaaaaaaaaaabbbbbaaaaaaaaaaac**",
+"*aaaaaaaaaaaabbbbbaaaaaaaaaaac**",
+"*aaaaaaaaaaaabbbbbaaaaaaaaaaac**",
+"*aaaaaaaaaaaabbbbbaaaaaaaaaaac**",
+".*aaaaaaaaaaabbbbbaaaaaaaaaac***",
+".*aaaaaaaaaaabbbbbaaaaaaaaaac***",
+"..*aaaaaaaaaabbbbbaaaaaaaaac***.",
+"...caaaaaaabbbbbbbbbaaaaaac****.",
+"....caaaaaaaaaaaaaaaaaaaac****..",
+".....caaaaaaaaaaaaaaaaaac****...",
+"......ccaaaaaaaaaaaaaacc****....",
+".......*cccaaaaaaaaccc*****.....",
+"........***cccaaaac*******......",
+"..........****caaac*****........",
+".............*caaac**...........",
+"...............caac**...........",
+"................cac**...........",
+".................cc**...........",
+"..................***...........",
+"...................**..........."};
+/* XPM */
+static const char* const warning_xpm[]={
+"32 32 4 1",
+". c None",
+"a c #ffff00",
+"* c #000000",
+"b c #999999",
+".............***................",
+"............*aaa*...............",
+"...........*aaaaa*b.............",
+"...........*aaaaa*bb............",
+"..........*aaaaaaa*bb...........",
+"..........*aaaaaaa*bb...........",
+".........*aaaaaaaaa*bb..........",
+".........*aaaaaaaaa*bb..........",
+"........*aaaaaaaaaaa*bb.........",
+"........*aaaa***aaaa*bb.........",
+".......*aaaa*****aaaa*bb........",
+".......*aaaa*****aaaa*bb........",
+"......*aaaaa*****aaaaa*bb.......",
+"......*aaaaa*****aaaaa*bb.......",
+".....*aaaaaa*****aaaaaa*bb......",
+".....*aaaaaa*****aaaaaa*bb......",
+"....*aaaaaaaa***aaaaaaaa*bb.....",
+"....*aaaaaaaa***aaaaaaaa*bb.....",
+"...*aaaaaaaaa***aaaaaaaaa*bb....",
+"...*aaaaaaaaaa*aaaaaaaaaa*bb....",
+"..*aaaaaaaaaaa*aaaaaaaaaaa*bb...",
+"..*aaaaaaaaaaaaaaaaaaaaaaa*bb...",
+".*aaaaaaaaaaaa**aaaaaaaaaaa*bb..",
+".*aaaaaaaaaaa****aaaaaaaaaa*bb..",
+"*aaaaaaaaaaaa****aaaaaaaaaaa*bb.",
+"*aaaaaaaaaaaaa**aaaaaaaaaaaa*bb.",
+"*aaaaaaaaaaaaaaaaaaaaaaaaaaa*bbb",
+"*aaaaaaaaaaaaaaaaaaaaaaaaaaa*bbb",
+".*aaaaaaaaaaaaaaaaaaaaaaaaa*bbbb",
+"..*************************bbbbb",
+"....bbbbbbbbbbbbbbbbbbbbbbbbbbb.",
+".....bbbbbbbbbbbbbbbbbbbbbbbbb.."};
+/* XPM */
+static const char* const critical_xpm[]={
+"32 32 4 1",
+". c None",
+"a c #999999",
+"* c #ff0000",
+"b c #ffffff",
+"...........********.............",
+".........************...........",
+".......****************.........",
+"......******************........",
+".....********************a......",
+"....**********************a.....",
+"...************************a....",
+"..*******b**********b*******a...",
+"..******bbb********bbb******a...",
+".******bbbbb******bbbbb******a..",
+".*******bbbbb****bbbbb*******a..",
+"*********bbbbb**bbbbb*********a.",
+"**********bbbbbbbbbb**********a.",
+"***********bbbbbbbb***********aa",
+"************bbbbbb************aa",
+"************bbbbbb************aa",
+"***********bbbbbbbb***********aa",
+"**********bbbbbbbbbb**********aa",
+"*********bbbbb**bbbbb*********aa",
+".*******bbbbb****bbbbb*******aa.",
+".******bbbbb******bbbbb******aa.",
+"..******bbb********bbb******aaa.",
+"..*******b**********b*******aa..",
+"...************************aaa..",
+"....**********************aaa...",
+"....a********************aaa....",
+".....a******************aaa.....",
+"......a****************aaa......",
+".......aa************aaaa.......",
+".........aa********aaaaa........",
+"...........aaaaaaaaaaa..........",
+".............aaaaaaa............"};
+
+QPixmap KStyle::stylePixmap( StylePixmap stylepixmap,
+ const QWidget* widget,
+ const QStyleOption& opt) const
+{
+ switch (stylepixmap) {
+ case SP_TitleBarShadeButton:
+ return QPixmap(const_cast<const char**>(kstyle_shade_xpm));
+ case SP_TitleBarUnshadeButton:
+ return QPixmap(const_cast<const char**>(kstyle_unshade_xpm));
+ case SP_TitleBarNormalButton:
+ return QPixmap(const_cast<const char**>(kstyle_normalizeup_xpm));
+ case SP_TitleBarMinButton:
+ return QPixmap(const_cast<const char**>(kstyle_minimize_xpm));
+ case SP_TitleBarMaxButton:
+ return QPixmap(const_cast<const char**>(kstyle_maximize_xpm));
+ case SP_TitleBarCloseButton:
+ return QPixmap(const_cast<const char**>(kstyle_close_xpm));
+ case SP_DockWindowCloseButton:
+ return QPixmap(const_cast<const char**>(dock_window_close_xpm ));
+ case SP_MessageBoxInformation:
+ return QPixmap(const_cast<const char**>(information_xpm));
+ case SP_MessageBoxWarning:
+ return QPixmap(const_cast<const char**>(warning_xpm));
+ case SP_MessageBoxCritical:
+ return QPixmap(const_cast<const char**>(critical_xpm));
+ default:
+ break;
+ }
+ return QCommonStyle::stylePixmap(stylepixmap, widget, opt);
+}
+
+
+int KStyle::styleHint( StyleHint sh, const QWidget* w,
+ const QStyleOption &opt, QStyleHintReturn* shr) const
+{
+ switch (sh)
+ {
+ case SH_EtchDisabledText:
+ return d->etchDisabledText ? 1 : 0;
+
+ case SH_PopupMenu_Scrollable:
+ return d->scrollablePopupmenus ? 1 : 0;
+
+ case SH_MenuBar_AltKeyNavigation:
+ return d->menuAltKeyNavigation ? 1 : 0;
+
+ case SH_PopupMenu_SubMenuPopupDelay:
+ if ( styleHint( SH_PopupMenu_SloppySubMenus, w ) )
+ return QMIN( 100, d->popupMenuDelay );
+ else
+ return d->popupMenuDelay;
+
+ case SH_PopupMenu_SloppySubMenus:
+ return d->sloppySubMenus;
+
+ case SH_ItemView_ChangeHighlightOnFocus:
+ case SH_Slider_SloppyKeyEvents:
+ case SH_MainWindow_SpaceBelowMenuBar:
+ case SH_PopupMenu_AllowActiveAndDisabled:
+ return 0;
+
+ case SH_Slider_SnapToValue:
+ case SH_PrintDialog_RightAlignButtons:
+ case SH_FontDialog_SelectAssociatedText:
+ case SH_MenuBar_MouseTracking:
+ case SH_PopupMenu_MouseTracking:
+ case SH_ComboBox_ListMouseTracking:
+ case SH_ScrollBar_MiddleClickAbsolutePosition:
+ return 1;
+ case SH_LineEdit_PasswordCharacter:
+ {
+ if (w) {
+ const QFontMetrics &fm = w->fontMetrics();
+ if (fm.inFont(QChar(0x25CF))) {
+ return 0x25CF;
+ } else if (fm.inFont(QChar(0x2022))) {
+ return 0x2022;
+ }
+ }
+ return '*';
+ }
+
+ default:
+ return QCommonStyle::styleHint(sh, w, opt, shr);
+ }
+}
+
+
+bool KStyle::eventFilter( QObject* object, QEvent* event )
+{
+ if ( d->useFilledFrameWorkaround )
+ {
+ // Make the QMenuBar/QToolBar paintEvent() cover a larger area to
+ // ensure that the filled frame contents are properly painted.
+ // We essentially modify the paintEvent's rect to include the
+ // panel border, which also paints the widget's interior.
+ // This is nasty, but I see no other way to properly repaint
+ // filled frames in all QMenuBars and QToolBars.
+ // -- Karol.
+ QFrame *frame = 0;
+ if ( event->type() == QEvent::Paint
+ && (frame = ::qt_cast<QFrame*>(object)) )
+ {
+ if (frame->frameShape() != QFrame::ToolBarPanel && frame->frameShape() != QFrame::MenuBarPanel)
+ return false;
+
+ bool horizontal = true;
+ QPaintEvent* pe = (QPaintEvent*)event;
+ QToolBar *toolbar = ::qt_cast< QToolBar *>( frame );
+ QRect r = pe->rect();
+
+ if (toolbar && toolbar->orientation() == Qt::Vertical)
+ horizontal = false;
+
+ if (horizontal) {
+ if ( r.height() == frame->height() )
+ return false; // Let QFrame handle the painting now.
+
+ // Else, send a new paint event with an updated paint rect.
+ QPaintEvent dummyPE( QRect( r.x(), 0, r.width(), frame->height()) );
+ QApplication::sendEvent( frame, &dummyPE );
+ }
+ else { // Vertical
+ if ( r.width() == frame->width() )
+ return false;
+
+ QPaintEvent dummyPE( QRect( 0, r.y(), frame->width(), r.height()) );
+ QApplication::sendEvent( frame, &dummyPE );
+ }
+
+ // Discard this event as we sent a new paintEvent.
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+// -----------------------------------------------------------------------------
+// I N T E R N A L - KStyle menu transparency handler
+// -----------------------------------------------------------------------------
+
+TransparencyHandler::TransparencyHandler( KStyle* style,
+ TransparencyEngine tEngine, float menuOpacity, bool useDropShadow )
+ : QObject()
+{
+ te = tEngine;
+ kstyle = style;
+ opacity = menuOpacity;
+ dropShadow = useDropShadow;
+ pix.setOptimization(QPixmap::BestOptim);
+}
+
+TransparencyHandler::~TransparencyHandler()
+{
+}
+
+// This is meant to be ugly but fast.
+void TransparencyHandler::rightShadow(QImage& dst)
+{
+ if (dst.depth() != 32)
+ dst = dst.convertDepth(32);
+
+ // blend top-right corner.
+ int pixels = dst.width() * dst.height();
+#ifdef WORDS_BIGENDIAN
+ register unsigned char* data = dst.bits() + 1; // Skip alpha
+#else
+ register unsigned char* data = dst.bits(); // Skip alpha
+#endif
+ for(register int i = 0; i < 16; i++) {
+ *data = (unsigned char)((*data)*top_right_corner[i]); data++;
+ *data = (unsigned char)((*data)*top_right_corner[i]); data++;
+ *data = (unsigned char)((*data)*top_right_corner[i]); data++;
+ data++; // skip alpha
+ }
+
+ pixels -= 32; // tint right strip without rounded edges.
+ register int c = 0;
+ for(register int i = 0; i < pixels; i++) {
+ *data = (unsigned char)((*data)*shadow_strip[c]); data++;
+ *data = (unsigned char)((*data)*shadow_strip[c]); data++;
+ *data = (unsigned char)((*data)*shadow_strip[c]); data++;
+ data++; // skip alpha
+ ++c;
+ c %= 4;
+ }
+
+ // tint bottom edge
+ for(register int i = 0; i < 16; i++) {
+ *data = (unsigned char)((*data)*bottom_right_corner[i]); data++;
+ *data = (unsigned char)((*data)*bottom_right_corner[i]); data++;
+ *data = (unsigned char)((*data)*bottom_right_corner[i]); data++;
+ data++; // skip alpha
+ }
+}
+
+void TransparencyHandler::bottomShadow(QImage& dst)
+{
+ if (dst.depth() != 32)
+ dst = dst.convertDepth(32);
+
+ int line = 0;
+ int width = dst.width() - 4;
+ double strip_data = shadow_strip[0];
+ double* corner = const_cast<double*>(bottom_left_corner);
+
+#ifdef WORDS_BIGENDIAN
+ register unsigned char* data = dst.bits() + 1; // Skip alpha
+#else
+ register unsigned char* data = dst.bits(); // Skip alpha
+#endif
+
+ for(int y = 0; y < 4; y++)
+ {
+ // Bottom-left Corner
+ for(register int x = 0; x < 4; x++) {
+ *data = (unsigned char)((*data)*(*corner)); data++;
+ *data = (unsigned char)((*data)*(*corner)); data++;
+ *data = (unsigned char)((*data)*(*corner)); data++;
+ data++; // skip alpha
+ corner++;
+ }
+
+ // Scanline
+ for(register int x = 0; x < width; x++) {
+ *data = (unsigned char)((*data)*strip_data); data++;
+ *data = (unsigned char)((*data)*strip_data); data++;
+ *data = (unsigned char)((*data)*strip_data); data++;
+ data++;
+ }
+
+ strip_data = shadow_strip[++line];
+ }
+}
+
+// Create a shadow of thickness 4.
+void TransparencyHandler::createShadowWindows(const QPopupMenu* p)
+{
+#ifdef Q_WS_X11
+ int x2 = p->x()+p->width();
+ int y2 = p->y()+p->height();
+ QRect shadow1(x2, p->y() + 4, 4, p->height());
+ QRect shadow2(p->x() + 4, y2, p->width() - 4, 4);
+
+ // Create a fake drop-down shadow effect via blended Xwindows
+ ShadowElements se;
+ se.w1 = new QWidget(0, 0, WStyle_Customize | WType_Popup | WX11BypassWM );
+ se.w2 = new QWidget(0, 0, WStyle_Customize | WType_Popup | WX11BypassWM );
+ se.w1->setGeometry(shadow1);
+ se.w2->setGeometry(shadow2);
+ XSelectInput(qt_xdisplay(), se.w1->winId(), StructureNotifyMask );
+ XSelectInput(qt_xdisplay(), se.w2->winId(), StructureNotifyMask );
+
+ // Insert a new ShadowMap entry
+ shadowMap()[p] = se;
+
+ // Some hocus-pocus here to create the drop-shadow.
+ QPixmap pix_shadow1 = QPixmap::grabWindow(qt_xrootwin(),
+ shadow1.x(), shadow1.y(), shadow1.width(), shadow1.height());
+ QPixmap pix_shadow2 = QPixmap::grabWindow(qt_xrootwin(),
+ shadow2.x(), shadow2.y(), shadow2.width(), shadow2.height());
+
+ QImage img;
+ img = pix_shadow1.convertToImage();
+ rightShadow(img);
+ pix_shadow1.convertFromImage(img);
+ img = pix_shadow2.convertToImage();
+ bottomShadow(img);
+ pix_shadow2.convertFromImage(img);
+
+ // Set the background pixmaps
+ se.w1->setErasePixmap(pix_shadow1);
+ se.w2->setErasePixmap(pix_shadow2);
+
+ // Show the 'shadow' just before showing the popup menu window
+ // Don't use QWidget::show() so we don't confuse QEffects, thus causing broken focus.
+ XMapWindow(qt_xdisplay(), se.w1->winId());
+ XMapWindow(qt_xdisplay(), se.w2->winId());
+#else
+ Q_UNUSED( p )
+#endif
+}
+
+void TransparencyHandler::removeShadowWindows(const QPopupMenu* p)
+{
+#ifdef Q_WS_X11
+ ShadowMap::iterator it = shadowMap().find(p);
+ if (it != shadowMap().end())
+ {
+ ShadowElements se = it.data();
+ XUnmapWindow(qt_xdisplay(), se.w1->winId()); // hide
+ XUnmapWindow(qt_xdisplay(), se.w2->winId());
+ XFlush(qt_xdisplay()); // try to hide faster
+ delete se.w1;
+ delete se.w2;
+ shadowMap().erase(it);
+ }
+#else
+ Q_UNUSED( p )
+#endif
+}
+
+bool TransparencyHandler::eventFilter( QObject* object, QEvent* event )
+{
+#if !defined Q_WS_MAC && !defined Q_WS_WIN
+ // Transparency idea was borrowed from KDE2's "MegaGradient" Style,
+ // Copyright (C) 2000 Daniel M. Duley <mosfet@kde.org>
+
+ // Added 'fake' menu shadows <04-Jul-2002> -- Karol
+ QPopupMenu* p = (QPopupMenu*)object;
+ QEvent::Type et = event->type();
+
+ if (et == QEvent::Show)
+ {
+ // Handle translucency
+ if (te != Disabled)
+ {
+ pix = QPixmap::grabWindow(qt_xrootwin(),
+ p->x(), p->y(), p->width(), p->height());
+
+ switch (te) {
+#ifdef HAVE_XRENDER
+ case XRender:
+ if (qt_use_xrender) {
+ XRenderBlendToPixmap(p);
+ break;
+ }
+ // Fall through intended
+#else
+ case XRender:
+#endif
+ case SoftwareBlend:
+ blendToPixmap(p->colorGroup(), p);
+ break;
+
+ case SoftwareTint:
+ default:
+ blendToColor(p->colorGroup().button());
+ };
+
+ p->setErasePixmap(pix);
+ }
+
+ // Handle drop shadow
+ // * FIXME : !shadowMap().contains(p) is a workaround for leftover
+ // * shadows after duplicate show events.
+ // * TODO : determine real cause for duplicate events
+ // * till 20021005
+ if (dropShadow && p->width() > 16 && p->height() > 16 && !shadowMap().contains( p ))
+ createShadowWindows(p);
+ }
+ else if (et == QEvent::Hide)
+ {
+ // Handle drop shadow
+ if (dropShadow)
+ removeShadowWindows(p);
+
+ // Handle translucency
+ if (te != Disabled)
+ p->setErasePixmap(QPixmap());
+ }
+
+#endif
+ return false;
+}
+
+
+// Blends a QImage to a predefined color, with a given opacity.
+void TransparencyHandler::blendToColor(const QColor &col)
+{
+ if (opacity < 0.0 || opacity > 1.0)
+ return;
+
+ QImage img = pix.convertToImage();
+ KImageEffect::blend(col, img, opacity);
+ pix.convertFromImage(img);
+}
+
+
+void TransparencyHandler::blendToPixmap(const QColorGroup &cg, const QPopupMenu* p)
+{
+ if (opacity < 0.0 || opacity > 1.0)
+ return;
+
+ KPixmap blendPix;
+ blendPix.resize( pix.width(), pix.height() );
+
+ if (blendPix.width() != pix.width() ||
+ blendPix.height() != pix.height())
+ return;
+
+ // Allow styles to define the blend pixmap - allows for some interesting effects.
+ kstyle->renderMenuBlendPixmap( blendPix, cg, p );
+
+ QImage blendImg = blendPix.convertToImage();
+ QImage backImg = pix.convertToImage();
+ KImageEffect::blend(blendImg, backImg, opacity);
+ pix.convertFromImage(backImg);
+}
+
+
+#ifdef HAVE_XRENDER
+// Here we go, use XRender in all its glory.
+// NOTE: This is actually a bit slower than the above routines
+// on non-accelerated displays. -- Karol.
+void TransparencyHandler::XRenderBlendToPixmap(const QPopupMenu* p)
+{
+ KPixmap renderPix;
+ renderPix.resize( pix.width(), pix.height() );
+
+ // Allow styles to define the blend pixmap - allows for some interesting effects.
+ kstyle->renderMenuBlendPixmap( renderPix, p->colorGroup(), p );
+
+ Display* dpy = qt_xdisplay();
+ Pixmap alphaPixmap;
+ Picture alphaPicture;
+ XRenderPictFormat Rpf;
+ XRenderPictureAttributes Rpa;
+ XRenderColor clr;
+ clr.alpha = ((unsigned short)(255*opacity) << 8);
+
+ Rpf.type = PictTypeDirect;
+ Rpf.depth = 8;
+ Rpf.direct.alphaMask = 0xff;
+ Rpa.repeat = True; // Tile
+
+ XRenderPictFormat* xformat = XRenderFindFormat(dpy,
+ PictFormatType | PictFormatDepth | PictFormatAlphaMask, &Rpf, 0);
+
+ alphaPixmap = XCreatePixmap(dpy, p->handle(), 1, 1, 8);
+ alphaPicture = XRenderCreatePicture(dpy, alphaPixmap, xformat, CPRepeat, &Rpa);
+
+ XRenderFillRectangle(dpy, PictOpSrc, alphaPicture, &clr, 0, 0, 1, 1);
+
+ XRenderComposite(dpy, PictOpOver,
+ renderPix.x11RenderHandle(), alphaPicture, pix.x11RenderHandle(), // src, mask, dst
+ 0, 0, // srcx, srcy
+ 0, 0, // maskx, masky
+ 0, 0, // dstx, dsty
+ pix.width(), pix.height());
+
+ XRenderFreePicture(dpy, alphaPicture);
+ XFreePixmap(dpy, alphaPixmap);
+}
+#endif
+
+void KStyle::virtual_hook( int, void* )
+{ /*BASE::virtual_hook( id, data );*/ }
+
+// vim: set noet ts=4 sw=4:
+// kate: indent-width 4; replace-tabs off; tab-width 4; space-indent off;
+
+#include "kstyle.moc"
diff --git a/kdefx/kstyle.h b/kdefx/kstyle.h
new file mode 100644
index 000000000..91d581ac7
--- /dev/null
+++ b/kdefx/kstyle.h
@@ -0,0 +1,344 @@
+/*
+ * $Id$
+ *
+ * KStyle
+ * Copyright (C) 2001-2002 Karol Szwed <gallium@kde.org>
+ *
+ * QWindowsStyle CC_ListView and style images were kindly donated by TrollTech,
+ * Copyright (C) 1998-2000 TrollTech AS.
+ *
+ * Many thanks to Bradley T. Hughes for the 3 button scrollbar code.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __KSTYLE_H
+#define __KSTYLE_H
+
+// W A R N I N G
+// -------------
+// This API is still subject to change.
+// I will remove this warning when I feel the API is sufficiently flexible.
+
+#include <qcommonstyle.h>
+
+#include <kdelibs_export.h>
+
+class KPixmap;
+
+struct KStylePrivate;
+/**
+ * Simplifies and extends the QStyle API to make style coding easier.
+ *
+ * The KStyle class provides a simple internal menu transparency engine
+ * which attempts to use XRender for accelerated blending where requested,
+ * or falls back to fast internal software tinting/blending routines.
+ * It also simplifies more complex portions of the QStyle API, such as
+ * the PopupMenuItems, ScrollBars and Sliders by providing extra "primitive
+ * elements" which are simple to implement by the style writer.
+ *
+ * @see QStyle::QStyle
+ * @see QCommonStyle::QCommonStyle
+ * @author Karol Szwed (gallium@kde.org)
+ * @version $Id$
+ */
+class KDEFX_EXPORT KStyle: public QCommonStyle
+{
+ Q_OBJECT
+
+ public:
+
+ /**
+ * KStyle Flags:
+ *
+ * @li Default - Default style setting, where menu transparency
+ * and the FilledFrameWorkaround are disabled.
+ *
+ * @li AllowMenuTransparency - Enable this flag to use KStyle's
+ * internal menu transparency engine.
+ *
+ * @li FilledFrameWorkaround - Enable this flag to facilitate
+ * proper repaints of QMenuBars and QToolBars when the style chooses
+ * to paint the interior of a QFrame. The style primitives in question
+ * are PE_PanelMenuBar and PE_PanelDockWindow. The HighColor style uses
+ * this workaround to enable painting of gradients in menubars and
+ * toolbars.
+ */
+ typedef uint KStyleFlags;
+ enum KStyleOption {
+ Default = 0x00000000, //!< All options disabled
+ AllowMenuTransparency = 0x00000001, //!< Internal transparency enabled
+ FilledFrameWorkaround = 0x00000002 //!< Filled frames enabled
+ };
+
+ /**
+ * KStyle ScrollBarType:
+ *
+ * Allows the style writer to easily select what type of scrollbar
+ * should be used without having to duplicate large amounts of source
+ * code by implementing the complex control CC_ScrollBar.
+ *
+ * @li WindowsStyleScrollBar - Two button scrollbar with the previous
+ * button at the top/left, and the next button at the bottom/right.
+ *
+ * @li PlatinumStyleScrollBar - Two button scrollbar with both the
+ * previous and next buttons at the bottom/right.
+ *
+ * @li ThreeButtonScrollBar - %KDE style three button scrollbar with
+ * two previous buttons, and one next button. The next button is always
+ * at the bottom/right, whilst the two previous buttons are on either
+ * end of the scrollbar.
+ *
+ * @li NextStyleScrollBar - Similar to the PlatinumStyle scroll bar, but
+ * with the buttons grouped on the opposite end of the scrollbar.
+ *
+ * @see KStyle::KStyle()
+ */
+ enum KStyleScrollBarType {
+ WindowsStyleScrollBar = 0x00000000, //!< two button, windows style
+ PlatinumStyleScrollBar = 0x00000001, //!< two button, platinum style
+ ThreeButtonScrollBar = 0x00000002, //!< three buttons, %KDE style
+ NextStyleScrollBar = 0x00000004 //!< two button, NeXT style
+ };
+
+ /**
+ * Constructs a KStyle object.
+ *
+ * Select the appropriate KStyle flags and scrollbar type
+ * for your style. The user's style preferences selected in KControl
+ * are read by using QSettings and are automatically applied to the style.
+ * As a fallback, KStyle paints progressbars and tabbars. It inherits from
+ * QCommonStyle for speed, so don't expect much to be implemented.
+ *
+ * It is advisable to use a currently implemented style such as the HighColor
+ * style as a foundation for any new KStyle, so the limited number of
+ * drawing fallbacks should not prove problematic.
+ *
+ * @param flags the style to be applied
+ * @param sbtype the scroll bar type
+ * @see KStyle::KStyleFlags
+ * @see KStyle::KStyleScrollBarType
+ * @author Karol Szwed (gallium@kde.org)
+ */
+ KStyle( KStyleFlags flags = KStyle::Default,
+ KStyleScrollBarType sbtype = KStyle::WindowsStyleScrollBar );
+
+ /**
+ * Destructs the KStyle object.
+ */
+ ~KStyle();
+
+ /**
+ * Returns the default widget style depending on color depth.
+ */
+ static QString defaultStyle();
+
+ /**
+ * Modifies the scrollbar type used by the style.
+ *
+ * This function is only provided for convenience. It allows
+ * you to make a late decision about what scrollbar type to use for the
+ * style after performing some processing in your style's constructor.
+ * In most situations however, setting the scrollbar type via the KStyle
+ * constructor should suffice.
+ * @param sbtype the scroll bar type
+ * @see KStyle::KStyleScrollBarType
+ */
+ void setScrollBarType(KStyleScrollBarType sbtype);
+
+ /**
+ * Returns the KStyle flags used to initialize the style.
+ *
+ * This is used solely for the kcmstyle module, and hence is internal.
+ */
+ KStyleFlags styleFlags() const;
+
+ // ---------------------------------------------------------------------------
+
+ /**
+ * This virtual function defines the pixmap used to blend between the popup
+ * menu and the background to create different menu transparency effects.
+ * For example, you can fill the pixmap "pix" with a gradient based on the
+ * popup's colorGroup, a texture, or some other fancy painting routine.
+ * KStyle will then internally blend this pixmap with a snapshot of the
+ * background behind the popupMenu to create the illusion of transparency.
+ *
+ * This virtual is never called if XRender/Software blending is disabled by
+ * the user in KDE's style control module.
+ */
+ virtual void renderMenuBlendPixmap( KPixmap& pix, const QColorGroup& cg,
+ const QPopupMenu* popup ) const;
+
+ /**
+ * KStyle Primitive Elements:
+ *
+ * The KStyle class extends the Qt's Style API by providing certain
+ * simplifications for parts of QStyle. To do this, the KStylePrimitive
+ * elements were defined, which are very similar to Qt's PrimitiveElement.
+ *
+ * The first three Handle primitives simplify and extend PE_DockWindowHandle,
+ * so do not reimplement PE_DockWindowHandle if you want the KStyle handle
+ * simplifications to be operable. Similarly do not reimplement CC_Slider,
+ * SC_SliderGroove and SC_SliderHandle when using the KStyle slider
+ * primitives. KStyle automatically double-buffers slider painting
+ * when they are drawn via these KStyle primitives to avoid flicker.
+ *
+ * @li KPE_DockWindowHandle - This primitive is already implemented in KStyle,
+ * and paints a bevelled rect with the DockWindow caption text. Re-implement
+ * this primitive to perform other more fancy effects when drawing the dock window
+ * handle.
+ *
+ * @li KPE_ToolBarHandle - This primitive must be reimplemented. It currently
+ * only paints a filled rectangle as default behavior. This primitive is used
+ * to render QToolBar handles.
+ *
+ * @li KPE_GeneralHandle - This primitive must be reimplemented. It is used
+ * to render general handles that are not part of a QToolBar or QDockWindow, such
+ * as the applet handles used in Kicker. The default implementation paints a filled
+ * rect of arbitrary color.
+ *
+ * @li KPE_SliderGroove - This primitive must be reimplemented. It is used to
+ * paint the slider groove. The default implementation paints a filled rect of
+ * arbitrary color.
+ *
+ * @li KPE_SliderHandle - This primitive must be reimplemented. It is used to
+ * paint the slider handle. The default implementation paints a filled rect of
+ * arbitrary color.
+ *
+ * @li KPE_ListViewExpander - This primitive is already implemented in KStyle. It
+ * is used to draw the Expand/Collapse element in QListViews. To indicate the
+ * expanded state, the style flags are set to Style_Off, while Style_On implies collapsed.
+ *
+ * @li KPE_ListViewBranch - This primitive is already implemented in KStyle. It is
+ * used to draw the ListView branches where necessary.
+ */
+ enum KStylePrimitive {
+ KPE_DockWindowHandle,
+ KPE_ToolBarHandle,
+ KPE_GeneralHandle,
+
+ KPE_SliderGroove,
+ KPE_SliderHandle,
+
+ KPE_ListViewExpander,
+ KPE_ListViewBranch
+ };
+
+ /**
+ * This function is identical to Qt's QStyle::drawPrimitive(), except that
+ * it adds one further parameter, 'widget', that can be used to determine
+ * the widget state of the KStylePrimitive in question.
+ *
+ * @see KStyle::KStylePrimitive
+ * @see QStyle::drawPrimitive
+ * @see QStyle::drawComplexControl
+ */
+ virtual void drawKStylePrimitive( KStylePrimitive kpe,
+ QPainter* p,
+ const QWidget* widget,
+ const QRect &r,
+ const QColorGroup &cg,
+ SFlags flags = Style_Default,
+ const QStyleOption& = QStyleOption::Default ) const;
+
+
+ enum KStylePixelMetric {
+ KPM_MenuItemSeparatorHeight = 0x00000001,
+ KPM_MenuItemHMargin = 0x00000002,
+ KPM_MenuItemVMargin = 0x00000004,
+ KPM_MenuItemHFrame = 0x00000008,
+ KPM_MenuItemVFrame = 0x00000010,
+ KPM_MenuItemCheckMarkHMargin = 0x00000020,
+ KPM_MenuItemArrowHMargin = 0x00000040,
+ KPM_MenuItemTabSpacing = 0x00000080,
+ KPM_ListViewBranchThickness = 0x00000100
+ };
+
+ int kPixelMetric( KStylePixelMetric kpm, const QWidget* widget = 0 ) const;
+
+ // ---------------------------------------------------------------------------
+
+ void polish( QWidget* widget );
+ void unPolish( QWidget* widget );
+ void polishPopupMenu( QPopupMenu* );
+
+ void drawPrimitive( PrimitiveElement pe,
+ QPainter* p,
+ const QRect &r,
+ const QColorGroup &cg,
+ SFlags flags = Style_Default,
+ const QStyleOption& = QStyleOption::Default ) const;
+
+ void drawControl( ControlElement element,
+ QPainter* p,
+ const QWidget* widget,
+ const QRect &r,
+ const QColorGroup &cg,
+ SFlags flags = Style_Default,
+ const QStyleOption& = QStyleOption::Default ) const;
+
+ void drawComplexControl( ComplexControl control,
+ QPainter *p,
+ const QWidget* widget,
+ const QRect &r,
+ const QColorGroup &cg,
+ SFlags flags = Style_Default,
+ SCFlags controls = SC_All,
+ SCFlags active = SC_None,
+ const QStyleOption& = QStyleOption::Default ) const;
+
+ SubControl querySubControl( ComplexControl control,
+ const QWidget* widget,
+ const QPoint &pos,
+ const QStyleOption& = QStyleOption::Default ) const;
+
+ QRect querySubControlMetrics( ComplexControl control,
+ const QWidget* widget,
+ SubControl sc,
+ const QStyleOption& = QStyleOption::Default ) const;
+
+ int pixelMetric( PixelMetric m,
+ const QWidget* widget = 0 ) const;
+
+ QRect subRect( SubRect r,
+ const QWidget* widget ) const;
+
+ QPixmap stylePixmap( StylePixmap stylepixmap,
+ const QWidget* widget = 0,
+ const QStyleOption& = QStyleOption::Default ) const;
+
+ int styleHint( StyleHint sh,
+ const QWidget* w = 0,
+ const QStyleOption &opt = QStyleOption::Default,
+ QStyleHintReturn* shr = 0 ) const;
+
+ protected:
+ bool eventFilter( QObject* object, QEvent* event );
+
+ private:
+ // Disable copy constructor and = operator
+ KStyle( const KStyle & );
+ KStyle& operator=( const KStyle & );
+
+ protected:
+ virtual void virtual_hook( int id, void* data );
+ private:
+ KStylePrivate *d;
+};
+
+
+// vim: set noet ts=4 sw=4:
+#endif
+
diff --git a/kdefx/libkdefx.nmcheck b/kdefx/libkdefx.nmcheck
new file mode 100644
index 000000000..a0bf7e60c
--- /dev/null
+++ b/kdefx/libkdefx.nmcheck
@@ -0,0 +1,12 @@
+# KDE namespace check file
+
+# kdefx classes
+K*::*
+
+# these should preferably go in some namespace in KDE4
+kColorBitmaps
+kDrawBeButton
+kDrawRoundMask
+kDrawNextButton
+kDrawRoundButton
+kRoundMaskRegion
diff --git a/kdefx/libkdefx_weak.nmcheck b/kdefx/libkdefx_weak.nmcheck
new file mode 100644
index 000000000..6482e1938
--- /dev/null
+++ b/kdefx/libkdefx_weak.nmcheck
@@ -0,0 +1,3 @@
+# KDE namespace check file
+
+# KDE classes