summaryrefslogtreecommitdiffstats
path: root/kipi-plugins/imageviewer
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-01-19 18:22:05 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-01-19 18:22:05 +0000
commit57e10fedbcb8c3e8c6590ff0935dbf017ce5587f (patch)
tree3000a7649ca4e40e43f9e7feed963236a0b0f56b /kipi-plugins/imageviewer
downloadkipi-plugins-57e10fedbcb8c3e8c6590ff0935dbf017ce5587f.tar.gz
kipi-plugins-57e10fedbcb8c3e8c6590ff0935dbf017ce5587f.zip
Import abandoned KDE3 version of kipi plugins
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/libraries/kipi-plugins@1077221 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kipi-plugins/imageviewer')
-rw-r--r--kipi-plugins/imageviewer/Makefile.am25
-rw-r--r--kipi-plugins/imageviewer/README69
-rw-r--r--kipi-plugins/imageviewer/TODO2
-rw-r--r--kipi-plugins/imageviewer/cursors/Makefile.am3
-rw-r--r--kipi-plugins/imageviewer/cursors/hand.pngbin0 -> 168 bytes
-rw-r--r--kipi-plugins/imageviewer/cursors/nullImage.pngbin0 -> 83245 bytes
-rw-r--r--kipi-plugins/imageviewer/cursors/zoom.pngbin0 -> 352 bytes
-rw-r--r--kipi-plugins/imageviewer/help.ui122
-rw-r--r--kipi-plugins/imageviewer/hi16-action-ogl.pngbin0 -> 1033 bytes
-rw-r--r--kipi-plugins/imageviewer/hi32-action-ogl.pngbin0 -> 2749 bytes
-rw-r--r--kipi-plugins/imageviewer/kipiplugin_viewer.desktop49
-rw-r--r--kipi-plugins/imageviewer/plugin_viewer.cpp116
-rw-r--r--kipi-plugins/imageviewer/plugin_viewer.h55
-rw-r--r--kipi-plugins/imageviewer/texture.cpp440
-rw-r--r--kipi-plugins/imageviewer/texture.h80
-rw-r--r--kipi-plugins/imageviewer/timer.cpp45
-rw-r--r--kipi-plugins/imageviewer/timer.h43
-rw-r--r--kipi-plugins/imageviewer/viewerwidget.cpp690
-rw-r--r--kipi-plugins/imageviewer/viewerwidget.h129
19 files changed, 1868 insertions, 0 deletions
diff --git a/kipi-plugins/imageviewer/Makefile.am b/kipi-plugins/imageviewer/Makefile.am
new file mode 100644
index 0000000..628c44f
--- /dev/null
+++ b/kipi-plugins/imageviewer/Makefile.am
@@ -0,0 +1,25 @@
+# set the include path for X, qt and KDE
+INCLUDES = $(KIPI_PLUGINS_COMMON_INCLUDE) $(LIBKIPI_CFLAGS) $(LIBKDCRAW_CFLAGS) $(all_includes)
+
+# let automoc handle all of the meta source files (moc)
+METASOURCES = AUTO
+
+# Install this plugin in the KDE modules directory
+kde_module_LTLIBRARIES = kipiplugin_viewer.la
+
+kipiplugin_viewer_la_DEPENDENCIES = $(LIBKIPI_LIBS_DEP) $(LIBKDCRAW_LIBS_DEP)
+kipiplugin_viewer_la_SOURCES = viewerwidget.cpp texture.cpp timer.cpp plugin_viewer.cpp help.ui
+kipiplugin_viewer_la_LIBADD = $(LIBKIPI_LIBS) $(LIBKDCRAW_LIBS) $(LIB_KIO) $(LIB_KDEUI) $(LIB_KDECORE) $(LIB_QT) -lGL
+kipiplugin_viewer_la_LDFLAGS = $(KIPI_PLUGINS_COMMON_LDFLAGS) -module $(KDE_PLUGIN) $(all_libraries)
+
+kde_services_DATA = kipiplugin_viewer.desktop
+
+
+kipiviewericondir = $(kde_datadir)/kipiplugin_viewer/icons
+kipiviewericon_ICON = AUTO
+
+messages: rc.cpp
+ $(XGETTEXT) *.cpp *.h -o $(podir)/kipiplugin_viewer.pot
+
+
+#icon is still missing \ No newline at end of file
diff --git a/kipi-plugins/imageviewer/README b/kipi-plugins/imageviewer/README
new file mode 100644
index 0000000..6adf663
--- /dev/null
+++ b/kipi-plugins/imageviewer/README
@@ -0,0 +1,69 @@
+-------------------------------
+KIPI image viewer plugin
+-------------------------------
+
+This image viewer was designed with speed and ease of usage in mind as well as for mouseless setups (notebooks). A image is converted to a non-power-of two OpenGL texture. This means your video driver has to support the GL_ARB_texture_rectangle OpenGL extension. Please verify with the command glxinfo. Please keep in mind that you need alot of video memory: a 5MP image requires 20MB texture memory. If you run out texture ram, the downloading time of the texture can be increased up to several seconds. Next generation window managers like Beryl will require a massive amount of video memory to run this plugin.
+
+-------------------
+Handbook
+-------------------
+
+1) Start
+--------
+
+- select the images you'd like to view. Ctrl-A for all images
+ if no image is selected, all images of the album are loaded and the first image will be displayed
+ if one image is selected, all images are loaded and the selected image image will be displayed
+ if several images are selected, only the selected images are loaded
+- in Digikam, select Tool->Image viewer
+ in Gwenview, select Plugins->Tools->Image viewer
+- for convenience, create a shortcut in the host application. In Digikam, select Settings->Configure Shortcut, search for "image viewer"
+
+
+2) Usage
+--------
+
+next image : scrollwheel down or n or down arrow or right arrow or Space
+previous image : scrollwheel up or p or up arrow or left arrow
+toggle fullscreen/normal : f
+quit : Esc
+toggle scrollwheel action : c (scrollwheel either zooms or changes the image), default is changing images
+rotation : r
+reset view : double click
+original size : o
+
+
+zooming:
+
+- move mouse in y-direction while pressing the right mouse button
+- alternatively, press c and use the scrollwheel
+- plus/minus
+- ctrl + scrollwheel
+
+
+panning:
+
+- move mouse while pressing the left button
+
+
+3) Performace & technical details
+---------------------------------
+
+On a PentiumM 1Ghz, slow 2.5 inch harddrive, I measured the following times for changing an image (5 megapixel, 2MB)
+- loading image from cache: 0ms
+- download texture to video memory: 15ms
+- draw image: 16ms
+ the new image is on the screen after 31ms of the user interaction
+- preload next image to cache 690ms
+ only after preloading is done, the next image can be displayed.
+
+The most expensive part is instanciating a QImage(myimage) object with 690ms, which includes reading the file from the harddrive. Downscaling to screensize and converting to GL format takes around 70ms. At least on my setup, downscaling by CPU is still faster than copying a 20Mb texture to video mem and downscaling by GPU.
+
+Other programs/libraries: time of loading and displaying an image
+SDL : 750ms
+Kuickshow: (imlib) 690ms (without smooth rendering)
+Kuickshow: (imlib) 1770ms (with smooth rendering)
+feh: (imlib2) 670ms
+this Image Viewer plugin: 720ms (implicit smooth rendering due to OGL)
+
+
diff --git a/kipi-plugins/imageviewer/TODO b/kipi-plugins/imageviewer/TODO
new file mode 100644
index 0000000..da964cb
--- /dev/null
+++ b/kipi-plugins/imageviewer/TODO
@@ -0,0 +1,2 @@
+- slideshow (in progress)
+- make plugin configurable \ No newline at end of file
diff --git a/kipi-plugins/imageviewer/cursors/Makefile.am b/kipi-plugins/imageviewer/cursors/Makefile.am
new file mode 100644
index 0000000..b970bcd
--- /dev/null
+++ b/kipi-plugins/imageviewer/cursors/Makefile.am
@@ -0,0 +1,3 @@
+METASOURCES = AUTO
+mycursorsdir = $(kde_datadir)/kipiplugin_imageviewer/pics
+mycursors_DATA = hand.png zoom.png nullImage.png
diff --git a/kipi-plugins/imageviewer/cursors/hand.png b/kipi-plugins/imageviewer/cursors/hand.png
new file mode 100644
index 0000000..0d9790c
--- /dev/null
+++ b/kipi-plugins/imageviewer/cursors/hand.png
Binary files differ
diff --git a/kipi-plugins/imageviewer/cursors/nullImage.png b/kipi-plugins/imageviewer/cursors/nullImage.png
new file mode 100644
index 0000000..9757c84
--- /dev/null
+++ b/kipi-plugins/imageviewer/cursors/nullImage.png
Binary files differ
diff --git a/kipi-plugins/imageviewer/cursors/zoom.png b/kipi-plugins/imageviewer/cursors/zoom.png
new file mode 100644
index 0000000..2250544
--- /dev/null
+++ b/kipi-plugins/imageviewer/cursors/zoom.png
Binary files differ
diff --git a/kipi-plugins/imageviewer/help.ui b/kipi-plugins/imageviewer/help.ui
new file mode 100644
index 0000000..c35a135
--- /dev/null
+++ b/kipi-plugins/imageviewer/help.ui
@@ -0,0 +1,122 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>KIPIviewer::HelpDialog</class>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>HelpDialog</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>712</width>
+ <height>539</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Usage Image Viewer</string>
+ </property>
+ <property name="modal">
+ <bool>false</bool>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>pushButton1</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>260</x>
+ <y>500</y>
+ <width>230</width>
+ <height>26</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>O&amp;K</string>
+ </property>
+ <property name="accel">
+ <string>Alt+K</string>
+ </property>
+ </widget>
+ <widget class="QTextBrowser">
+ <property name="name">
+ <cstring>textBrowser2</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>10</y>
+ <width>690</width>
+ <height>480</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>&lt;b&gt;&lt;font color="#5500ff"&gt;&lt;font size="+2"&gt;Image Access&lt;/font&gt;&lt;/font&gt;&lt;/b&gt;&lt;br&gt;
+&lt;TABLE&gt;
+ &lt;TR&gt;
+ &lt;TD&gt;next image&lt;/TD&gt;
+ &lt;TD&gt;scrollwheel down/down arrow/right arrow/PgDown/Space/n&lt;/TD&gt;
+ &lt;/TR&gt;
+ &lt;TR&gt;
+ &lt;TD&gt;previous image &lt;/TD&gt;
+ &lt;TD&gt;scrollwheel up/up arrow/left arrow/PgUp/p &lt;/TD&gt;
+ &lt;/TR&gt;
+&lt;TR&gt;
+ &lt;TD&gt;quit&lt;/TD&gt;
+ &lt;TD&gt;Esc&lt;/TD&gt;
+ &lt;/TR&gt;
+ &lt;/TABLE&gt;
+&lt;br&gt;
+ &lt;TH&gt;&lt;b&gt;&lt;font color="#5500ff"&gt;&lt;font size="+2"&gt;Display&lt;/font&gt;&lt;/font&gt;&lt;/b&gt;&lt;/TH&gt; &lt;/br&gt;
+&lt;TABLE&gt;
+ &lt;TR&gt;
+ &lt;TD&gt;toggle fullscreen/normal &lt;/TD&gt;
+ &lt;TD&gt;f&lt;/TD&gt;
+ &lt;/TR&gt;
+ &lt;TR&gt;
+ &lt;TD&gt;toggle scrollwheel action&lt;/TD&gt;
+ &lt;TD&gt;c (either zoom or change image)&lt;/TD&gt;
+ &lt;/TR&gt;
+ &lt;TR&gt;
+ &lt;TD&gt;rotation &lt;/TD&gt;
+ &lt;TD&gt;r&lt;/TD&gt;
+ &lt;/TR&gt;
+ &lt;TR&gt;
+ &lt;TD&gt;reset view &lt;/TD&gt;
+ &lt;TD&gt;double click&lt;/TD&gt;
+ &lt;/TR&gt;
+ &lt;TR&gt;
+ &lt;TD&gt;original size&lt;/TD&gt;
+ &lt;TD&gt;o&lt;/TD&gt;
+ &lt;/TR&gt;
+ &lt;/TABLE&gt;
+&lt;br&gt;
+
+
+&lt;b&gt;&lt;font color="#5500ff" size="+2"&gt;Zooming&lt;/font&gt;&lt;/b&gt;&lt;br&gt;
+&lt;UL&gt;
+&lt;LI&gt;move mouse in up/down-direction while pressing the right mouse button
+&lt;LI&gt;alternatively, press c and use the scrollwheel&lt;br&gt;
+&lt;LI&gt;plus/minus
+&lt;LI&gt;ctrl + scrollwheel
+&lt;/UL&gt;
+
+&lt;b&gt;&lt;font color="#5500ff" size="+2"&gt;Panning&lt;/font&gt;&lt;/b&gt;&lt;br&gt;
+&lt;UL&gt;
+&lt;LI&gt;move mouse while pressing the left button
+&lt;/UL&gt;</string>
+ </property>
+ </widget>
+</widget>
+<connections>
+ <connection>
+ <sender>pushButton1</sender>
+ <signal>clicked()</signal>
+ <receiver>HelpDialog</receiver>
+ <slot>close()</slot>
+ </connection>
+</connections>
+<slots>
+ <slot>pushButton1_clicked()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kipi-plugins/imageviewer/hi16-action-ogl.png b/kipi-plugins/imageviewer/hi16-action-ogl.png
new file mode 100644
index 0000000..4ed606c
--- /dev/null
+++ b/kipi-plugins/imageviewer/hi16-action-ogl.png
Binary files differ
diff --git a/kipi-plugins/imageviewer/hi32-action-ogl.png b/kipi-plugins/imageviewer/hi32-action-ogl.png
new file mode 100644
index 0000000..45ae1a1
--- /dev/null
+++ b/kipi-plugins/imageviewer/hi32-action-ogl.png
Binary files differ
diff --git a/kipi-plugins/imageviewer/kipiplugin_viewer.desktop b/kipi-plugins/imageviewer/kipiplugin_viewer.desktop
new file mode 100644
index 0000000..6514b21
--- /dev/null
+++ b/kipi-plugins/imageviewer/kipiplugin_viewer.desktop
@@ -0,0 +1,49 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=Viewer
+Name[br]=Gweler
+Name[ca]=Visualitzador
+Name[da]=Visning
+Name[de]=Betrachter
+Name[el]=Προβολέας
+Name[es]=Visor
+Name[et]=Pildinäitaja
+Name[fi]=Katselin
+Name[fr]=Afficheur
+Name[it]=Visore
+Name[ja]=ビューア
+Name[nds]=Kieker
+Name[pa]=ਦਰਸ਼ਕ
+Name[pl]=Przeglądarka
+Name[pt]=Visualizador
+Name[sr]=Прегледач
+Name[sr@Latn]=Pregledač
+Name[sv]=Visning
+Name[xx]=xxViewerxx
+Name[zh_CN]=查看器
+Comment=KIPI OpenGL Image Viewer
+Comment[ca]=Visualitzador d'imatges OpenGL del KIPI
+Comment[da]=KIPI-OpenGL billedvisning
+Comment[de]=Ein KIPI-Modul zum Betrachten von Bildern mittels OpenGL
+Comment[el]=Προβολέας εικόνων OpenGL των KIPI
+Comment[es]=Visor KIPI de imágenes OpenGL
+Comment[et]=KIPI OpenGL pildinäitaja
+Comment[fi]=KIPI-OpenGL -kuvankatselin
+Comment[fr]=Afficheur d'image OpenGL KIPI
+Comment[it]=Visore di immagini OpenGL di KIPI
+Comment[ja]=Kipi OpenGL 画像ビューア
+Comment[nds]=KIPI-Bildkieker för OpenGL
+Comment[nl]=KIPI OpenGL Afbeeldingsviewer
+Comment[pa]=KIPI OpenGL ਚਿੱਤਰ ਦਰਸ਼ਕ
+Comment[pl]=Wtyczka KIPI - Przeglądarka OpenGL zdjęć
+Comment[pt]=Visualizador de Imagens em OpenGL do KIPI
+Comment[pt_BR]=Visualizador de Imagens OpenGL do KIPI
+Comment[sr]=KIPI-јев OpenGL прегледач слика
+Comment[sr@Latn]=KIPI-jev OpenGL pregledač slika
+Comment[sv]=KIPI-OpenGL bildvisning
+Comment[xx]=xxKIPI OpenGL Image Viewerxx
+Comment[zh_CN]=KIPI OpenGL 图像查看器
+Type=Service
+ServiceTypes=KIPI/Plugin
+X-KDE-Library=kipiplugin_viewer
+author=Markus Leuthold
diff --git a/kipi-plugins/imageviewer/plugin_viewer.cpp b/kipi-plugins/imageviewer/plugin_viewer.cpp
new file mode 100644
index 0000000..9c9ebe1
--- /dev/null
+++ b/kipi-plugins/imageviewer/plugin_viewer.cpp
@@ -0,0 +1,116 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Markus Leuthold *
+ * <kusi (+at) forum.titlis.org> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+// KDE includes
+#include <kgenericfactory.h>
+#include <kmessagebox.h>
+#include <kurl.h>
+
+// QT includes
+#include <qmessagebox.h>
+
+// kipi includes
+#include <libkipi/imageinfo.h>
+
+// local includes
+#include "plugin_viewer.h"
+#include "viewerwidget.h"
+
+typedef KGenericFactory<Plugin_viewer> Factory;
+
+K_EXPORT_COMPONENT_FACTORY( kipiplugin_viewer,
+ Factory("kipiplugin_viewer"))
+
+Plugin_viewer::Plugin_viewer( QObject *parent, const char* name, const QStringList& )
+ :KIPI::Plugin::Plugin( Factory::instance(), parent, name )
+{
+ kdDebug(51001) << "image viewer plugin loaded" << endl;
+}
+
+void Plugin_viewer::setup( QWidget* widget )
+{
+ KIPI::Plugin::setup( widget );
+
+ KIPI::Interface* interface = dynamic_cast<KIPI::Interface*>( parent() );
+
+ if ( !interface )
+ {
+ kdError( 51000 ) << "Kipi interface is null!" << endl;
+ return;
+ }
+
+ actionViewer = new KAction (i18n("Image Viewer"),
+ "ViewerWidget",
+ 0, // do never set shortcuts from plugins.
+ this,
+ SLOT(slotActivate()),
+ actionCollection(),
+ "viewer");
+ addAction(actionViewer);
+ widget=0;
+}
+
+KIPI::Category Plugin_viewer::category( KAction* action ) const
+{
+ if ( action == actionViewer ) {
+ return KIPI::TOOLSPLUGIN;
+ }
+ else {
+ kdWarning( 51000 ) << "Unrecognized action for plugin category identification" << endl;
+ return KIPI::TOOLSPLUGIN; // no warning from compiler, please
+ }
+}
+
+
+
+
+
+/*!
+ \fn Plugin_viewer::slotActivate()
+ */
+void Plugin_viewer::slotActivate()
+{
+ KIPI::Interface* interface = dynamic_cast<KIPI::Interface*>( parent() );
+
+ if ( !interface )
+ {
+ kdError( 51000 ) << "Kipi interface is null!" << endl;
+ return;
+ }
+
+ widget = new KIPIviewer::ViewerWidget(interface);
+
+ switch(widget->getOGLstate()) {
+ case KIPIviewer::oglOK:
+ widget->show();
+ break;
+
+ case KIPIviewer::oglNoRectangularTexture:
+ kdError( 51000 ) << "GL_ARB_texture_rectangle not supported" << endl;
+ delete widget;
+ QMessageBox::critical(new QWidget(),"OpenGL error","GL_ARB_texture_rectangle not supported");
+ break;
+
+ case KIPIviewer::oglNoContext:
+ kdError( 51000 ) << "no OpenGL context found" << endl;
+ delete widget;
+ QMessageBox::critical(new QWidget(),"OpenGL error","no OpenGL context found");
+ }
+}
diff --git a/kipi-plugins/imageviewer/plugin_viewer.h b/kipi-plugins/imageviewer/plugin_viewer.h
new file mode 100644
index 0000000..5ae5547
--- /dev/null
+++ b/kipi-plugins/imageviewer/plugin_viewer.h
@@ -0,0 +1,55 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Markus Leuthold *
+ * <kusi (+at) forum.titlis.org> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef PLUGIN_VIEWER_H
+#define PLUGIN_VIEWER_H
+
+// kipi includes
+#include <libkipi/plugin.h>
+
+// local includes
+#include "viewerwidget.h"
+
+
+class KAction;
+
+/**
+ * @short integration with KIPI
+ * @author Markus Leuthold <kusi (+at) forum.titlis.org>
+ * @version 0.2
+ */
+
+class Plugin_viewer :public KIPI::Plugin
+{
+ Q_OBJECT
+public:
+ Plugin_viewer( QObject *parent, const char* name, const QStringList& );
+ virtual void setup( QWidget* widget );
+ virtual KIPI::Category category( KAction* action ) const;
+
+protected:
+ KIPIviewer::ViewerWidget * widget;
+ KAction * actionViewer;
+
+public slots:
+ void slotActivate();
+};
+
+#endif /* PLUGIN_VIEWER_H */
+
diff --git a/kipi-plugins/imageviewer/texture.cpp b/kipi-plugins/imageviewer/texture.cpp
new file mode 100644
index 0000000..3581be2
--- /dev/null
+++ b/kipi-plugins/imageviewer/texture.cpp
@@ -0,0 +1,440 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Markus Leuthold *
+ * <kusi (+at) forum.titlis.org> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#undef PERFORMANCE_ANALYSIS
+
+// Qt includes
+#include <qwmatrix.h>
+#include <qfileinfo.h>
+
+//KDE includes
+#include <kdebug.h>
+
+// LibKDcraw includes.
+
+#include <libkdcraw/version.h>
+#include <libkdcraw/kdcraw.h>
+
+#if KDCRAW_VERSION < 0x000106
+#include <libkdcraw/dcrawbinary.h>
+#endif
+
+// libkipi includes
+
+#include <libkipi/interface.h>
+#include <libkipi/imagecollection.h>
+
+// Local includes
+#ifdef PERFORMANCE_ANALYSIS
+ #include "timer.h"
+#endif
+#include "texture.h"
+
+using namespace KIPIviewer;
+
+Texture::Texture(KIPI::Interface *i)
+{
+ kipiInterface = i;
+ rotate_list[0]=90;
+ rotate_list[1]=180;
+ rotate_list[2]=270;
+ rotate_list[3]=180;
+ rotate_idx=0;
+ reset();
+}
+
+
+Texture::~Texture()
+{
+}
+
+
+/*!
+ \fn Texture::height()
+ */
+int Texture::height()
+{
+ return glimage.height();
+}
+
+
+/*!
+ \fn Texture::width()
+ */
+int Texture::width()
+{
+ return glimage.width();
+}
+
+
+/*!
+ \fn Texture::load(QString fn, QSize size, GLuint tn)
+ \brief load file from disc and save it in texture
+ \param fn filename to load
+ \param size size of image which is downloaded to texture mem
+ \param tn texture id generated by glGenTexture
+ if "size" is set to image size, scaling is only performed by the GPU but not
+ by the CPU, however the AGP usage to texture memory is increased (20MB for a 5mp image)
+ */
+bool Texture::load(QString fn, QSize size, GLuint tn)
+{
+ filename=fn;
+ initial_size=size;
+ _texnr=tn;
+
+ // check if its a RAW file.
+#if KDCRAW_VERSION < 0x000106
+ QString rawFilesExt(KDcrawIface::DcrawBinary::instance()->rawFiles());
+#else
+ QString rawFilesExt(KDcrawIface::KDcraw::rawFiles());
+#endif
+ QFileInfo fileInfo(fn);
+ if (rawFilesExt.upper().contains( fileInfo.extension(false).upper() )) {
+ // it's a RAW file, use the libkdcraw loader
+ KDcrawIface::KDcraw::loadDcrawPreview(qimage, fn);
+ } else {
+ // use the standard loader
+ qimage=QImage(fn);
+ }
+
+ //handle rotation
+ KIPI::ImageInfo info = kipiInterface->info(filename);
+ if (info.angle() != 0) {
+ QWMatrix r;
+ r.rotate(info.angle());
+ qimage=qimage.xForm(r);
+ kdDebug(51000) << "image rotated by " << info.angle() << " degree" << endl;
+ }
+
+ if (qimage.isNull()) {
+ return false;
+ }
+
+ _load();
+ reset();
+ rotate_idx=0;
+ return true;
+}
+
+/*!
+ \fn Texture::load(QImage im, QSize size, GLuint tn)
+ \brief copy file from QImage to texture
+ \param im Qimage to be copied from
+ \param size size of image which is downloaded to texture mem
+ \param tn texture id generated by glGenTexture
+ if "size" is set to image size, scaling is only performed by the GPU but not
+ by the CPU, however the AGP usage to texture memory is increased (20MB for a 5mp image)
+ */
+bool Texture::load(QImage im, QSize size, GLuint tn)
+{
+ qimage=im;
+ initial_size=size;
+ _texnr=tn;
+ _load();
+ reset();
+ rotate_idx=0;
+ return true;
+}
+
+/*!
+ \fn Texture::load()
+ internal load function
+ rt[xy] <= 1
+ */
+bool Texture::_load()
+{
+ int w=initial_size.width();
+ int h=initial_size.height();
+
+ if (w==0 || w>qimage.width() || h>qimage.height()) {
+ glimage=QGLWidget::convertToGLFormat(qimage);
+ } else {
+ glimage=QGLWidget::convertToGLFormat(qimage.scale(w,h,QImage::ScaleMin));
+ }
+
+ w=glimage.width();
+ h=glimage.height();
+ if (h < w) {
+ rtx = 1;
+ rty = float(h)/float(w);
+ }
+ else {
+ rtx = float(w)/float(h);
+ rty = 1;
+ }
+ return true;
+}
+
+/*!
+ \fn Texture::data()
+ */
+GLvoid * Texture::data()
+{
+ return glimage.bits();
+}
+
+
+/*!
+ \fn Texture::texnr()
+ */
+GLuint Texture::texnr()
+{
+ return _texnr;
+}
+
+
+
+/*!
+ \fn Texture::zoom(float delta, QPoint mousepos)
+ \brief calculate new tex coords on zooming
+ \param delta delta between previous zoom and current zoom
+ \param mousepos mouse position returned by QT
+ \TODO rename mousepos to something more generic
+ */
+void Texture::zoom(float delta, QPoint mousepos)
+//u: start in texture, u=[0..1], u=0 is begin, u=1 is end of texture
+//z=[0..1], z=1 -> no zoom
+//l: length of tex in glFrustum coordinate system
+//rt: ratio of tex, rt<=1, see _load() for definition
+//rd: ratio of display, rd>=1
+//m: mouse pos normalized, cd=[0..rd]
+//c: mouse pos normalized to zoom*l, c=[0..1]
+{
+ z*=delta;
+ delta=z*(1.0/delta-1.0); //convert to real delta=z_old-z_new
+
+ float mx=mousepos.x()/(float)display_x*rdx;
+ float cx=(mx-rdx/2.0+rtx/2.0)/rtx;
+ float vx=ux+cx*z;
+ ux=ux+(vx-ux)*delta/z;
+
+ float my=mousepos.y()/(float)display_y*rdy;
+ float cy=(my-rdy/2.0+rty/2.0)/rty;
+ cy=1-cy;
+ float vy=uy+cy*z;
+ uy=uy+(vy-uy)*delta/z;
+
+ calcVertex();
+}
+
+/*!
+ \fn Texture::calcVertex()
+ Calculate vertices according internal state variables
+ z, ux, uy are calculated in Texture::zoom()
+ */
+void Texture::calcVertex()
+// rt: ratio of tex, rt<=1, see _load() for definition
+// u: start in texture, u=[0..1], u=0 is begin, u=1 is end of texture
+// l: length of tex in glFrustum coordinate system
+// halftexel: the color of a texel is determined by a corner of the texel and not its center point
+// this seems to introduce a visible jump on changing the tex-size.
+//
+// the glFrustum coord-sys is visible in [-rdx..rdx] ([-1..1] for square screen) for z=1 (no zoom)
+// the tex coord-sys goes from [-rtx..rtx] ([-1..1] for square texture)
+{
+ // x part
+ float lx=2*rtx/z; //length of tex
+ float tsx=lx/(float)glimage.width(); //texelsize in glFrustum coordinates
+ float halftexel_x = tsx/2.0;
+ float wx=lx*(1-ux-z);
+ vleft = -rtx-ux*lx - halftexel_x; //left
+ vright = rtx+wx - halftexel_x; //right
+
+ // y part
+ float ly=2*rty/z;
+ float tsy=ly/(float)glimage.height(); //texelsize in glFrustum coordinates
+ float halftexel_y = tsy/2.0;
+ float wy=ly*(1-uy-z);
+ vbottom = -rty - uy*ly + halftexel_y; //bottom
+ vtop = rty + wy + halftexel_y; //top
+
+}
+
+/*!
+ \fn Texture::vertex_bottom()
+ */
+GLfloat Texture::vertex_bottom()
+{
+ return (GLfloat) vbottom;
+}
+
+/*!
+ \fn Texture::vertex_top()
+ */
+GLfloat Texture::vertex_top()
+{
+ return (GLfloat) vtop;
+}
+
+
+/*!
+ \fn Texture::vertex_left()
+ */
+GLfloat Texture::vertex_left()
+{
+ return (GLfloat) vleft;
+}
+
+
+/*!
+ \fn Texture::vertex_right()
+ */
+GLfloat Texture::vertex_right()
+{
+ return (GLfloat) vright;
+}
+
+
+/*!
+ \fn Texture::setViewport(int w, int h)
+ \param w width of window
+ \param h height of window
+ Set widget's viewport. Ensures that rdx & rdy are always > 1
+ */
+void Texture::setViewport(int w, int h)
+{
+ if (h>w) {
+ rdx=1.0;
+ rdy=h/float(w);
+ }
+ else {
+ rdx=w/float(h);
+ rdy=1.0;
+ }
+ display_x=w;
+ display_y=h;
+}
+
+
+/*!
+ \fn Texture::move(QPoint diff)
+ new tex coordinates have to be calculated if the view is panned
+ */
+void Texture::move(QPoint diff)
+{
+ ux=ux-diff.x()/float(display_x)*z*rdx/rtx;
+ uy=uy+diff.y()/float(display_y)*z*rdy/rty;
+ calcVertex();
+}
+
+
+/*!
+ \fn Texture::reset()
+ */
+void Texture::reset()
+{
+ ux=0;
+ uy=0;
+ z=1.0;
+ float zoomdelta=0;
+
+ if ((rtx<rty) && (rdx<rdy) && (rtx/rty < rdx/rdy)) {
+ zoomdelta=z-rdx/rdy;
+ }
+ if ((rtx<rty) && (rtx/rty > rdx/rdy)) {
+ zoomdelta=z-rtx;
+ }
+
+ if ((rtx>=rty) && (rdy<rdx) && (rty/rtx < rdy/rdx)) {
+ zoomdelta=z-rdy/rdx;
+ }
+ if ((rtx>=rty) && (rty/rtx > rdy/rdx)) {
+ zoomdelta=z-rty;
+ }
+ QPoint p = QPoint(display_x/2,display_y/2);
+ zoom(1.0-zoomdelta,p);
+
+ calcVertex();
+}
+
+/*!
+ \fn Texture::setSize(QSize size)
+ \param size desired texture size. QSize(0,0) will take the full image
+ \return true if size has changed, false otherwise
+ set new texture size in order to reduce AGP bandwidth
+ */
+bool Texture::setSize(QSize size)
+{
+ //don't allow larger textures than the original image. the image will be upsampled by
+ //OpenGL if necessary and not by QImage::scale
+ size=size.boundedTo(qimage.size());
+
+ if (glimage.width()==size.width()) {
+ return false;
+ }
+
+ int w=size.width();
+ int h=size.height();
+
+ if (w==0) {
+ glimage=QGLWidget::convertToGLFormat(qimage);
+ } else {
+ glimage=QGLWidget::convertToGLFormat(qimage.scale(w,h,QImage::ScaleMin));
+ }
+
+ //recalculate half-texel offset
+ calcVertex();
+
+ return true;
+}
+
+
+/*!
+ \fn Texture::rotate()
+ \brief smart image rotation
+ since the two most frequent usecases are a CW or CCW rotation of 90,
+ perform these rotation with one (+90) or two (-90) calls of rotation()
+ */
+void Texture::rotate()
+{
+ QWMatrix r;
+ r.rotate(rotate_list[rotate_idx%4]);
+ qimage=qimage.xForm(r);
+ _load();
+
+ //save new rotation in exif header
+ KIPI::ImageInfo info = kipiInterface->info(filename);
+ info.setAngle(rotate_list[rotate_idx%4]);
+
+ reset();
+ rotate_idx++;
+}
+
+
+/*!
+ \fn Texture::setToOriginalSize()
+ zoom image such that each pixel of the screen corresponds to a pixel in the jpg
+ remember that OpenGL is not a pixel exact specification, and the image will still be filtered by OpenGL
+ */
+void Texture::zoomToOriginal()
+{
+ float zoomfactorToOriginal;
+ reset();
+
+ if (qimage.width()/qimage.height() > float(display_x)/float(display_y)) {
+ //image touches right and left edge of window
+ zoomfactorToOriginal=float(display_x)/qimage.width();
+ } else {
+ //image touches upper and lower edge of window
+ zoomfactorToOriginal=float(display_y)/qimage.height();
+ }
+
+ zoom(zoomfactorToOriginal,QPoint(display_x/2,display_y/2));
+}
diff --git a/kipi-plugins/imageviewer/texture.h b/kipi-plugins/imageviewer/texture.h
new file mode 100644
index 0000000..47fbfdd
--- /dev/null
+++ b/kipi-plugins/imageviewer/texture.h
@@ -0,0 +1,80 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Markus Leuthold *
+ * <kusi (+at) forum.titlis.org> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#ifndef TEXTURE_H
+#define TEXTURE_H
+
+//QT includes
+#include <qgl.h>
+#include <qstring.h>
+#include <qdatetime.h>
+#include <qimage.h>
+
+// libkipi includes
+
+#include <libkipi/interface.h>
+#include <libkipi/imagecollection.h>
+
+/**
+ * @short Texture class
+ * @author Markus Leuthold <kusi (+at) forum.titlis.org>
+ * @version 0.2
+ */
+
+namespace KIPIviewer {
+class Texture{
+public:
+ Texture(KIPI::Interface *);
+ ~Texture();
+ int height();
+ int width();
+ bool load(QString fn, QSize size, GLuint tn);
+ bool load(QImage im, QSize size, GLuint tn);
+ GLvoid * data();
+ GLuint texnr();
+ GLfloat vertex_bottom();
+ GLfloat vertex_top();
+ GLfloat vertex_left();
+ GLfloat vertex_right();
+ void setViewport(int w, int h);
+ void zoom(float delta, QPoint mousepos);
+ void reset();
+ void move(QPoint diff);
+ bool setSize(QSize size);
+ void rotate();
+ void zoomToOriginal();
+
+protected:
+ bool _load();
+ void calcVertex();
+
+ int display_x, display_y;
+ GLuint _texnr;
+ QSize initial_size;
+ QString filename;
+ QImage qimage, glimage;
+ float rdx,rdy,z,ux,uy,rtx,rty;
+ float vtop, vbottom, vleft, vright;
+ int rotate_list[4], rotate_idx;
+ KIPI::Interface * kipiInterface;
+};
+}; //namespace KIPIviewer
+
+#endif
diff --git a/kipi-plugins/imageviewer/timer.cpp b/kipi-plugins/imageviewer/timer.cpp
new file mode 100644
index 0000000..5c8d818
--- /dev/null
+++ b/kipi-plugins/imageviewer/timer.cpp
@@ -0,0 +1,45 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Markus Leuthold *
+ * <kusi (+at) forum.titlis.org> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <kdebug.h>
+#include "timer.h"
+
+using namespace KIPIviewer;
+
+Timer::Timer()
+{
+}
+
+void Timer::start()
+{
+ timer.start();
+ meantime=0;
+}
+
+void Timer::at(QString s)
+{
+ meantime=timer.elapsed()-meantime;
+ kdDebug(51000) << "stopwatch:"<< s << ": " << meantime << " ms overall: " << timer.elapsed() << " ms" << endl;
+}
+
+Timer::~Timer()
+{
+}
+
+
diff --git a/kipi-plugins/imageviewer/timer.h b/kipi-plugins/imageviewer/timer.h
new file mode 100644
index 0000000..f05e222
--- /dev/null
+++ b/kipi-plugins/imageviewer/timer.h
@@ -0,0 +1,43 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Markus Leuthold *
+ * <kusi at forum.titlis.org> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef TIMER_H
+#define TIMER_H
+#include <qstring.h>
+#include <qdatetime.h>
+
+/**
+ * @short convenience class for profiling
+ * @author Markus Leuthold <kusi (+at) forum.titlis.org>
+ * @version 0.2
+ */
+
+namespace KIPIviewer {
+class Timer{
+public:
+ Timer();
+ ~Timer();
+ void start();
+ void at(QString s);
+protected:
+ QTime timer;
+ int meantime;
+};
+}; //namespace KIPIviewer
+#endif
diff --git a/kipi-plugins/imageviewer/viewerwidget.cpp b/kipi-plugins/imageviewer/viewerwidget.cpp
new file mode 100644
index 0000000..082d27f
--- /dev/null
+++ b/kipi-plugins/imageviewer/viewerwidget.cpp
@@ -0,0 +1,690 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Markus Leuthold *
+ * <kusi (+at) forum.titlis.org> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#undef PERFORMANCE_ANALYSIS
+
+// global includes
+#include <qgl.h>
+#include <iostream>
+#include <qlabel.h>
+#include <klocale.h>
+#include <qdragobject.h>
+#include <qbitmap.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <kstandarddirs.h>
+#include <kmessagebox.h>
+
+// local includes.
+#include "viewerwidget.h"
+#include "texture.h"
+#include "help.h"
+#ifdef PERFORMANCE_ANALYSIS
+ #include "timer.h"
+#endif
+#include "viewerwidget.moc"
+
+// using namespace std;
+using namespace KIPIviewer;
+
+ViewerWidget::ViewerWidget(KIPI::Interface* i) {
+
+ kipiInterface = i;
+
+ KIPI::ImageCollection selection = kipiInterface->currentSelection();
+ KIPI::ImageCollection album = kipiInterface->currentAlbum();
+
+
+ KURL::List myfiles; //pics which are displayed in imageviewer
+ QString selectedImage; //selected pic in hostapp
+
+ int foundNumber=0;
+ file_idx=0; //index of picture to be displayed
+
+ if ( selection.images().count()==0 ) {
+ kdDebug(51000) << "no image selected, load entire album" << endl;
+ myfiles = album.images();
+ }
+ else if ( selection.images().count()==1 ) {
+ kdDebug(51000) << "one image selected, load entire album and start with selected image" << endl;
+ selectedImage = selection.images().first().path();
+ myfiles = album.images();
+ }
+ else if ( selection.images().count()>1 ) {
+ kdDebug(51000) << "load " << selection.images().count() << " selected images" << endl;
+ myfiles = selection.images();
+ }
+
+ // populate QStringList::files
+ for(KURL::List::Iterator it = myfiles.begin(); it != myfiles.end();it++) {
+
+ // find selected image in album in order to determine the first displayed image
+ // in case one image was selected and the entire album was loaded
+ QString s = (*it).path();
+ if ( s==selectedImage ) {
+ kdDebug(51000) << "selected img " << selectedImage << " has idx=" << foundNumber << endl;
+ file_idx=foundNumber;
+ }
+
+ // only add images to files
+ KMimeType::Ptr type = KMimeType::findByURL(s);
+ bool isImage=type->name().find("image")>=0;
+
+ if ( isImage ) {
+ files.append(s);
+ foundNumber++; //counter for searching the start image in case one image is selected
+ kdDebug(51000) << s << " type=" << type->name() << endl;
+ }
+ }
+
+ firstImage=true;
+ kdDebug(51000) << files.count() << "images loaded" << endl;
+
+ // initialize cache
+ for(int i=0;i<CACHESIZE;i++) {
+ cache[i].file_index=EMPTY;
+ cache[i].texture=new Texture(kipiInterface);
+ }
+
+ // define zoomfactors for one zoom step
+ zoomfactor_scrollwheel = 1.1;
+ zoomfactor_mousemove = 1.03;
+ zoomfactor_keyboard = 1.05;
+
+ // load cursors for zooming and panning
+ QString file;
+ file = locate( "data", "kipiplugin_imageviewer/pics/zoom.png" );
+ zoomCursor=QCursor(file);
+ file = locate( "data", "kipiplugin_imageviewer/pics/hand.png" );
+ moveCursor=QCursor(file);
+
+ // get path of nullImage in case QImage can't load the image
+ nullImage = locate( "data", "kipiplugin_imageviewer/pics/nullImage.png" );
+
+ showFullScreen();
+
+ // let the cursor dissapear after 2sec of inactivity
+ connect( &timerMouseMove, SIGNAL(timeout()),this, SLOT( timeoutMouseMove()) );
+ timerMouseMove.start(2000);
+ setMouseTracking(true);
+
+ // while zooming is performed, the image is downsampled to zoomsize. This seems to
+ // be the optimal way for a PentiumM 1.4G, Nvidia FX5200. For a faster setup, this might
+ // not be necessary anymore
+ zoomsize=QSize(1024,768);
+
+ // other initialisations
+ wheelAction = changeImage;
+}
+
+
+/*!
+ \fn ViewerWidget::initializeGL()
+ \todo blending
+ */
+void ViewerWidget::initializeGL() {
+ glEnable(GL_TEXTURE_RECTANGLE_ARB);
+ // Clear The Background Color
+ glClearColor(0.0, 0.0, 0.0, 1.0f);
+ // Turn Blending On
+ glEnable(GL_BLEND);
+ // Blending Function For Translucency Based On Source Alpha Value
+ glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
+ // Enable perspective vision
+ glClearDepth(1.0f);
+ // Generate texture
+ glGenTextures(1, tex);
+}
+
+
+/*!
+ \fn ViewerWidget::paintGL()
+ */
+void ViewerWidget::paintGL() {
+ //prepare 1st image
+ //this test has to be performed here since QWidget::width() is only updated now
+ if (firstImage) {
+ texture=loadImage(file_idx);
+ texture->reset();
+ downloadTex(texture);
+ }
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glLoadIdentity();
+ glTranslatef(0.0f,0.0f,-5.0f);
+ drawImage(texture);
+
+ //preload the 2nd image
+ if (firstImage) {
+ if (file_idx<(files.count()-1)) {
+ loadImage(file_idx+1);
+ }
+ firstImage=false;
+ }
+}
+
+
+
+
+/*!
+ \fn ViewerWidget::resizeGL(int w, int h)
+ */
+void ViewerWidget::resizeGL(int w, int h)
+{
+ glViewport(0, 0, (GLint)w, (GLint)h);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+
+ if (h>w) {
+ ratio_view_x=1.0;
+ ratio_view_y=h/float(w);
+ }
+ else {
+ ratio_view_x=w/float(h);
+ ratio_view_y=1.0;
+ }
+
+ glFrustum( -ratio_view_x, ratio_view_x, -ratio_view_y, ratio_view_y,5, 5000.0 );
+ glMatrixMode( GL_MODELVIEW );
+ glLoadIdentity();
+
+ if (!firstImage) {
+ texture->setViewport(w,h);
+ }
+}
+
+
+
+
+
+/*!
+ \fn ViewerWidget::drawImage(Texture * texture)
+ \brief render the image
+ */
+void ViewerWidget::drawImage(Texture * texture)
+{
+// cout << "enter drawImage: target=" << texture->texnr() << " dim=" << texture->height() << " " << texture->width() << endl;
+ glBindTexture(GL_TEXTURE_RECTANGLE_NV, texture->texnr());
+ glBegin(GL_QUADS);
+ glTexCoord2f(0, 0);
+ glVertex3f(texture->vertex_left(), texture->vertex_bottom(), 0);
+
+ glTexCoord2f(texture->width(), 0);
+ glVertex3f(texture->vertex_right(), texture->vertex_bottom(), 0);
+
+ glTexCoord2f(texture->width(), texture->height());
+ glVertex3f(texture->vertex_right(), texture->vertex_top(), 0);
+
+ glTexCoord2f(0, texture->height());
+ glVertex3f(texture->vertex_left(), texture->vertex_top(), 0);
+ glEnd();
+}
+
+
+/*!
+ \fn ViewerWidget::keyPressEvent(QKeyEvent *k)
+ Handle all keyboard events. All events which are not handled trigger
+ a help window.
+ */
+void ViewerWidget::keyPressEvent(QKeyEvent *k)
+{
+ QPoint middlepoint;
+
+ switch (k->key()) {
+ // next image
+ case Key_N:
+ case Key_Right:
+ case Key_Down:
+ case Key_PageDown:
+ case Key_Space:
+ nextImage();
+ break;
+
+ // previous image
+ case Key_P:
+ case Key_Left:
+ case Key_Up:
+ case Key_PageUp:
+ prevImage();
+ break;
+
+ // rotate image
+ case Key_R:
+ texture->rotate();
+ downloadTex(texture);
+ updateGL();
+ break;
+
+ // terminate image viewer
+ case Key_Escape:
+ // clean up: where does this have to be done?
+ close(true);
+ break;
+
+ // full screen
+ case Key_F:
+ // according to QT documentation, showFullScreen() has some
+ // serious issues on window managers that do not follow modern
+ // post-ICCCM specifications
+ if (isFullScreen()) {
+ texture->reset();
+ showNormal();
+ } else {
+ texture->reset();
+ showFullScreen();
+ }
+ break;
+
+ // reset size and redraw
+ case Key_Z:
+ texture->reset();
+ updateGL();
+ break;
+
+ // toggle permanent between "show next image" and "zoom" on mousewheel change
+ case Key_C:
+ if (wheelAction==zoomImage)
+ wheelAction=changeImage;
+ else
+ wheelAction=zoomImage;
+ break;
+
+ // zoom in
+ case Key_Plus:
+ middlepoint = QPoint(width()/2,height()/2);
+ if (texture->setSize( zoomsize )) {
+ downloadTex(texture); //load full resolution image
+ };
+ zoom(-1, middlepoint, zoomfactor_keyboard);
+ break;
+
+ // zoom out
+ case Key_Minus:
+ middlepoint = QPoint(width()/2,height()/2);
+ if (texture->setSize( zoomsize ))
+ downloadTex(texture); //load full resolution image
+ zoom(1, middlepoint, zoomfactor_keyboard);
+ break;
+
+ // zoom to original size
+ case Key_O:
+ texture->zoomToOriginal();
+ updateGL();
+ break;
+
+ // toggle temorarily between "show next image" and "zoom" on mousewheel change
+ case Key_Control:
+ if (wheelAction==zoomImage)
+ //scrollwheel changes to the next image
+ wheelAction=changeImage;
+ else {
+ //scrollwheel does zoom
+ wheelAction=zoomImage;
+ setCursor (zoomCursor);
+ timerMouseMove.stop();
+ }
+ break;
+
+
+ //do noting, don't trigger the help dialog
+ case Key_Shift:
+ break;
+
+ //key is not bound to any action, therefore show help dialog to enlighten the user
+ default:
+ QDialog * h = new HelpDialog(0,0,Qt::WStyle_StaysOnTop);
+ h->show();
+ }
+}
+
+/*!
+ \fn ViewerWidget::keyReleaseEvent ( QKeyEvent * e )
+ */
+void ViewerWidget::keyReleaseEvent ( QKeyEvent * e )
+{
+ switch (e->key()) {
+ case Key_Plus:
+ case Key_Minus:
+ if (!e->isAutoRepeat()) {
+ unsetCursor();
+ if (texture->setSize(QSize(0,0))) {
+ downloadTex(texture); //load full resolution image
+ }
+ updateGL();
+ } else
+ e->ignore();
+ break;
+
+ case Key_Control:
+ if (wheelAction==zoomImage)
+ wheelAction=changeImage;
+ else
+ wheelAction=zoomImage;
+ unsetCursor();
+ timerMouseMove.start(2000);
+ break;
+
+ default:
+ e->ignore();
+ break;
+ }
+
+}
+
+
+/*!
+ \fn ViewerWidget::downloadTex(Texture * tex)
+ download texture to video memory
+ */
+void ViewerWidget::downloadTex(Texture * tex)
+{
+ glBindTexture(GL_TEXTURE_RECTANGLE_NV, tex->texnr());
+ // glTexParameterf(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER_ARB);
+ // glTexParameterf(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER_ARB);
+
+// uncomment the following line to enable flat shading of texels -> debugging
+// glTexParameterf(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_MAG_FILTER,GL_NEAREST);
+
+ glTexImage2D( GL_TEXTURE_RECTANGLE_NV, 0, GL_RGBA, tex->width(), tex->height(), 0,GL_RGBA, GL_UNSIGNED_BYTE, tex->data());
+}
+
+
+
+/*!
+ \fn ViewerWidget::loadImage(int file_index)
+ \param file_index index to QStringList files
+ load files[file_index] into a texture object if it is not already cached
+ */
+Texture * ViewerWidget::loadImage(int file_index)
+{
+ int imod=file_index%CACHESIZE; //index for cache
+
+ if (cache[imod].file_index==file_index){
+ //image is already cached
+ kdDebug(51000) << "image " << file_index << " is already in cache@" << imod << endl;
+ return cache[imod].texture;
+
+ } else {
+ // image is net yet loaded
+ QString f = files[file_index];
+ kdDebug(51000) << "loading image " << f << "(idx=" << file_index << ") to cache@" << imod << endl;
+ cache[imod].file_index=file_index;
+
+ // handle non-loadable images
+ if (!cache[imod].texture->load(f,QSize(width(),height()),tex[0])) {
+ cache[imod].texture->load(nullImage,QSize(width(),height()),tex[0]);
+ }
+
+ cache[imod].texture->setViewport(width(),height());
+ return cache[imod].texture;
+ }
+}
+
+
+/*!
+ \fn ViewerWidget::wheelEvent ( QWheelEvent * e )
+ */
+void ViewerWidget::wheelEvent ( QWheelEvent * e )
+{
+ switch(wheelAction) {
+ // mousewheel triggers zoom
+ case zoomImage:
+ setCursor(zoomCursor);
+ zoom(e->delta(), e->pos(), zoomfactor_scrollwheel);
+ break;
+
+ // mousewheel triggers image change
+ case changeImage:
+ if (e->delta() < 0)
+ nextImage();
+ else
+ prevImage();
+ break;
+ }
+}
+
+
+/*!
+ \fn ViewerWidget::mousePressEvent ( QMouseEvent * e )
+ */
+void ViewerWidget::mousePressEvent ( QMouseEvent * e )
+{
+ // begin zoom
+ // scale down texture for fast zooming
+ // texture will be set to original size on mouse up
+ if (texture->setSize( zoomsize )) { //load downsampled image
+ downloadTex(texture);
+ }
+
+ timerMouseMove.stop(); //user is something up to, therefore keep the cursor
+ if ( e->button() == LeftButton ) {
+ setCursor (moveCursor); //defined in constructor
+ }
+
+ if ( e->button() == RightButton ) {
+ setCursor (zoomCursor);
+ }
+
+ startdrag=e->pos();
+ previous_pos=e->pos();
+}
+
+
+/*!
+ \fn ViewerWidget::mouseMoveEvent ( QMouseEvent * e )
+ */
+void ViewerWidget::mouseMoveEvent ( QMouseEvent * e )
+{
+ if ( e->state() == LeftButton ) {
+ //panning
+ QPoint diff=e->pos()-startdrag;
+ texture->move(diff);
+ updateGL();
+ startdrag=e->pos();
+ } else if ( e->state() == RightButton ) {
+ //zooming
+ zoom(previous_pos.y()-e->y(), startdrag, zoomfactor_mousemove );
+ previous_pos=e->pos();
+ } else {
+ //no key is pressed while moving mouse
+ //don't do anything if ctrl is pressed
+ if (timerMouseMove.isActive()) {
+ //ctrl is not pressed, no zooming, therefore restore and hide cursor in 2 sec
+ unsetCursor();
+ timerMouseMove.start(2000);
+ }
+ }
+ return;
+}
+
+
+
+
+/*!
+ \fn ViewerWidget::prevImage()
+ */
+void ViewerWidget::prevImage()
+{
+ #ifdef PERFORMANCE_ANALYSIS
+ Timer timer;
+ #endif
+
+ if (file_idx>0)
+ file_idx--;
+ else
+ return;
+#ifdef PERFORMANCE_ANALYSIS
+ timer.start();
+#endif
+ texture = loadImage(file_idx);
+ texture->reset();
+
+#ifdef PERFORMANCE_ANALYSIS
+ timer.at("loadImage");
+#endif
+
+ downloadTex(texture);
+
+#ifdef PERFORMANCE_ANALYSIS
+ timer.at("downloadTex");
+#endif
+
+ updateGL();
+
+#ifdef PERFORMANCE_ANALYSIS
+ timer.at("updateGL");
+#endif
+
+ //image preloading
+ if (file_idx>0)
+ loadImage(file_idx-1);
+}
+
+
+/*!
+ \fn ViewerWidget::nextImage()
+ */
+void ViewerWidget::nextImage()
+{
+#ifdef PERFORMANCE_ANALYSIS
+ Timer timer;
+#endif
+ if (file_idx<(files.count()-1))
+ file_idx++;
+ else
+ return;
+#ifdef PERFORMANCE_ANALYSIS
+ timer.start();
+#endif
+ texture = loadImage(file_idx);
+ texture->reset();
+#ifdef PERFORMANCE_ANALYSIS
+ timer.at("loadImage");
+#endif
+ downloadTex(texture);
+#ifdef PERFORMANCE_ANALYSIS
+ timer.at("downloadTex");
+#endif
+ updateGL();
+#ifdef PERFORMANCE_ANALYSIS
+ timer.at("updateGL");
+#endif
+
+ //image preloading
+ if (file_idx<(files.count()-1)) {
+ loadImage(file_idx+1);
+#ifdef PERFORMANCE_ANALYSIS
+ timer.at("preloading");
+#endif
+ }
+}
+
+
+/*!
+ \fn ViewerWidget::zoom(int mdelta, QPos pos)
+ \param mdelta delta of mouse movement:
+ mdelta>0: zoom in
+ mdelta<0: zoom out
+ mdelta=0: do nothing
+ \param pos position of mouse
+ \param factor zoom factor:scrollwheel needs a higher factor that right click mouse move. factor=1 -> no zoom
+ */
+void ViewerWidget::zoom(int mdelta, QPoint pos, float factor)
+{
+ if (mdelta==0) {
+ //do nothing
+ return;
+ }
+
+ if (mdelta > 0) {
+ //multiplicator for zooming in
+ delta=factor;
+ }
+
+ if (mdelta < 0) {
+ //multiplicator for zooming out
+ delta=2.0-factor;
+ }
+
+ texture->zoom(delta,pos);
+ updateGL();
+}
+
+
+/*!
+ \fn ViewerWidget::mouseDoubleClickEvent(QMouseEvent * e )
+ a double click resets the view (zoom and move)
+ */
+void ViewerWidget::mouseDoubleClickEvent(QMouseEvent * )
+{
+ texture->reset();
+ updateGL();
+}
+
+
+/*!
+ \fn ViewerWidget::mouseReleaseEvent(QMouseEvent * e)
+ */
+void ViewerWidget::mouseReleaseEvent(QMouseEvent * )
+{
+ timerMouseMove.start(2000);
+ unsetCursor();
+ if (texture->setSize(QSize(0,0))) { //load full resolution image
+ downloadTex(texture);
+ }
+ updateGL();
+}
+
+
+/*!
+ \fn ViewerWidget::timeoutMouseMove()
+ being called if user didn't move the mouse for longer than 2 sec
+ */
+void ViewerWidget::timeoutMouseMove()
+{
+ setCursor (QCursor (blankCursor));
+}
+
+
+
+/*!
+ \fn ViewerWidget::getOGLstate()
+ check if OpenGL engine is ready. This function is called from outside the widget.
+ If OpenGL doen't work correctly, the widget can be destroyed
+ \return OGLstate::oglNoContext No OpenGl context could be retrieved
+ \return OGLstate::oglNoRectangularTexture GLGL_ARB_texture_rectangle is not supported
+ \return OGLstate::oglOK all is fine
+ */
+OGLstate ViewerWidget::getOGLstate()
+{
+ //no OpenGL context is found. Are the drivers ok?
+ if ( !isValid() ) {
+ return oglNoContext;
+ }
+
+ //GL_ARB_texture_rectangle is not supported
+ QString s = QString ( ( char* ) glGetString ( GL_EXTENSIONS ) );
+ if ( !s.contains ( "GL_ARB_texture_rectangle",false ) ) {
+ return oglNoRectangularTexture;
+ }
+
+ //everything is ok!
+ return oglOK;
+}
diff --git a/kipi-plugins/imageviewer/viewerwidget.h b/kipi-plugins/imageviewer/viewerwidget.h
new file mode 100644
index 0000000..6ea093c
--- /dev/null
+++ b/kipi-plugins/imageviewer/viewerwidget.h
@@ -0,0 +1,129 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Markus Leuthold *
+ * <kusi (+at) forum.titlis.org> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#ifndef _VIEWERWIDGET_H_
+#define _VIEWERWIDGET_H_
+
+//QT includes
+#include <qgl.h>
+#include <qdir.h>
+#include <qimage.h>
+#include <qdatetime.h>
+#include <iostream>
+#include <kurl.h>
+#include <kmimetype.h>
+#include <qregexp.h>
+#include <qcursor.h>
+#include <qtimer.h>
+
+//kipi includes
+#include <libkipi/imageinfo.h>
+#include <libkipi/interface.h>
+#include <libkipi/imagecollection.h>
+
+//local includes
+#include "texture.h"
+
+/**
+ * @short OpenGL widget for image viewer
+ * @author Markus Leuthold <kusi (+at) forum.titlis.org>
+ * @version 0.2
+ */
+
+
+//keep in mind that one cache entry takes 20MB for a 5mpix pic
+#define CACHESIZE 4
+#define EMPTY 99999
+
+namespace KIPIviewer {
+using namespace std;
+
+enum OGLstate {
+ oglOK, oglNoRectangularTexture, oglNoContext
+};
+
+class ViewerWidget : public QGLWidget
+{
+ Q_OBJECT
+
+public:
+ ViewerWidget(KIPI::Interface*);
+ ~ViewerWidget() {
+ glDeleteTextures(1,tex);
+ for(int i=0;i<CACHESIZE;i++) {
+ cache[i].file_index=EMPTY;
+ delete cache[i].texture;
+ }
+ }
+
+ virtual void initializeGL();
+ virtual void resizeGL(int w, int h);
+ virtual void paintGL();
+ void drawImage(Texture * tex);
+ void downloadTex(Texture * tex);
+ Texture * loadImage(int file_index);
+ void prevImage();
+ void nextImage();
+ void zoom(int mdelta, QPoint pos, float factor);
+ virtual void mouseReleaseEvent(QMouseEvent * e);
+ virtual void keyReleaseEvent ( QKeyEvent * e );
+ OGLstate getOGLstate();
+
+protected:
+ struct Cache {
+ int file_index;
+ Texture * texture;
+
+ };
+
+ enum WheelAction {
+ zoomImage, changeImage
+ };
+ Texture * texture;
+ unsigned int old_file_idx,file_idx,idx, oldidx;
+ float ratio_view_y,ratio_view_x,delta;
+ QTime timer;
+ QDir directory;
+ QStringList files;
+ unsigned char * imageJPEGLIB;
+ Cache cache[CACHESIZE];
+ GLuint tex[3];
+ float vertex_height,vertex_width,vertex_left,vertex_top,vertex_right,vertex_bottom;
+ QPoint startdrag, previous_pos;
+ WheelAction wheelAction;
+ bool firstImage;
+ QSize zoomsize;
+ QTimer timerMouseMove;
+ QCursor moveCursor, zoomCursor;
+ float zoomfactor_scrollwheel, zoomfactor_mousemove, zoomfactor_keyboard;
+ QString nullImage;
+ KIPI::Interface * kipiInterface;
+
+protected:
+ virtual void keyPressEvent(QKeyEvent *k);
+ virtual void wheelEvent ( QWheelEvent * e );
+ virtual void mouseMoveEvent ( QMouseEvent * e );
+ virtual void mousePressEvent ( QMouseEvent * e );
+ virtual void mouseDoubleClickEvent(QMouseEvent * e );
+private slots:
+ void timeoutMouseMove();
+};
+}; //namespace KIPIviewer
+#endif // _VIEWERWIDGET_H_