summaryrefslogtreecommitdiffstats
path: root/kuickshow/src/imlibwidget.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kuickshow/src/imlibwidget.cpp')
-rw-r--r--kuickshow/src/imlibwidget.cpp715
1 files changed, 715 insertions, 0 deletions
diff --git a/kuickshow/src/imlibwidget.cpp b/kuickshow/src/imlibwidget.cpp
new file mode 100644
index 00000000..5b5cb84b
--- /dev/null
+++ b/kuickshow/src/imlibwidget.cpp
@@ -0,0 +1,715 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998-2002 Carsten Pfeiffer <[email protected]>
+
+ 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, version 2.
+
+ 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; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kuickdata.h"
+
+#include <sys/time.h>
+#include <unistd.h>
+
+#include <stdlib.h>
+#include <assert.h>
+
+#include <qcolor.h>
+#include <qfile.h>
+#include <qglobal.h>
+#include <qimage.h>
+#include <qobject.h>
+#include <qpalette.h>
+
+#include <kcursor.h>
+#include <kdebug.h>
+#include <kfilemetainfo.h>
+#include <kimageio.h>
+
+#include "filecache.h"
+#include "kuickfile.h"
+#include "kuickimage.h"
+#include "imlibwidget.h"
+
+const int ImlibWidget::ImlibOffset = 256;
+
+ImlibWidget::ImlibWidget( ImData *_idata, QWidget *parent, const char *name ) :
+ QWidget( parent, name, WDestructiveClose )
+{
+ idata = _idata;
+ deleteImData = false;
+ deleteImlibData = true;
+
+ if ( !idata ) { // if no imlib configuration was given, create one ourself
+ idata = new ImData;
+ deleteImData = true;
+ }
+
+ ImlibInitParams par;
+
+ // PARAMS_PALETTEOVERRIDE taken out because of segfault in imlib :o(
+ par.flags = ( PARAMS_REMAP | PARAMS_VISUALID |
+ PARAMS_FASTRENDER | PARAMS_HIQUALITY | PARAMS_DITHER |
+ PARAMS_IMAGECACHESIZE | PARAMS_PIXMAPCACHESIZE );
+
+ Visual* defaultvis = DefaultVisual(x11Display(), x11Screen());
+
+ par.paletteoverride = idata->ownPalette ? 1 : 0;
+ par.remap = idata->fastRemap ? 1 : 0;
+ par.fastrender = idata->fastRender ? 1 : 0;
+ par.hiquality = idata->dither16bit ? 1 : 0;
+ par.dither = idata->dither8bit ? 1 : 0;
+ par.visualid = defaultvis->visualid;
+ uint maxcache = idata->maxCache;
+
+ // 0 == no cache
+ par.imagecachesize = maxcache * 1024;
+ par.pixmapcachesize = maxcache * 1024;
+
+ id = Imlib_init_with_params( x11Display(), &par );
+
+ init();
+}
+
+
+ImlibWidget::ImlibWidget( ImData *_idata, ImlibData *_id, QWidget *parent,
+ const char *name )
+ : QWidget( parent, name, WDestructiveClose )
+{
+ id = _id;
+ idata = _idata;
+ deleteImData = false;
+ deleteImlibData = false;
+
+ if ( !idata ) {
+ idata = new ImData;
+ deleteImData = true;
+ }
+
+ init();
+}
+
+
+void ImlibWidget::init()
+{
+ int w = 1; // > 0 for XCreateWindow
+ int h = 1;
+ myBackgroundColor = Qt::black;
+ m_kuim = 0L;
+ m_kuickFile = 0L;
+
+ if ( !id )
+ qFatal("ImlibWidget: Imlib not initialized, aborting.");
+
+ setAutoRender( true );
+
+ setPalette( QPalette( myBackgroundColor ));
+ setBackgroundMode( PaletteBackground );
+
+ imageCache = new ImageCache( id, 4 ); // cache 4 images (FIXME?)
+ connect( imageCache, SIGNAL( sigBusy() ), SLOT( setBusyCursor() ));
+ connect( imageCache, SIGNAL( sigIdle() ), SLOT( restoreCursor() ));
+
+ win = XCreateSimpleWindow(x11Display(), winId(), 0,0,w,h,0,0,0);
+}
+
+ImlibWidget::~ImlibWidget()
+{
+ delete imageCache;
+ if ( deleteImlibData && id ) free ( id );
+ if ( win ) XDestroyWindow( x11Display(), win );
+ if ( deleteImData ) delete idata;
+}
+
+KURL ImlibWidget::url() const
+{
+ if ( m_kuickFile )
+ return m_kuickFile->url();
+
+ return KURL();
+}
+
+KuickFile * ImlibWidget::currentFile() const
+{
+ return m_kuickFile;
+}
+
+// tries to load "file" and returns the according KuickImage *
+// or 0L if unsuccessful
+// Note that the given file MUST already be downloaded prior to calling this function
+KuickImage * ImlibWidget::loadImageInternal( KuickFile * file )
+{
+ assert( file->isAvailable() );
+
+ // apply default image modifications
+ mod.brightness = idata->brightness + ImlibOffset;
+ mod.contrast = idata->contrast + ImlibOffset;
+ mod.gamma = idata->gamma + ImlibOffset;
+
+ KuickImage *kuim = imageCache->getKuimage( file, mod );
+ if ( !kuim ) {// couldn't load file, maybe corrupt or wrong format
+ kdWarning() << "ImlibWidget: can't load image " << file->url().prettyURL() << endl;
+ return 0L;
+ }
+
+ loaded( kuim ); // maybe upscale/downscale/rotate in subclasses
+
+ return kuim;
+}
+
+// overridden in subclass
+void ImlibWidget::loaded( KuickImage * )
+{
+}
+
+bool ImlibWidget::loadImage( const KURL& url )
+{
+ return loadImage( FileCache::self()->getFile( url ));
+}
+
+bool ImlibWidget::loadImage( KuickFile * file )
+{
+ if ( file->waitForDownload( this ) != KuickFile::OK)
+ return false;
+
+ KuickImage *kuim = loadImageInternal( file );
+ // FIXME - check everywhere if we have a kuim or not!
+
+ if ( kuim ) {
+ m_kuim = kuim;
+ autoUpdate( true ); // -> updateWidget() -> updateGeometry()
+ m_kuickFile = file;
+ return true;
+ }
+
+ return false;
+}
+
+
+bool ImlibWidget::cacheImage( const KURL& url )
+{
+// qDebug("cache image: %s", url.url().latin1());
+ KuickFile *file = FileCache::self()->getFile( url );
+ if ( file->isAvailable() )
+ return cacheImage( file );
+ else {
+ if ( !file->download() ) {
+ return false;
+ }
+ connect( file, SIGNAL( downloaded( KuickFile * )), SLOT( cacheImage( KuickFile * )) );
+ return true; // optimistic
+ }
+}
+
+bool ImlibWidget::cacheImage( KuickFile * file )
+{
+// qDebug("cache image: %s", file->url().url().latin1());
+ KuickImage *kuim = loadImageInternal( file );
+ if ( kuim ) {
+ kuim->renderPixmap();
+ return true;
+ }
+ return false;
+}
+
+
+void ImlibWidget::showImage()
+{
+ XMapWindow( x11Display(), win );
+ XSync( x11Display(), False );
+}
+
+
+// -256..256
+void ImlibWidget::setBrightness( int factor )
+{
+ mod.brightness = factor + ImlibOffset;
+ setImageModifier();
+
+ autoUpdate();
+}
+
+
+// -256..256
+void ImlibWidget::setContrast( int factor )
+{
+ mod.contrast = factor + ImlibOffset;
+ setImageModifier();
+
+ autoUpdate();
+}
+
+
+// -256..256
+void ImlibWidget::setGamma( int factor )
+{
+ mod.gamma = factor + ImlibOffset;
+ setImageModifier();
+
+ autoUpdate();
+}
+
+
+Rotation ImlibWidget::rotation() const
+{
+ return m_kuim ? m_kuim->absRotation() : ROT_0;
+}
+
+FlipMode ImlibWidget::flipMode() const
+{
+ return m_kuim ? m_kuim->flipMode() : FlipNone;
+}
+
+void ImlibWidget::zoomImage( float factor )
+{
+ if ( factor == 1 || factor == 0 || !m_kuim )
+ return;
+
+ int newWidth = (int) (factor * (float) m_kuim->width());
+ int newHeight = (int) (factor * (float) m_kuim->height());
+
+ if ( canZoomTo( newWidth, newHeight ) )
+ {
+ m_kuim->resize( newWidth, newHeight, idata->smoothScale ? KuickImage::SMOOTH : KuickImage::FAST );
+ autoUpdate( true );
+ }
+}
+
+bool ImlibWidget::canZoomTo( int newWidth, int newHeight )
+{
+ if ( newWidth <= 2 || newHeight <= 2 ) // minimum size for an image is 2x2 pixels
+ return false;
+
+ return true;
+}
+
+
+void ImlibWidget::showImageOriginalSize()
+{
+ if ( !m_kuim )
+ return;
+
+ m_kuim->restoreOriginalSize();
+ autoUpdate( true );
+
+ showImage();
+}
+
+bool ImlibWidget::autoRotate( KuickImage *kuim )
+{
+ KFileMetaInfo metadatas( kuim->file().localFile() );
+ if ( !metadatas.isValid() )
+ return false;
+
+ KFileMetaInfoItem metaitem = metadatas.item("Orientation");
+ if ( !metaitem.isValid()
+#if QT_VERSION >= 0x030100
+ || metaitem.value().isNull()
+#endif
+ )
+ return false;
+
+
+ switch ( metaitem.value().toInt() )
+ {
+ // Orientation:
+ // 1: normal
+ // 2: flipped horizontally
+ // 3: ROT 180
+ // 4: flipped vertically
+ // 5: ROT 90 -> flip horizontally
+ // 6: ROT 90
+ // 7: ROT 90 -> flip vertically
+ // 8: ROT 270
+
+ case 1:
+ default:
+ kuim->rotateAbs( ROT_0 );
+ break;
+ case 2:
+ kuim->flipAbs( FlipHorizontal );
+ break;
+ case 3:
+ kuim->rotateAbs( ROT_180 );
+ break;
+ case 4:
+ kuim->flipAbs( FlipVertical );
+ break;
+ case 5:
+ kuim->rotateAbs( ROT_90 );
+ kuim->flipAbs( FlipHorizontal );
+ break;
+ case 6:
+ kuim->rotateAbs( ROT_90 );
+ break;
+ case 7:
+ kuim->rotateAbs( ROT_90 );
+ kuim->flipAbs( FlipVertical );
+ break;
+ case 8:
+ kuim->rotateAbs( ROT_270 );
+ break;
+ }
+
+ return true;
+}
+
+
+void ImlibWidget::setRotation( Rotation rot )
+{
+ if ( m_kuim )
+ {
+ if ( m_kuim->rotateAbs( rot ) )
+ autoUpdate( true );
+ }
+}
+
+
+// slots connected to Accels and popupmenu
+void ImlibWidget::rotate90()
+{
+ if ( !m_kuim )
+ return;
+
+ m_kuim->rotate( ROT_90 );
+ rotated( m_kuim, ROT_90 );
+ autoUpdate( true );
+}
+
+void ImlibWidget::rotate180()
+{
+ if ( !m_kuim )
+ return;
+
+ m_kuim->rotate( ROT_180 );
+ rotated( m_kuim, ROT_180 );
+ autoUpdate();
+}
+
+void ImlibWidget::rotate270()
+{
+ if ( !m_kuim )
+ return;
+
+ m_kuim->rotate( ROT_270 );
+ rotated( m_kuim, ROT_270 );
+ autoUpdate( true );
+}
+
+
+// should this go into a subclass?
+void ImlibWidget::flipHoriz()
+{
+ if ( !m_kuim )
+ return;
+
+ m_kuim->flip( FlipHorizontal );
+ autoUpdate();
+}
+
+void ImlibWidget::flipVert()
+{
+ if ( !m_kuim )
+ return;
+
+ m_kuim->flip( FlipVertical );
+ autoUpdate();
+}
+// end slots
+
+
+void ImlibWidget::setFlipMode( int mode )
+{
+ if ( !m_kuim )
+ return;
+
+ if ( m_kuim->flipAbs( mode ) )
+ autoUpdate();
+}
+
+
+void ImlibWidget::updateWidget( bool geometryUpdate )
+{
+ if ( !m_kuim )
+ return;
+
+// if ( geometryUpdate )
+// XUnmapWindow( x11Display(), win );// remove the old image -> no flicker
+
+ XSetWindowBackgroundPixmap( x11Display(), win, m_kuim->pixmap() );
+
+ if ( geometryUpdate )
+ updateGeometry( m_kuim->width(), m_kuim->height() );
+
+ XClearWindow( x11Display(), win );
+
+ showImage();
+}
+
+
+// here we just use the size of m_kuim, may be overridden in subclass
+void ImlibWidget::updateGeometry( int w, int h )
+{
+ XMoveWindow( x11Display(), win, 0, 0 ); // center?
+ XResizeWindow( x11Display(), win, w, h );
+ resize( w, h );
+}
+
+
+void ImlibWidget::closeEvent( QCloseEvent *e )
+{
+ e->accept();
+ QWidget::closeEvent( e );
+}
+
+
+void ImlibWidget::setBackgroundColor( const QColor& color )
+{
+ myBackgroundColor = color;
+ setPalette( QPalette( myBackgroundColor ));
+ repaint( false); // FIXME - false? necessary at all?
+}
+
+const QColor& ImlibWidget::backgroundColor() const
+{
+ return myBackgroundColor;
+}
+
+
+void ImlibWidget::setImageModifier()
+{
+ if ( !m_kuim )
+ return;
+
+ Imlib_set_image_modifier( id, m_kuim->imlibImage(), &mod );
+ m_kuim->setDirty( true );
+}
+
+int ImlibWidget::imageWidth() const
+{
+ return m_kuim ? m_kuim->width() : 0;
+}
+
+int ImlibWidget::imageHeight() const
+{
+ return m_kuim ? m_kuim->height() : 0;
+}
+
+void ImlibWidget::setBusyCursor()
+{
+ if ( ownCursor() )
+ m_oldCursor = cursor();
+ else
+ m_oldCursor = QCursor();
+
+ setCursor( KCursor::waitCursor() );
+}
+
+void ImlibWidget::restoreCursor()
+{
+ if ( cursor().shape() == KCursor::waitCursor().shape() ) // only if nobody changed the cursor in the meantime!
+ setCursor( m_oldCursor );
+}
+
+// Reparenting a widget in Qt in fact means destroying the old X window of the widget
+// and creating a new one. And since the X window used for the Imlib image is a child
+// of this widget's X window, destroying this widget's X window would mean also
+// destroying the Imlib image X window. Therefore it needs to be temporarily reparented
+// away and reparented back to the new X window.
+// Reparenting may happen e.g. when doing the old-style (non-NETWM) fullscreen changes.
+void ImlibWidget::reparent( QWidget* parent, WFlags f, const QPoint& p, bool showIt )
+{
+ XWindowAttributes attr;
+ XGetWindowAttributes( x11Display(), win, &attr );
+ XUnmapWindow( x11Display(), win );
+ XReparentWindow( x11Display(), win, attr.root, 0, 0 );
+ QWidget::reparent( parent, f, p, showIt );
+ XReparentWindow( x11Display(), win, winId(), attr.x, attr.y );
+ if( attr.map_state != IsUnmapped )
+ XMapWindow( x11Display(), win );
+}
+
+void ImlibWidget::rotated( KuickImage *, int )
+{
+}
+
+//----------
+
+
+// uhh ugly, we have two lists to map from filename to KuickImage :-/
+ImageCache::ImageCache( ImlibData *id, int maxImages )
+{
+ myId = id;
+ idleCount = 0;
+ myMaxImages = maxImages;
+ kuickList.setAutoDelete( true );
+ fileList.clear();
+ kuickList.clear();
+}
+
+
+ImageCache::~ImageCache()
+{
+ kuickList.clear();
+ fileList.clear();
+}
+
+
+void ImageCache::setMaxImages( int maxImages )
+{
+ myMaxImages = maxImages;
+ int count = kuickList.count();
+ while ( count > myMaxImages ) {
+ kuickList.removeLast();
+ fileList.remove( fileList.fromLast() );
+ count--;
+ }
+}
+
+void ImageCache::slotBusy()
+{
+ if ( idleCount == 0 )
+ emit sigBusy();
+
+ idleCount++;
+}
+
+void ImageCache::slotIdle()
+{
+ idleCount--;
+
+ if ( idleCount == 0 )
+ emit sigIdle();
+}
+
+
+KuickImage * ImageCache::getKuimage( KuickFile * file,
+ ImlibColorModifier mod )
+{
+ if ( !file )
+ return 0L;
+
+ assert( file->isAvailable() ); // debug build
+ if ( file->waitForDownload( 0L ) != KuickFile::OK ) // and for users
+ return 0L;
+
+ KuickImage *kuim = 0L;
+ int index = fileList.findIndex( file );
+ if ( index != -1 ) {
+ if ( index == 0 )
+ kuim = kuickList.at( 0 );
+
+ // need to reorder the lists, otherwise we might delete the current
+ // image when a new one is cached and the current one is the last!
+ else {
+ kuim = kuickList.take( index );
+ kuickList.insert( 0, kuim );
+ fileList.remove( file );
+ fileList.prepend( file );
+ }
+
+ return kuim;
+ }
+
+ if ( !kuim ) {
+ slotBusy();
+
+// #ifndef NDEBUG
+// struct timeval tms1, tms2;
+// gettimeofday( &tms1, NULL );
+// #endif
+
+ ImlibImage *im = Imlib_load_image( myId,
+ QFile::encodeName( file->localFile() ).data() );
+
+// #ifndef NDEBUG
+// gettimeofday( &tms2, NULL );
+// qDebug("*** LOADING image: %s, took %ld ms", file.latin1(),
+// (tms2.tv_usec - tms1.tv_usec)/1000);
+// #endif
+
+ slotIdle();
+ if ( !im ) {
+ slotBusy();
+ im = loadImageWithQt( file->localFile() );
+ slotIdle();
+ if ( !im )
+ return 0L;
+ }
+
+ Imlib_set_image_modifier( myId, im, &mod );
+ kuim = new KuickImage( file, im, myId );
+ connect( kuim, SIGNAL( startRendering() ), SLOT( slotBusy() ));
+ connect( kuim, SIGNAL( stoppedRendering() ), SLOT( slotIdle() ));
+
+ kuickList.insert( 0, kuim );
+ fileList.prepend( file );
+ }
+
+ if ( kuickList.count() > (uint) myMaxImages ) {
+// qDebug(":::: now removing from cache: %s", (*fileList.fromLast()).latin1());
+ kuickList.removeLast();
+ fileList.remove( fileList.fromLast() );
+ }
+
+ return kuim;
+}
+
+
+// Note: the returned image's filename will not be the real filename (which it usually
+// isn't anyway, according to Imlib's sources).
+ImlibImage * ImageCache::loadImageWithQt( const QString& fileName ) const
+{
+ kdDebug() << "Trying to load " << fileName << " with KImageIO..." << endl;
+
+ KImageIO::registerFormats();
+
+ QImage image( fileName );
+ if ( image.isNull() )
+ return 0L;
+ if ( image.depth() != 32 ) {
+ image.setAlphaBuffer(false);
+ image = image.convertDepth(32);
+
+ if ( image.isNull() )
+ return 0L;
+ }
+
+ // convert to 24 bpp (discard alpha)
+ int numPixels = image.width() * image.height();
+ const int NUM_BYTES_NEW = 3; // 24 bpp
+ uchar *newImageData = new uchar[numPixels * NUM_BYTES_NEW];
+ uchar *newData = newImageData;
+
+ int w = image.width();
+ int h = image.height();
+
+ for (int y = 0; y < h; y++) {
+ QRgb *scanLine = reinterpret_cast<QRgb *>( image.scanLine(y) );
+ for (int x = 0; x < w; x++) {
+ const QRgb& pixel = scanLine[x];
+ *(newData++) = qRed(pixel);
+ *(newData++) = qGreen(pixel);
+ *(newData++) = qBlue(pixel);
+ }
+ }
+
+ ImlibImage *im = Imlib_create_image_from_data( myId, newImageData, NULL,
+ image.width(), image.height() );
+
+ delete[] newImageData;
+
+ return im;
+}
+
+#include "imlibwidget.moc"