Commit d42ff360 authored by Jesper Pedersen's avatar Jesper Pedersen

phote album project

svn path=/trunk/kdeextragear-2/kimdaba/; revision=198778
parents
#include "imageclient.h"
#include "imagemanager.h"
ImageClient::~ImageClient()
{
ImageManager::instance()->stop( this );
}
#ifndef IMAGECLIENT_H
#define IMAGECLIENT_H
class QPixmap;
class QString;
class ImageClient {
public:
virtual ~ImageClient();
virtual void pixmapLoaded( const QString& fileName, int width, int height, const QPixmap& ) = 0;
};
#endif /* IMAGECLIENT_H */
#include "metainfo.h"
#include "imageconfig.h"
#include "listselect.h"
#include <qspinbox.h>
#include <qcombobox.h>
#include <qtextedit.h>
#include <qpushbutton.h>
#include <qlineedit.h>
#include <qlabel.h>
#include "imagemanager.h"
ImageConfig::ImageConfig( QWidget* parent, const char* name )
: ImageConfigUI( parent, name )
{
persons->setLabel( "Persons" );
keywords->setLabel("Keywords");
MetaInfo* minfo = MetaInfo::instance();
persons->insertStringList( minfo->items( "persons" ) );
keywords->insertStringList( minfo->items( "keywords" ) );
}
void ImageConfig::slotRevert()
{
load();
}
void ImageConfig::slotPrev()
{
save();
_current--;
load();
}
void ImageConfig::slotNext()
{
if ( _current != -1 ) {
save();
}
_current++;
load();
}
void ImageConfig::slotDone()
{
save();
}
void ImageConfig::load()
{
ImageInfo* info = _list.at( _current );
year->setValue( info->year() );
month->setCurrentItem( info->month() );
day->setValue( info->day() );
hour->setValue( info->hour() );
minute->setValue( info->minute() );
quality->setCurrentItem( info->quality() );
label->setText( info->label() );
description->setText( info->description() );
persons->setSelection( info->optionValue( persons->label() ) );
keywords->setSelection( info->optionValue( keywords->label() ) );
nextBut->setEnabled( _current != (int)_list.count()-1 );
prevBut->setEnabled( _current != 0 );
if ( _preloadImageMap.contains( info->fileName() ) )
preview->setPixmap( _preloadImageMap[ info->fileName() ] );
else
preview->setText( "<qt>Loading<br>preview</qt>" );
}
void ImageConfig::save()
{
ImageInfo* info = _list.at( _current );
info->setDate( year->value(), month->currentItem(), day->value() );
info->setTime( hour->value(), minute->value() );
info->setQuality( quality->currentItem() );
info->setLabel( label->text() );
info->setDescription( description->text() );
QStringList list = persons->selection();
info->setOption( persons->label(), persons->selection() );
info->setOption( keywords->label(), keywords->selection() );
}
void ImageConfig::setImageInfo( ImageInfoList list )
{
_list = list;
_current = -1;
slotNext();
_preloadImageMap.clear();
for( QPtrListIterator<ImageInfo> it( list ); *it; ++it ) {
ImageManager::instance()->load( (*it)->fileName(), this, 256, 256, false );
}
}
void ImageConfig::pixmapLoaded( const QString& fileName, int, int, const QPixmap& pixmap )
{
if ( fileName == _list.at( _current )->fileName() )
preview->setPixmap( pixmap );
_preloadImageMap[ fileName ] = pixmap;
}
#ifndef IMAGECONFIG_H
#define IMAGECONFIG_H
#include "imageinfo.h"
#include "imageconfigui.h"
#include "imageclient.h"
class ImageConfig :public ImageConfigUI, public ImageClient {
public:
ImageConfig( QWidget* parent, const char* name = 0 );
void setImageInfo( ImageInfoList list );
virtual void pixmapLoaded( const QString&, int, int, const QPixmap& );
protected:
void slotRevert();
void slotPrev();
void slotNext();
void slotDone();
void load();
void save();
private:
ImageInfoList _list;
int _current;
QMap<QString, QPixmap> _preloadImageMap;
};
#endif /* IMAGECONFIG_H */
This diff is collapsed.
#include "imageinfo.h"
#include <qfileinfo.h>
#include <qimage.h>
#include <qdom.h>
#include "options.h"
ImageInfo::ImageInfo()
{
}
ImageInfo::ImageInfo( const QString& fileName, QDomElement elm )
: _fileName( fileName )
{
QFileInfo fi( fileName );
_label = elm.attribute( "label", fi.baseName() );
_description = elm.attribute( "description" );
int year = -1, month = -1, day = -1, hour = -1, minute = -1;
if ( Options::instance()->trustFileTimeStamps() ) {
QDate date = fi.created().date();
QTime time = fi.created().time();
year = date.year();
month = date.month();
day = date.day();
hour = time.hour();
minute = time.minute();
}
_year = elm.attribute( "year", QString::number(year) ).toInt();
_month = elm.attribute( "month", QString::number(month) ).toInt();
_day = elm.attribute( "day", QString::number(day) ).toInt();
_hour = elm.attribute( "hour", QString::number(hour) ).toInt();
_minute = elm.attribute( "minute", QString::number(minute) ).toInt();
_quality = elm.attribute( "quality" ).toInt();
for ( QDomNode nodeOption = elm.firstChild(); !nodeOption.isNull(); nodeOption = nodeOption.nextSibling() ) {
if ( nodeOption.isElement() ) {
QDomElement elmOption = nodeOption.toElement();
Q_ASSERT( elmOption.tagName() == "option" );
QString name = elmOption.attribute( "name" );
if ( !name.isNull() ) {
for ( QDomNode nodeValue = elmOption.firstChild(); !nodeValue.isNull(); nodeValue = nodeValue.nextSibling() ) {
if ( nodeValue.isElement() ) {
QDomElement elmValue = nodeValue.toElement();
Q_ASSERT( elmValue.tagName() == "value" );
QString value = elmValue.attribute( "value" );
if ( !value.isNull() ) {
_options[name].append( value );
}
}
}
}
}
}
}
void ImageInfo::setLabel( const QString& desc )
{
_label = desc;
}
QString ImageInfo::label() const
{
return _label;
}
void ImageInfo::setDescription( const QString& desc )
{
_description = desc;
}
QString ImageInfo::description() const
{
return _description;
}
void ImageInfo::setDate( int year, int month, int day )
{
_year = year;
_month = month;
_day = day;
}
int ImageInfo::year() const
{
return _year;
}
int ImageInfo::month() const
{
return _month;
}
int ImageInfo::day() const
{
return _day;
}
void ImageInfo::setTime( int hour, int minute )
{
_hour = hour;
_minute = minute;
}
int ImageInfo::hour() const
{
return _hour;
}
int ImageInfo::minute() const
{
return _minute;
}
void ImageInfo::setOption( const QString& key, const QStringList& value )
{
_options[key] = value;
}
QStringList ImageInfo::optionValue( const QString& key ) const
{
return _options[key];
}
void ImageInfo::setQuality( int quality )
{
_quality = quality;
}
int ImageInfo::quality() const
{
return _quality;
}
QString ImageInfo::fileName()
{
return _fileName;
}
QPixmap ImageInfo::pixmap( int width, int height )
{
QImage img( _fileName );
QPixmap pix;
img = img.smoothScale( width, height, QImage::ScaleMin );
pix.convertFromImage( img );
return pix;
}
QDomElement ImageInfo::save( QDomDocument& doc )
{
QDomElement elm = doc.createElement( "Image" );
elm.setAttribute( "file", QFileInfo( _fileName ).fileName() );
elm.setAttribute( "label", _label );
elm.setAttribute( "description", _description );
elm.setAttribute( "year", _year );
elm.setAttribute( "month", _month );
elm.setAttribute( "day", _day );
elm.setAttribute( "hour", _hour );
elm.setAttribute( "minute", _minute );
elm.setAttribute( "quality", _quality );
for( QMapIterator<QString,QStringList> it= _options.begin(); it != _options.end(); ++it ) {
QDomElement opt = doc.createElement( "option" );
opt.setAttribute( "name", it.key() );
elm.appendChild( opt );
QStringList list = it.data();
for( QStringList::Iterator it2 = list.begin(); it2 != list.end(); ++it2 ) {
QDomElement val = doc.createElement( "value" );
val.setAttribute( "value", *it2 );
opt.appendChild( val );
}
}
return elm;
}
#include <qstring.h>
#include <qstringlist.h>
#include <qmap.h>
#include <qvaluelist.h>
#include <qpixmap.h>
#include <qdom.h>
#include <qobject.h>
#include <qdeepcopy.h>
#ifndef IMAGEINFO_H
#define IMAGEINFO_H
class ImageInfo :public QObject {
public:
ImageInfo();
ImageInfo( const QString& fileName, QDomElement elm );
QString fileName();
void setLabel( const QString& );
QString label() const;
void setDescription( const QString& );
QString description() const;
void setDate( int year, int month, int day );
int year() const;
int month() const;
int day() const;
void setTime( int hour, int minute );
int hour() const;
int minute() const;
void setQuality( int );
int quality() const;
void setOption( const QString& key, const QStringList& value );
QStringList optionValue( const QString& key ) const;
QPixmap pixmap( int width, int height );
QDomElement save( QDomDocument& doc );
private:
// This string is accessed on several threads, so we need to make it a deep copy!
QDeepCopy<QString> _fileName;
QString _label;
QString _description;
int _year, _month, _day, _hour, _minute;
int _quality;
QMap<QString, QStringList> _options;
};
typedef QPtrList<ImageInfo> ImageInfoList;
#endif /* IMAGEINFO_H */
#include "imageloader.h"
#include <qwaitcondition.h>
#include "imagemanager.h"
#include "thumbnail.h"
#include <qfileinfo.h>
#include <qapplication.h>
#include "options.h"
#include <qdir.h>
extern "C" {
#include <jpeglib.h>
#include <stdio.h>
}
ImageLoader::ImageLoader( QWaitCondition* sleeper )
: _sleeper( sleeper )
{
}
void ImageLoader::run()
{
while ( true ) {
LoadInfo li = ImageManager::instance()->next();
if ( !li.isNull() ) {
QString cacheDir = QFileInfo( li.fileName() ).dirPath() + "/ThumbNails";
QString cacheFile = cacheDir + QString("/%1x%2-%3").arg(li.width()).arg(li.height()).arg( QFileInfo( li.fileName() ).baseName() );
if ( li.width() != -1 ) {
// Try to load thumbernail from cache
QImage img;
if ( QFileInfo( cacheFile ).exists() ) {
if ( img.load( cacheFile ) ) {
li.setImage( img );
ImageEvent* iew = new ImageEvent( li );
QApplication::postEvent( ImageManager::instance(), iew );
continue; // Done.
}
}
}
QImage orig;
if (!isJPEG(li) || !loadJPEG(&orig, li)) {
// Fetch the original unscaled image using normal Qt image loading
if ( !li.image().isNull() ) {
orig = li.image();
}
else {
if ( orig.load( li.fileName() ) ) {
// hmmmm sending this information will fill the cache very fast
// LoadInfo liOrigImage( li.fileName(), -1, -1, orig );
// ImageEvent* iew = new ImageEvent( liOrigImage );
// QApplication::postEvent( ImageManager::instance(), iew );
}
}
}
// If we are looking for a scaled version, then scale
if ( li.width() != -1 ) {
QImage scaled = orig.scale( li.width(), li.height(), QImage::ScaleMin );
// Save thumbnail to disk
if ( Options::instance()->cacheThumbNails() ) {
if ( ! QDir( cacheDir ).exists() ) {
QDir().mkdir( cacheDir, true );
}
scaled.save( cacheFile, "JPEG" );
}
li.setImage( scaled );
ImageEvent* iew = new ImageEvent( li );
QApplication::postEvent( ImageManager::instance(), iew );
}
}
else
_sleeper->wait();
sleep( 1 );
}
}
bool ImageLoader::isJPEG( const LoadInfo& li )
{
QString format=QImageIO::imageFormat( li.fileName() );
return format=="JPEG";
}
// Fudged Fast JPEG decoding code from GWENVIEW (picked out out digikam)
bool ImageLoader::loadJPEG(QImage* image, const LoadInfo& li )
{
FILE* inputFile=fopen(li.fileName().data(), "rb");
if(!inputFile) return false;
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, inputFile);
jpeg_read_header(&cinfo, TRUE);
int size = li.width();
int imgSize = QMAX(cinfo.image_width, cinfo.image_height);
int scale=1;
while(size*scale*2<=imgSize) {
scale*=2;
}
if(scale>8) scale=8;
cinfo.scale_num=1;
cinfo.scale_denom=scale;
// Create QImage
jpeg_start_decompress(&cinfo);
switch(cinfo.output_components) {
case 3:
case 4:
image->create( cinfo.output_width, cinfo.output_height, 32 );
break;
case 1: // B&W image
image->create( cinfo.output_width, cinfo.output_height, 8, 256 );
for (int i=0; i<256; i++)
image->setColor(i, qRgb(i,i,i));
break;
default:
return false;
}
uchar** lines = image->jumpTable();
while (cinfo.output_scanline < cinfo.output_height)
jpeg_read_scanlines(&cinfo, lines + cinfo.output_scanline,
cinfo.output_height);
jpeg_finish_decompress(&cinfo);
// Expand 24->32 bpp
if ( cinfo.output_components == 3 ) {
for (uint j=0; j<cinfo.output_height; j++) {
uchar *in = image->scanLine(j) + cinfo.output_width*3;
QRgb *out = (QRgb*)( image->scanLine(j) );
for (uint i=cinfo.output_width; i--; ) {
in-=3;
out[i] = qRgb(in[0], in[1], in[2]);
}
}
}
int newMax = QMAX(cinfo.output_width, cinfo.output_height);
int newx = size*cinfo.output_width / newMax;
int newy = size*cinfo.output_height / newMax;
//image=image.smoothScale( newx, newy);
jpeg_destroy_decompress(&cinfo);
fclose(inputFile);
return true;
}
#ifndef IMAGELOADER_H
#define IMAGELOADER_H
#include <qthread.h>
class ImageManager;
class QWaitCondition;
class LoadInfo;
class ImageLoader :public QThread {
public:
ImageLoader( QWaitCondition* sleeper );
protected:
virtual void run();
bool isJPEG( const LoadInfo& li );
bool loadJPEG(QImage* image, const LoadInfo& li );
private:
QWaitCondition* _sleeper;
};
#endif /* IMAGELOADER_H */
#include "imagemanager.h"
#include "imageloader.h"
#include "options.h"
#include <qpixmapcache.h>
#include "imageclient.h"
#include <qdatetime.h>
ImageManager* ImageManager::_instance = 0;
ImageManager::ImageManager()
{
}
// We need this as a separate method as the _instance variable will otherwise not be initialized corrected before the thread starts.
void ImageManager::init()
{
_sleepers = new QWaitCondition(); // Is it necessary to load this using new?
_lock = new QMutex(); // necessary with new?
for ( int i = 0; i < Options::instance()->numThreads(); ++i ) {
ImageLoader* imageLoader = new ImageLoader( _sleepers );
_imageLoaders.append( imageLoader );
imageLoader->start();
}
}
void ImageManager::load( const QString& fileName, ImageClient* client, int width, int height, bool cache )
{
QString key = QString("%1-%2x%3").arg( fileName ).arg( width ).arg( height );
QPixmap* pixmap = QPixmapCache::find( key );
if ( pixmap ) {
if ( client )
client->pixmapLoaded( fileName, width, height, *pixmap );
}
else {
_lock->lock();
QPixmap pix;
if ( width == -1 && height == -1 ) {
key = QString("%1-%2x%3").arg( fileName ).arg( -1 ).arg( -1 );
QPixmap* pp = QPixmapCache::find( key );
if ( pp )
pix = *pp;
}
LoadInfo li( fileName, width, height, pix, client );
li.setCache( cache );
_loadList.append( li );
_lock->unlock();
if ( client )
_clientMap.insert( li, client );
_sleepers->wakeOne();
}
}
LoadInfo ImageManager::next()
{
_lock->lock();
LoadInfo info;
if ( _loadList.count() != 0 ) {
info = _loadList.first();