2014-05-30 08:53:19 +00:00
/*
This file is part of Telegram Desktop ,
2014-12-01 10:47:38 +00:00
the official desktop version of Telegram messaging app , see https : //telegram.org
2014-05-30 08:53:19 +00:00
Telegram Desktop 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 3 of the License , or
( at your option ) any later version .
It 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 .
Full license : https : //github.com/telegramdesktop/tdesktop/blob/master/LICENSE
2014-12-01 10:47:38 +00:00
Copyright ( c ) 2014 John Preston , https : //desktop.telegram.org
2014-05-30 08:53:19 +00:00
*/
# include "stdafx.h"
# include "localimageloader.h"
2014-08-11 09:03:45 +00:00
# include "gui/filedialog.h"
2014-05-30 08:53:19 +00:00
# include <libexif/exif-data.h>
2014-06-16 09:31:10 +00:00
LocalImageLoaderPrivate : : LocalImageLoaderPrivate ( int32 currentUser , LocalImageLoader * loader , QThread * thread ) : QObject ( 0 )
, loader ( loader )
, user ( currentUser )
{
2014-05-30 08:53:19 +00:00
moveToThread ( thread ) ;
connect ( loader , SIGNAL ( needToPrepare ( ) ) , this , SLOT ( prepareImages ( ) ) ) ;
connect ( this , SIGNAL ( imageReady ( ) ) , loader , SLOT ( onImageReady ( ) ) ) ;
connect ( this , SIGNAL ( imageFailed ( quint64 ) ) , loader , SLOT ( onImageFailed ( quint64 ) ) ) ;
} ;
void LocalImageLoaderPrivate : : prepareImages ( ) {
2015-01-05 20:17:33 +00:00
QString file , filename , mime , stickerMime = qsl ( " image/webp " ) ;
2014-07-14 05:16:21 +00:00
int32 filesize = 0 ;
2014-05-30 08:53:19 +00:00
QImage img ;
QByteArray data ;
PeerId peer ;
2015-01-05 20:17:33 +00:00
uint64 id , thumbId = 0 ;
QString thumbExt = " jpg " ;
2014-05-30 08:53:19 +00:00
ToPrepareMediaType type ;
2014-12-22 23:11:37 +00:00
bool animated = false ;
2014-10-17 19:14:42 +00:00
bool ctrlShiftEnter = false ;
2014-05-30 08:53:19 +00:00
{
QMutexLocker lock ( loader - > toPrepareMutex ( ) ) ;
ToPrepareMedias & list ( loader - > toPrepareMedias ( ) ) ;
if ( list . isEmpty ( ) ) return ;
file = list . front ( ) . file ;
img = list . front ( ) . img ;
data = list . front ( ) . data ;
peer = list . front ( ) . peer ;
id = list . front ( ) . id ;
type = list . front ( ) . type ;
2014-10-17 19:14:42 +00:00
ctrlShiftEnter = list . front ( ) . ctrlShiftEnter ;
2014-05-30 08:53:19 +00:00
}
if ( img . isNull ( ) ) {
if ( ! file . isEmpty ( ) ) {
QFileInfo info ( file ) ;
if ( type = = ToPrepareAuto ) {
QString lower ( file . toLower ( ) ) ;
const QStringList & photoExtensions ( cPhotoExtensions ( ) ) ;
for ( QStringList : : const_iterator i = photoExtensions . cbegin ( ) , e = photoExtensions . cend ( ) ; i ! = e ; + + i ) {
if ( lower . lastIndexOf ( * i ) = = lower . size ( ) - i - > size ( ) ) {
if ( info . size ( ) < MaxUploadPhotoSize ) {
type = ToPreparePhoto ;
break ;
}
}
}
if ( type = = ToPrepareAuto & & info . size ( ) < MaxUploadDocumentSize ) {
type = ToPrepareDocument ;
}
}
if ( type = = ToPrepareDocument ) {
2015-01-02 23:44:34 +00:00
mime = mimeTypeForFile ( info ) . name ( ) ;
2014-05-30 08:53:19 +00:00
}
2015-01-05 20:17:33 +00:00
if ( type ! = ToPrepareAuto & & info . size ( ) < MaxUploadPhotoSize ) {
bool opaque = ( mime ! = stickerMime ) ;
img = App : : readImage ( file , 0 , opaque , & animated ) ;
}
2014-05-30 08:53:19 +00:00
filename = info . fileName ( ) ;
filesize = info . size ( ) ;
} else if ( ! data . isEmpty ( ) ) {
2014-12-22 23:11:37 +00:00
img = App : : readImage ( data , 0 , true , & animated ) ;
2014-05-30 08:53:19 +00:00
if ( type = = ToPrepareAuto ) {
if ( ! img . isNull ( ) & & data . size ( ) < MaxUploadPhotoSize ) {
type = ToPreparePhoto ;
} else if ( data . size ( ) < MaxUploadDocumentSize ) {
type = ToPrepareDocument ;
} else {
img = QImage ( ) ;
}
}
2015-01-02 23:44:34 +00:00
MimeType mimeType = mimeTypeForData ( data ) ;
2014-05-30 08:53:19 +00:00
if ( type = = ToPrepareDocument ) {
mime = mimeType . name ( ) ;
}
2015-01-05 20:17:33 +00:00
if ( mime = = " image/jpeg " ) {
filename = filedialogDefaultName ( qsl ( " image " ) , qsl ( " .jpg " ) , QString ( ) , true ) ;
} else {
QString ext ;
QStringList patterns = mimeType . globPatterns ( ) ;
if ( ! patterns . isEmpty ( ) ) {
ext = patterns . front ( ) . replace ( ' * ' , QString ( ) ) ;
}
filename = filedialogDefaultName ( qsl ( " doc " ) , ext , QString ( ) , true ) ;
2014-05-30 08:53:19 +00:00
}
filesize = data . size ( ) ;
}
} else {
2014-08-11 09:03:45 +00:00
if ( type = = ToPrepareDocument ) {
filename = filedialogDefaultName ( qsl ( " image " ) , qsl ( " .png " ) , QString ( ) , true ) ;
2015-01-02 23:44:34 +00:00
mime = mimeTypeForName ( " image/png " ) . name ( ) ;
2014-08-11 09:03:45 +00:00
data = QByteArray ( ) ;
{
QBuffer b ( & data ) ;
img . save ( & b , " PNG " ) ;
}
filesize = data . size ( ) ;
} else {
2015-01-05 20:17:33 +00:00
if ( img . hasAlphaChannel ( ) ) {
QImage solid ( img . width ( ) , img . height ( ) , QImage : : Format_ARGB32_Premultiplied ) ;
solid . fill ( st : : white - > c ) ;
{
QPainter ( & solid ) . drawImage ( 0 , 0 , img ) ;
}
img = solid ;
}
type = ToPreparePhoto ;
2014-08-11 09:03:45 +00:00
filename = qsl ( " Untitled.jpg " ) ;
filesize = 0 ;
}
2014-05-30 08:53:19 +00:00
}
2014-06-16 09:31:10 +00:00
if ( ( img . isNull ( ) & & ( type ! = ToPrepareDocument | | ! filesize ) ) | | type = = ToPrepareAuto | | ( img . isNull ( ) & & file . isEmpty ( ) & & data . isEmpty ( ) ) ) { // if could not decide what type
2014-05-30 08:53:19 +00:00
{
QMutexLocker lock ( loader - > toPrepareMutex ( ) ) ;
ToPrepareMedias & list ( loader - > toPrepareMedias ( ) ) ;
list . pop_front ( ) ;
}
QTimer : : singleShot ( 1 , this , SLOT ( prepareImages ( ) ) ) ;
emit imageFailed ( id ) ;
} else {
PreparedPhotoThumbs photoThumbs ;
QVector < MTPPhotoSize > photoSizes ;
2014-12-22 23:11:37 +00:00
QVector < MTPDocumentAttribute > attributes ( 1 , MTP_documentAttributeFilename ( MTP_string ( filename ) ) ) ;
2014-05-30 08:53:19 +00:00
MTPPhotoSize thumb ( MTP_photoSizeEmpty ( MTP_string ( " " ) ) ) ;
MTPPhoto photo ( MTP_photoEmpty ( MTP_long ( 0 ) ) ) ;
MTPDocument document ( MTP_documentEmpty ( MTP_long ( 0 ) ) ) ;
QByteArray jpeg ;
if ( type = = ToPreparePhoto ) {
int32 w = img . width ( ) , h = img . height ( ) ;
2014-12-22 23:11:37 +00:00
QPixmap thumb = ( w > 100 | | h > 100 ) ? QPixmap : : fromImage ( img . scaled ( 100 , 100 , Qt : : KeepAspectRatio , Qt : : SmoothTransformation ) , Qt : : ColorOnly ) : QPixmap : : fromImage ( img ) ;
2014-05-30 08:53:19 +00:00
photoThumbs . insert ( ' s ' , thumb ) ;
photoSizes . push_back ( MTP_photoSize ( MTP_string ( " s " ) , MTP_fileLocationUnavailable ( MTP_long ( 0 ) , MTP_int ( 0 ) , MTP_long ( 0 ) ) , MTP_int ( thumb . width ( ) ) , MTP_int ( thumb . height ( ) ) , MTP_int ( 0 ) ) ) ;
2014-12-22 23:11:37 +00:00
QPixmap medium = ( w > 320 | | h > 320 ) ? QPixmap : : fromImage ( img . scaled ( 320 , 320 , Qt : : KeepAspectRatio , Qt : : SmoothTransformation ) , Qt : : ColorOnly ) : QPixmap : : fromImage ( img ) ;
2014-08-15 11:19:32 +00:00
photoThumbs . insert ( ' m ' , medium ) ;
photoSizes . push_back ( MTP_photoSize ( MTP_string ( " m " ) , MTP_fileLocationUnavailable ( MTP_long ( 0 ) , MTP_int ( 0 ) , MTP_long ( 0 ) ) , MTP_int ( medium . width ( ) ) , MTP_int ( medium . height ( ) ) , MTP_int ( 0 ) ) ) ;
2014-12-22 23:11:37 +00:00
QPixmap full = ( w > 1280 | | h > 1280 ) ? QPixmap : : fromImage ( img . scaled ( 1280 , 1280 , Qt : : KeepAspectRatio , Qt : : SmoothTransformation ) , Qt : : ColorOnly ) : QPixmap : : fromImage ( img ) ;
2014-12-12 16:27:03 +00:00
photoThumbs . insert ( ' y ' , full ) ;
photoSizes . push_back ( MTP_photoSize ( MTP_string ( " y " ) , MTP_fileLocationUnavailable ( MTP_long ( 0 ) , MTP_int ( 0 ) , MTP_long ( 0 ) ) , MTP_int ( full . width ( ) ) , MTP_int ( full . height ( ) ) , MTP_int ( 0 ) ) ) ;
2014-05-30 08:53:19 +00:00
{
QBuffer jpegBuffer ( & jpeg ) ;
2014-12-12 16:27:03 +00:00
full . save ( & jpegBuffer , " JPG " , 77 ) ;
2014-05-30 08:53:19 +00:00
}
if ( ! filesize ) filesize = jpeg . size ( ) ;
photo = MTP_photo ( MTP_long ( id ) , MTP_long ( 0 ) , MTP_int ( user ) , MTP_int ( unixtime ( ) ) , MTP_string ( " " ) , MTP_geoPointEmpty ( ) , MTP_vector < MTPPhotoSize > ( photoSizes ) ) ;
2015-01-05 20:17:33 +00:00
thumbId = id ;
2014-05-30 08:53:19 +00:00
} else if ( ( type = = ToPrepareVideo | | type = = ToPrepareDocument ) & & ! img . isNull ( ) ) {
int32 w = img . width ( ) , h = img . height ( ) ;
2015-01-02 14:55:24 +00:00
QByteArray thumbFormat = " JPG " ;
2015-01-05 20:17:33 +00:00
int32 thumbQuality = 87 ;
2015-01-02 14:55:24 +00:00
if ( animated ) {
attributes . push_back ( MTP_documentAttributeAnimated ( ) ) ;
2015-01-05 20:17:33 +00:00
} else if ( mime = = stickerMime & & w > 0 & & h > 0 & & w < = StickerMaxSize & & h < = StickerMaxSize & & filesize < StickerInMemory ) {
2015-01-02 14:55:24 +00:00
attributes . push_back ( MTP_documentAttributeSticker ( ) ) ;
thumbFormat = " webp " ;
2015-01-05 20:17:33 +00:00
thumbExt = qsl ( " webp " ) ;
2015-01-02 14:55:24 +00:00
}
2014-12-22 23:11:37 +00:00
attributes . push_back ( MTP_documentAttributeImageSize ( MTP_int ( w ) , MTP_int ( h ) ) ) ;
2015-01-05 20:17:33 +00:00
if ( w < 20 * h & & h < 20 * w ) {
QPixmap full = ( w > 90 | | h > 90 ) ? QPixmap : : fromImage ( img . scaled ( 90 , 90 , Qt : : KeepAspectRatio , Qt : : SmoothTransformation ) , Qt : : ColorOnly ) : QPixmap : : fromImage ( img , Qt : : ColorOnly ) ;
2014-05-30 08:53:19 +00:00
2015-01-05 20:17:33 +00:00
{
QBuffer jpegBuffer ( & jpeg ) ;
full . save ( & jpegBuffer , thumbFormat , thumbQuality ) ;
}
2014-05-30 08:53:19 +00:00
2015-01-05 20:17:33 +00:00
photoThumbs . insert ( ' 0 ' , full ) ;
thumb = MTP_photoSize ( MTP_string ( " " ) , MTP_fileLocationUnavailable ( MTP_long ( 0 ) , MTP_int ( 0 ) , MTP_long ( 0 ) ) , MTP_int ( full . width ( ) ) , MTP_int ( full . height ( ) ) , MTP_int ( 0 ) ) ;
2014-05-30 08:53:19 +00:00
2015-01-05 20:17:33 +00:00
thumbId = MTP : : nonce < uint64 > ( ) ;
}
2014-05-30 08:53:19 +00:00
}
if ( type = = ToPrepareDocument ) {
2014-12-22 23:11:37 +00:00
document = MTP_document ( MTP_long ( id ) , MTP_long ( 0 ) , MTP_int ( unixtime ( ) ) , MTP_string ( mime ) , MTP_int ( filesize ) , thumb , MTP_int ( MTP : : maindc ( ) ) , MTP_vector < MTPDocumentAttribute > ( attributes ) ) ;
2014-05-30 08:53:19 +00:00
}
{
QMutexLocker lock ( loader - > readyMutex ( ) ) ;
2015-01-05 20:17:33 +00:00
loader - > readyList ( ) . push_back ( ReadyLocalMedia ( type , file , filename , filesize , data , id , thumbId , thumbExt , peer , photo , photoThumbs , document , jpeg , ctrlShiftEnter ) ) ;
2014-05-30 08:53:19 +00:00
}
{
QMutexLocker lock ( loader - > toPrepareMutex ( ) ) ;
ToPrepareMedias & list ( loader - > toPrepareMedias ( ) ) ;
list . pop_front ( ) ;
}
QTimer : : singleShot ( 1 , this , SLOT ( prepareImages ( ) ) ) ;
emit imageReady ( ) ;
}
}
LocalImageLoaderPrivate : : ~ LocalImageLoaderPrivate ( ) {
loader = 0 ;
}
LocalImageLoader : : LocalImageLoader ( QObject * parent ) : QObject ( parent ) , thread ( 0 ) , priv ( 0 ) {
}
void LocalImageLoader : : append ( const QStringList & files , const PeerId & peer , ToPrepareMediaType t ) {
{
QMutexLocker lock ( toPrepareMutex ( ) ) ;
for ( QStringList : : const_iterator i = files . cbegin ( ) , e = files . cend ( ) ; i ! = e ; + + i ) {
2014-10-17 19:14:42 +00:00
toPrepare . push_back ( ToPrepareMedia ( * i , peer , t , false ) ) ;
2014-05-30 08:53:19 +00:00
}
}
if ( ! thread ) {
thread = new QThread ( ) ;
priv = new LocalImageLoaderPrivate ( MTP : : authedId ( ) , this , thread ) ;
thread - > start ( ) ;
}
emit needToPrepare ( ) ;
}
PhotoId LocalImageLoader : : append ( const QByteArray & img , const PeerId & peer , ToPrepareMediaType t ) {
PhotoId result = 0 ;
{
QMutexLocker lock ( toPrepareMutex ( ) ) ;
2014-10-17 19:14:42 +00:00
toPrepare . push_back ( ToPrepareMedia ( img , peer , t , false ) ) ;
2014-05-30 08:53:19 +00:00
result = toPrepare . back ( ) . id ;
}
if ( ! thread ) {
thread = new QThread ( ) ;
priv = new LocalImageLoaderPrivate ( MTP : : authedId ( ) , this , thread ) ;
thread - > start ( ) ;
}
emit needToPrepare ( ) ;
return result ;
}
2014-10-17 19:14:42 +00:00
PhotoId LocalImageLoader : : append ( const QImage & img , const PeerId & peer , ToPrepareMediaType t , bool ctrlShiftEnter ) {
2014-05-30 08:53:19 +00:00
PhotoId result = 0 ;
{
QMutexLocker lock ( toPrepareMutex ( ) ) ;
2014-10-17 19:14:42 +00:00
toPrepare . push_back ( ToPrepareMedia ( img , peer , t , ctrlShiftEnter ) ) ;
2014-05-30 08:53:19 +00:00
result = toPrepare . back ( ) . id ;
}
if ( ! thread ) {
thread = new QThread ( ) ;
priv = new LocalImageLoaderPrivate ( MTP : : authedId ( ) , this , thread ) ;
thread - > start ( ) ;
}
emit needToPrepare ( ) ;
return result ;
}
PhotoId LocalImageLoader : : append ( const QString & file , const PeerId & peer , ToPrepareMediaType t ) {
PhotoId result = 0 ;
{
QMutexLocker lock ( toPrepareMutex ( ) ) ;
2014-10-17 19:14:42 +00:00
toPrepare . push_back ( ToPrepareMedia ( file , peer , t , false ) ) ;
2014-05-30 08:53:19 +00:00
result = toPrepare . back ( ) . id ;
}
if ( ! thread ) {
thread = new QThread ( ) ;
priv = new LocalImageLoaderPrivate ( MTP : : authedId ( ) , this , thread ) ;
thread - > start ( ) ;
}
emit needToPrepare ( ) ;
return result ;
}
void LocalImageLoader : : onImageReady ( ) {
{
QMutexLocker lock ( toPrepareMutex ( ) ) ;
if ( toPrepare . isEmpty ( ) ) {
if ( priv ) priv - > deleteLater ( ) ;
priv = 0 ;
if ( thread ) thread - > deleteLater ( ) ;
thread = 0 ;
}
}
emit imageReady ( ) ;
}
void LocalImageLoader : : onImageFailed ( quint64 id ) {
{
QMutexLocker lock ( toPrepareMutex ( ) ) ;
if ( toPrepare . isEmpty ( ) ) {
if ( priv ) priv - > deleteLater ( ) ;
priv = 0 ;
if ( thread ) thread - > deleteLater ( ) ;
thread = 0 ;
}
}
emit imageFailed ( id ) ;
}
QMutex * LocalImageLoader : : readyMutex ( ) {
return & readyLock ;
}
ReadyLocalMedias & LocalImageLoader : : readyList ( ) {
return ready ;
}
QMutex * LocalImageLoader : : toPrepareMutex ( ) {
return & toPrepareLock ;
}
ToPrepareMedias & LocalImageLoader : : toPrepareMedias ( ) {
return toPrepare ;
}
LocalImageLoader : : ~ LocalImageLoader ( ) {
delete priv ;
delete thread ;
}