2014-05-30 12:53:19 +04:00
/*
This file is part of Telegram Desktop ,
2014-12-01 13:47:38 +03:00
the official desktop version of Telegram messaging app , see https : //telegram.org
2014-05-30 12:53:19 +04: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 .
2015-10-03 16:16:42 +03:00
In addition , as a special exception , the copyright holders give permission
to link the code of portions of this program with the OpenSSL library .
2014-05-30 12:53:19 +04:00
Full license : https : //github.com/telegramdesktop/tdesktop/blob/master/LICENSE
2015-10-03 16:16:42 +03:00
Copyright ( c ) 2014 - 2015 John Preston , https : //desktop.telegram.org
2014-05-30 12:53:19 +04:00
*/
# include "stdafx.h"
# include "localimageloader.h"
2014-08-11 13:03:45 +04:00
# include "gui/filedialog.h"
2015-07-01 00:07:05 +03:00
# include "audio.h"
2015-10-26 22:39:02 -04:00
# include "boxes/photosendbox.h"
# include "mainwidget.h"
# include "window.h"
# include "lang.h"
# include "boxes/confirmbox.h"
2014-05-30 12:53:19 +04:00
2015-09-29 21:44:31 +03:00
LocalImageLoaderPrivate : : LocalImageLoaderPrivate ( LocalImageLoader * loader , QThread * thread ) : QObject ( 0 )
2014-06-16 13:31:10 +04:00
, loader ( loader )
{
2014-05-30 12:53:19 +04: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 23:17:33 +03:00
QString file , filename , mime , stickerMime = qsl ( " image/webp " ) ;
2014-07-14 09:16:21 +04:00
int32 filesize = 0 ;
2014-05-30 12:53:19 +04:00
QImage img ;
QByteArray data ;
PeerId peer ;
2015-01-05 23:17:33 +03:00
uint64 id , thumbId = 0 ;
2015-05-29 21:52:43 +03:00
int32 duration = 0 ;
2015-01-05 23:17:33 +03:00
QString thumbExt = " jpg " ;
2015-10-26 22:39:02 -04:00
PrepareMediaType type ;
2014-12-23 02:11:37 +03:00
bool animated = false ;
2015-09-21 23:57:42 +03:00
bool broadcast = false ;
2014-10-17 23:14:42 +04:00
bool ctrlShiftEnter = false ;
2015-09-03 13:48:40 +03:00
MsgId replyTo ;
2014-05-30 12:53:19 +04: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 ;
2015-05-29 21:52:43 +03:00
duration = list . front ( ) . duration ;
2015-09-21 23:57:42 +03:00
broadcast = list . front ( ) . broadcast ;
2014-10-17 23:14:42 +04:00
ctrlShiftEnter = list . front ( ) . ctrlShiftEnter ;
2015-03-19 12:18:19 +03:00
replyTo = list . front ( ) . replyTo ;
2014-05-30 12:53:19 +04:00
}
if ( img . isNull ( ) ) {
if ( ! file . isEmpty ( ) ) {
QFileInfo info ( file ) ;
2015-10-26 22:39:02 -04:00
if ( type = = PrepareAuto ) {
2014-05-30 12:53:19 +04:00
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 ) {
2015-10-26 22:39:02 -04:00
type = PreparePhoto ;
2014-05-30 12:53:19 +04:00
break ;
}
}
}
2015-10-26 22:39:02 -04:00
if ( type = = PrepareAuto & & info . size ( ) < MaxUploadDocumentSize ) {
type = PrepareDocument ;
2014-05-30 12:53:19 +04:00
}
}
2015-10-26 22:39:02 -04:00
if ( type ! = PrepareAuto & & info . size ( ) < MaxUploadPhotoSize ) {
2015-01-05 23:17:33 +03:00
bool opaque = ( mime ! = stickerMime ) ;
img = App : : readImage ( file , 0 , opaque , & animated ) ;
2015-08-14 18:47:56 +03:00
if ( animated ) {
2015-10-26 22:39:02 -04:00
type = PrepareDocument ;
2015-08-14 18:47:56 +03:00
}
}
2015-10-26 22:39:02 -04:00
if ( type = = PrepareDocument ) {
2015-08-14 18:47:56 +03:00
mime = mimeTypeForFile ( info ) . name ( ) ;
2015-01-05 23:17:33 +03:00
}
2014-05-30 12:53:19 +04:00
filename = info . fileName ( ) ;
filesize = info . size ( ) ;
} else if ( ! data . isEmpty ( ) ) {
2015-10-26 22:39:02 -04:00
if ( type ! = PrepareAudio ) {
2015-05-29 21:52:43 +03:00
img = App : : readImage ( data , 0 , true , & animated ) ;
2015-10-26 22:39:02 -04:00
if ( type = = PrepareAuto ) {
2015-05-29 21:52:43 +03:00
if ( ! img . isNull ( ) & & data . size ( ) < MaxUploadPhotoSize ) {
2015-10-26 22:39:02 -04:00
type = PreparePhoto ;
2015-05-29 21:52:43 +03:00
} else if ( data . size ( ) < MaxUploadDocumentSize ) {
2015-10-26 22:39:02 -04:00
type = PrepareDocument ;
2015-05-29 21:52:43 +03:00
} else {
img = QImage ( ) ;
}
2014-05-30 12:53:19 +04:00
}
}
2015-01-03 02:44:34 +03:00
MimeType mimeType = mimeTypeForData ( data ) ;
2015-10-26 22:39:02 -04:00
if ( type = = PrepareDocument | | type = = PrepareAudio ) {
2014-05-30 12:53:19 +04:00
mime = mimeType . name ( ) ;
}
2015-01-05 23:17:33 +03:00
if ( mime = = " image/jpeg " ) {
filename = filedialogDefaultName ( qsl ( " image " ) , qsl ( " .jpg " ) , QString ( ) , true ) ;
2015-10-26 22:39:02 -04:00
} else if ( type = = PrepareAudio ) {
2015-05-29 21:52:43 +03:00
filename = filedialogDefaultName ( qsl ( " audio " ) , qsl ( " .ogg " ) , QString ( ) , true ) ;
mime = " audio/ogg " ;
2015-01-05 23:17:33 +03:00
} else {
QString ext ;
QStringList patterns = mimeType . globPatterns ( ) ;
if ( ! patterns . isEmpty ( ) ) {
ext = patterns . front ( ) . replace ( ' * ' , QString ( ) ) ;
}
2015-10-26 22:39:02 -04:00
filename = filedialogDefaultName ( ( type = = PrepareAudio ) ? qsl ( " audio " ) : qsl ( " doc " ) , ext , QString ( ) , true ) ;
2014-05-30 12:53:19 +04:00
}
filesize = data . size ( ) ;
}
} else {
2015-10-26 22:39:02 -04:00
if ( type = = PrepareDocument ) {
2014-08-11 13:03:45 +04:00
filename = filedialogDefaultName ( qsl ( " image " ) , qsl ( " .png " ) , QString ( ) , true ) ;
2015-01-03 02:44:34 +03:00
mime = mimeTypeForName ( " image/png " ) . name ( ) ;
2014-08-11 13:03:45 +04:00
data = QByteArray ( ) ;
{
QBuffer b ( & data ) ;
img . save ( & b , " PNG " ) ;
}
filesize = data . size ( ) ;
} else {
2015-01-05 23:17:33 +03: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 ;
}
2015-10-26 22:39:02 -04:00
type = PreparePhoto ;
2014-08-11 13:03:45 +04:00
filename = qsl ( " Untitled.jpg " ) ;
filesize = 0 ;
}
2014-05-30 12:53:19 +04:00
}
2015-10-26 22:39:02 -04:00
if ( ( img . isNull ( ) & & ( ( type ! = PrepareDocument & & type ! = PrepareAudio ) | | ! filesize ) ) | | type = = PrepareAuto | | ( img . isNull ( ) & & file . isEmpty ( ) & & data . isEmpty ( ) ) ) { // if could not decide what type
2014-05-30 12:53:19 +04: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-23 02:11:37 +03:00
QVector < MTPDocumentAttribute > attributes ( 1 , MTP_documentAttributeFilename ( MTP_string ( filename ) ) ) ;
2014-05-30 12:53:19 +04:00
MTPPhotoSize thumb ( MTP_photoSizeEmpty ( MTP_string ( " " ) ) ) ;
MTPPhoto photo ( MTP_photoEmpty ( MTP_long ( 0 ) ) ) ;
MTPDocument document ( MTP_documentEmpty ( MTP_long ( 0 ) ) ) ;
2015-05-29 21:52:43 +03:00
MTPAudio audio ( MTP_audioEmpty ( MTP_long ( 0 ) ) ) ;
2014-05-30 12:53:19 +04:00
2015-07-01 00:07:05 +03:00
bool isSong = false ;
2014-05-30 12:53:19 +04:00
QByteArray jpeg ;
2015-10-26 22:39:02 -04:00
if ( type = = PrepareDocument ) {
2015-07-17 22:30:24 +03:00
if ( mime = = qstr ( " audio/mp3 " ) | | mime = = qstr ( " audio/m4a " ) | | mime = = qstr ( " audio/aac " ) | | mime = = qstr ( " audio/ogg " ) | | mime = = qstr ( " audio/flac " ) | |
2015-07-01 00:07:05 +03:00
filename . endsWith ( qstr ( " .mp3 " ) , Qt : : CaseInsensitive ) | | filename . endsWith ( qstr ( " .m4a " ) , Qt : : CaseInsensitive ) | |
2015-07-17 22:30:24 +03:00
filename . endsWith ( qstr ( " .aac " ) , Qt : : CaseInsensitive ) | | filename . endsWith ( qstr ( " .ogg " ) , Qt : : CaseInsensitive ) | |
filename . endsWith ( qstr ( " .flac " ) , Qt : : CaseInsensitive ) ) {
2015-07-01 00:07:05 +03:00
QImage cover ;
QByteArray coverBytes , coverFormat ;
MTPDocumentAttribute audioAttribute = audioReadSongAttributes ( file , data , cover , coverBytes , coverFormat ) ;
if ( audioAttribute . type ( ) = = mtpc_documentAttributeAudio ) {
attributes . push_back ( audioAttribute ) ;
isSong = true ;
if ( ! cover . isNull ( ) ) { // cover to thumb
int32 cw = cover . width ( ) , ch = cover . height ( ) ;
if ( cw < 20 * ch & & ch < 20 * cw ) {
QPixmap full = ( cw > 90 | | ch > 90 ) ? QPixmap : : fromImage ( cover . scaled ( 90 , 90 , Qt : : KeepAspectRatio , Qt : : SmoothTransformation ) , Qt : : ColorOnly ) : QPixmap : : fromImage ( cover , Qt : : ColorOnly ) ;
{
QByteArray thumbFormat = " JPG " ;
int32 thumbQuality = 87 ;
QBuffer jpegBuffer ( & jpeg ) ;
full . save ( & jpegBuffer , thumbFormat , thumbQuality ) ;
}
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 ) ) ;
thumbId = MTP : : nonce < uint64 > ( ) ;
}
}
}
}
}
2015-10-26 22:39:02 -04:00
if ( type = = PreparePhoto ) {
2014-05-30 12:53:19 +04:00
int32 w = img . width ( ) , h = img . height ( ) ;
2014-12-23 02:11:37 +03: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 12:53:19 +04: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-23 02:11:37 +03: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 15:19:32 +04: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-23 02:11:37 +03: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 19:27:03 +03: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 12:53:19 +04:00
{
QBuffer jpegBuffer ( & jpeg ) ;
2014-12-12 19:27:03 +03:00
full . save ( & jpegBuffer , " JPG " , 77 ) ;
2014-05-30 12:53:19 +04:00
}
if ( ! filesize ) filesize = jpeg . size ( ) ;
2015-08-12 21:01:32 +03:00
photo = MTP_photo ( MTP_long ( id ) , MTP_long ( 0 ) , MTP_int ( unixtime ( ) ) , MTP_vector < MTPPhotoSize > ( photoSizes ) ) ;
2014-05-30 12:53:19 +04:00
2015-01-05 23:17:33 +03:00
thumbId = id ;
2015-10-26 22:39:02 -04:00
} else if ( ( type = = PrepareVideo | | type = = PrepareDocument ) & & ! img . isNull ( ) & & ! isSong ) {
2014-05-30 12:53:19 +04:00
int32 w = img . width ( ) , h = img . height ( ) ;
2015-01-02 17:55:24 +03:00
QByteArray thumbFormat = " JPG " ;
2015-01-05 23:17:33 +03:00
int32 thumbQuality = 87 ;
2015-01-02 17:55:24 +03:00
if ( animated ) {
attributes . push_back ( MTP_documentAttributeAnimated ( ) ) ;
2015-01-05 23:17:33 +03:00
} else if ( mime = = stickerMime & & w > 0 & & h > 0 & & w < = StickerMaxSize & & h < = StickerMaxSize & & filesize < StickerInMemory ) {
2015-05-11 15:44:27 +03:00
attributes . push_back ( MTP_documentAttributeSticker ( MTP_string ( " " ) , MTP_inputStickerSetEmpty ( ) ) ) ;
2015-01-02 17:55:24 +03:00
thumbFormat = " webp " ;
2015-01-05 23:17:33 +03:00
thumbExt = qsl ( " webp " ) ;
2015-01-02 17:55:24 +03:00
}
2014-12-23 02:11:37 +03:00
attributes . push_back ( MTP_documentAttributeImageSize ( MTP_int ( w ) , MTP_int ( h ) ) ) ;
2015-01-05 23:17:33 +03: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 12:53:19 +04:00
2015-01-05 23:17:33 +03:00
{
QBuffer jpegBuffer ( & jpeg ) ;
full . save ( & jpegBuffer , thumbFormat , thumbQuality ) ;
}
2014-05-30 12:53:19 +04:00
2015-01-05 23:17:33 +03: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 12:53:19 +04:00
2015-01-05 23:17:33 +03:00
thumbId = MTP : : nonce < uint64 > ( ) ;
}
2014-05-30 12:53:19 +04:00
}
2015-10-26 22:39:02 -04:00
if ( type = = PrepareDocument ) {
2014-12-23 02:11:37 +03: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 ) ) ;
2015-10-26 22:39:02 -04:00
} else if ( type = = PrepareAudio ) {
2015-08-12 21:01:32 +03:00
audio = MTP_audio ( MTP_long ( id ) , MTP_long ( 0 ) , MTP_int ( unixtime ( ) ) , MTP_int ( duration ) , MTP_string ( mime ) , MTP_int ( filesize ) , MTP_int ( MTP : : maindc ( ) ) ) ;
2014-05-30 12:53:19 +04:00
}
{
QMutexLocker lock ( loader - > readyMutex ( ) ) ;
2015-09-21 23:57:42 +03:00
loader - > readyList ( ) . push_back ( ReadyLocalMedia ( type , file , filename , filesize , data , id , thumbId , thumbExt , peer , photo , audio , photoThumbs , document , jpeg , broadcast , ctrlShiftEnter , replyTo ) ) ;
2014-05-30 12:53:19 +04: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 ) {
}
2015-10-26 22:39:02 -04:00
void LocalImageLoader : : append ( const QStringList & files , const PeerId & peer , bool broadcast , MsgId replyTo , PrepareMediaType t ) {
2014-05-30 12:53:19 +04:00
{
QMutexLocker lock ( toPrepareMutex ( ) ) ;
for ( QStringList : : const_iterator i = files . cbegin ( ) , e = files . cend ( ) ; i ! = e ; + + i ) {
2015-09-21 23:57:42 +03:00
toPrepare . push_back ( ToPrepareMedia ( * i , peer , t , broadcast , false , replyTo ) ) ;
2014-05-30 12:53:19 +04:00
}
}
if ( ! thread ) {
thread = new QThread ( ) ;
2015-09-29 21:44:31 +03:00
priv = new LocalImageLoaderPrivate ( this , thread ) ;
2014-05-30 12:53:19 +04:00
thread - > start ( ) ;
}
emit needToPrepare ( ) ;
}
2015-10-26 22:39:02 -04:00
PhotoId LocalImageLoader : : append ( const QByteArray & img , const PeerId & peer , bool broadcast , MsgId replyTo , PrepareMediaType t ) {
2014-05-30 12:53:19 +04:00
PhotoId result = 0 ;
{
QMutexLocker lock ( toPrepareMutex ( ) ) ;
2015-09-21 23:57:42 +03:00
toPrepare . push_back ( ToPrepareMedia ( img , peer , t , broadcast , false , replyTo ) ) ;
2014-05-30 12:53:19 +04:00
result = toPrepare . back ( ) . id ;
}
if ( ! thread ) {
thread = new QThread ( ) ;
2015-09-29 21:44:31 +03:00
priv = new LocalImageLoaderPrivate ( this , thread ) ;
2014-05-30 12:53:19 +04:00
thread - > start ( ) ;
}
emit needToPrepare ( ) ;
return result ;
}
2015-10-26 22:39:02 -04:00
AudioId LocalImageLoader : : append ( const QByteArray & audio , int32 duration , const PeerId & peer , bool broadcast , MsgId replyTo , PrepareMediaType t ) {
2015-05-29 21:52:43 +03:00
AudioId result = 0 ;
{
QMutexLocker lock ( toPrepareMutex ( ) ) ;
2015-09-21 23:57:42 +03:00
toPrepare . push_back ( ToPrepareMedia ( audio , duration , peer , t , broadcast , false , replyTo ) ) ;
2015-05-29 21:52:43 +03:00
result = toPrepare . back ( ) . id ;
}
if ( ! thread ) {
thread = new QThread ( ) ;
2015-09-29 21:44:31 +03:00
priv = new LocalImageLoaderPrivate ( this , thread ) ;
2015-05-29 21:52:43 +03:00
thread - > start ( ) ;
}
emit needToPrepare ( ) ;
return result ;
}
2015-10-26 22:39:02 -04:00
PhotoId LocalImageLoader : : append ( const QImage & img , const PeerId & peer , bool broadcast , MsgId replyTo , PrepareMediaType t , bool ctrlShiftEnter ) {
2014-05-30 12:53:19 +04:00
PhotoId result = 0 ;
{
QMutexLocker lock ( toPrepareMutex ( ) ) ;
2015-09-21 23:57:42 +03:00
toPrepare . push_back ( ToPrepareMedia ( img , peer , t , broadcast , ctrlShiftEnter , replyTo ) ) ;
2014-05-30 12:53:19 +04:00
result = toPrepare . back ( ) . id ;
}
if ( ! thread ) {
thread = new QThread ( ) ;
2015-09-29 21:44:31 +03:00
priv = new LocalImageLoaderPrivate ( this , thread ) ;
2014-05-30 12:53:19 +04:00
thread - > start ( ) ;
}
emit needToPrepare ( ) ;
return result ;
}
2015-10-26 22:39:02 -04:00
PhotoId LocalImageLoader : : append ( const QString & file , const PeerId & peer , bool broadcast , MsgId replyTo , PrepareMediaType t ) {
2014-05-30 12:53:19 +04:00
PhotoId result = 0 ;
{
QMutexLocker lock ( toPrepareMutex ( ) ) ;
2015-09-21 23:57:42 +03:00
toPrepare . push_back ( ToPrepareMedia ( file , peer , t , broadcast , false , replyTo ) ) ;
2014-05-30 12:53:19 +04:00
result = toPrepare . back ( ) . id ;
}
if ( ! thread ) {
thread = new QThread ( ) ;
2015-09-29 21:44:31 +03:00
priv = new LocalImageLoaderPrivate ( this , thread ) ;
2014-05-30 12:53:19 +04:00
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 ;
}
2015-09-29 21:44:31 +03:00
2015-09-29 21:59:26 +03:00
TaskQueue : : TaskQueue ( QObject * parent , int32 stopTimeoutMs ) : QObject ( parent ) , _thread ( 0 ) , _worker ( 0 ) , _stopTimer ( 0 ) {
2015-09-29 21:44:31 +03:00
if ( stopTimeoutMs > 0 ) {
_stopTimer = new QTimer ( this ) ;
connect ( _stopTimer , SIGNAL ( timeout ( ) ) , this , SLOT ( stop ( ) ) ) ;
_stopTimer - > setSingleShot ( true ) ;
_stopTimer - > setInterval ( stopTimeoutMs ) ;
}
}
TaskId TaskQueue : : addTask ( TaskPtr task ) {
{
QMutexLocker lock ( & _tasksToProcessMutex ) ;
_tasksToProcess . push_back ( task ) ;
}
if ( ! _thread ) {
_thread = new QThread ( ) ;
_worker = new TaskQueueWorker ( this ) ;
_worker - > moveToThread ( _thread ) ;
connect ( this , SIGNAL ( taskAdded ( ) ) , _worker , SLOT ( onTaskAdded ( ) ) ) ;
connect ( _worker , SIGNAL ( taskProcessed ( ) ) , this , SLOT ( onTaskProcessed ( ) ) ) ;
_thread - > start ( ) ;
}
if ( _stopTimer ) _stopTimer - > stop ( ) ;
emit taskAdded ( ) ;
return task - > id ( ) ;
}
void TaskQueue : : cancelTask ( TaskId id ) {
QMutexLocker lock ( & _tasksToProcessMutex ) ;
for ( int32 i = 0 , l = _tasksToProcess . size ( ) ; i < l ; + + i ) {
if ( _tasksToProcess . at ( i ) - > id ( ) = = id ) {
_tasksToProcess . removeAt ( i ) ;
break ;
}
}
}
void TaskQueue : : onTaskProcessed ( ) {
do {
TaskPtr task ;
{
QMutexLocker lock ( & _tasksToFinishMutex ) ;
if ( _tasksToFinish . isEmpty ( ) ) break ;
task = _tasksToFinish . front ( ) ;
_tasksToFinish . pop_front ( ) ;
}
task - > finish ( ) ;
} while ( true ) ;
if ( _stopTimer ) {
QMutexLocker lock ( & _tasksToProcessMutex ) ;
if ( _tasksToProcess . isEmpty ( ) ) {
_stopTimer - > start ( ) ;
}
}
}
void TaskQueue : : stop ( ) {
if ( _thread ) {
_thread - > requestInterruption ( ) ;
_thread - > quit ( ) ;
_thread - > wait ( ) ;
delete _worker ;
delete _thread ;
_worker = 0 ;
_thread = 0 ;
}
_tasksToProcess . clear ( ) ;
_tasksToFinish . clear ( ) ;
}
TaskQueue : : ~ TaskQueue ( ) {
stop ( ) ;
delete _stopTimer ;
}
void TaskQueueWorker : : onTaskAdded ( ) {
if ( _inTaskAdded ) return ;
_inTaskAdded = true ;
bool someTasksLeft = false ;
do {
TaskPtr task ;
{
QMutexLocker lock ( & _queue - > _tasksToProcessMutex ) ;
if ( ! _queue - > _tasksToProcess . isEmpty ( ) ) {
task = _queue - > _tasksToProcess . front ( ) ;
}
}
if ( task ) {
task - > process ( ) ;
bool emitTaskProcessed = false ;
{
QMutexLocker lockToProcess ( & _queue - > _tasksToProcessMutex ) ;
if ( ! _queue - > _tasksToProcess . isEmpty ( ) & & _queue - > _tasksToProcess . front ( ) = = task ) {
_queue - > _tasksToProcess . pop_front ( ) ;
someTasksLeft = ! _queue - > _tasksToProcess . isEmpty ( ) ;
QMutexLocker lockToFinish ( & _queue - > _tasksToFinishMutex ) ;
emitTaskProcessed = _queue - > _tasksToFinish . isEmpty ( ) ;
_queue - > _tasksToFinish . push_back ( task ) ;
}
}
if ( emitTaskProcessed ) {
emit taskProcessed ( ) ;
}
}
QCoreApplication : : processEvents ( ) ;
} while ( someTasksLeft & & ! thread ( ) - > isInterruptionRequested ( ) ) ;
_inTaskAdded = false ;
}
2015-10-26 22:39:02 -04:00
FileLoadTask : : FileLoadTask ( const QString & filepath , PrepareMediaType type , const FileLoadTo & to , FileLoadForceConfirmType confirm ) : _id ( MTP : : nonce < uint64 > ( ) )
, _to ( to )
, _filepath ( filepath )
, _duration ( 0 )
, _type ( type )
, _confirm ( confirm )
, _result ( 0 ) {
}
FileLoadTask : : FileLoadTask ( const QByteArray & content , PrepareMediaType type , const FileLoadTo & to ) : _id ( MTP : : nonce < uint64 > ( ) )
, _to ( to )
, _content ( content )
, _duration ( 0 )
, _type ( type )
, _confirm ( FileLoadNoForceConfirm )
, _result ( 0 ) {
}
FileLoadTask : : FileLoadTask ( const QImage & image , const FileLoadTo & to , FileLoadForceConfirmType confirm , const QString & originalText ) : _id ( MTP : : nonce < uint64 > ( ) )
, _to ( to )
, _image ( image )
, _duration ( 0 )
, _type ( PrepareAuto )
, _confirm ( confirm )
, _originalText ( originalText )
, _result ( 0 ) {
}
FileLoadTask : : FileLoadTask ( const QByteArray & audio , int32 duration , const FileLoadTo & to ) : _id ( MTP : : nonce < uint64 > ( ) )
, _to ( to )
, _content ( audio )
, _duration ( duration )
, _type ( PrepareAudio )
, _confirm ( FileLoadNoForceConfirm )
, _result ( 0 ) {
}
void FileLoadTask : : process ( ) {
const QString stickerMime = qsl ( " image/webp " ) ;
_result = FileLoadResultPtr ( new FileLoadResult ( _id , _to ) ) ;
QString filename , filemime ;
qint64 filesize = 0 ;
QByteArray filedata ;
uint64 thumbId = 0 ;
QString thumbname = " thumb.jpg " ;
QByteArray thumbdata ;
bool animated = false ;
QImage fullimage = _image ;
if ( ! _filepath . isEmpty ( ) ) {
QFileInfo info ( _filepath ) ;
filesize = info . size ( ) ;
filemime = mimeTypeForFile ( info ) . name ( ) ;
filename = info . fileName ( ) ;
if ( filesize < = MaxUploadPhotoSize & & _type ! = PrepareAudio ) {
bool opaque = ( filemime ! = stickerMime ) ;
fullimage = App : : readImage ( _filepath , 0 , opaque , & animated ) ;
}
} else if ( ! _content . isEmpty ( ) ) {
filesize = _content . size ( ) ;
MimeType mimeType = mimeTypeForData ( _content ) ;
filemime = mimeType . name ( ) ;
if ( filesize < = MaxUploadPhotoSize & & _type ! = PrepareAudio ) {
bool opaque = ( filemime ! = stickerMime ) ;
fullimage = App : : readImage ( _content , 0 , opaque , & animated ) ;
}
if ( filemime = = " image/jpeg " ) {
filename = filedialogDefaultName ( qsl ( " image " ) , qsl ( " .jpg " ) , QString ( ) , true ) ;
} else if ( _type = = PrepareAudio ) {
filename = filedialogDefaultName ( qsl ( " audio " ) , qsl ( " .ogg " ) , QString ( ) , true ) ;
filemime = " audio/ogg " ;
} else {
QString ext ;
QStringList patterns = mimeType . globPatterns ( ) ;
if ( ! patterns . isEmpty ( ) ) {
ext = patterns . front ( ) . replace ( ' * ' , QString ( ) ) ;
}
filename = filedialogDefaultName ( qsl ( " file " ) , ext , QString ( ) , true ) ;
}
} else if ( ! _image . isNull ( ) ) {
_image = QImage ( ) ;
filemime = mimeTypeForName ( " image/png " ) . name ( ) ;
filename = filedialogDefaultName ( qsl ( " image " ) , qsl ( " .png " ) , QString ( ) , true ) ;
{
QBuffer buffer ( & _content ) ;
fullimage . save ( & buffer , " PNG " ) ;
}
filesize = _content . size ( ) ;
if ( fullimage . hasAlphaChannel ( ) ) {
QImage solid ( fullimage . width ( ) , fullimage . height ( ) , QImage : : Format_ARGB32_Premultiplied ) ;
solid . fill ( st : : white - > c ) ;
{
QPainter ( & solid ) . drawImage ( 0 , 0 , fullimage ) ;
}
fullimage = solid ;
}
}
_result - > filesize = ( int32 ) qMin ( filesize , qint64 ( INT_MAX ) ) ;
if ( ! filesize | | filesize > MaxUploadDocumentSize ) {
return ;
}
PreparedPhotoThumbs photoThumbs ;
QVector < MTPPhotoSize > photoSizes ;
QPixmap thumb ;
QVector < MTPDocumentAttribute > attributes ( 1 , MTP_documentAttributeFilename ( MTP_string ( filename ) ) ) ;
MTPPhotoSize thumbSize ( MTP_photoSizeEmpty ( MTP_string ( " " ) ) ) ;
MTPPhoto photo ( MTP_photoEmpty ( MTP_long ( 0 ) ) ) ;
MTPDocument document ( MTP_documentEmpty ( MTP_long ( 0 ) ) ) ;
MTPAudio audio ( MTP_audioEmpty ( MTP_long ( 0 ) ) ) ;
bool song = false ;
if ( _type ! = PrepareAudio ) {
if ( filemime = = qstr ( " audio/mp3 " ) | | filemime = = qstr ( " audio/m4a " ) | | filemime = = qstr ( " audio/aac " ) | | filemime = = qstr ( " audio/ogg " ) | | filemime = = qstr ( " audio/flac " ) | |
filename . endsWith ( qstr ( " .mp3 " ) , Qt : : CaseInsensitive ) | | filename . endsWith ( qstr ( " .m4a " ) , Qt : : CaseInsensitive ) | |
filename . endsWith ( qstr ( " .aac " ) , Qt : : CaseInsensitive ) | | filename . endsWith ( qstr ( " .ogg " ) , Qt : : CaseInsensitive ) | |
filename . endsWith ( qstr ( " .flac " ) , Qt : : CaseInsensitive ) ) {
QImage cover ;
QByteArray coverBytes , coverFormat ;
MTPDocumentAttribute audioAttribute = audioReadSongAttributes ( _filepath , _content , cover , coverBytes , coverFormat ) ;
if ( audioAttribute . type ( ) = = mtpc_documentAttributeAudio ) {
attributes . push_back ( audioAttribute ) ;
song = true ;
if ( ! cover . isNull ( ) ) { // cover to thumb
int32 cw = cover . width ( ) , ch = cover . height ( ) ;
if ( cw < 20 * ch & & ch < 20 * cw ) {
QPixmap full = ( cw > 90 | | ch > 90 ) ? QPixmap : : fromImage ( cover . scaled ( 90 , 90 , Qt : : KeepAspectRatio , Qt : : SmoothTransformation ) , Qt : : ColorOnly ) : QPixmap : : fromImage ( cover , Qt : : ColorOnly ) ;
{
QByteArray thumbFormat = " JPG " ;
int32 thumbQuality = 87 ;
QBuffer buffer ( & thumbdata ) ;
full . save ( & buffer , thumbFormat , thumbQuality ) ;
}
thumb = full ;
thumbSize = 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 ) ) ;
thumbId = MTP : : nonce < uint64 > ( ) ;
}
}
}
}
}
if ( ! fullimage . isNull ( ) & & fullimage . width ( ) > 0 & & ! song ) {
int32 w = fullimage . width ( ) , h = fullimage . height ( ) ;
attributes . push_back ( MTP_documentAttributeImageSize ( MTP_int ( w ) , MTP_int ( h ) ) ) ;
if ( w < 20 * h & & h < 20 * w ) {
if ( animated ) {
attributes . push_back ( MTP_documentAttributeAnimated ( ) ) ;
} else {
QPixmap thumb = ( w > 100 | | h > 100 ) ? QPixmap : : fromImage ( fullimage . scaled ( 100 , 100 , Qt : : KeepAspectRatio , Qt : : SmoothTransformation ) , Qt : : ColorOnly ) : QPixmap : : fromImage ( fullimage ) ;
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 ) ) ) ;
QPixmap medium = ( w > 320 | | h > 320 ) ? QPixmap : : fromImage ( fullimage . scaled ( 320 , 320 , Qt : : KeepAspectRatio , Qt : : SmoothTransformation ) , Qt : : ColorOnly ) : QPixmap : : fromImage ( fullimage ) ;
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 ) ) ) ;
QPixmap full = ( w > 1280 | | h > 1280 ) ? QPixmap : : fromImage ( fullimage . scaled ( 1280 , 1280 , Qt : : KeepAspectRatio , Qt : : SmoothTransformation ) , Qt : : ColorOnly ) : QPixmap : : fromImage ( fullimage ) ;
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 ) ) ) ;
{
QBuffer buffer ( & filedata ) ;
full . save ( & buffer , " JPG " , 77 ) ;
}
photo = MTP_photo ( MTP_long ( _id ) , MTP_long ( 0 ) , MTP_int ( unixtime ( ) ) , MTP_vector < MTPPhotoSize > ( photoSizes ) ) ;
}
QByteArray thumbFormat = " JPG " ;
int32 thumbQuality = 87 ;
if ( ! animated & & filemime = = stickerMime & & w > 0 & & h > 0 & & w < = StickerMaxSize & & h < = StickerMaxSize & & filesize < StickerInMemory ) {
attributes . push_back ( MTP_documentAttributeSticker ( MTP_string ( " " ) , MTP_inputStickerSetEmpty ( ) ) ) ;
thumbFormat = " webp " ;
thumbname = qsl ( " thumb.webp " ) ;
}
QPixmap full = ( w > 90 | | h > 90 ) ? QPixmap : : fromImage ( fullimage . scaled ( 90 , 90 , Qt : : KeepAspectRatio , Qt : : SmoothTransformation ) , Qt : : ColorOnly ) : QPixmap : : fromImage ( fullimage , Qt : : ColorOnly ) ;
{
QBuffer buffer ( & thumbdata ) ;
full . save ( & buffer , thumbFormat , thumbQuality ) ;
}
thumb = full ;
thumbSize = 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 ) ) ;
thumbId = MTP : : nonce < uint64 > ( ) ;
}
}
if ( _type = = PrepareAudio ) {
audio = MTP_audio ( MTP_long ( _id ) , MTP_long ( 0 ) , MTP_int ( unixtime ( ) ) , MTP_int ( _duration ) , MTP_string ( filemime ) , MTP_int ( filesize ) , MTP_int ( MTP : : maindc ( ) ) ) ;
} else {
document = MTP_document ( MTP_long ( _id ) , MTP_long ( 0 ) , MTP_int ( unixtime ( ) ) , MTP_string ( filemime ) , MTP_int ( filesize ) , thumbSize , MTP_int ( MTP : : maindc ( ) ) , MTP_vector < MTPDocumentAttribute > ( attributes ) ) ;
if ( photo . type ( ) = = mtpc_photoEmpty ) {
_type = PrepareDocument ;
}
}
_result - > type = _type ;
_result - > filepath = _filepath ;
_result - > content = _content ;
_result - > filename = filename ;
_result - > setFileData ( filedata ) ;
_result - > thumbId = thumbId ;
_result - > thumbname = thumbname ;
_result - > setThumbData ( thumbdata ) ;
_result - > thumb = thumb ;
_result - > photo = photo ;
_result - > audio = audio ;
_result - > document = document ;
_result - > photoThumbs = photoThumbs ;
}
void FileLoadTask : : finish ( ) {
if ( ! _result | | ! _result - > filesize ) {
if ( _result ) App : : main ( ) - > onSendFileCancel ( _result ) ;
App : : wnd ( ) - > replaceLayer ( new InformBox ( lang ( lng_send_image_empty ) ) ) ;
return ;
}
if ( _result - > filesize > MaxUploadDocumentSize ) {
App : : main ( ) - > onSendFileCancel ( _result ) ;
App : : wnd ( ) - > replaceLayer ( new InformBox ( lang ( lng_send_image_too_large ) ) ) ;
return ;
}
if ( App : : main ( ) ) {
bool confirm = ( _confirm = = FileLoadAlwaysConfirm ) | | ( _result - > photo . type ( ) ! = mtpc_photoEmpty & & _confirm ! = FileLoadNeverConfirm ) ;
if ( confirm ) {
App : : wnd ( ) - > showLayerLast ( new PhotoSendBox ( _result ) ) ;
} else {
App : : main ( ) - > onSendFileConfirm ( _result , false ) ;
}
}
}