From 58de461c198722582fddee30c6ba30fb3bc02ffd Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 29 Sep 2015 21:44:31 +0300 Subject: [PATCH] other thread loads local images --- Telegram/SourceFiles/dialogswidget.cpp | 1 + Telegram/SourceFiles/gui/images.cpp | 9 +- Telegram/SourceFiles/gui/images.h | 49 ------- Telegram/SourceFiles/localimageloader.cpp | 132 +++++++++++++++++- Telegram/SourceFiles/localimageloader.h | 76 +++++++++- Telegram/SourceFiles/localstorage.cpp | 104 ++++++++++++-- Telegram/SourceFiles/localstorage.h | 8 +- .../SourceFiles/mtproto/mtpFileLoader.cpp | 83 ++++++++--- Telegram/SourceFiles/mtproto/mtpFileLoader.h | 77 +++++++++- 9 files changed, 437 insertions(+), 102 deletions(-) diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index 1b613afd71..830a3a39b5 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -1538,6 +1538,7 @@ DialogsWidget::DialogsWidget(MainWidget *parent) : QWidget(parent) connect(&_addContact, SIGNAL(clicked()), this, SLOT(onAddContact())); connect(&_newGroup, SIGNAL(clicked()), this, SLOT(onNewGroup())); connect(&_cancelSearch, SIGNAL(clicked()), this, SLOT(onCancelSearch())); + connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update())); _chooseByDragTimer.setSingleShot(true); connect(&_chooseByDragTimer, SIGNAL(timeout()), this, SLOT(onChooseByDrag())); diff --git a/Telegram/SourceFiles/gui/images.cpp b/Telegram/SourceFiles/gui/images.cpp index f8fbfca8a5..67a704400a 100644 --- a/Telegram/SourceFiles/gui/images.cpp +++ b/Telegram/SourceFiles/gui/images.cpp @@ -607,17 +607,12 @@ int32 StorageImage::height() const { bool StorageImage::check() const { if (loader->done()) { - switch (loader->fileType()) { - case mtpc_storage_fileGif: format = "GIF"; break; - case mtpc_storage_fileJpeg: format = "JPG"; break; - case mtpc_storage_filePng: format = "PNG"; break; - default: format = QByteArray(); break; - } if (!data.isNull()) { globalAquiredSize -= int64(data.width()) * data.height() * 4; } + format = loader->imageFormat(); + data = loader->imagePixmap(); QByteArray bytes = loader->bytes(); - data = QPixmap::fromImage(App::readImage(bytes, &format, false), Qt::ColorOnly); if (!data.isNull()) { globalAquiredSize += int64(data.width()) * data.height() * 4; } diff --git a/Telegram/SourceFiles/gui/images.h b/Telegram/SourceFiles/gui/images.h index 359d7b318d..34df39d390 100644 --- a/Telegram/SourceFiles/gui/images.h +++ b/Telegram/SourceFiles/gui/images.h @@ -158,55 +158,6 @@ inline StorageKey storageKey(const StorageImageLocation &location) { return storageKey(location.dc, location.volume, location.local); } -enum StorageFileType { - StorageFileUnknown = 0xaa963b05, // mtpc_storage_fileUnknown - StorageFileJpeg = 0x7efe0e, // mtpc_storage_fileJpeg - StorageFileGif = 0xcae1aadf, // mtpc_storage_fileGif - StorageFilePng = 0xa4f63c0, // mtpc_storage_filePng - StorageFilePdf = 0xae1e508d, // mtpc_storage_filePdf - StorageFileMp3 = 0x528a0677, // mtpc_storage_fileMp3 - StorageFileMov = 0x4b09ebbc, // mtpc_storage_fileMov - StorageFilePartial = 0x40bc6f52, // mtpc_storage_filePartial - StorageFileMp4 = 0xb3cea0e4, // mtpc_storage_fileMp4 - StorageFileWebp = 0x1081464c, // mtpc_storage_fileWebp -}; -inline StorageFileType mtpToStorageType(mtpTypeId type) { - switch (type) { - case mtpc_storage_fileJpeg: return StorageFileJpeg; - case mtpc_storage_fileGif: return StorageFileGif; - case mtpc_storage_filePng: return StorageFilePng; - case mtpc_storage_filePdf: return StorageFilePdf; - case mtpc_storage_fileMp3: return StorageFileMp3; - case mtpc_storage_fileMov: return StorageFileMov; - case mtpc_storage_filePartial: return StorageFilePartial; - case mtpc_storage_fileMp4: return StorageFileMp4; - case mtpc_storage_fileWebp: return StorageFileWebp; - case mtpc_storage_fileUnknown: - default: return StorageFileUnknown; - } -} -inline mtpTypeId mtpFromStorageType(StorageFileType type) { - switch (type) { - case StorageFileGif: return mtpc_storage_fileGif; - case StorageFilePng: return mtpc_storage_filePng; - case StorageFilePdf: return mtpc_storage_filePdf; - case StorageFileMp3: return mtpc_storage_fileMp3; - case StorageFileMov: return mtpc_storage_fileMov; - case StorageFilePartial: return mtpc_storage_filePartial; - case StorageFileMp4: return mtpc_storage_fileMp4; - case StorageFileWebp: return mtpc_storage_fileWebp; - case StorageFileUnknown: - default: return mtpc_storage_fileUnknown; - } -} -struct StorageImageSaved { - StorageImageSaved() : type(StorageFileUnknown) { - } - StorageImageSaved(StorageFileType type, const QByteArray &data) : type(type), data(data) { - } - StorageFileType type; - QByteArray data; -}; class StorageImage : public Image { public: diff --git a/Telegram/SourceFiles/localimageloader.cpp b/Telegram/SourceFiles/localimageloader.cpp index 8dfe4324d4..9d5dfb6cc2 100644 --- a/Telegram/SourceFiles/localimageloader.cpp +++ b/Telegram/SourceFiles/localimageloader.cpp @@ -21,9 +21,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "audio.h" #include -LocalImageLoaderPrivate::LocalImageLoaderPrivate(int32 currentUser, LocalImageLoader *loader, QThread *thread) : QObject(0) +LocalImageLoaderPrivate::LocalImageLoaderPrivate(LocalImageLoader *loader, QThread *thread) : QObject(0) , loader(loader) - , user(currentUser) { moveToThread(thread); connect(loader, SIGNAL(needToPrepare()), this, SLOT(prepareImages())); @@ -295,7 +294,7 @@ void LocalImageLoader::append(const QStringList &files, const PeerId &peer, bool } if (!thread) { thread = new QThread(); - priv = new LocalImageLoaderPrivate(MTP::authedId(), this, thread); + priv = new LocalImageLoaderPrivate(this, thread); thread->start(); } emit needToPrepare(); @@ -310,7 +309,7 @@ PhotoId LocalImageLoader::append(const QByteArray &img, const PeerId &peer, bool } if (!thread) { thread = new QThread(); - priv = new LocalImageLoaderPrivate(MTP::authedId(), this, thread); + priv = new LocalImageLoaderPrivate(this, thread); thread->start(); } emit needToPrepare(); @@ -326,7 +325,7 @@ AudioId LocalImageLoader::append(const QByteArray &audio, int32 duration, const } if (!thread) { thread = new QThread(); - priv = new LocalImageLoaderPrivate(MTP::authedId(), this, thread); + priv = new LocalImageLoaderPrivate(this, thread); thread->start(); } emit needToPrepare(); @@ -342,7 +341,7 @@ PhotoId LocalImageLoader::append(const QImage &img, const PeerId &peer, bool bro } if (!thread) { thread = new QThread(); - priv = new LocalImageLoaderPrivate(MTP::authedId(), this, thread); + priv = new LocalImageLoaderPrivate(this, thread); thread->start(); } emit needToPrepare(); @@ -358,7 +357,7 @@ PhotoId LocalImageLoader::append(const QString &file, const PeerId &peer, bool b } if (!thread) { thread = new QThread(); - priv = new LocalImageLoaderPrivate(MTP::authedId(), this, thread); + priv = new LocalImageLoaderPrivate(this, thread); thread->start(); } emit needToPrepare(); @@ -413,3 +412,122 @@ LocalImageLoader::~LocalImageLoader() { delete priv; delete thread; } + + +TaskQueue::TaskQueue(QObject *parent, int32 stopTimeoutMs) : QObject(parent), _worker(0), _thread(0), _stopTimer(0) { + 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; +} diff --git a/Telegram/SourceFiles/localimageloader.h b/Telegram/SourceFiles/localimageloader.h index 7b179b6a1f..0adfc13c4f 100644 --- a/Telegram/SourceFiles/localimageloader.h +++ b/Telegram/SourceFiles/localimageloader.h @@ -87,7 +87,7 @@ class LocalImageLoaderPrivate : public QObject { public: - LocalImageLoaderPrivate(int32 currentUser, LocalImageLoader *loader, QThread *thread); + LocalImageLoaderPrivate(LocalImageLoader *loader, QThread *thread); ~LocalImageLoaderPrivate(); public slots: @@ -102,7 +102,6 @@ signals: private: LocalImageLoader *loader; - int32 user; }; @@ -146,3 +145,76 @@ private: LocalImageLoaderPrivate *priv; }; + +class Task { +public: + + virtual void process() = 0; // is executed in a separate thread + virtual void finish() = 0; // is executed in the same as TaskQueue thread + TaskId id() const { + return TaskId(this); + } + +}; +typedef QSharedPointer TaskPtr; + +class TaskQueue : public QObject { + Q_OBJECT + +public: + + TaskQueue(QObject *parent, int32 stopTimeoutMs = 0); // <= 0 - never stop worker + + TaskId addTask(TaskPtr task); + void cancelTask(TaskId id); // this task finish() won't be called + + template + TaskId addTask(DerivedTask *task) { + return addTask(TaskPtr(task)); + } + + ~TaskQueue(); + +signals: + + void taskAdded(); + +public slots: + + void onTaskProcessed(); + void stop(); + +private: + + typedef QList Tasks; + friend class TaskQueueWorker; + + Tasks _tasksToProcess, _tasksToFinish; + QMutex _tasksToProcessMutex, _tasksToFinishMutex; + QThread *_thread; + TaskQueueWorker *_worker; + QTimer *_stopTimer; + +}; + +class TaskQueueWorker : public QObject { + Q_OBJECT + +public: + + TaskQueueWorker(TaskQueue *queue) : _queue(queue), _inTaskAdded(false) { + } + +signals: + + void taskProcessed(); + +public slots: + + void onTaskAdded(); + +private: + TaskQueue *_queue; + bool _inTaskAdded; + +}; diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index d5b292a015..0aa38af556 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -70,6 +70,7 @@ namespace { bool _started = false; _local_inner::Manager *_manager = 0; + TaskQueue *_localLoader = 0; bool _working() { return _manager && !_basePath.isEmpty(); @@ -1823,6 +1824,7 @@ namespace Local { if (!_started) { _started = true; _manager = new _local_inner::Manager(); + _localLoader = new TaskQueue(0, 5000); } } @@ -1832,6 +1834,8 @@ namespace Local { _manager->finish(); _manager->deleteLater(); _manager = 0; + delete _localLoader; + _localLoader = 0; } } @@ -2001,6 +2005,9 @@ namespace Local { } void reset() { + if (_localLoader) { + _localLoader->stop(); + } _passKeySalt.clear(); // reset passcode, local key _draftsMap.clear(); _draftsPositionsMap.clear(); @@ -2272,12 +2279,75 @@ namespace Local { } } - bool startImageLoad(const StorageKey &location) { - StorageMap::iterator j = _imagesMap.find(location); - if (j == _imagesMap.cend()) { - return false; + class ImageLoadTask : public Task { + public: + + ImageLoadTask(const FileKey &key, const StorageKey &location, mtpFileLoader *loader) : + _key(key), _location(location), _loader(loader), _result(0) { } - return true; + void process() { + FileReadDescriptor image; + if (!readEncryptedFile(image, _key, UserPath)) { + return; + } + + QByteArray imageData; + quint64 locFirst, locSecond; + quint32 imageType; + image.stream >> locFirst >> locSecond >> imageType >> imageData; + + if (locFirst != _location.first || locSecond != _location.second) { + return; + } + + _result = new Result(StorageFileType(imageType), imageData); + } + void finish() { + if (_result) { + _loader->localLoaded(_result->image, _result->format, _result->pixmap); + } else { + StorageMap::iterator j = _imagesMap.find(_location); + if (j != _imagesMap.cend() && j->first == _key) { + clearKey(_key, UserPath); + _storageImagesSize -= j->second; + _imagesMap.erase(j); + } + _loader->localLoaded(StorageImageSaved()); + } + } + + private: + FileKey _key; + StorageKey _location; + struct Result { + Result(StorageFileType type, const QByteArray &data) : image(type, data) { + QByteArray guessFormat; + switch (type) { + case mtpc_storage_fileGif: guessFormat = "GIF"; break; + case mtpc_storage_fileJpeg: guessFormat = "JPG"; break; + case mtpc_storage_filePng: guessFormat = "PNG"; break; + default: guessFormat = QByteArray(); break; + } + pixmap = QPixmap::fromImage(App::readImage(data, &guessFormat, false), Qt::ColorOnly); + if (!pixmap.isNull()) { + format = guessFormat; + } + } + StorageImageSaved image; + QByteArray format; + QPixmap pixmap; + }; + mtpFileLoader *_loader; + Result *_result; + + }; + + TaskId startImageLoad(const StorageKey &location, mtpFileLoader *loader) { + StorageMap::iterator j = _imagesMap.find(location); + if (j == _imagesMap.cend() || !_localLoader) { + return 0; + } + return _localLoader->addTask(new ImageLoadTask(j->first, location, loader)); } StorageImageSaved readImage(const StorageKey &location) { @@ -2285,8 +2355,8 @@ namespace Local { if (j == _imagesMap.cend()) { return StorageImageSaved(); } - FileReadDescriptor draft; - if (!readEncryptedFile(draft, j.value().first, UserPath)) { + FileReadDescriptor image; + if (!readEncryptedFile(image, j.value().first, UserPath)) { clearKey(j.value().first, UserPath); _storageImagesSize -= j.value().second; _imagesMap.erase(j); @@ -2296,7 +2366,7 @@ namespace Local { QByteArray imageData; quint64 locFirst, locSecond; quint32 imageType; - draft.stream >> locFirst >> locSecond >> imageType >> imageData; + image.stream >> locFirst >> locSecond >> imageType >> imageData; return (locFirst == location.first && locSecond == location.second) ? StorageImageSaved(StorageFileType(imageType), imageData) : StorageImageSaved(); } @@ -2333,12 +2403,12 @@ namespace Local { } } - bool startStickerImageLoad(const StorageKey &location) { + TaskId startStickerImageLoad(const StorageKey &location, mtpFileLoader *loader) { StorageMap::iterator j = _stickerImagesMap.find(location); if (j == _stickerImagesMap.cend()) { - return false; + return 0; } - return true; + return 0; } QByteArray readStickerImage(const StorageKey &location) { @@ -2393,12 +2463,12 @@ namespace Local { } } - bool startAudioLoad(const StorageKey &location) { + TaskId startAudioLoad(const StorageKey &location, mtpFileLoader *loader) { StorageMap::iterator j = _audiosMap.find(location); if (j == _audiosMap.cend()) { - return false; + return 0; } - return true; + return 0; } QByteArray readAudio(const StorageKey &location) { @@ -2429,6 +2499,12 @@ namespace Local { return _storageAudiosSize; } + void cancelTask(TaskId id) { + if (_localLoader) { + _localLoader->cancelTask(id); + } + } + void _writeStorageImageLocation(QDataStream &stream, const StorageImageLocation &loc) { stream << qint32(loc.width) << qint32(loc.height); stream << qint32(loc.dc) << quint64(loc.volume) << qint32(loc.local) << quint64(loc.secret); diff --git a/Telegram/SourceFiles/localstorage.h b/Telegram/SourceFiles/localstorage.h index 24597790af..02361be3a7 100644 --- a/Telegram/SourceFiles/localstorage.h +++ b/Telegram/SourceFiles/localstorage.h @@ -120,23 +120,25 @@ namespace Local { void writeImage(const StorageKey &location, const ImagePtr &img); void writeImage(const StorageKey &location, const StorageImageSaved &jpeg, bool overwrite = true); - bool startImageLoad(const StorageKey &location); + TaskId startImageLoad(const StorageKey &location, mtpFileLoader *loader); StorageImageSaved readImage(const StorageKey &location); int32 hasImages(); qint64 storageImagesSize(); void writeStickerImage(const StorageKey &location, const QByteArray &data, bool overwrite = true); - bool startStickerImageLoad(const StorageKey &location); + TaskId startStickerImageLoad(const StorageKey &location, mtpFileLoader *loader); QByteArray readStickerImage(const StorageKey &location); int32 hasStickers(); qint64 storageStickersSize(); void writeAudio(const StorageKey &location, const QByteArray &data, bool overwrite = true); - bool startAudioLoad(const StorageKey &location); + TaskId startAudioLoad(const StorageKey &location, mtpFileLoader *loader); QByteArray readAudio(const StorageKey &location); int32 hasAudios(); qint64 storageAudiosSize(); + void cancelTask(TaskId id); + void writeStickers(); void readStickers(); diff --git a/Telegram/SourceFiles/mtproto/mtpFileLoader.cpp b/Telegram/SourceFiles/mtproto/mtpFileLoader.cpp index d325533c68..56c625dd5e 100644 --- a/Telegram/SourceFiles/mtproto/mtpFileLoader.cpp +++ b/Telegram/SourceFiles/mtproto/mtpFileLoader.cpp @@ -48,7 +48,7 @@ mtpFileLoader::mtpFileLoader(int32 dc, const uint64 &volume, int32 local, const priority(0), inQueue(false), complete(false), _localStatus(LocalNotTried), skippedBytes(0), nextRequestOffset(0), lastComplete(false), dc(dc), _locationType(UnknownFileLocation), volume(volume), local(local), secret(secret), -id(0), access(0), fileIsOpen(false), size(size), type(mtpc_storage_fileUnknown) { +id(0), access(0), fileIsOpen(false), size(size), type(mtpc_storage_fileUnknown), _localTaskId(0) { LoaderQueues::iterator i = queues.find(dc); if (i == queues.cend()) { i = queues.insert(dc, mtpFileLoaderQueue()); @@ -60,7 +60,7 @@ mtpFileLoader::mtpFileLoader(int32 dc, const uint64 &id, const uint64 &access, L priority(0), inQueue(false), complete(false), _localStatus(LocalNotTried), skippedBytes(0), nextRequestOffset(0), lastComplete(false), dc(dc), _locationType(type), volume(0), local(0), secret(0), -id(id), access(access), file(to), fname(to), fileIsOpen(false), duplicateInData(todata), size(size), type(mtpc_storage_fileUnknown) { +id(id), access(access), file(to), fname(to), fileIsOpen(false), duplicateInData(todata), size(size), type(mtpc_storage_fileUnknown), _localTaskId(0) { LoaderQueues::iterator i = queues.find(MTP::dld[0] + dc); if (i == queues.cend()) { i = queues.insert(MTP::dld[0] + dc, mtpFileLoaderQueue()); @@ -68,20 +68,32 @@ id(id), access(access), file(to), fname(to), fileIsOpen(false), duplicateInData( queue = &i.value(); } -QString mtpFileLoader::fileName() const { - return fname; +QByteArray mtpFileLoader::imageFormat() const { + if (_imageFormat.isEmpty() && _locationType == UnknownFileLocation) { + readImage(); + } + return _imageFormat; } -bool mtpFileLoader::done() const { - return complete; +QPixmap mtpFileLoader::imagePixmap() const { + if (_imagePixmap.isNull() && _locationType == UnknownFileLocation) { + readImage(); + } + return _imagePixmap; } -mtpTypeId mtpFileLoader::fileType() const { - return type; -} - -const QByteArray &mtpFileLoader::bytes() const { - return data; +void mtpFileLoader::readImage() const { + QByteArray format; + switch (type) { + case mtpc_storage_fileGif: format = "GIF"; break; + case mtpc_storage_fileJpeg: format = "JPG"; break; + case mtpc_storage_filePng: format = "PNG"; break; + default: format = QByteArray(); break; + } + _imagePixmap = QPixmap::fromImage(App::readImage(data, &format, false), Qt::ColorOnly); + if (!_imagePixmap.isNull()) { + _imageFormat = format; + } } float64 mtpFileLoader::currentProgress() const { @@ -314,10 +326,10 @@ bool mtpFileLoader::tryLoadLocal() { } if (_locationType == UnknownFileLocation) { - StorageImageSaved cached = Local::readImage(storageKey(dc, volume, local)); - if (cached.type != StorageFileUnknown) { - data = cached.data; - type = mtpFromStorageType(cached.type); + _localTaskId = Local::startImageLoad(storageKey(dc, volume, local), this); + if (_localTaskId) { + _localStatus = LocalLoading; + return true; } } else { if (duplicateInData) { @@ -361,6 +373,42 @@ bool mtpFileLoader::tryLoadLocal() { return true; } +void mtpFileLoader::localLoaded(const StorageImageSaved &result, const QByteArray &imageFormat, const QPixmap &imagePixmap) { + _localTaskId = 0; + if (result.type == StorageFileUnknown) { + _localStatus = LocalFailed; + start(true); + return; + } + data = result.data; + type = mtpFromStorageType(result.type); + if (_locationType == UnknownFileLocation) { // photo + _imageFormat = imageFormat; + _imagePixmap = imagePixmap; + } + _localStatus = LocalLoaded; + if (!fname.isEmpty() && duplicateInData) { + if (!fileIsOpen) fileIsOpen = file.open(QIODevice::WriteOnly); + if (!fileIsOpen) { + finishFail(); + return; + } + if (file.write(data) != qint64(data.size())) { + finishFail(); + return; + } + } + complete = true; + if (fileIsOpen) { + file.close(); + fileIsOpen = false; + psPostprocessFile(QFileInfo(file).absoluteFilePath()); + } + emit App::wnd()->imageLoaded(); + emit progress(this); + loadNext(); +} + void mtpFileLoader::start(bool loadFirst, bool prior) { if (complete || tryLoadLocal()) return; @@ -502,6 +550,9 @@ void mtpFileLoader::started(bool loadFirst, bool prior) { } mtpFileLoader::~mtpFileLoader() { + if (_localTaskId) { + Local::cancelTask(_localTaskId); + } removeFromQueue(); cancelRequests(); } diff --git a/Telegram/SourceFiles/mtproto/mtpFileLoader.h b/Telegram/SourceFiles/mtproto/mtpFileLoader.h index 11236aef08..d0ec0aa0bf 100644 --- a/Telegram/SourceFiles/mtproto/mtpFileLoader.h +++ b/Telegram/SourceFiles/mtproto/mtpFileLoader.h @@ -45,6 +45,56 @@ inline mtpTypeId mtpFromLocationType(LocationType type) { } } +enum StorageFileType { + StorageFileUnknown = 0xaa963b05, // mtpc_storage_fileUnknown + StorageFileJpeg = 0x7efe0e, // mtpc_storage_fileJpeg + StorageFileGif = 0xcae1aadf, // mtpc_storage_fileGif + StorageFilePng = 0xa4f63c0, // mtpc_storage_filePng + StorageFilePdf = 0xae1e508d, // mtpc_storage_filePdf + StorageFileMp3 = 0x528a0677, // mtpc_storage_fileMp3 + StorageFileMov = 0x4b09ebbc, // mtpc_storage_fileMov + StorageFilePartial = 0x40bc6f52, // mtpc_storage_filePartial + StorageFileMp4 = 0xb3cea0e4, // mtpc_storage_fileMp4 + StorageFileWebp = 0x1081464c, // mtpc_storage_fileWebp +}; +inline StorageFileType mtpToStorageType(mtpTypeId type) { + switch (type) { + case mtpc_storage_fileJpeg: return StorageFileJpeg; + case mtpc_storage_fileGif: return StorageFileGif; + case mtpc_storage_filePng: return StorageFilePng; + case mtpc_storage_filePdf: return StorageFilePdf; + case mtpc_storage_fileMp3: return StorageFileMp3; + case mtpc_storage_fileMov: return StorageFileMov; + case mtpc_storage_filePartial: return StorageFilePartial; + case mtpc_storage_fileMp4: return StorageFileMp4; + case mtpc_storage_fileWebp: return StorageFileWebp; + case mtpc_storage_fileUnknown: + default: return StorageFileUnknown; + } +} +inline mtpTypeId mtpFromStorageType(StorageFileType type) { + switch (type) { + case StorageFileGif: return mtpc_storage_fileGif; + case StorageFilePng: return mtpc_storage_filePng; + case StorageFilePdf: return mtpc_storage_filePdf; + case StorageFileMp3: return mtpc_storage_fileMp3; + case StorageFileMov: return mtpc_storage_fileMov; + case StorageFilePartial: return mtpc_storage_filePartial; + case StorageFileMp4: return mtpc_storage_fileMp4; + case StorageFileWebp: return mtpc_storage_fileWebp; + case StorageFileUnknown: + default: return mtpc_storage_fileUnknown; + } +} +struct StorageImageSaved { + StorageImageSaved() : type(StorageFileUnknown) { + } + StorageImageSaved(StorageFileType type, const QByteArray &data) : type(type), data(data) { + } + StorageFileType type; + QByteArray data; +}; + enum LocalLoadStatus { LocalNotTried, LocalNotFound, @@ -53,6 +103,8 @@ enum LocalLoadStatus { LocalFailed, }; +typedef void *TaskId; // no interface, just id + struct mtpFileLoaderQueue; class mtpFileLoader : public QObject, public RPCSender { Q_OBJECT @@ -61,10 +113,20 @@ public: mtpFileLoader(int32 dc, const uint64 &volume, int32 local, const uint64 &secret, int32 size = 0); mtpFileLoader(int32 dc, const uint64 &id, const uint64 &access, LocationType type, const QString &to, int32 size, bool todata = false); - bool done() const; - mtpTypeId fileType() const; - const QByteArray &bytes() const; - QString fileName() const; + bool done() const { + return complete; + } + mtpTypeId fileType() const { + return type; + } + const QByteArray &bytes() const { + return data; + } + QByteArray imageFormat() const; + QPixmap imagePixmap() const; + QString fileName() const { + return fname; + } float64 currentProgress() const; int32 currentOffset(bool includeSkipped = false) const; int32 fullSize() const; @@ -80,6 +142,8 @@ public: ~mtpFileLoader(); + void localLoaded(const StorageImageSaved &result, const QByteArray &imageFormat = QByteArray(), const QPixmap &imagePixmap = QPixmap()); + mtpFileLoader *prev, *next; int32 priority; @@ -131,4 +195,9 @@ private: int32 size; mtpTypeId type; + TaskId _localTaskId; + mutable QByteArray _imageFormat; + mutable QPixmap _imagePixmap; + void readImage() const; + };