author | zecke <zecke> | 2004-03-22 23:32:41 (UTC) |
---|---|---|
committer | zecke <zecke> | 2004-03-22 23:32:41 (UTC) |
commit | 428b687982966dc2efabaf6dbcc55ad0ea30aa10 (patch) (side-by-side diff) | |
tree | 86da20abd2e4b97a59dc32e17996bde5ee74cc91 /noncore/graphics | |
parent | 7ce623c6351646ce738a81e103632d73c5454ecc (diff) | |
download | opie-428b687982966dc2efabaf6dbcc55ad0ea30aa10.zip opie-428b687982966dc2efabaf6dbcc55ad0ea30aa10.tar.gz opie-428b687982966dc2efabaf6dbcc55ad0ea30aa10.tar.bz2 |
Initial Check in of the Eye Of Zilla. This ImageViewer features
Image Infos, EXIF, Jpeg,Png,Gif support. It supports scaled loading
of Jpegs. an smart image cache....
GUI needs some work and we need to find a bug in QCOP as well.
TODO:
Add Image Service for example Mailer
Add ImageCanvas/Zoomer/Display
41 files changed, 3947 insertions, 0 deletions
diff --git a/noncore/graphics/opie-eye/DESIGN b/noncore/graphics/opie-eye/DESIGN new file mode 100644 index 0000000..b3943df --- a/dev/null +++ b/noncore/graphics/opie-eye/DESIGN @@ -0,0 +1,22 @@ +The Design of the Eye Caramba Opie Image Viewer. + +It consists out of several Parts: + +1.) A interface for Getting Information, Files and Requesting Pixmap and the Full Image +2.) A ImageCache which uses the current Interface to request the Pixmap in the given side + (the gui will listen to the slot and insert the image ) +3.) A small library which gives the Image Info and Pixmap. Internally it'll have a forked slave + to do the actual work. It'll also write thumbnails. +4.) The Interface parts are designed for stuff like FileSystem View or Digi Cam support which would + read thumbnail from different locations + + + +TODO: + - Special QIconListViewItem painting routines to include BEAM,Rename,Delete,Rotate buttons + - Create SlideShows + -Add from View. Will ask to add to a slide show + -And special slideshow widget for the order + -Save in Config within AppDir of eyecaramba + + diff --git a/noncore/graphics/opie-eye/gui/filesystem.cpp b/noncore/graphics/opie-eye/gui/filesystem.cpp new file mode 100644 index 0000000..91bcf67 --- a/dev/null +++ b/noncore/graphics/opie-eye/gui/filesystem.cpp @@ -0,0 +1,52 @@ +/* + * GPLv2 zecke@handhelds.org + * No WArranty... + */ + +#include <qpopupmenu.h> +#include <qtoolbar.h> + +#include <qpe/resource.h> +#include <qpe/storage.h> + + +#include "filesystem.h" + +PFileSystem::PFileSystem( QToolBar* bar) + : QToolButton( bar ) +{ + setIconSet( Resource::loadIconSet( "cardmon/pcmcia" ) ); + + m_pop = new QPopupMenu( this ); + connect( m_pop, SIGNAL( activated( int ) ), + this, SLOT(slotSelectDir( int ) ) ); + + m_storage = new StorageInfo(); + connect(m_storage, SIGNAL(disksChanged() ), + this, SLOT( changed() ) ); + changed(); + + setPopup( m_pop ); +} + +PFileSystem::~PFileSystem() { + delete m_storage; +} + + +void PFileSystem::changed() { + m_pop->clear(); + m_dev.clear(); + const QList<FileSystem> &fs = m_storage->fileSystems(); + QListIterator<FileSystem> it(fs ); + for ( ; it.current(); ++it ) { + const QString disk = (*it)->name(); + const QString path = (*it)->path(); + m_dev.insert( disk, path ); + m_pop->insertItem( disk ); + } +} + +void PFileSystem::slotSelectDir( int id ) { + emit changeDir( m_dev[m_pop->text(id )] ); +} diff --git a/noncore/graphics/opie-eye/gui/filesystem.h b/noncore/graphics/opie-eye/gui/filesystem.h new file mode 100644 index 0000000..a29ad87 --- a/dev/null +++ b/noncore/graphics/opie-eye/gui/filesystem.h @@ -0,0 +1,34 @@ +/* + * GPLv2 zecke@handhelds.org + * No WArranty... + */ + +#ifndef PHUNK_FILE_SYSTEM_H +#define PHUNK_FILE_SYSTEM_H + +#include <qtoolbutton.h> +#include <qmap.h> + +class QPopupMenu; +class StorageInfo; +class PFileSystem : public QToolButton { + Q_OBJECT +public: + PFileSystem( QToolBar* ); + ~PFileSystem(); + +signals: + void changeDir( const QString& ); + +private slots: + void slotSelectDir( int ); + void changed(); + +private: + QPopupMenu* m_pop; + StorageInfo *m_storage; + QMap<QString, QString> m_dev; +}; + + +#endif diff --git a/noncore/graphics/opie-eye/gui/iconview.cpp b/noncore/graphics/opie-eye/gui/iconview.cpp new file mode 100644 index 0000000..0b80012 --- a/dev/null +++ b/noncore/graphics/opie-eye/gui/iconview.cpp @@ -0,0 +1,296 @@ +/* + * GPLv2 zecke@handhelds.org + * No WArranty... + */ + +#include "iconview.h" + +#include <lib/imagecache.h> + +#include <iface/dirview.h> +#include <iface/dirlister.h> + +#include <qpe/config.h> +#include <qpe/resource.h> +#include <qpe/qpemessagebox.h> +#include <qpe/ir.h> +#include <qpe/qcopenvelope_qws.h> + +#include <qiconview.h> +#include <qlabel.h> +#include <qhbox.h> +#include <qcombobox.h> +#include <qdir.h> +#include <qapplication.h> +#include <qmainwindow.h> +#include <qtimer.h> +#include <qstyle.h> + + + +namespace { + QPixmap* _dirPix = 0; + QPixmap* _unkPix = 0; + class IconViewItem : public QIconViewItem { + public: + IconViewItem( QIconView*, const QString& path, const QString& name, bool isDir = false); + QPixmap* pixmap()const; + QString path()const { return m_path; } + bool isDir()const { return m_isDir; } + void setText( const QString& ); + private: + mutable QPixmap* m_pix; + QString m_path; + bool m_isDir : 1; + bool m_noInfo :1; + }; + + +/* + * If we request an Image or String + * we add it to the map + */ + QMap<QString, IconViewItem*> g_stringInf; + QMap<QString, IconViewItem*> g_stringPix; + + IconViewItem::IconViewItem( QIconView* view,const QString& path, + const QString& name, bool isDir ) + : QIconViewItem( view ), m_path( path ), m_isDir( isDir ), + m_noInfo( false ) + { + QIconViewItem::setText( name ); + if ( isDir && !_dirPix ) + _dirPix = new QPixmap( Resource::loadPixmap("advancedfm/FileBrowser")); + else if ( !isDir && !_unkPix ) + _unkPix = new QPixmap( Resource::loadPixmap( "UnknownDocument" ) ); + } + inline QPixmap* IconViewItem::pixmap()const { + if ( m_isDir ) + return _dirPix; + else{ + if (!m_noInfo && !g_stringInf.contains( m_path ) ) { + currentView()->dirLister()->imageInfo( m_path ); + g_stringInf.insert( m_path, const_cast<IconViewItem*>(this)); + } + + m_pix = PPixmapCache::self()->cachedImage( m_path, 64, 64 ); + if ( !m_pix && !g_stringPix.contains( m_path )) { + currentView()->dirLister()->thumbNail( m_path, 64, 64 ); + g_stringPix.insert( m_path, const_cast<IconViewItem*>(this)); + } + return m_pix ? m_pix : _unkPix; + } + } + inline void IconViewItem::setText( const QString& str ) { + QString text = QIconViewItem::text()+"\n"+str; + m_noInfo = true; + QIconViewItem::setText( text ); + } +} + + +PIconView::PIconView( QWidget* wid, Config* cfg ) + : QVBox( wid ), m_cfg( cfg ) +{ + { + QCopEnvelope( "QPE/Application/opie-eye_slave", "foo()" ); + } + m_path = QDir::homeDirPath(); + + QHBox *hbox = new QHBox( this ); + QLabel* lbl = new QLabel( hbox ); + lbl->setText( tr("View as" ) ); + + m_views = new QComboBox( hbox, "View As" ); + connect( m_views, SIGNAL(activated(int)), + this, SLOT(slotViewChanged(int)) ); + + m_view= new QIconView( this ); + connect(m_view, SIGNAL(clicked(QIconViewItem*) ), + this, SLOT(slotClicked(QIconViewItem*)) ); + + m_view->setArrangement( QIconView::LeftToRight ); + m_view->setItemTextPos( QIconView::Right ); + + + int dw = QApplication::desktop()->width(); + int viewerWidth = dw-style().scrollBarExtent().width(); + m_view->setGridX( viewerWidth-2*m_view->spacing() ); + m_view->setGridY( fontMetrics().height()*2+40 ); + loadViews(); + slotViewChanged( m_views->currentItem() ); +} + +PIconView::~PIconView() { +} + +void PIconView::slotDirUp() { + QDir dir( m_path ); + dir.cdUp(); + slotChangeDir( dir.absPath() ); + +} + +void PIconView::slotChangeDir(const QString& path) { + if ( !currentView() ) + return; + + PDirLister *lister = currentView()->dirLister(); + if (!lister ) + return; + + lister->setStartPath( path ); + m_path = lister->currentPath(); + + m_view->clear(); + addFolders( lister->folders() ); + addFiles( lister->files() ); + + // looks ugly + static_cast<QMainWindow*>(parent())->setCaption( QObject::tr("%1 - O View", "Name of the dir").arg( m_path ) ); +} + +QString PIconView::currentFileName(bool &isDir)const { + isDir = false; + QIconViewItem* _it = m_view->currentItem(); + if ( !_it ) + return QString::null; + + IconViewItem* it = static_cast<IconViewItem*>( _it ); + isDir = it->isDir(); + return it->path(); +} + +void PIconView::slotTrash() { + bool isDir; + QString pa = currentFileName( isDir ); + if ( isDir && pa.isEmpty() ) + return; + + if (!QPEMessageBox::confirmDelete( this, + tr("Delete Image" ), + tr("the Image %1" ).arg(pa))) + return + + + currentView()->dirLister()->deleteImage( pa ); + delete m_view->currentItem(); +} +void PIconView::loadViews() { + ViewMap::Iterator it; + ViewMap* map = viewMap(); + for ( it = map->begin(); it != map->end(); ++it ) + m_views->insertItem( QObject::tr(it.key() ) ); +} + +void PIconView::resetView() { + slotViewChanged(m_views->currentItem()); +} + +void PIconView::slotViewChanged( int i) { + if (!m_views->count() ) { + setCurrentView( 0l); + return; + } + + PDirView* cur = currentView(); + delete cur; + QString str = m_views->text(i); + cur = (*(*viewMap())[str])(*m_cfg); + setCurrentView( cur ); + + /* connect to the signals of the lister */ + PDirLister* lis = cur->dirLister(); + connect(lis, SIGNAL(sig_thumbInfo(const QString&, const QString& )), + this, SLOT( slotThumbInfo(const QString&, const QString&))); + connect(lis, SIGNAL( sig_thumbNail(const QString&, const QPixmap&)), + this, SLOT(slotThumbNail(const QString&, const QPixmap&))); + connect(lis, SIGNAL(sig_start()), + this, SLOT(slotStart())); + connect(lis, SIGNAL(sig_end()) , + this, SLOT(slotEnd()) ); + + + /* reload now */ + QTimer::singleShot( 0, this, SLOT(slotReloadDir())); +} + + +void PIconView::slotReloadDir() { + slotChangeDir( m_path ); +} + + +void PIconView::addFolders( const QStringList& lst) { + QStringList::ConstIterator it; + + for(it=lst.begin(); it != lst.end(); ++it ) { + (void)new IconViewItem( m_view, m_path+"/"+(*it), (*it), true ); + } + +} + +void PIconView::addFiles( const QStringList& lst) { + QStringList::ConstIterator it; + for (it=lst.begin(); it!= lst.end(); ++it ) + (void)new IconViewItem( m_view, m_path+"/"+(*it), (*it) ); + +} + +void PIconView::slotClicked(QIconViewItem* _it) { + if(!_it ) + return; + + IconViewItem* it = static_cast<IconViewItem*>(_it); + if( it->isDir() ) + slotChangeDir( it->path() ); + else // view image + ; +} + +void PIconView::slotThumbInfo( const QString& _path, const QString& str ) { + if ( g_stringInf.contains( _path ) ) { + IconViewItem* item = g_stringInf[_path]; + item->setText( str ); + item->repaint(); + g_stringInf.remove( _path ); + } +} +void PIconView::slotThumbNail(const QString& _path, const QPixmap &pix) { + if ( g_stringPix.contains( _path ) ) { + IconViewItem* item = g_stringPix[_path]; + PPixmapCache::self()->insertImage( _path, pix, 64, 64 ); + item->repaint(); + g_stringPix.remove( _path ); + } +} + + +void PIconView::slotRename() { + +} + +void PIconView::slotBeam() { + bool isDir; + QString pa = currentFileName( isDir ); + if ( isDir && pa.isEmpty() ) + return; + + Ir* ir = new Ir( this ); + connect( ir, SIGNAL(done(Ir*)), + this, SLOT(slotBeamDone(Ir*))); + ir->send(pa, tr( "Image" ) ); + +} + +void PIconView::slotBeamDone( Ir* ir) { + delete ir; +} + +void PIconView::slotStart() { + m_view->setUpdatesEnabled( false ); +} + +void PIconView::slotEnd() { + m_view->setUpdatesEnabled( true ); +} diff --git a/noncore/graphics/opie-eye/gui/iconview.h b/noncore/graphics/opie-eye/gui/iconview.h new file mode 100644 index 0000000..439833a --- a/dev/null +++ b/noncore/graphics/opie-eye/gui/iconview.h @@ -0,0 +1,59 @@ +/* + * GPLv2 zecke@handhelds.org + * No WArranty... + */ + +#ifndef PHUNK_ICON_VIEW_H +#define PHUNK_ICON_VIEW_H + +#include <qvbox.h> + +#include <qpe/config.h> + +class QIconView; +class QIconViewItem; +class QComboBox; +class PIconViewItem; +class PDirLister; +class Ir; +class PIconView : public QVBox { + Q_OBJECT + friend class PIconViewItem; +public: + PIconView( QWidget* wid, Config *cfg ); + ~PIconView(); + void resetView(); + +private: + QString currentFileName(bool &isDir)const; + void loadViews(); + +private slots: + void slotDirUp(); + void slotChangeDir(const QString&); + void slotTrash(); + void slotViewChanged( int ); + void slotReloadDir(); + void slotRename(); + void slotBeam(); + void slotBeamDone( Ir* ); + + void slotStart(); + void slotEnd(); + +/* for performance reasons make it inline in the future */ + void addFolders( const QStringList& ); + void addFiles( const QStringList& ); + void slotClicked(QIconViewItem* ); + +/**/ + void slotThumbInfo(const QString&, const QString&); + void slotThumbNail(const QString&, const QPixmap&); +private: + Config *m_cfg; + QComboBox* m_views; + QIconView* m_view; + QString m_path; +}; + +#endif diff --git a/noncore/graphics/opie-eye/gui/mainwindow.cpp b/noncore/graphics/opie-eye/gui/mainwindow.cpp new file mode 100644 index 0000000..0a2fcab --- a/dev/null +++ b/noncore/graphics/opie-eye/gui/mainwindow.cpp @@ -0,0 +1,114 @@ +/* + * GPLv2 zecke@handhelds.org + * No WArranty... + */ + +#include <qtoolbar.h> +#include <qtoolbutton.h> +#include <qlayout.h> +#include <qdialog.h> +#include <qmap.h> + +#include <qpe/resource.h> +#include <qpe/config.h> +#include <qpe/ir.h> + +#include <opie/oapplicationfactory.h> +#include <opie/otabwidget.h> + +#include <iface/ifaceinfo.h> +#include <iface/dirview.h> + +#include "iconview.h" +#include "filesystem.h" + +#include "mainwindow.h" + +OPIE_EXPORT_APP( OApplicationFactory<PMainWindow> ) + +PMainWindow::PMainWindow(QWidget* wid, const char* name, WFlags style) + : QMainWindow( wid, name, style ), m_cfg("phunkview") +{ + setCaption( QObject::tr("Opie Eye Caramba" ) ); + m_cfg.setGroup("Zecke_view" ); + /* + * Initialize ToolBar and IconView + * And Connect Them + */ + QToolBar *bar = new QToolBar( this ); + bar->setHorizontalStretchable( true ); + setToolBarsMovable( false ); + + m_view = new PIconView( this, &m_cfg ); + setCentralWidget( m_view ); + + QToolButton *btn = new QToolButton( bar ); + btn->setIconSet( Resource::loadIconSet( "up" ) ); + connect( btn, SIGNAL(clicked()), + m_view, SLOT(slotDirUp()) ); + + btn = new PFileSystem( bar ); + connect( btn, SIGNAL( changeDir( const QString& ) ), + m_view, SLOT(slotChangeDir( const QString& ) ) ); + + btn = new QToolButton( bar ); + btn->setIconSet( Resource::loadIconSet( "edit" ) ); + connect( btn, SIGNAL(clicked()), + m_view, SLOT(slotRename()) ); + + if ( Ir::supported() ) { + btn = new QToolButton( bar ); + btn->setIconSet( Resource::loadIconSet( "beam" ) ); + connect( btn, SIGNAL(clicked()), + m_view, SLOT(slotBeam()) ); + } + + btn = new QToolButton( bar ); + btn->setIconSet( Resource::loadIconSet( "trash" ) ); + connect( btn, SIGNAL(clicked() ), + m_view, SLOT(slotTrash() ) ); + + btn = new QToolButton( bar ); + btn->setIconSet( Resource::loadIconSet( "SettingsIcon" ) ); + connect( btn, SIGNAL(clicked() ), + this, SLOT(slotConfig() ) ); + +} + +PMainWindow::~PMainWindow() { +} + + +void PMainWindow::slotConfig() { + QDialog dlg(this, 0, true); + dlg.setCaption( tr("Phunk View - Config" ) ); + + QHBoxLayout *lay = new QHBoxLayout(&dlg); + OTabWidget *wid = new OTabWidget(&dlg ); + lay->addWidget( wid ); + ViewMap *vM = viewMap(); + ViewMap::Iterator _it = vM->begin(); + QMap<PDirView*, QWidget*> lst; + + for( ; _it != vM->end(); ++_it ) { + PDirView *view = (_it.data())(m_cfg); + PInterfaceInfo *inf = view->interfaceInfo(); + QWidget *_wid = inf->configWidget( m_cfg ); + _wid->reparent(wid, QPoint() ); + lst.insert( view, _wid ); + wid->addTab( _wid, QString::null, inf->name() ); + } + + dlg.showMaximized(); + bool act = ( dlg.exec() == QDialog::Accepted ); + + QMap<PDirView*, QWidget*>::Iterator it; + for ( it = lst.begin(); it != lst.end(); ++it ) { + if ( act ) + it.key()->interfaceInfo()->writeConfig(it.data(), m_cfg); + delete it.key(); + } + + if ( act ) + m_view->resetView(); +} diff --git a/noncore/graphics/opie-eye/gui/mainwindow.h b/noncore/graphics/opie-eye/gui/mainwindow.h new file mode 100644 index 0000000..408fe32 --- a/dev/null +++ b/noncore/graphics/opie-eye/gui/mainwindow.h @@ -0,0 +1,29 @@ +/* + * GPLv2 zecke@handhelds.org + * No WArranty... + */ + +#ifndef PHUNK_MAIN_WINDOW_H +#define PHUNK_MAIN_WINDOW_H + +#include <qmainwindow.h> + +#include <qpe/config.h> + +class PIconView; +class PMainWindow : public QMainWindow { + Q_OBJECT +public: + static QString appName() { return QString::fromLatin1("opie-eye" ); } + PMainWindow(QWidget*, const char*, WFlags ); + ~PMainWindow(); + +private: + Config m_cfg; + PIconView* m_view; + +private slots: + void slotConfig(); +}; + +#endif diff --git a/noncore/graphics/opie-eye/gui/viewmap.cpp b/noncore/graphics/opie-eye/gui/viewmap.cpp new file mode 100644 index 0000000..2dffb38 --- a/dev/null +++ b/noncore/graphics/opie-eye/gui/viewmap.cpp @@ -0,0 +1,9 @@ +#include <iface/dirview.h> + +namespace { + ViewMap m_view; +} + +ViewMap* viewMap() { + return m_view; +} diff --git a/noncore/graphics/opie-eye/iface/dirlister.cpp b/noncore/graphics/opie-eye/iface/dirlister.cpp new file mode 100644 index 0000000..7cf4361 --- a/dev/null +++ b/noncore/graphics/opie-eye/iface/dirlister.cpp @@ -0,0 +1,9 @@ +#include "dirlister.h" + + +PDirLister::PDirLister( const char* name ) + : QObject( 0, name ) +{} + +PDirLister::~PDirLister() +{} diff --git a/noncore/graphics/opie-eye/iface/dirlister.h b/noncore/graphics/opie-eye/iface/dirlister.h new file mode 100644 index 0000000..fcc55ec --- a/dev/null +++ b/noncore/graphics/opie-eye/iface/dirlister.h @@ -0,0 +1,47 @@ +/* + * GPLv2 zecke@handhelds.org + * No WArranty... + */ + +#ifndef PHUNK_DIR_LISTER_H +#define PHUNK_DIR_LISTER_H + +#include <qobject.h> +#include <qstring.h> +#include <qstringlist.h> + + +class PDirLister : public QObject { + Q_OBJECT +public: + enum Factor { Width, Height, None }; + + PDirLister( const char* name ); + + virtual QString defaultPath()const = 0; + virtual QString setStartPath( const QString& ) = 0; + virtual QString currentPath()const = 0; + virtual QStringList folders()const = 0; + virtual QStringList files()const = 0; +public slots: + virtual void deleteImage( const QString& ) = 0; + virtual void imageInfo( const QString&) = 0; + virtual void fullImageInfo( const QString& ) = 0; + virtual void thumbNail( const QString&, int max_wid, int max_h ) = 0; + virtual QImage image( const QString&, Factor, int max = 0) = 0; + +signals: + void sig_dirchanged(); + void sig_filechanged(); + void sig_start(); + void sig_end(); +// If this app ever happens to get multithreaded... + void sig_thumbInfo( const QString&, const QString& ); + void sig_fullInfo( const QString&, const QString& ); + void sig_thumbNail( const QString&, const QPixmap& ); + +protected: + ~PDirLister(); +}; + +#endif diff --git a/noncore/graphics/opie-eye/iface/dirview.cpp b/noncore/graphics/opie-eye/iface/dirview.cpp new file mode 100644 index 0000000..c3d1e86 --- a/dev/null +++ b/noncore/graphics/opie-eye/iface/dirview.cpp @@ -0,0 +1,9 @@ +#include "dirview.h" + +PDirView::PDirView( const Config& ) { + +} + +PDirView::~PDirView() { + +} diff --git a/noncore/graphics/opie-eye/iface/dirview.h b/noncore/graphics/opie-eye/iface/dirview.h new file mode 100644 index 0000000..20d9062 --- a/dev/null +++ b/noncore/graphics/opie-eye/iface/dirview.h @@ -0,0 +1,40 @@ +/* + * GPLv2 zecke@handhelds.org + * No WArranty... + */ + +#ifndef PHUNK_DIR_VIEW_H +#define PHUNK_DIR_VIEW_H + +#include <qmap.h> + +#include <qpe/config.h> + +class PInterfaceInfo; +class PDirLister; + +struct PDirView { + PDirView( const Config& ); + virtual ~PDirView(); + virtual PInterfaceInfo* interfaceInfo()const = 0; + virtual PDirLister* dirLister()const = 0; +}; + +typedef PDirView* (*phunkViewCreateFunc )(const Config& ); +typedef QMap<QString,phunkViewCreateFunc> ViewMap; + +ViewMap* viewMap(); +PDirView* currentView(); +void setCurrentView( PDirView* ); + + + +#define PHUNK_VIEW_INTERFACE( NAME, IMPL ) \ + static PDirView *create_ ## IMPL( const Config& cfg ) { \ + return new IMPL( cfg ); \ + } \ + static ViewMap::Iterator dummy_ ## IMPL = viewMap()->insert( NAME, create_ ## IMPL ); + + + +#endif diff --git a/noncore/graphics/opie-eye/iface/ifaceinfo.h b/noncore/graphics/opie-eye/iface/ifaceinfo.h new file mode 100644 index 0000000..74e0db6 --- a/dev/null +++ b/noncore/graphics/opie-eye/iface/ifaceinfo.h @@ -0,0 +1,19 @@ +/* + * GPLv2 zecke@handhelds.org + * No WArranty... + */ + +#ifndef PHUNK_INTERFACE_INFO_H +#define PHUNK_INTERFACE_INFO_H + +#include <qstring.h> + +class QWidget; +class Config; +struct PInterfaceInfo { + virtual QString name()const = 0; + virtual QWidget* configWidget( const Config& ) = 0; + virtual void writeConfig( QWidget* wid, Config& ) = 0; +}; + +#endif diff --git a/noncore/graphics/opie-eye/iface/slaveiface.h b/noncore/graphics/opie-eye/iface/slaveiface.h new file mode 100644 index 0000000..e1ecf1f --- a/dev/null +++ b/noncore/graphics/opie-eye/iface/slaveiface.h @@ -0,0 +1,47 @@ +/* + *GPLv2 + */ + +#ifndef SLAVE_INTERFACE_H +#define SLAVE_INTERFACE_H + +#include <qpixmap.h> +#include <qstring.h> + +/** + * The Data Packets we use + */ + +struct ImageInfo { + ImageInfo() : kind(false){} + bool operator==( const ImageInfo other ) { + if ( kind != other.kind ) return false; + if ( file != other.file ) return false; + return true; + } + bool kind; + QString file; + QString info; +}; + +struct PixmapInfo { + PixmapInfo() : width( -1 ), height( -1 ) {} + bool operator==( const PixmapInfo& r ) { + if ( width != r.width ) return false; + if ( height != r.height ) return false; + if ( file != r.file ) return false; + return true; + } + int width, height; + QString file; + QPixmap pixmap; +}; + + +/* + * Image Infos + */ + + + +#endif diff --git a/noncore/graphics/opie-eye/impl/dir/dir_dirview.cpp b/noncore/graphics/opie-eye/impl/dir/dir_dirview.cpp new file mode 100644 index 0000000..97e3dcb --- a/dev/null +++ b/noncore/graphics/opie-eye/impl/dir/dir_dirview.cpp @@ -0,0 +1,29 @@ +#include "dir_lister.h" +#include "dir_ifaceinfo.h" +#include "dir_dirview.h" + +PHUNK_VIEW_INTERFACE("Dir View", Dir_DirView ); + + +Dir_DirView::Dir_DirView( const Config& cfg) + : PDirView(cfg) +{ + m_cfg = cfg.readBoolEntry( "Dir_Check_All_Files", true); + m_lister = 0; + m_info = 0; +} + +Dir_DirView::~Dir_DirView() { +} + +PInterfaceInfo* Dir_DirView::interfaceInfo()const{ + if (!m_info ) + m_info =new DirInterfaceInfo; + return m_info; +} + +PDirLister* Dir_DirView::dirLister()const{ + if (!m_lister ) + m_lister = new Dir_DirLister(m_cfg); + return m_lister; +} diff --git a/noncore/graphics/opie-eye/impl/dir/dir_dirview.h b/noncore/graphics/opie-eye/impl/dir/dir_dirview.h new file mode 100644 index 0000000..3b9b7a6 --- a/dev/null +++ b/noncore/graphics/opie-eye/impl/dir/dir_dirview.h @@ -0,0 +1,24 @@ +/* + * GPLv2 only zecke@handhelds.org + */ + +#ifndef DIR_DIR_VIEW_H +#define DIR_DIR_VIEW_H + +#include <iface/dirview.h> + + +struct Dir_DirView : public PDirView { + Dir_DirView( const Config& ); + ~Dir_DirView(); + + PInterfaceInfo* interfaceInfo()const; + PDirLister* dirLister()const; +private: + bool m_cfg : 1; + mutable PDirLister* m_lister; + mutable PInterfaceInfo *m_info; +}; + + +#endif diff --git a/noncore/graphics/opie-eye/impl/dir/dir_ifacceinfo.h b/noncore/graphics/opie-eye/impl/dir/dir_ifacceinfo.h new file mode 100644 index 0000000..8c6262f --- a/dev/null +++ b/noncore/graphics/opie-eye/impl/dir/dir_ifacceinfo.h @@ -0,0 +1,21 @@ +/* + * GPLv2 + * zecke@handhelds.org + */ + +#ifndef DIR_IFACE_INFO_H +#define DIR_IFACE_INFO_H + +#include <iface/ifaceinfo.h>s + +class DirInterfaceInfo : public PInterfaceInfo { +public: + DirInterfaceInfo(); + ~DirInterfaceInfo(); + + QString name()const; + QWidget* configWidget(const Config&); + void writeConfig( QWidget* wid, Config& ); +}; + +#endif diff --git a/noncore/graphics/opie-eye/impl/dir/dir_ifaceinfo.cpp b/noncore/graphics/opie-eye/impl/dir/dir_ifaceinfo.cpp new file mode 100644 index 0000000..79f4510 --- a/dev/null +++ b/noncore/graphics/opie-eye/impl/dir/dir_ifaceinfo.cpp @@ -0,0 +1,47 @@ +/* + * GPLv2 + * zecke@handhelds.org + */ + +#include <qwidget.h> +#include <qcheckbox.h> +#include <qhbox.h> +#include <qlabel.h> + +#include <qpe/config.h> + +#include "dir_ifaceinfo.h" + +namespace { + class DirImageWidget : public QHBox { + public: + DirImageWidget() { + chkbox = new QCheckBox( QObject::tr("Show all files"), this ); + } + ~DirImageWidget() {} + QCheckBox* chkbox; + }; +} + + +DirInterfaceInfo::DirInterfaceInfo() { +} +DirInterfaceInfo::~DirInterfaceInfo() { +} + +QString DirInterfaceInfo::name()const { + return QString::fromLatin1(QObject::tr("DirView" )); +} + +QWidget* DirInterfaceInfo::configWidget(const Config& cfg) { + DirImageWidget* wid = new DirImageWidget(); + wid->chkbox->setChecked( cfg.readBoolEntry("Dir_Check_All_Files", true) ); + + return wid; +} + +void DirInterfaceInfo::writeConfig( QWidget* _wid, Config& cfg) { + qWarning( "Write Config" ); + DirImageWidget* wid = static_cast<DirImageWidget*>(_wid); + cfg.writeEntry("Dir_Check_All_Files", wid->chkbox->isChecked() ); +} diff --git a/noncore/graphics/opie-eye/impl/dir/dir_ifaceinfo.h b/noncore/graphics/opie-eye/impl/dir/dir_ifaceinfo.h new file mode 100644 index 0000000..c919dde --- a/dev/null +++ b/noncore/graphics/opie-eye/impl/dir/dir_ifaceinfo.h @@ -0,0 +1,21 @@ +/* + * GPLv2 + * zecke@handhelds.org + */ + +#ifndef DIR_IFACE_INFO_H +#define DIR_IFACE_INFO_H + +#include <iface/ifaceinfo.h> + +class DirInterfaceInfo : public PInterfaceInfo { +public: + DirInterfaceInfo(); + virtual ~DirInterfaceInfo(); + + QString name()const; + QWidget* configWidget(const Config&); + void writeConfig( QWidget* wid, Config& ); +}; + +#endif diff --git a/noncore/graphics/opie-eye/impl/dir/dir_lister.cpp b/noncore/graphics/opie-eye/impl/dir/dir_lister.cpp new file mode 100644 index 0000000..ffea29e --- a/dev/null +++ b/noncore/graphics/opie-eye/impl/dir/dir_lister.cpp @@ -0,0 +1,94 @@ +/* + * GPLv2 zecke@handhelds.org + */ + +#include "dir_lister.h" + +#include <lib/slavemaster.h> + + +#include <qpe/config.h> +#include <qpe/qpeapplication.h> + +#include <qdir.h> +#include <qfileinfo.h> + + +Dir_DirLister::Dir_DirLister( bool list ) + : PDirLister( "dir_dir_lister" ) +{ + m_allFiles = list; + qWarning("All Files %d", m_allFiles ); + + SlaveMaster* master = SlaveMaster::self(); + connect( master, SIGNAL(sig_start()), this, SIGNAL(sig_start()) ); + connect( master, SIGNAL(sig_end()), this, SIGNAL(sig_end()) ); + connect( master, SIGNAL(sig_thumbInfo(const QString&, const QString&)), + this, SIGNAL(sig_thumbInfo(const QString&, const QString&)) ); + connect( master, SIGNAL(sig_fullInfo(const QString&, const QString&)), + this, SIGNAL(sig_fullInfo(const QString&, const QString&)) ); + connect( master, SIGNAL(sig_thumbNail(const QString&, const QPixmap&)), + this, SIGNAL(sig_thumbNail(const QString&, const QPixmap&)) ); + +} + +QString Dir_DirLister::defaultPath()const { + return QPEApplication::documentDir(); +} + +QString Dir_DirLister::setStartPath( const QString& path ) { + m_currentDir.cd( path ); + if (!m_currentDir.exists() ) + m_currentDir.cd(defaultPath()); + + + return m_currentDir.absPath(); +} + +QString Dir_DirLister::currentPath()const { + return m_currentDir.absPath(); +} + + +QStringList Dir_DirLister::folders()const { + return m_currentDir.entryList( QDir::Dirs ); +} + +QStringList Dir_DirLister::files()const { + if ( m_allFiles ) + return m_currentDir.entryList( QDir::Files ); + else { + QStringList out; + QStringList list = m_currentDir.entryList( QDir::Files | QDir::Readable ); + for (QStringList::Iterator it = list.begin(); it != list.end();++it ) { + QFileInfo inf( *it ); + QString ext = inf.extension(false).lower(); + if( ext == QString::fromLatin1("jpg") || + ext == QString::fromLatin1("jpeg" ) || + ext == QString::fromLatin1("png" ) || + ext == QString::fromLatin1("gif" ) ) + out.append( *it ); + } + return out; + } +} + +void Dir_DirLister::deleteImage( const QString& fl) { + QFile::remove( fl ); +} + +void Dir_DirLister::thumbNail( const QString& str, int w, int h) { + SlaveMaster::self()->thumbNail( str, w, h ); +} + +QImage Dir_DirLister::image( const QString& str, Factor f, int m) { + return SlaveMaster::self()->image( str, f, m ); +} + +void Dir_DirLister::imageInfo( const QString& str) { + SlaveMaster::self()->thumbInfo( str ); +} + +void Dir_DirLister::fullImageInfo( const QString& str) { + SlaveMaster::self()->imageInfo( str ); +} diff --git a/noncore/graphics/opie-eye/impl/dir/dir_lister.h b/noncore/graphics/opie-eye/impl/dir/dir_lister.h new file mode 100644 index 0000000..fc63bb0 --- a/dev/null +++ b/noncore/graphics/opie-eye/impl/dir/dir_lister.h @@ -0,0 +1,34 @@ +/* + * GPLv2 zecke@handhelds.org + */ + +#ifndef DIR_LISTER_INTERFACE_LISTER_H +#define DIR_LISTER_INTERFACE_LISTER_H + +#include <qdir.h> + +#include <iface/dirlister.h> + +class Config; +class Dir_DirLister : public PDirLister { +public: + Dir_DirLister( bool ); + + QString defaultPath()const; + QString setStartPath( const QString& ); + QString currentPath()const; + QStringList folders()const; + QStringList files()const; + + void deleteImage( const QString& ); + void thumbNail( const QString&, int, int ); + QImage image( const QString&, Factor, int ); + void imageInfo( const QString& ); + void fullImageInfo( const QString& ); + +private: + bool m_allFiles; + QDir m_currentDir; +}; + +#endif diff --git a/noncore/graphics/opie-eye/lib/imagecache.cpp b/noncore/graphics/opie-eye/lib/imagecache.cpp new file mode 100644 index 0000000..3b74a83 --- a/dev/null +++ b/noncore/graphics/opie-eye/lib/imagecache.cpp @@ -0,0 +1,87 @@ +/* + * GPLv2 zecke@handhelds.org + * No WArranty... + */ + +#include <iface/dirview.h> +#include <iface/dirlister.h> + +#include "imagecache.h" + +namespace { + PImageCache * _imgCache = 0; + PPixmapCache* _pxmCache = 0; +} + + +PImageCache::PImageCache() + : QCache<QImage>() +{ + /* just to set an initial value.. 4 big images */ + setMaxCost( (1024*1024*16)/8*4 ); +} + +PImageCache::~PImageCache() { +} + +PImageCache* PImageCache::self() { + if ( !_imgCache ) + _imgCache = new PImageCache; + return _imgCache; +} + +QImage* PImageCache::cachedImage( const QString& _path, int ori, int max ) { + QString path = QString( "%1_%2:" ).arg( ori ).arg( max ); + path += _path; + + QImage* img = find( path ); + if ( !img ) { +// img = currentView()->dirLister()->image( _path, PDirLister::Factor(ori), max); +// insertImage( _path, img, ori, max ); + currentView()->dirLister()->image( _path, PDirLister::Factor( ori ), max ); + } + + + return img; +} + +void PImageCache::insertImage( const QString& _path, const QImage* img, int ori, int max ) { + QString path = QString("%1_%2:" ).arg( ori ).arg( max ); + path += _path; + insert( path, img, (img->height()*img->width()*img->depth())/8 ); +} + + +PPixmapCache::PPixmapCache() { + /* + * 20 64x64 16 bit images + */ + setMaxCost( 64*64*QPixmap::defaultDepth()/8*20 ); +} + +PPixmapCache::~PPixmapCache() { +} + +PPixmapCache* PPixmapCache::self() { + if ( !_pxmCache ) + _pxmCache = new PPixmapCache; + + return _pxmCache; +} + +QPixmap* PPixmapCache::cachedImage( const QString& _path, int width, int height ) { + QString path = QString( "%1_%2:" ).arg( width ).arg( height ); + path += _path; + + QPixmap* pxm = find( path ); + + + + return pxm; +} + +void PPixmapCache::insertImage( const QString& _path, const QPixmap* pix, int width, int height ) { + QString path = QString("%1_%2:" ).arg( width ).arg( height ); + path += _path; + insert( path, pix, (pix->height()*pix->width()*pix->depth())/8 ); +} diff --git a/noncore/graphics/opie-eye/lib/imagecache.h b/noncore/graphics/opie-eye/lib/imagecache.h new file mode 100644 index 0000000..076ecd3 --- a/dev/null +++ b/noncore/graphics/opie-eye/lib/imagecache.h @@ -0,0 +1,46 @@ +/* + * GPLv2 zecke@handhelds.org + * No WArranty... + */ + +#ifndef PHUNK_IMAGE_CACHE_H +#define PHUNK_IMAGE_CACHE_H + +#include <qimage.h> +#include <qpixmap.h> +#include <qcache.h> + + +class PImageCache : public QCache<QImage> { +private: + PImageCache(); + ~PImageCache(); + +public: + static PImageCache *self(); + QImage* cachedImage( const QString& path, int orientation = 3, int max = 0); //const; + void insertImage( const QString& path, const QImage &, int orien = 3, int max = 0); + void insertImage( const QString& path, const QImage *, int orien=3, int max = 0 ); +}; + + +class PPixmapCache : public QCache<QPixmap> { +private: + PPixmapCache(); + ~PPixmapCache(); +public: + static PPixmapCache *self(); + QPixmap* cachedImage( const QString& path, int width, int height ); + void insertImage( const QString& path, const QPixmap &, int width, int height ); + void insertImage( const QString& path, const QPixmap *, int width, int height ); +}; + +inline void PPixmapCache::insertImage( const QString& path, const QPixmap& p, int width, int height ) { + insertImage( path, new QPixmap( p ), width, height ); +} + +inline void PImageCache::insertImage( const QString& path, const QImage& p, int width, int height ) { + insertImage( path, new QImage( p ), width, height ); +} + +#endif diff --git a/noncore/graphics/opie-eye/lib/slavemaster.cpp b/noncore/graphics/opie-eye/lib/slavemaster.cpp new file mode 100644 index 0000000..18dc883 --- a/dev/null +++ b/noncore/graphics/opie-eye/lib/slavemaster.cpp @@ -0,0 +1,145 @@ +#include "slavemaster.h" + +#include <qpe/qpeapplication.h> +#include <qpe/qcopenvelope_qws.h> + +#include <qcopchannel_qws.h> +#include <qtimer.h> + +QDataStream & operator << (QDataStream & str, bool b) +{ + str << Q_INT8(b); + return str; +} +QDataStream & operator >> (QDataStream & str, bool & b) +{ + Q_INT8 l; + str >> l; + b = bool(l); + return str; +} +QDataStream &operator<<( QDataStream& s, const PixmapInfo& inf) { + return s << inf.file << inf.pixmap << inf.width << inf.height; +} +QDataStream &operator>>( QDataStream& s, PixmapInfo& inf ) { + s >> inf.file >> inf.pixmap >> inf.width >> inf.height; + return s; +} +QDataStream &operator<<( QDataStream& s, const ImageInfo& i) { + return s << i.kind << i.file << i.info; +} +QDataStream &operator>>( QDataStream& s, ImageInfo& i ) { + s >> i.kind >> i.file >> i.info; + return s; +} + + + +SlaveMaster* SlaveMaster::m_master = 0; + +SlaveMaster::SlaveMaster() + : m_started( false ) +{ + QCopChannel *chan= new QCopChannel( "QPE/opie-eye",this ); + connect(chan, SIGNAL(received(const QCString&,const QByteArray&)), + this, SLOT(recieve(const QCString&,const QByteArray&)) ); +} + +SlaveMaster::~SlaveMaster() { +} + +SlaveMaster* SlaveMaster::self() { + if ( !m_master ) + m_master = new SlaveMaster; + return m_master; +} + +void SlaveMaster::thumbInfo( const QString& str) { + m_inThumbInfo.append( str ); + + if ( !m_started ) { + QTimer::singleShot( 0, this, SLOT(slotTimerStart())); + m_started = true; + } +} + +void SlaveMaster::imageInfo( const QString& str ) { + m_inImageInfo.append( str ); + if ( !m_started ) { + QTimer::singleShot( 0, this, SLOT(slotTimerStart())); + m_started = true; + } +} + +void SlaveMaster::thumbNail( const QString& str, int w, int h ) { + if ( str.isEmpty() ) { + qWarning( "Asking for empty nail" ); + return; + } + qWarning( "Asking for thumbNail in size %d %d" + str, w,h ); + PixmapInfo item; + item.file = str; item.width = w; item.height = h; + item.pixmap = QPixmap(); + m_inThumbNail.append( item ); + + if ( !m_started ) { + QTimer::singleShot( 0, this, SLOT(slotTimerStart())); + m_started = true; + } +} + + +void SlaveMaster::recieve( const QCString& str, const QByteArray& at) { + + ImageInfos infos; + PixmapInfos pixinfos; + + QDataStream stream( at, IO_ReadOnly ); + if ( str == "pixmapsHandled(PixmapList)" ) + stream >> pixinfos; + else if ( str == "pixmapsHandled(StringList)" ) + stream >> infos; + + qWarning( "PixInfos %d", pixinfos.count() ); + + bool got_data = ( !infos.isEmpty() || !pixinfos.isEmpty() ); + if ( got_data ) { + emit sig_start(); + for ( ImageInfos::Iterator _it = infos.begin(); _it != infos.end(); ++_it ) { + if ( (*_it).kind ) + emit sig_fullInfo( (*_it).file, (*_it).info ); + else + emit sig_thumbInfo( (*_it).file, (*_it).info ); + } + + for ( PixmapInfos::Iterator it = pixinfos.begin(); it != pixinfos.end(); ++it ) + emit sig_thumbNail( (*it).file, (*it).pixmap ); + emit sig_end(); + } +} + +void SlaveMaster::slotTimerStart() { + m_started = false; + + if ( !m_inThumbInfo.isEmpty() ) { + QCopEnvelope env("QPE/opie-eye_slave", "thumbInfos(QStringList)" ); + env << m_inThumbInfo; + } + if ( !m_inImageInfo.isEmpty() ) { + QCopEnvelope env("QPE/opie-eye_slave", "fullInfos(QStringList)" ); + env << m_inImageInfo; + } + if ( !m_inThumbNail.isEmpty() ) { + QCopEnvelope env("QPE/opie-eye_slave", "pixmapInfos(PixmapInfos)" ); + env << m_inThumbNail; + } + + + m_inThumbInfo.clear(); + m_inImageInfo.clear(); + m_inThumbNail.clear(); +} + +QImage SlaveMaster::image( const QString& str, PDirLister::Factor, int ) { + return QImage(); +} diff --git a/noncore/graphics/opie-eye/lib/slavemaster.h b/noncore/graphics/opie-eye/lib/slavemaster.h new file mode 100644 index 0000000..f5284a6 --- a/dev/null +++ b/noncore/graphics/opie-eye/lib/slavemaster.h @@ -0,0 +1,43 @@ +#ifndef OPIE_EYE_SLAVE_MASTER_H +#define OPIE_EYE_SLAVE_MASTER_H + +#include <iface/dirlister.h> +#include <iface/slaveiface.h> + +#include <qobject.h> +#include <qstring.h> +#include <qsize.h> + +class SlaveMaster : public QObject { + Q_OBJECT + typedef QValueList<ImageInfo> ImageInfos; + typedef QValueList<PixmapInfo> PixmapInfos; +public: + static SlaveMaster *self(); + + void thumbInfo( const QString& ); + void imageInfo( const QString& ); + void thumbNail( const QString&, int w, int h ); + QImage image( const QString&, PDirLister::Factor, int ); +signals: + void sig_start(); + void sig_end(); + + void sig_thumbInfo( const QString&, const QString& ); + void sig_fullInfo( const QString&, const QString& ); + void sig_thumbNail( const QString&, const QPixmap& ); +private slots: + void recieve( const QCString&, const QByteArray& ); + void slotTimerStart(); +private: + SlaveMaster(); + ~SlaveMaster(); + static SlaveMaster *m_master; + bool m_started : 1; + QStringList m_inThumbInfo; + QStringList m_inImageInfo; + PixmapInfos m_inThumbNail; +}; + + +#endif diff --git a/noncore/graphics/opie-eye/lib/viewmap.cpp b/noncore/graphics/opie-eye/lib/viewmap.cpp new file mode 100644 index 0000000..ca242b7 --- a/dev/null +++ b/noncore/graphics/opie-eye/lib/viewmap.cpp @@ -0,0 +1,25 @@ +#include <iface/dirview.h> + + +namespace { + ViewMap *_viewMap = 0; + PDirView *_dirView = 0; +} + + +ViewMap *viewMap() { + if ( !_viewMap ) + _viewMap = new ViewMap; + + return _viewMap; +} + + +PDirView* currentView(){ + return _dirView; +} + + +void setCurrentView( PDirView* view ) { + _dirView = view; +} diff --git a/noncore/graphics/opie-eye/phunk_view.pro b/noncore/graphics/opie-eye/phunk_view.pro new file mode 100644 index 0000000..21178ee --- a/dev/null +++ b/noncore/graphics/opie-eye/phunk_view.pro @@ -0,0 +1,34 @@ +CONFIG += qt warn_on #quick-app +DESTDIR = $(OPIEDIR)/bin +TEMPLATE = app +TARGET = opie-eye +# the name of the resulting object + +HEADERS = gui/iconview.h gui/filesystem.h gui/mainwindow.h \ + lib/imagecache.h impl/dir/dir_dirview.h \ + iface/dirview.h iface/dirlister.h iface/ifaceinfo.h \ + impl/dir/dir_lister.h impl/dir/dir_ifaceinfo.h \ + lib/slavemaster.h \ + iface/slaveiface.h + +# A list header files + + +SOURCES = gui/iconview.cpp gui/filesystem.cpp gui/mainwindow.cpp \ + lib/imagecache.cpp lib/viewmap.cpp \ + impl/dir/dir_dirview.cpp iface/dirlister.cpp \ + iface/dirview.cpp impl/dir/dir_lister.cpp \ + impl/dir/dir_ifaceinfo.cpp lib/slavemaster.cpp +# A list of source files + +INTERFACES = +# list of ui files + +INCLUDEPATH += . $(OPIEDIR)/include +DEPENDPATH += $(OPIEDIR)/include + + + +LIBS += -lqpe -lopie + +include ( $(OPIEDIR)/include.pro ) diff --git a/noncore/graphics/opie-eye/slave/gif_slave.cpp b/noncore/graphics/opie-eye/slave/gif_slave.cpp new file mode 100644 index 0000000..feb69b6 --- a/dev/null +++ b/noncore/graphics/opie-eye/slave/gif_slave.cpp @@ -0,0 +1,305 @@ +#include "gif_slave.h" + +#include "thumbnailtool.h" + +#include <qimage.h> +#include <qobject.h> +#include <qfile.h> +#include <qpixmap.h> + + +PHUNK_VIEW_INTERFACE( "Gif", GifSlave ); + + +namespace { +/* +** $Id$ +** +** Minimal GIF parser, for use in extracting and setting metadata. +** Modified for standalone & KDE calling by Bryce Nesbitt +** +** TODO: +** Support gif comments that span more than one comment block. +** Verify that Unicode utf-8 is fully unmolested by this code. +** Implement gif structure verifier. +** +** Based on: GIFtrans v1.12.2 +** Copyright (C) 24.2.94 by Andreas Ley <ley@rz.uni-karlsruhe.de> +** +******************************************************************************* +** +** Original distribution site is +** ftp://ftp.rz.uni-karlsruhe.de/pub/net/www/tools/giftrans/giftrans.c +** A man-page by knordlun@fltxa.helsinki.fi (Kai Nordlund) is at +** ftp://ftp.rz.uni-karlsruhe.de/pub/net/www/tools/giftrans/giftrans.1 +** An online version by taylor@intuitive.com (Dave Taylor) is at +** http://www.intuitive.com/coolweb/Addons/giftrans-doc.html +** To compile for MS-DOS or OS/2, you need getopt: +** ftp://ftp.rz.uni-karlsruhe.de/pub/net/www/tools/giftrans/getopt.c +** MS-DOS executable can be found at +** ftp://ftp.rz.uni-karlsruhe.de/pub/net/www/tools/giftrans/giftrans.exe +** OS/2 executable can be found at +** ftp://ftp.rz.uni-karlsruhe.de/pub/net/www/tools/giftrans/giftrans.os2.exe +** A template rgb.txt for use with the MS-DOS version can be found at +** ftp://ftp.rz.uni-karlsruhe.de/pub/net/www/tools/giftrans/rgb.txt +** Additional info can be found on +** http://melmac.corp.harris.com/transparent_images.html +** +** The GIF file format is documented in +** ftp://ftp.uu.net/doc/literary/obi/Standards/Graphics/Formats/gif89a.doc.Z +** A good quick reference is at: +** http://www.goice.co.jp/member/mo/formats/gif.html +** +******************************************************************************* +** +** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +*/ +#define SUCCESS (0) +#define FAILURE (1) + +#define READ_BINARY "r" +#define WRITE_BINARY "w" + + +static long int pos; +static char skipcomment,verbose; +char *global_comment; + +#define readword(buffer) ((buffer)[0]+256*(buffer)[1]) +#define readflag(buffer) ((buffer)?true:false) +#define hex(c) ('a'<=(c)&&(c)<='z'?(c)-'a'+10:'A'<=(c)&&(c)<='Z'?(c)-'A'+10:(c)-'0') + +static bool debug = false; +static bool output= false; + +void dump(long int, unsigned char *, size_t) { +} + +void skipdata(FILE *src) +{ + unsigned char size,buffer[256]; + + do { + pos=ftell(src); + (void)fread((void *)&size,1,1,src); + if (debug) + dump(pos,&size,1); + if (debug) { + pos=ftell(src); + (void)fread((void *)buffer,(size_t)size,1,src); + dump(pos,buffer,(size_t)size); + } + else + (void)fseek(src,(long int)size,SEEK_CUR); + } while (!feof(src)&&size>0); +} + + +void transdata(FILE *src, FILE *dest) +{ + unsigned char size,buffer[256]; + + do { + pos=ftell(src); + (void)fread((void *)&size,1,1,src); + pos=ftell(src); + (void)fread((void *)buffer,(size_t)size,1,src); + if (debug) + dump(pos,buffer,(size_t)size); + if (output) + (void)fwrite((void *)buffer,(size_t)size,1,dest); + } while (!feof(src)&&size>0); +} + +void transblock(FILE *src, FILE* dest) +{ + unsigned char size,buffer[256]; + + pos=ftell(src); + (void)fread((void *)&size,1,1,src); + if (debug) + dump(pos,&size,1); + if (output) + (void)fwrite((void *)&size,1,1,dest); + pos=ftell(src); + (void)fread((void *)buffer,(size_t)size,1,src); + if (debug) + dump(pos,buffer,(size_t)size); + if (output) + (void)fwrite((void *)buffer,(size_t)size,1,dest); +} + +void dumpcomment(FILE *src, QCString& str) +{ + unsigned char size; + + pos=ftell(src); + (void)fread((void *)&size,1,1,src); + if (debug) + dump(pos,&size,1); + str.resize( size+1 ); + (void)fread((void *)str.data(),size,1,src); + (void)fseek(src,(long int)pos,SEEK_SET); +} + + + + +int giftrans(FILE *src, FILE* dest, QString& str, bool full) +{ + unsigned char buffer[3*256],lsd[7],gct[3*256]; + unsigned int size,gct_size; + + /* Header */ + pos=ftell(src); + (void)fread((void *)buffer,6,1,src); + if (strncmp((char *)buffer,"GIF",3)) { + str = QObject::tr("Not a GIF file"); + (void)fprintf(stderr,"Not GIF file!\n"); + return(1); + } + + /* Logical Screen Descriptor */ + pos=ftell(src); + (void)fread((void *)lsd,7,1,src); + //(void)fprintf(stderr,"Logical Screen Descriptor:\n"); + str += QObject::tr("Dimensions: %1x%2\n").arg( readword(lsd) ).arg( readword(lsd+2 ) ); + //(void)fprintf(stderr,"Global Color Table Flag: %s\n",readflag(lsd[4]&0x80)); + str += QObject::tr("Depth: %1 bits\n").arg( (lsd[4]&0x70>>4 )+1); + //(void)fprintf(stderr,"Depth : %d bits\n",(lsd[4]&0x70>>4)+1); + if (lsd[4]&0x80 && full) { + str += QObject::tr("Sort Flag: %1\n" ).arg(readflag(lsd[4]&0x8) ); + str += QObject::tr("Size of Global Color Table: %1 colors\n" ).arg( 2<<(lsd[4]&0x7)); + str += QObject::tr("Background Color Index: %1\n" ).arg(lsd[5]); + } + if (lsd[6] && full) + str += QObject::tr("Pixel Aspect Ratio: %1 (Aspect Ratio %2)\n" ).arg( lsd[6] ). + arg( ((double)lsd[6]+15)/64 ); + + /* Global Color Table */ + if (lsd[4]&0x80) { + gct_size=2<<(lsd[4]&0x7); + pos=ftell(src); + (void)fread((void *)gct,gct_size,3,src); + } + + do { + pos=ftell(src); + (void)fread((void *)buffer,1,1,src); + switch (buffer[0]) { + case 0x2c: /* Image Descriptor */ + (void)fread((void *)(buffer+1),9,1,src); + /* Local Color Table */ + if (buffer[8]&0x80) { + size=2<<(buffer[8]&0x7); + pos=ftell(src); + (void)fread((void *)buffer,size,3,src); + } + /* Table Based Image Data */ + pos=ftell(src); + (void)fread((void *)buffer,1,1,src); + transdata(src,dest); + break; + case 0x21: /* Extension */ + (void)fread((void *)(buffer+1),1,1,src); + switch (buffer[1]) { + case 0xfe: /* Comment Extension */ + if (true) + { + QCString st; + dumpcomment(src, st); + str += QObject::tr("Comment: %1\n" ).arg( st ); + } + if (skipcomment) + skipdata(src); + else { + transdata(src,dest); + } + break; + case 0x01: /* Plain Text Extension */ + case 0xf9: /* Graphic Control Extension */ + case 0xff: /* Application Extension */ + default: + transblock(src,dest); + transdata(src,dest); + break; + } + break; + case 0x3b: /* Trailer (write comment just before here) */ + break; + default: + (void)fprintf(stderr,"0x%08lx: Error, unknown block 0x%02x!\n",ftell(src)-1,buffer[0]); + return(1); + } + } while (buffer[0]!=0x3b&&!feof(src)); + return(buffer[0]==0x3b?SUCCESS:FAILURE); +} + + +/****************************************************************************/ +extern void get_gif_info( const char * original_filename, QString& str, + bool full =false) +{ +FILE * infile; + + if ((infile = fopen(original_filename, READ_BINARY)) == NULL) { + fprintf(stderr, "can't open gif image '%s'\n", original_filename); + return ; + } + + output = FALSE; + verbose = TRUE; + debug = FALSE; + skipcomment = FALSE; + giftrans( infile, NULL, str, full ); + fclose( infile ); +} + +} + + +GifSlave::GifSlave() + : SlaveInterface(QStringList("gif")) +{} + +GifSlave::~GifSlave() { + +} + +QString GifSlave::iconViewName(const QString& str) { + QString st; + get_gif_info(QFile::encodeName( str ).data(), st ); + return st; +} + +QString GifSlave::fullImageInfo( const QString& str) { + QString st; + get_gif_info(QFile::encodeName( str ).data(), st, true ); + return st; +} + +QPixmap GifSlave::pixmap(const QString& path, int width, int height ) { + static QImage img; + img.load( path ); + if ( img.isNull() ) { + QPixmap pix; + return pix; + } + + return ThumbNailTool::scaleImage( img, width,height ); +} + + diff --git a/noncore/graphics/opie-eye/slave/gif_slave.h b/noncore/graphics/opie-eye/slave/gif_slave.h new file mode 100644 index 0000000..cb8522d --- a/dev/null +++ b/noncore/graphics/opie-eye/slave/gif_slave.h @@ -0,0 +1,22 @@ +/* + * GPLv2 zecke@handhelds.org + */ + + +#ifndef SLAVE_GIF_IMPL_H +#define SLAVE_GIF_IMPL_H + +#include "slaveiface.h" + +class GifSlave : public SlaveInterface { +public: + GifSlave(); + ~GifSlave(); + + QString iconViewName( const QString& ); + QString fullImageInfo( const QString& ); + QPixmap pixmap( const QString&, int width, int height ); +}; + + +#endif diff --git a/noncore/graphics/opie-eye/slave/jpeg_slave.cpp b/noncore/graphics/opie-eye/slave/jpeg_slave.cpp new file mode 100644 index 0000000..95055fd --- a/dev/null +++ b/noncore/graphics/opie-eye/slave/jpeg_slave.cpp @@ -0,0 +1,1426 @@ +#include "jpeg_slave.h" + +#include "thumbnailtool.h" + +PHUNK_VIEW_INTERFACE( "JPEG", JpegSlave ) + +#include <qtopia/timestring.h> +#include <qobject.h> +#include <qimage.h> + +/** + exif.h +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <time.h> + +#include <qstring.h> +#include <qfile.h> +#include <qimage.h> + +typedef enum { + READ_EXIF = 1, + READ_IMAGE = 2, + READ_ALL = 3 +}ReadMode_t; + +//-------------------------------------------------------------------------- +// This structure is used to store jpeg file sections in memory. +typedef struct { + uchar * Data; + int Type; + unsigned Size; +}Section_t; + +typedef unsigned char uchar; + +typedef struct { + unsigned short Tag; + const char*const Desc; +}TagTable_t; + +#define MAX_SECTIONS 20 +#define PSEUDO_IMAGE_MARKER 0x123; // Extra value. + +class ExifData { + Section_t Sections[MAX_SECTIONS]; + + QString CameraMake; + QString CameraModel; + QString DateTime; + int Orientation; + int Height, Width; + int ExifImageLength, ExifImageWidth; + int IsColor; + int Process; + int FlashUsed; + float FocalLength; + float ExposureTime; + float ApertureFNumber; + float Distance; + int Whitebalance; + int MeteringMode; + float CCDWidth; + float ExposureBias; + int ExposureProgram; + int ISOequivalent; + int CompressionLevel; + QString UserComment; + QString Comment; + QImage Thumbnail; + + int ReadJpegSections (QFile & infile, ReadMode_t ReadMode); + void DiscardData(void); + int Get16u(void * Short); + int Get32s(void * Long); + unsigned Get32u(void * Long); + double ConvertAnyFormat(void * ValuePtr, int Format); + void ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase, unsigned ExifLength); + void process_COM (const uchar * Data, int length); + void process_SOFn (const uchar * Data, int marker); + int Get16m(const void * Short); + void process_EXIF(unsigned char * CharBuf, unsigned int length); + int Exif2tm(struct tm * timeptr, char * ExifTime); + +public: + ExifData(); + bool scan(const QString &); + QString getCameraMake() { return CameraMake; } + QString getCameraModel() { return CameraModel; } + QString getDateTime() { return DateTime; } + int getOrientation() { return Orientation; } + int getHeight() { return Height; } + int getWidth() { return Width; } + int getIsColor() { return IsColor; } + int getProcess() { return Process; } + int getFlashUsed() { return FlashUsed; } + float getFocalLength() { return FocalLength; } + float getExposureTime() { return ExposureTime; } + float getApertureFNumber() { return ApertureFNumber; } + float getDistance() { return Distance; } + int getWhitebalance() { return Whitebalance; } + int getMeteringMode() { return MeteringMode; } + float getCCDWidth() { return CCDWidth; } + float getExposureBias() { return ExposureBias; } + int getExposureProgram() { return ExposureProgram; } + int getISOequivalent() { return ISOequivalent; } + int getCompressionLevel() { return CompressionLevel; } + QString getUserComment() { return UserComment; } + QString getComment() { return Comment; } + QImage getThumbnail(); + bool isThumbnailSane(); + bool isNullThumbnail() { return !isThumbnailSane(); } +}; + +class FatalError { + const char* ex; +public: + FatalError(const char* s) { ex = s; } + void debug_print() const { qWarning("exception: %s", ex ); } +}; + + + +static unsigned char * LastExifRefd; +static int ExifSettingsLength; +static double FocalplaneXRes; +static double FocalplaneUnits; +static int MotorolaOrder = 0; +static int SectionsRead; +//static int HaveAll; + +//-------------------------------------------------------------------------- +// Table of Jpeg encoding process names + +#define M_SOF0 0xC0 // Start Of Frame N +#define M_SOF1 0xC1 // N indicates which compression process +#define M_SOF2 0xC2 // Only SOF0-SOF2 are now in common use +#define M_SOF3 0xC3 +#define M_SOF5 0xC5 // NB: codes C4 and CC are NOT SOF markers +#define M_SOF6 0xC6 +#define M_SOF7 0xC7 +#define M_SOF9 0xC9 +#define M_SOF10 0xCA +#define M_SOF11 0xCB +#define M_SOF13 0xCD +#define M_SOF14 0xCE +#define M_SOF15 0xCF +#define M_SOI 0xD8 // Start Of Image (beginning of datastream) +#define M_EOI 0xD9 // End Of Image (end of datastream) +#define M_SOS 0xDA // Start Of Scan (begins compressed data) +#define M_JFIF 0xE0 // Jfif marker +#define M_EXIF 0xE1 // Exif marker +#define M_COM 0xFE // COMment + + +TagTable_t ProcessTable[] = { + { M_SOF0, "Baseline"}, + { M_SOF1, "Extended sequential"}, + { M_SOF2, "Progressive"}, + { M_SOF3, "Lossless"}, + { M_SOF5, "Differential sequential"}, + { M_SOF6, "Differential progressive"}, + { M_SOF7, "Differential lossless"}, + { M_SOF9, "Extended sequential, arithmetic coding"}, + { M_SOF10, "Progressive, arithmetic coding"}, + { M_SOF11, "Lossless, arithmetic coding"}, + { M_SOF13, "Differential sequential, arithmetic coding"}, + { M_SOF14, "Differential progressive, arithmetic coding"}, + { M_SOF15, "Differential lossless, arithmetic coding"}, + { 0, "Unknown"} +}; + + + +//-------------------------------------------------------------------------- +// Describes format descriptor +static int BytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8}; +#define NUM_FORMATS 12 + +#define FMT_BYTE 1 +#define FMT_STRING 2 +#define FMT_USHORT 3 +#define FMT_ULONG 4 +#define FMT_URATIONAL 5 +#define FMT_SBYTE 6 +#define FMT_UNDEFINED 7 +#define FMT_SSHORT 8 +#define FMT_SLONG 9 +#define FMT_SRATIONAL 10 +#define FMT_SINGLE 11 +#define FMT_DOUBLE 12 + +//-------------------------------------------------------------------------- +// Describes tag values + +#define TAG_EXIF_OFFSET 0x8769 +#define TAG_INTEROP_OFFSET 0xa005 + +#define TAG_MAKE 0x010F +#define TAG_MODEL 0x0110 +#define TAG_ORIENTATION 0x0112 + +#define TAG_EXPOSURETIME 0x829A +#define TAG_FNUMBER 0x829D + +#define TAG_SHUTTERSPEED 0x9201 +#define TAG_APERTURE 0x9202 +#define TAG_MAXAPERTURE 0x9205 +#define TAG_FOCALLENGTH 0x920A + +#define TAG_DATETIME_ORIGINAL 0x9003 +#define TAG_USERCOMMENT 0x9286 + +#define TAG_SUBJECT_DISTANCE 0x9206 +#define TAG_FLASH 0x9209 + +#define TAG_FOCALPLANEXRES 0xa20E +#define TAG_FOCALPLANEUNITS 0xa210 +#define TAG_EXIF_IMAGEWIDTH 0xA002 +#define TAG_EXIF_IMAGELENGTH 0xA003 + +// the following is added 05-jan-2001 vcs +#define TAG_EXPOSURE_BIAS 0x9204 +#define TAG_WHITEBALANCE 0x9208 +#define TAG_METERING_MODE 0x9207 +#define TAG_EXPOSURE_PROGRAM 0x8822 +#define TAG_ISO_EQUIVALENT 0x8827 +#define TAG_COMPRESSION_LEVEL 0x9102 + +#define TAG_THUMBNAIL_OFFSET 0x0201 +#define TAG_THUMBNAIL_LENGTH 0x0202 + + + + +//-------------------------------------------------------------------------- +// Parse the marker stream until SOS or EOI is seen; +//-------------------------------------------------------------------------- +int ExifData::ReadJpegSections (QFile & infile, ReadMode_t ReadMode) +{ + int a; + + a = infile.getch(); + + if (a != 0xff || infile.getch() != M_SOI) { + SectionsRead = 0; + return false; + } + for(SectionsRead = 0; SectionsRead < MAX_SECTIONS-1; ){ + int marker = 0; + int got; + unsigned int ll,lh; + unsigned int itemlen; + uchar * Data; + + for (a=0;a<7;a++){ + marker = infile.getch(); + if (marker != 0xff) break; + + if (a >= 6){ + + qWarning( "too many padding bytes" ); + return false; + + } + } + + if (marker == 0xff){ + // 0xff is legal padding, but if we get that many, something's wrong. + return false; + } + + Sections[SectionsRead].Type = marker; + + // Read the length of the section. + lh = (uchar) infile.getch(); + ll = (uchar) infile.getch(); + + itemlen = (lh << 8) | ll; + + if (itemlen < 2) { + return false;; + } + + Sections[SectionsRead].Size = itemlen; + + Data = (uchar *)malloc(itemlen+1); // Add 1 to allow sticking a 0 at the end. + Sections[SectionsRead].Data = Data; + + // Store first two pre-read bytes. + Data[0] = (uchar)lh; + Data[1] = (uchar)ll; + + got = infile.readBlock((char*)Data+2, itemlen-2); // Read the whole section. + if (( unsigned ) got != itemlen-2){ + return false; + } + SectionsRead++; + + switch(marker){ + + case M_SOS: // stop before hitting compressed data + // If reading entire image is requested, read the rest of the data. + if (ReadMode & READ_IMAGE){ + unsigned long size; + + size = QMAX( 0ul, infile.size()-infile.at() ); + Data = (uchar *)malloc(size); + if (Data == NULL){ + return false; + } + + got = infile.readBlock((char*)Data, size); + if (( unsigned ) got != size){ + return false; + } + + Sections[SectionsRead].Data = Data; + Sections[SectionsRead].Size = size; + Sections[SectionsRead].Type = PSEUDO_IMAGE_MARKER; + SectionsRead ++; + //HaveAll = 1; + } + return true; + + case M_EOI: // in case it's a tables-only JPEG stream + qWarning( "No image in jpeg!" ); + return false; + + case M_COM: // Comment section + // pieczy 2002-02-12 + // now the User comment goes to UserComment + // so we can store a Comment section also in READ_EXIF mode + process_COM(Data, itemlen); + break; + + case M_JFIF: + // Regular jpegs always have this tag, exif images have the exif + // marker instead, althogh ACDsee will write images with both markers. + // this program will re-create this marker on absence of exif marker. + // hence no need to keep the copy from the file. + free(Sections[--SectionsRead].Data); + break; + + case M_EXIF: + // Seen files from some 'U-lead' software with Vivitar scanner + // that uses marker 31 for non exif stuff. Thus make sure + // it says 'Exif' in the section before treating it as exif. + if ((ReadMode & READ_EXIF) && memcmp(Data+2, "Exif", 4) == 0){ + process_EXIF((uchar *)Data, itemlen); + }else{ + // Discard this section. + free(Sections[--SectionsRead].Data); + } + break; + + case M_SOF0: + case M_SOF1: + case M_SOF2: + case M_SOF3: + case M_SOF5: + case M_SOF6: + case M_SOF7: + case M_SOF9: + case M_SOF10: + case M_SOF11: + case M_SOF13: + case M_SOF14: + case M_SOF15: + process_SOFn(Data, marker); + default: + break; + break; + } + } + return true; +} + + +//-------------------------------------------------------------------------- +// Discard read data. +//-------------------------------------------------------------------------- +void ExifData::DiscardData(void) +{ + for (int a=0; a < SectionsRead; a++) + free(Sections[a].Data); + SectionsRead = 0; +} + +//-------------------------------------------------------------------------- +// Convert a 16 bit unsigned value from file's native byte order +//-------------------------------------------------------------------------- +int ExifData::Get16u(void * Short) +{ + if (MotorolaOrder){ + return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1]; + }else{ + return (((uchar *)Short)[1] << 8) | ((uchar *)Short)[0]; + } +} + +//-------------------------------------------------------------------------- +// Convert a 32 bit signed value from file's native byte order +//-------------------------------------------------------------------------- +int ExifData::Get32s(void * Long) +{ + if (MotorolaOrder){ + return ((( char *)Long)[0] << 24) | (((uchar *)Long)[1] << 16) + | (((uchar *)Long)[2] << 8 ) | (((uchar *)Long)[3] << 0 ); + }else{ + return ((( char *)Long)[3] << 24) | (((uchar *)Long)[2] << 16) + | (((uchar *)Long)[1] << 8 ) | (((uchar *)Long)[0] << 0 ); + } +} + +//-------------------------------------------------------------------------- +// Convert a 32 bit unsigned value from file's native byte order +//-------------------------------------------------------------------------- +unsigned ExifData::Get32u(void * Long) +{ + return (unsigned)Get32s(Long) & 0xffffffff; +} + +//-------------------------------------------------------------------------- +// Evaluate number, be it int, rational, or float from directory. +//-------------------------------------------------------------------------- +double ExifData::ConvertAnyFormat(void * ValuePtr, int Format) +{ + double Value; + Value = 0; + + switch(Format){ + case FMT_SBYTE: Value = *(signed char *)ValuePtr; break; + case FMT_BYTE: Value = *(uchar *)ValuePtr; break; + + case FMT_USHORT: Value = Get16u(ValuePtr); break; + + case FMT_ULONG: Value = Get32u(ValuePtr); break; + + case FMT_URATIONAL: + case FMT_SRATIONAL: + { + int Num,Den; + Num = Get32s(ValuePtr); + Den = Get32s(4+(char *)ValuePtr); + if (Den == 0){ + Value = 0; + }else{ + Value = (double)Num/Den; + } + break; + } + + case FMT_SSHORT: Value = (signed short)Get16u(ValuePtr); break; + case FMT_SLONG: Value = Get32s(ValuePtr); break; + + // Not sure if this is correct (never seen float used in Exif format) + case FMT_SINGLE: Value = (double)*(float *)ValuePtr; break; + case FMT_DOUBLE: Value = *(double *)ValuePtr; break; + } + return Value; +} + +//-------------------------------------------------------------------------- +// Process one of the nested EXIF directories. +//-------------------------------------------------------------------------- +void ExifData::ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase, unsigned ExifLength) +{ + int de; + int a; + int NumDirEntries; + unsigned ThumbnailOffset = 0; + unsigned ThumbnailSize = 0; + + NumDirEntries = Get16u(DirStart); + #define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry)) + + { + unsigned char * DirEnd; + DirEnd = DIR_ENTRY_ADDR(DirStart, NumDirEntries); + if (DirEnd+4 > (OffsetBase+ExifLength)){ + if (DirEnd+2 == OffsetBase+ExifLength || DirEnd == OffsetBase+ExifLength){ + // Version 1.3 of jhead would truncate a bit too much. + // This also caught later on as well. + }else{ + // Note: Files that had thumbnails trimmed with jhead 1.3 or earlier + // might trigger this. + return; + } + } + if (DirEnd < LastExifRefd) LastExifRefd = DirEnd; + } + + for (de=0;de<NumDirEntries;de++){ + int Tag, Format, Components; + unsigned char * ValuePtr; + int ByteCount; + char * DirEntry; + DirEntry = (char *)DIR_ENTRY_ADDR(DirStart, de); + + Tag = Get16u(DirEntry); + Format = Get16u(DirEntry+2); + Components = Get32u(DirEntry+4); + + if ((Format-1) >= NUM_FORMATS) { + // (-1) catches illegal zero case as unsigned underflows to positive large. + return; + } + + ByteCount = Components * BytesPerFormat[Format]; + + if (ByteCount > 4){ + unsigned OffsetVal; + OffsetVal = Get32u(DirEntry+8); + // If its bigger than 4 bytes, the dir entry contains an offset. + if (OffsetVal+ByteCount > ExifLength){ + // Bogus pointer offset and / or bytecount value + //printf("Offset %d bytes %d ExifLen %d\n",OffsetVal, ByteCount, ExifLength); + + return; + } + ValuePtr = OffsetBase+OffsetVal; + }else{ + // 4 bytes or less and value is in the dir entry itself + ValuePtr = (unsigned char *)DirEntry+8; + } + + if (LastExifRefd < ValuePtr+ByteCount){ + // Keep track of last byte in the exif header that was actually referenced. + // That way, we know where the discardable thumbnail data begins. + LastExifRefd = ValuePtr+ByteCount; + } + + // Extract useful components of tag + switch(Tag){ + + case TAG_MAKE: + ExifData::CameraMake = QString((char*)ValuePtr); + break; + + case TAG_MODEL: + ExifData::CameraModel = QString((char*)ValuePtr); + break; + + case TAG_ORIENTATION: + Orientation = (int)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_DATETIME_ORIGINAL: + DateTime = QString((char*)ValuePtr); + break; + + case TAG_USERCOMMENT: + // Olympus has this padded with trailing spaces. Remove these first. + for (a=ByteCount;;){ + a--; + if ((ValuePtr)[a] == ' '){ + (ValuePtr)[a] = '\0'; + }else{ + break; + } + if (a == 0) break; + } + + // Copy the comment + if (memcmp(ValuePtr, "ASCII",5) == 0){ + for (a=5;a<10;a++){ + int c; + c = (ValuePtr)[a]; + if (c != '\0' && c != ' '){ + //strncpy(ImageInfo.Comments, (const char*)(a+ValuePtr), 199); + UserComment.sprintf("%s", (const char*)(a+ValuePtr)); + break; + } + } + }else{ + //strncpy(ImageInfo.Comments, (const char*)ValuePtr, 199); + UserComment.sprintf("%s", (const char*)ValuePtr); + } + break; + + case TAG_FNUMBER: + // Simplest way of expressing aperture, so I trust it the most. + // (overwrite previously computd value if there is one) + ExifData::ApertureFNumber = (float)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_APERTURE: + case TAG_MAXAPERTURE: + // More relevant info always comes earlier, so only use this field if we don't + // have appropriate aperture information yet. + if (ExifData::ApertureFNumber == 0){ + ExifData::ApertureFNumber + = (float)exp(ConvertAnyFormat(ValuePtr, Format)*log(2)*0.5); + } + break; + + case TAG_FOCALLENGTH: + // Nice digital cameras actually save the focal length as a function + // of how farthey are zoomed in. + ExifData::FocalLength = (float)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_SUBJECT_DISTANCE: + // Inidcates the distacne the autofocus camera is focused to. + // Tends to be less accurate as distance increases. + ExifData::Distance = (float)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_EXPOSURETIME: + // Simplest way of expressing exposure time, so I trust it most. + // (overwrite previously computd value if there is one) + ExifData::ExposureTime = (float)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_SHUTTERSPEED: + // More complicated way of expressing exposure time, so only use + // this value if we don't already have it from somewhere else. + if (ExifData::ExposureTime == 0){ + ExifData::ExposureTime + = (float)(1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2))); + } + break; + + case TAG_FLASH: + if (ConvertAnyFormat(ValuePtr, Format)){ + ExifData::FlashUsed = 1; + } + break; + + case TAG_EXIF_IMAGELENGTH: + ExifImageLength = (int)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_EXIF_IMAGEWIDTH: + ExifImageWidth = (int)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_FOCALPLANEXRES: + FocalplaneXRes = ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_FOCALPLANEUNITS: + switch((int)ConvertAnyFormat(ValuePtr, Format)){ + case 1: FocalplaneUnits = 25.4; break; // inch + case 2: + // According to the information I was using, 2 means meters. + // But looking at the Cannon powershot's files, inches is the only + // sensible value. + FocalplaneUnits = 25.4; + break; + + case 3: FocalplaneUnits = 10; break; // centimeter + case 4: FocalplaneUnits = 1; break; // milimeter + case 5: FocalplaneUnits = .001; break; // micrometer + } + break; + + // Remaining cases contributed by: Volker C. Schoech (schoech@gmx.de) + + case TAG_EXPOSURE_BIAS: + ExifData::ExposureBias = (float)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_WHITEBALANCE: + ExifData::Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_METERING_MODE: + ExifData::MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_EXPOSURE_PROGRAM: + ExifData::ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_ISO_EQUIVALENT: + ExifData::ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format); + if ( ExifData::ISOequivalent < 50 ) ExifData::ISOequivalent *= 200; + break; + + case TAG_COMPRESSION_LEVEL: + ExifData::CompressionLevel = (int)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_THUMBNAIL_OFFSET: + ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_THUMBNAIL_LENGTH: + ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format); + break; + + } + + if (Tag == TAG_EXIF_OFFSET || Tag == TAG_INTEROP_OFFSET){ + unsigned char * SubdirStart; + SubdirStart = OffsetBase + Get32u(ValuePtr); + if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength){ + return; + } + ProcessExifDir(SubdirStart, OffsetBase, ExifLength); + continue; + } + } + + { + // In addition to linking to subdirectories via exif tags, + // there's also a potential link to another directory at the end of each + // directory. this has got to be the result of a comitee! + unsigned char * SubdirStart; + unsigned Offset; + + if (DIR_ENTRY_ADDR(DirStart, NumDirEntries) + 4 <= OffsetBase+ExifLength){ + Offset = Get32u(DIR_ENTRY_ADDR(DirStart, NumDirEntries)); + // There is at least one jpeg from an HP camera having an Offset of almost MAXUINT. + // Adding OffsetBase to it produces an overflow, so compare with ExifLength here. + // See http://bugs.kde.org/show_bug.cgi?id=54542 + if (Offset && Offset < ExifLength){ + SubdirStart = OffsetBase + Offset; + if (SubdirStart > OffsetBase+ExifLength){ + if (SubdirStart < OffsetBase+ExifLength+20){ + // Jhead 1.3 or earlier would crop the whole directory! + // As Jhead produces this form of format incorrectness, + // I'll just let it pass silently + qWarning( "Thumbnail removed with Jhead 1.3 or earlier" ); + }else{ + return; + } + }else{ + if (SubdirStart <= OffsetBase+ExifLength){ + ProcessExifDir(SubdirStart, OffsetBase, ExifLength); + } + } + } + }else{ + // The exif header ends before the last next directory pointer. + } + } + + if (ThumbnailSize && ThumbnailOffset){ + if (ThumbnailSize + ThumbnailOffset <= ExifLength){ + // The thumbnail pointer appears to be valid. Store it. + Thumbnail.loadFromData(OffsetBase + ThumbnailOffset, ThumbnailSize, "JPEG"); + } + } +} + +//-------------------------------------------------------------------------- +// Process a COM marker. We want to leave the bytes unchanged. The +// progam that displays this text may decide to remove blanks, convert +// newlines, or otherwise modify the text. In particular we want to be +// safe for passing utf-8 text. +//-------------------------------------------------------------------------- +void ExifData::process_COM (const uchar * Data, int length) +{ + QChar ch; + int a; + + for (a=2;a<length;a++){ + ch = Data[a]; + if (ch == '\000') continue; // Remove nulls + Comment.append(ch); + } +} + + +//-------------------------------------------------------------------------- +// Process a SOFn marker. This is useful for the image dimensions +//-------------------------------------------------------------------------- +void ExifData::process_SOFn (const uchar * Data, int marker) +{ + int data_precision, num_components; + + data_precision = Data[2]; + ExifData::Height = Get16m(Data+3); + ExifData::Width = Get16m(Data+5); + num_components = Data[7]; + + if (num_components == 3){ + ExifData::IsColor = 1; + }else{ + ExifData::IsColor = 0; + } + + ExifData::Process = marker; + +} + +//-------------------------------------------------------------------------- +// Get 16 bits motorola order (always) for jpeg header stuff. +//-------------------------------------------------------------------------- +int ExifData::Get16m(const void * Short) +{ + return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1]; +} + + +//-------------------------------------------------------------------------- +// Process a EXIF marker +// Describes all the drivel that most digital cameras include... +//-------------------------------------------------------------------------- +void ExifData::process_EXIF(unsigned char * CharBuf, unsigned int length) +{ + ExifData::FlashUsed = 0; // If it s from a digicam, and it used flash, it says so. + + FocalplaneXRes = 0; + FocalplaneUnits = 0; + ExifImageWidth = 0; + ExifImageLength = 0; + + { // Check the EXIF header component + static const uchar ExifHeader[] = "Exif\0\0"; + if (memcmp(CharBuf+2, ExifHeader,6)){ + return; + } + } + + if (memcmp(CharBuf+8,"II",2) == 0){ + // printf("Exif section in Intel order\n"); + MotorolaOrder = 0; + }else{ + if (memcmp(CharBuf+8,"MM",2) == 0){ + // printf("Exif section in Motorola order\n"); + MotorolaOrder = 1; + }else{ + return; + } + } + + // Check the next two values for correctness. + if (Get16u(CharBuf+10) != 0x2a + || Get32u(CharBuf+12) != 0x08){ + return; + } + + LastExifRefd = CharBuf; + + // First directory starts 16 bytes in. Offsets start at 8 bytes in. + ProcessExifDir(CharBuf+16, CharBuf+8, length-6); + + // This is how far the interesting (non thumbnail) part of the exif went. + ExifSettingsLength = LastExifRefd - CharBuf; + + // Compute the CCD width, in milimeters. + if (FocalplaneXRes != 0){ + ExifData::CCDWidth = (float)(ExifImageWidth * FocalplaneUnits / FocalplaneXRes); + } +} + +//-------------------------------------------------------------------------- +// Convert exif time to Unix time structure +//-------------------------------------------------------------------------- +int ExifData::Exif2tm(struct tm * timeptr, char * ExifTime) +{ + int a; + + timeptr->tm_wday = -1; + + // Check for format: YYYY:MM:DD HH:MM:SS format. + a = sscanf(ExifTime, "%d:%d:%d %d:%d:%d", + &timeptr->tm_year, &timeptr->tm_mon, &timeptr->tm_mday, + &timeptr->tm_hour, &timeptr->tm_min, &timeptr->tm_sec); + + if (a == 6){ + timeptr->tm_isdst = -1; + timeptr->tm_mon -= 1; // Adjust for unix zero-based months + timeptr->tm_year -= 1900; // Adjust for year starting at 1900 + return true; // worked. + } + + return false; // Wasn't in Exif date format. +} + +//-------------------------------------------------------------------------- +// Contructor for initialising +//-------------------------------------------------------------------------- +ExifData::ExifData() +{ + ExifData::Whitebalance = -1; + ExifData::MeteringMode = -1; + ExifData::FlashUsed = -1; + Orientation = 0; + Height = 0; + Width = 0; + IsColor = 0; + Process = 0; + FocalLength = 0; + ExposureTime = 0; + ApertureFNumber = 0; + Distance = 0; + CCDWidth = 0; + ExposureBias = 0; + ExposureProgram = 0; + ISOequivalent = 0; + CompressionLevel = 0; +} + +//-------------------------------------------------------------------------- +// process a EXIF jpeg file +//-------------------------------------------------------------------------- +bool ExifData::scan(const QString & path) +{ + int ret; + + QFile f(path); + f.open(IO_ReadOnly); + + // Scan the JPEG headers. + ret = ReadJpegSections(f, READ_EXIF); + + if (ret == false){ + qWarning( "Not JPEG file!" ); + DiscardData(); + f.close(); + return false; + } + f.close(); + DiscardData(); + + //now make the strings clean, + // for exmaple my Casio is a "QV-4000 " + CameraMake = CameraMake.stripWhiteSpace(); + CameraModel = CameraModel.stripWhiteSpace(); + UserComment = UserComment.stripWhiteSpace(); + Comment = Comment.stripWhiteSpace(); + return true; +} + +//-------------------------------------------------------------------------- +// Does the embedded thumbnail match the jpeg image? +//-------------------------------------------------------------------------- +#ifndef JPEG_TOL +#define JPEG_TOL 0.02 +#endif +bool ExifData::isThumbnailSane() { + if (Thumbnail.isNull()) return false; + + // check whether thumbnail dimensions match the image + // not foolproof, but catches some altered images (jpegtran -rotate) + if (ExifImageLength != 0 && ExifImageLength != Height) return false; + if (ExifImageWidth != 0 && ExifImageWidth != Width) return false; + if (Thumbnail.width() == 0 || Thumbnail.height() == 0) return false; + if (Height == 0 || Width == 0) return false; + double d = (double)Height/Width*Thumbnail.width()/Thumbnail.height(); + return (1-JPEG_TOL < d) && (d < 1+JPEG_TOL); +} + + + +static QImage flip_image( const QImage& img ); +static QImage rotate_90( const QImage& img ); +static QImage rotate_180( const QImage& ); +static QImage rotate_270( const QImage& ); + +//-------------------------------------------------------------------------- +// return a thumbnail that respects the orientation flag +// only if it seems sane +//-------------------------------------------------------------------------- +QImage ExifData::getThumbnail() { + if (!isThumbnailSane()) return NULL; + if (!Orientation || Orientation == 1) return Thumbnail; + + // now fix orientation + + QImage dest = Thumbnail; + switch (Orientation) { // notice intentional fallthroughs + case 2: dest = flip_image( dest ); break; + case 4: dest = flip_image( dest ); + case 3: dest =rotate_180( dest ); break; + case 5: dest = flip_image( dest ); + case 6: dest = rotate_90( dest ); break; + case 7: dest = flip_image( dest ); + case 8: dest = rotate_270( dest ); break; + default: break; // should never happen + } + return dest; +} + + +/* + * + */ +static QImage flip_image( const QImage& img ) { + return img.mirror( TRUE, FALSE ); +} + + +static QImage dest; +static int x, y; +static unsigned int *srcData, *destData; // we're not threaded anyway +static unsigned char *srcData8, *destData8; // 8 bit is char +static unsigned int *srcTable, *destTable; // destination table + + +static QImage rotate_90_8( const QImage &img ) { + 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 ){ + srcData8 = (unsigned char *)img.scanLine(y); + for ( x=0; x < img.width(); ++x ){ + destData8 = (unsigned char *)dest.scanLine(x); + destData8[img.height()-y-1] = srcData8[x]; + } + } + return dest; +} + +static QImage rotate_90_all( const QImage& img ) { + 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]; + } + } + + return dest; +} + + +static QImage rotate_90( const QImage & img ) { + if ( img.depth() > 8) + return rotate_90_all( img ); + else + return rotate_90_8( img ); +} + +static QImage rotate_180_all( const QImage& img ) { + 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]; + } + return dest; +} + +static QImage rotate_180_8( const QImage& img ) { + 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 ){ + srcData8 = (unsigned char *)img.scanLine(y); + destData8 = (unsigned char *)dest.scanLine(img.height()-y-1); + for ( x=0; x < img.width(); ++x ) + destData8[img.width()-x-1] = srcData8[x]; + } + return dest; +} + +static QImage rotate_180( const QImage& img ) { + if ( img.depth() > 8 ) + return rotate_180_all( img ); + else + return rotate_180_8( img ); +} + + +static QImage rotate_270_8( const QImage& img ) { + 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 ){ + srcData8 = (unsigned char *)img.scanLine(y); + for ( x=0; x < img.width(); ++x ){ + destData8 = (unsigned char *)dest.scanLine(img.width()-x-1); + destData8[y] = srcData8[x]; + } + } + + return dest; +} + +static QImage rotate_270_all( const QImage& img ) { + 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]; + } + } + return dest; +} + +static QImage rotate_270( const QImage& img ) { + if ( img.depth() > 8 ) + return rotate_270_all( img ); + else + return rotate_270_8( img ); +} + + +static QString color_mode_to_string( bool b ) { + return b ? QObject::tr( "Colormode: Color\n" ) : QObject::tr( "Colormode: Black and white\n" ); +} + +static QString compression_to_string( int level ) { + QString str; + switch( level ) { + case 1: + str = QObject::tr( "Basic" ); + break; + case 2: + str = QObject::tr( "Normal" ); + break; + case 4: + str = QObject::tr( "Fine" ); + break; + default: + str = QObject::tr( "Unknown" ); + + } + return QObject::tr("Quality: %1\n").arg(str); +} + + +static QDateTime parseDateTime( const QString& string ) +{ + QDateTime dt; + if ( string.length() != 19 ) + return dt; + + QString year = string.left( 4 ); + QString month = string.mid( 5, 2 ); + QString day = string.mid( 8, 2 ); + QString hour = string.mid( 11, 2 ); + QString minute = string.mid( 14, 2 ); + QString seconds = string.mid( 18, 2 ); + + bool ok; + bool allOk = true; + int y = year.toInt( &ok ); + allOk &= ok; + + int mo = month.toInt( &ok ); + allOk &= ok; + + int d = day.toInt( &ok ); + allOk &= ok; + + int h = hour.toInt( &ok ); + allOk &= ok; + + int mi = minute.toInt( &ok ); + allOk &= ok; + + int s = seconds.toInt( &ok ); + allOk &= ok; + + if ( allOk ) { + dt.setDate( QDate( y, mo, d ) ); + dt.setTime( QTime( h, mi, s ) ); + } + + return dt; +} + +static QString white_balance_string( int i ) { + QString balance; + switch ( i ) { + case 0: + balance = QObject::tr( "Unknown" ); + break; + case 1: + balance = QObject::tr( "Daylight" ); + break; + case 2: + balance = QObject::tr( "Fluorescent" ); + break; + case 3: + balance = QObject::tr( "Tungsten" ); + break; + case 17: + balance = QObject::tr( "Standard light A" ); + break; + case 18: + balance = QObject::tr( "Standard light B" ); + break; + case 19: + balance = QObject::tr( "Standard light C" ); + break; + case 20: + balance = QObject::tr( "D55" ); + break; + case 21: + balance = QObject::tr( "D65" ); + break; + case 22: + balance = QObject::tr( "D75" ); + break; + case 255: + balance = QObject::tr( "Other" ); + break; + default: + balance = QObject::tr( "Unknown" ); + } + return QObject::tr( "White Balance: %1\n" ).arg( balance ); + +} + + +static QString metering_mode( int i) { + QString meter; + switch( i ) { + case 0: + meter = QObject::tr( "Unknown" ); + break; + case 1: + meter = QObject::tr( "Average" ); + break; + case 2: + meter = QObject::tr( "Center weighted average" ); + break; + case 3: + meter = QObject::tr( "Spot" ); + break; + case 4: + meter = QObject::tr( "MultiSpot" ); + break; + case 5: + meter = QObject::tr( "Pattern" ); + break; + case 6: + meter = QObject::tr( "Partial" ); + break; + case 255: + meter = QObject::tr( "Other" ); + break; + default: + meter = QObject::tr( "Unknown" ); + } + + return QObject::tr( "Metering Mode: %1\n" ).arg( meter ); +} + + +static QString exposure_program( int i ) { + QString exp; + switch( i ) { + case 0: + exp = QObject::tr( "Not defined" ); + break; + case 1: + exp = QObject::tr( "Manual" ); + break; + case 2: + exp = QObject::tr( "Normal progam" ); + break; + case 3: + exp = QObject::tr( "Aperture priority" ); + break; + case 4: + exp = QObject::tr( "Shutter priority" ); + break; + case 5: + exp = QObject::tr( "Creative progam\n(biased toward fast shutter speed" ); + break; + case 6: + exp = QObject::tr( "Action progam\n(biased toward fast shutter speed)" ); + break; + case 7: + exp = QObject::tr( "Portrait mode\n(for closeup photos with the background out of focus)" ); + break; + case 8: + exp = QObject::tr( "Landscape mode\n(for landscape photos with the background in focus)" ); + break; + default: + exp = QObject::tr( "Unknown" ); + } + + return QObject::tr( "Exposure Program: %1\n" ).arg( exp ); +} + +JpegSlave::JpegSlave() + : SlaveInterface( QStringList::split( " ", "jpeg jpg" ) ) +{} + +JpegSlave::~JpegSlave() {} + +QString JpegSlave::iconViewName( const QString& path) { + ExifData ImageInfo; + if ( !ImageInfo.scan( path ) ) + return QString::null; + + QString tag; + tag = QObject::tr( "<qt>Comment: %1\n" ).arg( ImageInfo.getComment() ); + { +// ODP fixme + QString timestring = TimeString::dateString( parseDateTime( ImageInfo.getDateTime() ), FALSE ); + tag += QObject::tr( "Date/Time: %1\n" ).arg( timestring ); + } + tag += QObject::tr( "Dimensions: %1x%2\n" ).arg(ImageInfo.getWidth()) + .arg(ImageInfo.getHeight() ); + + tag += color_mode_to_string( ImageInfo.getIsColor() ); + + tag += compression_to_string( ImageInfo.getCompressionLevel() ); + tag += QObject::tr( "</qt>" ); + + return tag; +} + + +/* + * messy messy string creation + */ +QString JpegSlave::fullImageInfo( const QString& path) { + ExifData ImageInfo; + if ( !ImageInfo.scan( path ) ) + return QString::null; + + QString tag, tmp; + tag = QObject::tr( "Comment: %1\n" ).arg( ImageInfo.getComment() ); + + tmp = ImageInfo.getCameraMake(); + if ( tmp.length() ) + tag += QObject::tr( "Manufacturer: %1\n" ).arg( tmp ); + tmp = ImageInfo.getCameraModel(); + if ( tmp.length() ) + tag += QObject::tr( "Model: %1\n" ).arg( tmp ); + { +// ODP fixme + tmp = TimeString::dateString( parseDateTime( ImageInfo.getDateTime() ), FALSE ); + tag += QObject::tr( "Date/Time: %1\n" ).arg( tmp ); + } + tag += QObject::tr( "Dimensions: %1x%2\n" ).arg(ImageInfo.getWidth()) + .arg(ImageInfo.getHeight() ); + + tag += color_mode_to_string( ImageInfo.getIsColor() ); + + tag += compression_to_string( ImageInfo.getCompressionLevel() ); + if ( ImageInfo.getOrientation() ) + tag += QObject::tr( "Orientation: %1\n" ).arg(ImageInfo.getOrientation() ); + + + { + int flash_used = ImageInfo.getFlashUsed(); + if ( flash_used >= 0 ) + tag += QObject::tr( "Flash used\n" ); + } + + if ( ImageInfo.getFocalLength() ) { + tag += QObject::tr( "Focal length: %1\n" ).arg( QString().sprintf( "%4.1f", ImageInfo.getFocalLength() ) ); + if ( ImageInfo.getCCDWidth() ) + tag += QObject::tr( "35mm equivalent: %1\n" ).arg( (int)(ImageInfo.getFocalLength()/ImageInfo.getCCDWidth()*35 + 0.5) ); + + } + + if ( ImageInfo.getCCDWidth() ) + tag += QObject::tr( "CCD width: %1" ).arg( ImageInfo.getCCDWidth() ); + if ( ImageInfo.getExposureTime() ) { + tmp = QString().sprintf("%4.2f", ImageInfo.getExposureTime() ); + float exposureTime = ImageInfo.getExposureTime(); + if ( exposureTime > 0 && exposureTime <= 0.5 ) + tmp += QString().sprintf(" (1/%d)", (int)(0.5 +1/exposureTime) ); + tag += QObject::tr( "Exposure time: %1\n" ).arg( tmp ); + } + + if ( ImageInfo.getApertureFNumber() ) + tag += QObject::tr( "Aperture: %1\n" ).arg( QString().sprintf("f/%3.1f", (double)ImageInfo.getApertureFNumber() ) ); + + if ( ImageInfo.getDistance() ) { + if ( ImageInfo.getDistance() < 0 ) + tag += QObject::tr( "Distance: %1\n" ).arg( QObject::tr( "Infinite" ) ); + else + tag += QObject::tr( "Distance: %1\n" ).arg( QString().sprintf( "%5.2fm", (double)ImageInfo.getDistance() ) ); + } + + if ( ImageInfo.getExposureBias() ) { + tag += QObject::tr( "Exposure bias: %1\n", QString().sprintf("%4.2f", (double)ImageInfo.getExposureBias() ) ); + } + + if ( ImageInfo.getWhitebalance() != -1 ) + tag += white_balance_string( ImageInfo.getWhitebalance() ); + + + if( ImageInfo.getMeteringMode() != -1 ) + tag += metering_mode( ImageInfo.getMeteringMode() ); + + if ( ImageInfo.getExposureProgram() ) + tag += exposure_program( ImageInfo.getExposureProgram() ); + if ( ImageInfo.getISOequivalent() ) + tag += QObject::tr( "ISO equivalent: %1\n" ).arg( QString().sprintf("%2d", ImageInfo.getISOequivalent() ) ); + + tmp = ImageInfo.getUserComment(); + if ( tmp.length() ) + tag += QObject::tr( "EXIF comment: %1" ).arg( tmp ); + + tag += QObject::tr( "</qt>" ); + + + + return tag; +} + +QPixmap JpegSlave::pixmap( const QString& path, int wid, int hei) { + ExifData ImageInfo; + if ( !ImageInfo.scan( path ) || ImageInfo.isNullThumbnail() ) { + QImage img; + QImageIO iio( path, 0l ); + QString str = QString( "Fast Shrink( 4 ) Scale( %1, %2, ScaleFree)" ).arg( wid ).arg( hei ); + iio.setParameters( str.latin1() );// will be strdupped anyway + img = iio.read() ? iio.image() : QImage(); + return ThumbNailTool::scaleImage( img, wid,hei ); + }else{ + QImage img = ImageInfo.getThumbnail(); + return ThumbNailTool::scaleImage( img, wid,hei ); + } +} diff --git a/noncore/graphics/opie-eye/slave/jpeg_slave.h b/noncore/graphics/opie-eye/slave/jpeg_slave.h new file mode 100644 index 0000000..e800dbd --- a/dev/null +++ b/noncore/graphics/opie-eye/slave/jpeg_slave.h @@ -0,0 +1,19 @@ +/* + * GPLv2 zecke@handhelds.org + */ +#ifndef JPEG_SLAVE_IMPL_H +#define JPEG_SLAVE_IMPL_H + +#include "slaveiface.h" + +class JpegSlave : public SlaveInterface { +public: + JpegSlave(); + ~JpegSlave(); + + QString iconViewName( const QString& ); + QString fullImageInfo( const QString& ); + QPixmap pixmap( const QString&, int, int ); +}; + +#endif diff --git a/noncore/graphics/opie-eye/slave/main.cpp b/noncore/graphics/opie-eye/slave/main.cpp new file mode 100644 index 0000000..37020e6 --- a/dev/null +++ b/noncore/graphics/opie-eye/slave/main.cpp @@ -0,0 +1,59 @@ +/* + * GPLv2 Slave Main + */ + +#include "gif_slave.h" +#include "png_slave.h" +#include "jpeg_slave.h" +#include "thumbnailtool.h" +#include "slavereciever.h" + +#include <qpixmap.h> +#include <qcopchannel_qws.h> + +#include <qtopia/qpeapplication.h> + +int main( int argc, char* argv[] ) { + QPEApplication app( argc, argv ); + SlaveReciever rec( 0 ); + + QCopChannel chan( "QPE/opie-eye_slave" ); + QObject::connect(&chan,SIGNAL(received(const QCString&, const QByteArray&)), + &rec, SLOT(recieveAnswer(const QCString&,const QByteArray&))); + QObject::connect(qApp,SIGNAL(appMessage(const QCString&, const QByteArray&)), + &rec, SLOT(recieveAnswer(const QCString&,const QByteArray&))); + + return app.exec(); +} + +#ifdef DEBUG_IT +int main( int argc, char* argv[] ) { + QString str = QString::fromLatin1(argv[2] ); + QApplication app( argc, argv ); + GifSlave slave; + qWarning( str +" "+slave.iconViewName(str ) ); + qWarning( str+" "+slave.fullImageInfo( str ) ); + + PNGSlave pngslave; + qWarning( str + " " + pngslave.iconViewName(str) ); + qWarning( str + " " + pngslave.fullImageInfo(str)); + + + JpegSlave jpgslave; + qWarning( str + " " + jpgslave.iconViewName(str ) ); + qWarning( str + " " + jpgslave.fullImageInfo( str ) ); +//return app.exec(); + QPixmap pix = ThumbNailTool::getThumb( str, 24, 24 ); + if ( pix.isNull() ) { + qWarning( "No Thumbnail" ); + pix = slave.pixmap(str, 24, 24); + } + + if (!pix.isNull() ) { + qWarning( "Saving Thumbnail" ); + ThumbNailTool::putThumb( str, pix, 24, 24 ); + } + +} + +#endif diff --git a/noncore/graphics/opie-eye/slave/png_slave.cpp b/noncore/graphics/opie-eye/slave/png_slave.cpp new file mode 100644 index 0000000..72b93cc --- a/dev/null +++ b/noncore/graphics/opie-eye/slave/png_slave.cpp @@ -0,0 +1,210 @@ +#include "png_slave.h" + +#include "thumbnailtool.h" + +#include <qobject.h> +#include <qfile.h> +#include <qimage.h> +#include <qpixmap.h> +#include <qstring.h> + +/* + * GPLv2 from kfile plugin + */ +PHUNK_VIEW_INTERFACE( "PNG", PNGSlave ); + +#define CHUNK_SIZE(data, index) ((data[index ]<<24) + (data[index+1]<<16) + \ + (data[index+2]<< 8) + data[index+3]) +#define CHUNK_TYPE(data, index) &data[index+4] +#define CHUNK_HEADER_SIZE 12 +#define CHUNK_DATA(data, index, offset) data[8+index+offset] + +/* TRANSLATOR QObject */ + +// known translations for common png keys +static const char* knownTranslations[] +#ifdef __GNUC__ +__attribute__((unused)) +#endif + = { + QT_TR_NOOP("Title"), + QT_TR_NOOP("Author"), + QT_TR_NOOP("Description"), + QT_TR_NOOP("Copyright"), + QT_TR_NOOP("Creation Time"), + QT_TR_NOOP("Software"), + QT_TR_NOOP("Disclaimer"), + QT_TR_NOOP("Warning"), + QT_TR_NOOP("Source"), + QT_TR_NOOP("Comment") +}; + +// and for the colors +static const char* colors[] = { + QT_TR_NOOP("Grayscale"), + QT_TR_NOOP("Unknown"), + QT_TR_NOOP("RGB"), + QT_TR_NOOP("Palette"), + QT_TR_NOOP("Grayscale/Alpha"), + QT_TR_NOOP("Unknown"), + QT_TR_NOOP("RGB/Alpha") +}; + + // and compressions +static const char* compressions[] = +{ + QT_TR_NOOP("Deflate") +}; + + // interlaced modes +static const char* interlaceModes[] = { + QT_TR_NOOP("None"), + QT_TR_NOOP("Adam7") +}; + + +static void read_comment( const QString& inf, + bool readComments, QString& str ) { + QFile f(inf); + f.open(IO_ReadOnly); + + if (f.size() < 26) return; + // the technical group will be read from the first 26 bytes. If the file + // is smaller, we can't even read this. + + uchar *data = new uchar[f.size()+1]; + f.readBlock(reinterpret_cast<char*>(data), f.size()); + data[f.size()]='\n'; + + // find the start + if (data[0] == 137 && data[1] == 80 && data[2] == 78 && data[3] == 71 && + data[4] == 13 && data[5] == 10 && data[6] == 26 && data[7] == 10 ) + { + // ok + // the IHDR chunk should be the first + if (!strncmp((char*)&data[12], "IHDR", 4)) + { + // we found it, get the dimensions + ulong x,y; + x = (data[16]<<24) + (data[17]<<16) + (data[18]<<8) + data[19]; + y = (data[20]<<24) + (data[21]<<16) + (data[22]<<8) + data[23]; + + uint type = data[25]; + uint bpp = data[24]; + + // the bpp are only per channel, so we need to multiply the with + // the channel count + switch (type) + { + case 0: break; // Grayscale + case 2: bpp *= 3; break; // RGB + case 3: break; // palette + case 4: bpp *= 2; break; // grayscale w. alpha + case 6: bpp *= 4; break; // RGBA + + default: // we don't get any sensible value here + bpp = 0; + } + + + str = QObject::tr("Dimensions: %1x%2\n" ).arg(x).arg(y); + str += QObject::tr("Depth: %1\n" ).arg(bpp); + str += QObject::tr("ColorMode: %1\n").arg( + (type < sizeof(colors)/sizeof(colors[0])) + ? QObject::tr(colors[data[25]]) : QObject::tr("Unknown") ); + + str += QObject::tr("Compression: %1\n").arg( + (data[26] < sizeof(compressions)/sizeof(compressions[0])) + ? QObject::tr(compressions[data[26]]) : QObject::tr("Unknown") ); + + str += QObject::tr("InterlaceMode: %1\n" ).arg( + (data[28] < sizeof(interlaceModes)/sizeof(interlaceModes[0])) + ? QObject::tr(interlaceModes[data[28]]) : QObject::tr("Unknown")); + } + + if ( readComments ) { + uint index = 8; + index += CHUNK_SIZE(data, index) + CHUNK_HEADER_SIZE; + + while(index<f.size()-12) + { + while (index < f.size() - 12 && + strncmp((char*)CHUNK_TYPE(data,index), "tEXt", 4)) + { + if (!strncmp((char*)CHUNK_TYPE(data,index), "IEND", 4)) + goto end; + + index += CHUNK_SIZE(data, index) + CHUNK_HEADER_SIZE; + } + + if (index < f.size() - 12) + { + // we found a tEXt field + // get the key, it's a null terminated string at the + // chunk start + + uchar* key = &CHUNK_DATA(data,index,0); + + int keysize=0; + for (;key[keysize]!=0; keysize++) + // look if we reached the end of the file + // (it might be corrupted) + if (8+index+keysize>=f.size()) + goto end; + + // the text comes after the key, but isn't null terminated + uchar* text = &CHUNK_DATA(data,index, keysize+1); + uint textsize = CHUNK_SIZE(data, index)-keysize-1; + + // security check, also considering overflow wraparound from the addition -- + // we may endup with a /smaller/ index if we wrap all the way around + uint firstIndex = (uint)(text - data); + uint onePastLastIndex = firstIndex + textsize; + + if ( onePastLastIndex > f.size() || onePastLastIndex <= firstIndex) + goto end; + + QByteArray arr(textsize); + arr = QByteArray(textsize).duplicate((const char*)text, + textsize); + str += QObject::tr( + QString(reinterpret_cast<char*>(key)), + QString(arr) ); + + index += CHUNK_SIZE(data, index) + CHUNK_HEADER_SIZE; + } + } + } + } +end: + delete[] data; + +} + + +PNGSlave::PNGSlave() + : SlaveInterface("png") +{ +} +PNGSlave::~PNGSlave() { +} +QString PNGSlave::iconViewName( const QString& path) { + QString str; + read_comment( path, false, str ); + return str; +} + +QString PNGSlave::fullImageInfo( const QString& path) { + QString str; + read_comment( path, true, str ); + return str; +} + + +QPixmap PNGSlave::pixmap( const QString& path, int width, int height) { + QImage img; img.load( path ); + if ( img.isNull() ) + return QPixmap(); + else + return ThumbNailTool::scaleImage( img, width,height ); +} diff --git a/noncore/graphics/opie-eye/slave/png_slave.h b/noncore/graphics/opie-eye/slave/png_slave.h new file mode 100644 index 0000000..408bfc4 --- a/dev/null +++ b/noncore/graphics/opie-eye/slave/png_slave.h @@ -0,0 +1,21 @@ +/* + * GPLv2 zecke@handhelds.org + */ +#ifndef PNG_SLAVE_IMPL_H +#define PNG_SLAVE_IMPL_H + +#include "slaveiface.h" + +class QString; +class QPixmap; +class PNGSlave : public SlaveInterface { +public: + PNGSlave(); + ~PNGSlave(); + + QString iconViewName( const QString& ); + QString fullImageInfo( const QString& ); + QPixmap pixmap( const QString&, int, int ); +}; + +#endif diff --git a/noncore/graphics/opie-eye/slave/slave.pro b/noncore/graphics/opie-eye/slave/slave.pro new file mode 100644 index 0000000..3f42495 --- a/dev/null +++ b/noncore/graphics/opie-eye/slave/slave.pro @@ -0,0 +1,18 @@ +CONFIG += qte +TEMPLATE = app +TARGET = opie-eye_slave +DESTDIR = $(OPIEDIR)/bin + +HEADERS = gif_slave.h slaveiface.h slavereciever.h \ + thumbnailtool.h png_slave.h jpeg_slave.h \ + ../iface/slaveiface.h +SOURCES = main.cpp gif_slave.cpp slavereciever.cpp \ + slaveiface.cpp thumbnailtool.cpp png_slave.cpp \ + jpeg_slave.cpp + +INCLUDEPATH += $(OPIEDIR)/include ../ +DEPENDSPATH += $(OPIEDIR)/include + +LIBS += -lqpe + +include ( $(OPIEDIR)/include.pro )
\ No newline at end of file diff --git a/noncore/graphics/opie-eye/slave/slaveiface.cpp b/noncore/graphics/opie-eye/slave/slaveiface.cpp new file mode 100644 index 0000000..170f7d5 --- a/dev/null +++ b/noncore/graphics/opie-eye/slave/slaveiface.cpp @@ -0,0 +1,26 @@ +/* + * GPLv2 zecke@handhelds.org + */ + +#include "slaveiface.h" + +static SlaveMap* _slaveMap = 0; +SlaveMap* slaveMap() { + if ( !_slaveMap ) + _slaveMap = new SlaveMap; + return _slaveMap; +} + +SlaveInterface::SlaveInterface( const QStringList& image ) + : m_list( image ) +{ + +} + +SlaveInterface::~SlaveInterface() { + +} + +QStringList SlaveInterface::imageFormats()const { + return m_list; +} diff --git a/noncore/graphics/opie-eye/slave/slaveiface.h b/noncore/graphics/opie-eye/slave/slaveiface.h new file mode 100644 index 0000000..18656c5 --- a/dev/null +++ b/noncore/graphics/opie-eye/slave/slaveiface.h @@ -0,0 +1,53 @@ +/* + * GPLv2 + */ + +#ifndef P_SLAVE_INTER_FACE_H +#define P_SLAVE_INTER_FACE_H + +#include <qfileinfo.h> +#include <qstringlist.h> +#include <qmap.h> + +/** + * @short The slave worker Interface for generating Preview + Image Info + Full + * IMage Info + */ +class QPixmap; +class SlaveInterface { +public: + SlaveInterface(const QStringList& imageformats); + virtual ~SlaveInterface(); + + QStringList imageFormats()const; + bool supports( const QString& )const; + virtual QString iconViewName(const QString&) = 0; + virtual QString fullImageInfo(const QString& )= 0; + virtual QPixmap pixmap( const QString&, int width, int height ) = 0; +private: + QStringList m_list; +}; + +inline bool SlaveInterface::supports( const QString& str)const { + return m_list.contains( QFileInfo( str ).extension(false) ); +} + +typedef SlaveInterface* (*phunkSlaveCreateFunc )(); +typedef QMap<QString,phunkSlaveCreateFunc> SlaveMap; + +typedef QMap<QString, SlaveInterface*> SlaveObjects; + +SlaveMap* slaveMap(); +SlaveObjects* slaveObjects(); + + + +#define PHUNK_VIEW_INTERFACE( NAME, IMPL ) \ + static SlaveInterface *create_ ## IMPL() { \ + return new IMPL(); \ + } \ + static SlaveMap::Iterator dummy_ ## IMPL = slaveMap()->insert( NAME, create_ ## IMPL ); + + + +#endif diff --git a/noncore/graphics/opie-eye/slave/slavereciever.cpp b/noncore/graphics/opie-eye/slave/slavereciever.cpp new file mode 100644 index 0000000..951f3df --- a/dev/null +++ b/noncore/graphics/opie-eye/slave/slavereciever.cpp @@ -0,0 +1,214 @@ +/* + * GPLv2 zecke@handhelds.org + */ + +#include "slavereciever.h" +#include "slaveiface.h" + +#include <qpe/qcopenvelope_qws.h> +#include <qpe/qpeapplication.h> + +#include <qtimer.h> + +static SlaveObjects* _slaveObj = 0; + +QDataStream & operator << (QDataStream & str, bool b) +{ + str << Q_INT8(b); + return str; +} + +QDataStream & operator >> (QDataStream & str, bool & b) +{ + Q_INT8 l; + str >> l; + b = bool(l); + return str; +} + + + +QDataStream &operator<<( QDataStream& s, const PixmapInfo& inf) { + return s << inf.file << inf.pixmap << inf.width << inf.height; +} +QDataStream &operator>>( QDataStream& s, PixmapInfo& inf ) { + s >> inf.file >> inf.pixmap >> inf.width >> inf.height; + return s; +} +QDataStream &operator<<( QDataStream& s, const ImageInfo& i) { + return s << i.kind << i.file << i.info; +} +QDataStream &operator>>( QDataStream& s, ImageInfo& i ) { + s >> i.kind >> i.file >> i.info; + return s; +} + + + +SlaveObjects* slaveObjects() { + if ( !_slaveObj ) + _slaveObj = new SlaveObjects; + return _slaveObj; +} + +SlaveReciever::SlaveReciever( QObject* par) + : QObject( par ) +{ + m_inf = new QTimer(this); + connect(m_inf,SIGNAL(timeout()), + this, SLOT(slotImageInfo())); + m_pix = new QTimer(this); + connect(m_pix,SIGNAL(timeout()), + this, SLOT(slotThumbNail())); + + m_out = new QTimer(this); + connect(m_out,SIGNAL(timeout()), + this, SLOT(slotSend())); + + SlaveObjects *obj = slaveObjects(); // won't be changed + SlaveMap::Iterator it; + SlaveMap* map = slaveMap(); // SlaveMap won't be changed during execution!!! + for(it = map->begin(); it != map->end(); ++it ) { + obj->insert( it.key(), (*it.data())() ); + } +} + +SlaveReciever::~SlaveReciever() { +} + +void SlaveReciever::recieveAnswer( const QCString& string, const QByteArray& ar) { + qWarning( "String is %s", string.data() ); + QDataStream stream(ar, IO_ReadOnly ); + QStringList lst; + static ImageInfo inf; + static PixmapInfo pix; + + if ( string == "thumbInfo(QString)" ) { + stream >> inf.file; + m_inList.append(inf); + }else if ( string == "thumbInfos(QStringList)" ) { + stream >> lst; + for(QStringList::Iterator it = lst.begin(); it != lst.end(); ++it ) { + qWarning( "Adding thumbinfo for file "+ *it ); + inf.file = (*it); + m_inList.append(inf); + } + }else if ( string == "fullInfo(QString)" ) { + inf.kind = true; + stream >> inf.file; + m_inList.append(inf); + }else if ( string == "fullInfos(QStringList)" ) { + stream >> lst; + for(QStringList::Iterator it = lst.begin(); it != lst.end(); ++it ) { + qWarning( "Adding fullInfo for"+ *it ); + inf.file = (*it); + inf.kind = true; + m_inList.append(inf); + } + }else if ( string == "pixmapInfo(QString,int,int)" ) { + stream >> pix.file >> pix.width >> pix.height; + m_inPix.append(pix); + }else if ( string == "pixmapInfos(PixmapInfos)" ) { + PixmapList list; + stream >> list; + for(PixmapList::Iterator it = list.begin(); it != list.end(); ++it ) { + qWarning( "Got %d %d " + (*it).file, (*it).width , (*it).height ); + m_inPix.append(*it); + } + } + + if (!m_inf->isActive() && !m_inList.isEmpty() ) + m_inf->start(5); + + if (!m_pix->isActive() && !m_inPix.isEmpty() ) + m_pix->start(5); + + QPEApplication::setKeepRunning(); + +} + +PixmapList SlaveReciever::outPix()const { + return m_outPix; +} + +StringList SlaveReciever::outInf()const{ + return m_outList; +} + +void SlaveReciever::slotImageInfo() { + ImageInfo inf = m_inList.first(); + m_inList.remove( inf ); + + static SlaveObjects::Iterator it; + static SlaveObjects* map = slaveObjects(); // SlaveMap won't be changed during execution!!! + for(it = map->begin(); it != map->end(); ++it ) { + if( (*it)->supports(inf.file ) ) { + /* full image info */ + if (inf.kind ) + inf.info = (*it)->fullImageInfo( inf.file ); + else + inf.info = (*it)->iconViewName( inf.file ); + m_outList.append( inf ); + break; + } + } + + if (m_inList.isEmpty() ) + m_inf->stop(); + if (!m_out->isActive() && !m_outList.isEmpty() ) + m_out->start( 100 ); +} + +void SlaveReciever::slotThumbNail() { + PixmapInfo inf = m_inPix.first(); + m_inPix.remove( inf ); + + static SlaveObjects::Iterator it; + static SlaveObjects* map = slaveObjects(); // SlaveMap won't be changed during execution!!! + for(it = map->begin(); it != map->end(); ++it ) { + SlaveInterface* iface = it.data(); + if( iface->supports(inf.file ) ) { + /* pixmap */ + qWarning( "Asking for thumbNail in size %d %d for "+inf.file, inf.width, inf.height ); + inf.pixmap = iface->pixmap(inf.file, 64, 64); + m_outPix.append( inf ); + break; + } + } + + + + if(m_inPix.isEmpty() ) + m_pix->stop(); + if(!m_out->isActive() && !m_outPix.isEmpty() ) + m_out->start(100); +} + +void SlaveReciever::slotSend() { + + m_out->stop(); + + qWarning( "Sending %d %d", outPix().count(), outInf().count() ); + /* queue it and send */ + /* if this ever gets a service introduce request queues + * so we can differinatate between different clients + */ + if (! m_outPix.isEmpty() ) { + QCopEnvelope answer("QPE/opie-eye", "pixmapsHandled(PixmapList)" ); + answer << outPix(); + for ( PixmapList::Iterator it = m_outPix.begin();it!=m_outPix.end();++it ) { + qWarning( "Sending out %s %d %d", (*it).file.latin1(), (*it).width, (*it).height ); + } + } + if ( !m_outList.isEmpty() ) { + QCopEnvelope answer("QPE/opie-eye", "pixmapsHandled(StringList)" ); + answer << outInf(); + for ( StringList::Iterator it = m_outList.begin();it!=m_outList.end();++it ) { + qWarning( "Sending out2 " + (*it).file ); + } + } + + m_outList.clear(); + m_outPix.clear(); +} + diff --git a/noncore/graphics/opie-eye/slave/slavereciever.h b/noncore/graphics/opie-eye/slave/slavereciever.h new file mode 100644 index 0000000..214bfc6 --- a/dev/null +++ b/noncore/graphics/opie-eye/slave/slavereciever.h @@ -0,0 +1,58 @@ +/* + * GPLv2 + */ + + +#ifndef SLAVE_RECEIVER_H +#define SLAVE_RECEIVER_H + +/** + * Receive Requests + */ + +#include <iface/slaveiface.h> + +#include <qobject.h> +#include <qdatastream.h> +#include <qstringlist.h> +#include <qvaluelist.h> +#include <qpixmap.h> + + + +typedef QValueList<PixmapInfo> PixmapList; +typedef QValueList<ImageInfo> StringList; + +class QTimer; +class QSocket; +class SlaveReciever : public QObject { + Q_OBJECT + + friend QDataStream &operator<<( QDataStream&, const PixmapInfo& ); + friend QDataStream &operator>>( QDataStream&, PixmapInfo& ); + friend QDataStream &operator<<( QDataStream&, const ImageInfo& ); + friend QDataStream &operator>>( QDataStream&, ImageInfo ); +public: + + enum Job { ImageInfoJob, FullImageInfoJob, ThumbNailJob }; + SlaveReciever( QObject* parent ); + ~SlaveReciever(); + +public slots: + void recieveAnswer( const QCString&, const QByteArray& ); +public: + PixmapList outPix()const; + StringList outInf()const; + +private slots: + void slotSend(); + void slotImageInfo(); + void slotThumbNail(); +private: + QTimer *m_inf, *m_pix, *m_out; + StringList m_inList, m_outList; + PixmapList m_inPix, m_outPix; +}; + + +#endif diff --git a/noncore/graphics/opie-eye/slave/thumbnailtool.cpp b/noncore/graphics/opie-eye/slave/thumbnailtool.cpp new file mode 100644 index 0000000..a202457 --- a/dev/null +++ b/noncore/graphics/opie-eye/slave/thumbnailtool.cpp @@ -0,0 +1,61 @@ +#include "thumbnailtool.h" + +#include <qfileinfo.h> +#include <qdir.h> +#include <qimage.h> +#include <qpixmap.h> +#include <qstring.h> + +static bool makeThumbDir( const QFileInfo& inf, bool make = false) { + QDir dir( inf.dirPath()+ "/.opie-eye" ); + if ( !dir.exists() ) + if ( make ) + return dir.mkdir(QString::null); + else + return false; + return true; +} + + +/* + * check if the Opie opie-eye dir exists + * check if a thumbnail exists + * load the thumbnail + * /foo/bar/imagefoo.gif + * check for a png in /foo/bar/.opie-eye/%dx%d-imagefoo.gif + */ +QPixmap ThumbNailTool::getThumb( const QString& path, int width, int height ) { + QFileInfo inf( path ); + qWarning( "Get Thumb" ); + if ( !makeThumbDir( inf ) ) { + QPixmap pix; + return pix; + } + QString str = QString( "/.opie-eye/%1x%2-%3" ).arg( width ).arg( height ).arg( inf.fileName() ); + qWarning( inf.dirPath()+str ); + return QPixmap( inf.dirPath()+str,"PNG" ); + +} + +void ThumbNailTool::putThumb( const QString& path, const QPixmap& pix, int width, int height ) { + QFileInfo inf( path ); + makeThumbDir( inf, true ); + QString str = QString( "/.opie-eye/%1x%2-%3" ).arg( width ).arg( height ).arg( inf.fileName() ); + qWarning( inf.dirPath()+str ); + pix.save( inf.dirPath()+str, "PNG" ); +} + + +QPixmap ThumbNailTool::scaleImage( QImage& img, int w, int h ) { + double hs = (double)h / (double)img.height() ; + double ws = (double)w / (double)img.width() ; + double scaleFactor = (hs > ws) ? ws : hs; + int smoothW = (int)(scaleFactor * img.width()); + int smoothH = (int)(scaleFactor * img.height()); + QPixmap pixmap; + if ( img.width() <= w && img.height() <= h ) + pixmap.convertFromImage( img ); + else + pixmap.convertFromImage( img.smoothScale( smoothW, smoothH) ); + return pixmap; +} diff --git a/noncore/graphics/opie-eye/slave/thumbnailtool.h b/noncore/graphics/opie-eye/slave/thumbnailtool.h new file mode 100644 index 0000000..4d0a30f --- a/dev/null +++ b/noncore/graphics/opie-eye/slave/thumbnailtool.h @@ -0,0 +1,19 @@ +/* + * GPLv2 + */ + +#ifndef THUMB_NAIL_TOOL_H +#define THUMB_NAIL_TOOL_H +class QString; +class QPixmap; +class QImage; + +struct ThumbNailTool { + static QPixmap scaleImage( QImage&, int width, int height ); +/* get one isInvalid() if non found */ + static QPixmap getThumb( const QString&, int width, int height ); +/* put one */ + static void putThumb( const QString&, const QPixmap&, int width, int heigh ); +}; + +#endif |