1 /* Copyright (C) 2003-2006 Jesper K. Pedersen <blackie@kde.org>
3 This program is free software; you can redistribute it and/or
4 modify it under the terms of the GNU General Public
5 License as published by the Free Software Foundation; either
6 version 2 of the License, or (at your option) any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 General Public License for more details.
13 You should have received a copy of the GNU General Public License
14 along with this program; see the file COPYING. If not, write to
15 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 Boston, MA 02110-1301, USA.
19 #include "ImageLoader.h"
21 #include "ImageDecoder.h"
23 #include "RawImageDecoder.h"
24 #include "Utilities/Util.h"
25 #include "ThumbnailStorage.h"
27 #include <qapplication.h>
28 #include <qfileinfo.h>
37 #include <sys/types.h>
45 namespace ImageManager
47 // Create a global instance. Its constructor will itself register it.
48 RAWImageDecoder rawdecoder
;
53 ImageManager::ImageLoader::ImageLoader(ThumbnailStorage
* storage
)
58 void ImageManager::ImageLoader::run()
61 ImageRequest
* request
= Manager::instance()->next();
65 QImage img
= tryLoadThumbnail( request
, ok
);
67 img
= loadImage( request
, ok
);
69 writeThumbnail( request
, img
);
74 img
= scaleAndRotate( request
, img
);
77 request
->setLoadedOK( ok
);
78 ImageEvent
* iew
= new ImageEvent( request
, img
);
79 QApplication::postEvent( Manager::instance(), iew
);
83 QImage
ImageManager::ImageLoader::rotateAndScale( QImage img
, int width
, int height
, int angle
)
87 matrix
.rotate( angle
);
88 img
= img
.transformed( matrix
);
90 img
= Utilities::scaleImage(img
, width
, height
, Qt::KeepAspectRatio
);
94 QImage
ImageManager::ImageLoader::tryLoadThumbnail( ImageRequest
* request
, bool& ok
)
97 QString key
= thumbnailKey( request
);
100 ok
= _storage
->retrieve(key
, &result
);
104 // TODO(hzeller): ppm do not store meta tags - we always accept them,
105 // because we cannot check 'outdatedness'.
106 // Find workaround (mtime(thumbnail) ? )
107 time_t thumbTime
= result
.text( "Thumb::MTime" ).toUInt();
109 if ( thumbTime
!= 0 && QFileInfo(request
->fileName()).exists() ) {
110 time_t fileTime
= QFileInfo(request
->fileName()).lastModified().toTime_t();
111 ok
= (thumbTime
== fileTime
);
117 QImage
ImageManager::ImageLoader::loadImage( ImageRequest
* request
, bool& ok
)
119 int dim
= calcLoadSize( request
);
123 if ( !QFile( request
->fileName() ).exists() )
127 if (Utilities::isJPEG(request
->fileName())) {
128 ok
= Utilities::loadJPEG(&img
, request
->fileName(), &fullSize
, dim
);
130 request
->setFullSize( fullSize
);
134 // At first, we have to give our RAW decoders a try. If we allowed
135 // QImage's load() method, it'd for example load a tiny thumbnail from
136 // NEF files, which is not what we want.
137 ok
= ImageDecoder::decode( &img
, request
->fileName(), &fullSize
, dim
);
139 request
->setFullSize( img
.size() );
143 // Now we can try QImage's stuff as a fallback...
144 ok
= img
.load( request
->fileName() );
146 request
->setFullSize( img
.size() );
153 void ImageManager::ImageLoader::writeThumbnail( ImageRequest
* request
, QImage img
)
155 int dim
= calcLoadSize( request
);
161 for( QList
<int>::Iterator it
= list
.begin(); it
!= list
.end(); ++it
) {
162 QString path
= thumbnailKey( requestURL( request
), *it
);
166 QFileInfo
fi( request
->fileName() );
167 QImage scaledImg
= Utilities::scaleImage(img
, *it
, *it
, Qt::KeepAspectRatio
);
168 scaledImg
.setText( "Software","",QString::fromLatin1( "KPhotoAlbum" ) );
169 scaledImg
.setText( "Thumb::URI", "", requestURL( request
) );
170 scaledImg
.setText( "Thumb::MTime", "", QString::number( fi
.lastModified().toTime_t() ) );
171 scaledImg
.setText( "Thumb::Size", "", QString::number( fi
.size() ) );
172 scaledImg
.setText( "Thumb::Image::Width", "", QString::number( request
->fullSize().width() ) );
173 scaledImg
.setText( "Thumb::Image::Height", "", QString::number( request
->fullSize().height() ) );
174 _storage
->store(path
, scaledImg
);
178 int ImageManager::ImageLoader::calcLoadSize( ImageRequest
* request
)
180 if ( request
->width() == -1 )
183 int max
= qMax( request
->width(), request
->height() );
186 else if ( max
> 128 )
192 QImage
ImageManager::ImageLoader::scaleAndRotate( ImageRequest
* request
, QImage img
)
194 if ( request
->angle() != 0 ) {
196 matrix
.rotate( request
->angle() );
197 img
= img
.transformed( matrix
);
198 int angle
= (request
->angle() + 360)%360;
199 Q_ASSERT( angle
>= 0 && angle
<= 360 );
200 if ( angle
== 90 || angle
== 270 )
201 request
->setFullSize( QSize( request
->fullSize().height(), request
->fullSize().width() ) );
204 // If we are looking for a scaled version, then scale
205 if ( shouldImageBeScale( img
, request
) )
206 img
= Utilities::scaleImage(img
, request
->width(), request
->height(), Qt::KeepAspectRatio
);
211 QString
ImageManager::ImageLoader::thumbnailKey( ImageRequest
* request
)
213 return thumbnailKey( requestURL( request
), calcLoadSize( request
) );
216 QString
ImageManager::ImageLoader::thumbnailKey( QString uri
, int dim
)
220 dir
= QString::fromLatin1( "large" );
221 else if ( dim
== 128 )
222 dir
= QString::fromLatin1( "normal" );
224 return QString::null
;
226 KMD5
md5( uri
.toUtf8() );
227 return QString::fromLatin1( "%1/%2" ).arg(dir
).arg(QString::fromUtf8(md5
.hexDigest()));
230 QString
ImageManager::ImageLoader::requestURL( ImageRequest
* request
)
233 url
.setPath( request
->fileName() );
237 bool ImageManager::ImageLoader::shouldImageBeScale( const QImage
& img
, ImageRequest
* request
)
239 // No size specified, meaning we want it full size.
240 if ( request
->width() == -1 )
243 if ( img
.width() < request
->width() && img
.height() < request
->height() ) {
244 // The image is smaller than the requets.
245 return request
->doUpScale();