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 |