summaryrefslogtreecommitdiff
authorzecke <zecke>2004-04-02 19:28:09 (UTC)
committer zecke <zecke>2004-04-02 19:28:09 (UTC)
commit97bdc55663590ba430a4f322ad97aab9f4c02bf3 (patch) (side-by-side diff)
tree8d340684ac97907b73d6b8abf995116f58680cdd
parent09d1ebb55ab44ebd129ff44b83b6b20054df77aa (diff)
downloadopie-97bdc55663590ba430a4f322ad97aab9f4c02bf3.zip
opie-97bdc55663590ba430a4f322ad97aab9f4c02bf3.tar.gz
opie-97bdc55663590ba430a4f322ad97aab9f4c02bf3.tar.bz2
-Fix realignment of the items but now it updates too often
-Fix slave not to include <qt></qt> for iconViewName
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--noncore/graphics/opie-eye/gui/iconview.cpp29
-rw-r--r--noncore/graphics/opie-eye/gui/mainwindow.cpp2
-rw-r--r--noncore/graphics/opie-eye/slave/gif_slave.cpp3
-rw-r--r--noncore/graphics/opie-eye/slave/jpeg_slave.cpp5
-rw-r--r--noncore/graphics/opie-eye/slave/png_slave.cpp3
5 files changed, 20 insertions, 22 deletions
diff --git a/noncore/graphics/opie-eye/gui/iconview.cpp b/noncore/graphics/opie-eye/gui/iconview.cpp
index b3f0006..ed9fc9c 100644
--- a/noncore/graphics/opie-eye/gui/iconview.cpp
+++ b/noncore/graphics/opie-eye/gui/iconview.cpp
@@ -1,378 +1,375 @@
/*
* GPLv2 zecke@handhelds.org
* No WArranty...
*/
#include "iconview.h"
#include <lib/imagecache.h>
#include <iface/dirview.h>
#include <iface/dirlister.h>
#include <opie2/oconfig.h>
#include <opie2/okeyconfigwidget.h>
+#include <opie2/odebug.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>
using Opie::Ui::OKeyConfigItem;
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& );
- void reCalc();
+
+
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 {
+ qWarning( "Name is " + m_path.right( 15 ) + " rect is %d %d %d %d | %d %d",
+ rect().x(),rect().y(),rect().width(),rect().height(),
+ iconView()->contentsX(), iconView()->contentsY());
+
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 );
- reCalc();
- }
-
- inline void IconViewItem::reCalc()
- {
- calcRect();
}
}
PIconView::PIconView( QWidget* wid, Opie::Core::OConfig* cfg )
: QVBox( wid ), m_cfg( cfg )
{
{
QCopEnvelope( "QPE/Application/opie-eye_slave", "refUp()" );
}
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 );
- m_view->setResizeMode(QIconView::Adjust);
int dw = QApplication::desktop()->width();
int viewerWidth = dw-style().scrollBarExtent().width();
- m_view->setGridX( viewerWidth-2*m_view->spacing() );
+ m_view->setGridX( viewerWidth-3*m_view->spacing());
m_view->setGridY( fontMetrics().height()*2+40 );
initKeys();
loadViews();
slotViewChanged( m_views->currentItem() );
}
PIconView::~PIconView() {
{
QCopEnvelope( "QPE/Application/opie-eye_slave", "refDown()" );
}
m_viewManager->save();
delete m_viewManager;
}
Opie::Ui::OKeyConfigManager* PIconView::manager() {
return m_viewManager;
}
void PIconView::initKeys() {
Opie::Ui::OKeyPair::List lst;
lst.append( Opie::Ui::OKeyPair::upArrowKey() );
lst.append( Opie::Ui::OKeyPair::downArrowKey() );
lst.append( Opie::Ui::OKeyPair::leftArrowKey() );
lst.append( Opie::Ui::OKeyPair::rightArrowKey() );
lst.append( Opie::Ui::OKeyPair::returnKey() );
m_viewManager = new Opie::Ui::OKeyConfigManager(m_cfg, "View-KeyBoard-Config",
lst, false,this, "keyconfig name" );
m_viewManager->addKeyConfig( OKeyConfigItem(tr("Beam Current Item") , "beam",
QString::fromLatin1("beam"), BeamItem,
Opie::Ui::OKeyPair(Qt::Key_B, Qt::ShiftButton),
this, SLOT(slotBeam())) );
m_viewManager->addKeyConfig( OKeyConfigItem(tr("Delete Current Item"), "delete",
QString::fromLatin1("trash"), DeleteItem,
Opie::Ui::OKeyPair(Qt::Key_D, Qt::ShiftButton),
this, SLOT(slotTrash())) );
m_viewManager->addKeyConfig( OKeyConfigItem(tr("View Current Item"), "view",
QString::fromLatin1("1to1"), ViewItem,
Opie::Ui::OKeyPair(Qt::Key_V, Qt::ShiftButton),
this, SLOT(slotShowImage())));
m_viewManager->addKeyConfig( OKeyConfigItem(tr("Show Image Info") , "info",
QString::fromLatin1("DocumentTypeWord"), InfoItem,
Opie::Ui::OKeyPair(Qt::Key_I, Qt::ShiftButton ),
this, SLOT(slotImageInfo()) ) );
m_viewManager->load();
m_viewManager->handleWidget( m_view );
}
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->setUpdatesEnabled( false );
+ m_view->viewport()->setUpdatesEnabled( false );
m_view->clear();
addFolders( lister->folders() );
addFiles( lister->files() );
- m_view->setUpdatesEnabled( true );
+ m_view->viewport()->setUpdatesEnabled( true );
// Also invalidate the cache. We can't cancel the operations anyway
g_stringPix.clear();
g_stringInf.clear();
// 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];
/* if set the view shows nonsens!
I dont know how to fix the format of displayed text :(*/
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];
if (pix.width()>0) {
PPixmapCache::self()->insertImage( _path, pix, 64, 64 );
/* required for a recalculated rectangle. otherwise the view show nonsense! */
- item->reCalc();
} else {
PPixmapCache::self()->insertImage(_path,Resource::loadPixmap( "UnknownDocument" ),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 );
+ m_view->viewport()->setUpdatesEnabled( false );
}
void PIconView::slotEnd() {
- m_view->setUpdatesEnabled( true );
+ m_view->arrangeItemsInGrid( );
+ m_view->viewport()->setUpdatesEnabled( true );
}
void PIconView::slotShowImage() {
}
void PIconView::slotShowImage( const QString& ) {
}
void PIconView::slotImageInfo() {
}
void PIconView::slotImageInfo( const QString& ) {
}
diff --git a/noncore/graphics/opie-eye/gui/mainwindow.cpp b/noncore/graphics/opie-eye/gui/mainwindow.cpp
index 0314659..83ff4f1 100644
--- a/noncore/graphics/opie-eye/gui/mainwindow.cpp
+++ b/noncore/graphics/opie-eye/gui/mainwindow.cpp
@@ -1,138 +1,138 @@
/*
* GPLv2 zecke@handhelds.org
* No WArranty...
*/
#include "mainwindow.h"
#include "iconview.h"
#include "filesystem.h"
#include <iface/ifaceinfo.h>
#include <iface/dirview.h>
#include <opie2/oapplicationfactory.h>
#include <opie2/otabwidget.h>
#include <opie2/okeyconfigwidget.h>
#include <qpe/resource.h>
#include <qpe/config.h>
#include <qpe/ir.h>
#include <qtoolbar.h>
#include <qtoolbutton.h>
#include <qlayout.h>
#include <qdialog.h>
#include <qmap.h>
OPIE_EXPORT_APP( Opie::Core::OApplicationFactory<PMainWindow> )
PMainWindow::PMainWindow(QWidget* wid, const char* name, WFlags style)
: QMainWindow( wid, name, style )
{
setCaption( QObject::tr("Opie Eye Caramba" ) );
m_cfg = new Opie::Core::OConfig("phunkview");
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() {
/*
* have a tab with the possible views
* a tab for globals image cache size.. scaled loading
* and one tab for the KeyConfigs
*/
QDialog dlg(this, 0, true);
dlg.setCaption( tr("Phunk View - Config" ) );
QHBoxLayout *lay = new QHBoxLayout(&dlg);
Opie::Ui::OTabWidget *wid = new Opie::Ui::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() );
+ wid->addTab( _wid, "fileopen", inf->name() );
}
/*
* Add the KeyConfigWidget
*/
Opie::Ui::OKeyConfigWidget* keyWid = new Opie::Ui::OKeyConfigWidget( wid, "key config" );
keyWid->setChangeMode( Opie::Ui::OKeyConfigWidget::Queue );
keyWid->insert( tr("Browser Keyboard Actions"), m_view->manager() );
keyWid->load();
wid->addTab( keyWid, QString::fromLatin1("AppsIcon" ), tr("Keyboard Configuration") );
bool act = ( QPEApplication::execDialog( &dlg ) == 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();
keyWid->save();
}
}
diff --git a/noncore/graphics/opie-eye/slave/gif_slave.cpp b/noncore/graphics/opie-eye/slave/gif_slave.cpp
index feb69b6..f0dc86e 100644
--- a/noncore/graphics/opie-eye/slave/gif_slave.cpp
+++ b/noncore/graphics/opie-eye/slave/gif_slave.cpp
@@ -1,305 +1,306 @@
#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;
+ QString st = "<qt>";
get_gif_info(QFile::encodeName( str ).data(), st, true );
+ st.append( "</qt>" );
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/jpeg_slave.cpp b/noncore/graphics/opie-eye/slave/jpeg_slave.cpp
index 95055fd..086b47f 100644
--- a/noncore/graphics/opie-eye/slave/jpeg_slave.cpp
+++ b/noncore/graphics/opie-eye/slave/jpeg_slave.cpp
@@ -1,1426 +1,1425 @@
#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() );
+ tag = QObject::tr( "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() );
+ tag = QObject::tr( "<qt>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/png_slave.cpp b/noncore/graphics/opie-eye/slave/png_slave.cpp
index 72b93cc..86e1cdc 100644
--- a/noncore/graphics/opie-eye/slave/png_slave.cpp
+++ b/noncore/graphics/opie-eye/slave/png_slave.cpp
@@ -1,210 +1,211 @@
#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;
+ QString str = "<qt>";
read_comment( path, true, str );
+ str += "</qt>";
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 );
}