diff --git a/CHANGELOG.md b/CHANGELOG.md index 20f2ce411..b199a9081 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fetch remote provisioning from URI handler and with confirmation. - Emojis picker. - Text edit in chat can now understand rich texts. +- Create thumbnails into memory instead of disk. ### Removed - Picture zoom on mouse over. diff --git a/linphone-app/src/app/paths/Paths.cpp b/linphone-app/src/app/paths/Paths.cpp index 31d61ebbf..538e20954 100644 --- a/linphone-app/src/app/paths/Paths.cpp +++ b/linphone-app/src/app/paths/Paths.cpp @@ -298,9 +298,6 @@ string Paths::getRootCaFilePath () { return getReadableFilePath(getAppRootCaFilePath()); } -string Paths::getThumbnailsDirPath () { - return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + Constants::PathThumbnails); -} string Paths::getToolsDirPath () { return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + Constants::PathTools); } diff --git a/linphone-app/src/app/paths/Paths.hpp b/linphone-app/src/app/paths/Paths.hpp index f5bf5cfeb..ec675de6a 100644 --- a/linphone-app/src/app/paths/Paths.hpp +++ b/linphone-app/src/app/paths/Paths.hpp @@ -51,7 +51,6 @@ namespace Paths { std::string getPluginsAppDirPath (); QStringList getPluginsAppFolders(); std::string getRootCaFilePath (); - std::string getThumbnailsDirPath (); std::string getToolsDirPath (); std::string getUserCertificatesDirPath (); std::string getZrtpDataFilePath (); diff --git a/linphone-app/src/app/providers/ThumbnailProvider.cpp b/linphone-app/src/app/providers/ThumbnailProvider.cpp index ffe62e13b..dd0473f86 100644 --- a/linphone-app/src/app/providers/ThumbnailProvider.cpp +++ b/linphone-app/src/app/providers/ThumbnailProvider.cpp @@ -19,7 +19,7 @@ */ #include "app/paths/Paths.hpp" -#include "utils/Utils.hpp" +#include "components/other/images/ImageModel.hpp" #include "ThumbnailProvider.hpp" @@ -31,11 +31,10 @@ ThumbnailProvider::ThumbnailProvider () : QQuickImageProvider( QQmlImageProviderBase::Image, QQmlImageProviderBase::ForceAsynchronousImageLoading ) { - mThumbnailsPath = Utils::coreStringToAppString(Paths::getThumbnailsDirPath()); } QImage ThumbnailProvider::requestImage (const QString &id, QSize *size, const QSize &) { - QImage image(mThumbnailsPath + id); - *size = image.size(); - return image; + QImage image = ImageModel::createThumbnail(id); + *size = image.size(); + return image; } diff --git a/linphone-app/src/app/providers/ThumbnailProvider.hpp b/linphone-app/src/app/providers/ThumbnailProvider.hpp index 380dc6b0f..faa2fb59d 100644 --- a/linphone-app/src/app/providers/ThumbnailProvider.hpp +++ b/linphone-app/src/app/providers/ThumbnailProvider.hpp @@ -32,9 +32,6 @@ public: QImage requestImage (const QString &id, QSize *size, const QSize &requestedSize) override; static const QString ProviderId; - -private: - QString mThumbnailsPath; }; #endif // THUMBNAIL_PROVIDER_H_ diff --git a/linphone-app/src/components/chat-events/ChatMessageModel.cpp b/linphone-app/src/components/chat-events/ChatMessageModel.cpp index 3fd0b7f76..c70f10504 100644 --- a/linphone-app/src/components/chat-events/ChatMessageModel.cpp +++ b/linphone-app/src/components/chat-events/ChatMessageModel.cpp @@ -263,17 +263,8 @@ void ChatMessageModel::resendMessage (){ } void ChatMessageModel::deleteEvent(){ - if (mChatMessage && mChatMessage->getFileTransferInformation()) {// Remove thumbnail + if (mChatMessage && mChatMessage->getFileTransferInformation()) { mChatMessage->cancelFileTransfer(); - QString appdata = QString::fromStdString(mChatMessage->getAppdata()); - QStringList fields = appdata.split(':'); - - if(fields[0].size() > 0) { - QString thumbnailPath = QString::fromStdString(Paths::getThumbnailsDirPath()) + fields[0]; - if (!QFile::remove(thumbnailPath)) - qWarning() << QStringLiteral("Unable to remove `%1`.").arg(thumbnailPath); - } - mChatMessage->setAppdata("");// Remove completely Thumbnail from the message } if(mChatMessage) mChatMessage->getChatRoom()->deleteMessage(mChatMessage); diff --git a/linphone-app/src/components/chat-events/ChatMessageModel.hpp b/linphone-app/src/components/chat-events/ChatMessageModel.hpp index 7f82fe959..73db2f7af 100644 --- a/linphone-app/src/components/chat-events/ChatMessageModel.hpp +++ b/linphone-app/src/components/chat-events/ChatMessageModel.hpp @@ -26,19 +26,7 @@ #include // ============================================================================= -/* -class Thumbnail{ -public: - Thumbnail(); - QString mId; - QString mPath; - - QString toString()const; - void fromString(const QString& ); - static QString toString(const QVector& ); - static QVector fromListString(const QString& ); -}; -*/ + #include "components/chat-room/ChatRoomModel.hpp" #include "ChatEvent.hpp" #include "components/participant-imdn/ParticipantImdnStateListModel.hpp" @@ -58,7 +46,7 @@ public: ChatMessageModel (std::shared_ptr chatMessage, QObject * parent = nullptr); virtual ~ChatMessageModel(); - class AppDataManager{// Used to manage appdata to store persistant data like created thumbnails + class AppDataManager{// Used to manage appdata to store persistant data public: AppDataManager(const QString&); QMap mData;// Path / ID diff --git a/linphone-app/src/components/chat-room/ChatRoomModel.cpp b/linphone-app/src/components/chat-room/ChatRoomModel.cpp index 2972b8a78..38f0ee3aa 100644 --- a/linphone-app/src/components/chat-room/ChatRoomModel.cpp +++ b/linphone-app/src/components/chat-room/ChatRoomModel.cpp @@ -38,8 +38,6 @@ #include "ChatRoomListener.hpp" #include "app/App.hpp" -#include "app/paths/Paths.hpp" -#include "app/providers/ThumbnailProvider.hpp" #include "components/calls/CallsListModel.hpp" #include "components/chat/ChatModel.hpp" #include "components/chat-events/ChatCallModel.hpp" @@ -53,8 +51,6 @@ #include "components/content/ContentModel.hpp" #include "components/core/CoreHandlers.hpp" #include "components/core/CoreManager.hpp" -#include "components/notifier/Notifier.hpp" -#include "components/settings/AccountSettingsModel.hpp" #include "components/settings/SettingsModel.hpp" #include "components/participant/ParticipantModel.hpp" #include "components/participant/ParticipantListModel.hpp" @@ -64,9 +60,7 @@ #include "components/timeline/TimelineModel.hpp" #include "components/timeline/TimelineListModel.hpp" #include "components/core/event-count-notifier/AbstractEventCountNotifier.hpp" -#include "utils/QExifImageHeader.hpp" #include "utils/Utils.hpp" -#include "utils/Constants.hpp" #include "utils/LinphoneEnums.hpp" diff --git a/linphone-app/src/components/chat/ChatModel.cpp b/linphone-app/src/components/chat/ChatModel.cpp index 158eb0b5f..952caaf89 100644 --- a/linphone-app/src/components/chat/ChatModel.cpp +++ b/linphone-app/src/components/chat/ChatModel.cpp @@ -26,15 +26,7 @@ #include #include "app/App.hpp" -#include "app/paths/Paths.hpp" -#include "app/providers/ThumbnailProvider.hpp" - -#include "components/chat-events/ChatMessageModel.hpp" - -#include "utils/QExifImageHeader.hpp" -#include "utils/Utils.hpp" -#include "utils/Constants.hpp" -#include "components/Components.hpp" +#include "components/content/ContentListModel.hpp" // ============================================================================= diff --git a/linphone-app/src/components/conference/ConferenceModel.cpp b/linphone-app/src/components/conference/ConferenceModel.cpp index e09a7d510..993e95360 100644 --- a/linphone-app/src/components/conference/ConferenceModel.cpp +++ b/linphone-app/src/components/conference/ConferenceModel.cpp @@ -27,14 +27,9 @@ #include #include "app/App.hpp" -#include "app/paths/Paths.hpp" -#include "app/providers/ThumbnailProvider.hpp" - - -#include "utils/QExifImageHeader.hpp" +#include "components/participant/ParticipantListModel.hpp" #include "utils/Utils.hpp" -#include "utils/Constants.hpp" -#include "components/Components.hpp" + void ConferenceModel::connectTo(ConferenceListener * listener){ connect(listener, &ConferenceListener::activeSpeakerParticipantDevice, this, &ConferenceModel::onActiveSpeakerParticipantDevice); diff --git a/linphone-app/src/components/conferenceInfo/ConferenceInfoModel.cpp b/linphone-app/src/components/conferenceInfo/ConferenceInfoModel.cpp index 31bd46a09..61f60ad71 100644 --- a/linphone-app/src/components/conferenceInfo/ConferenceInfoModel.cpp +++ b/linphone-app/src/components/conferenceInfo/ConferenceInfoModel.cpp @@ -35,34 +35,12 @@ #include #include "app/App.hpp" -#include "app/paths/Paths.hpp" -#include "app/providers/ThumbnailProvider.hpp" -#include "components/calls/CallsListModel.hpp" -#include "components/chat-events/ChatCallModel.hpp" -#include "components/chat-events/ChatEvent.hpp" -#include "components/chat-events/ChatMessageModel.hpp" -#include "components/chat-events/ChatNoticeModel.hpp" -#include "components/contact/ContactModel.hpp" -#include "components/contact/VcardModel.hpp" -#include "components/contacts/ContactsListModel.hpp" #include "components/conferenceScheduler/ConferenceScheduler.hpp" -#include "components/core/CoreHandlers.hpp" #include "components/core/CoreManager.hpp" -#include "components/notifier/Notifier.hpp" #include "components/other/timeZone/TimeZoneModel.hpp" -#include "components/settings/AccountSettingsModel.hpp" -#include "components/settings/SettingsModel.hpp" -#include "components/participant/ParticipantModel.hpp" #include "components/participant/ParticipantListModel.hpp" -#include "components/presence/Presence.hpp" -#include "components/recorder/RecorderManager.hpp" -#include "components/recorder/RecorderModel.hpp" -#include "components/timeline/TimelineModel.hpp" #include "components/timeline/TimelineListModel.hpp" -#include "components/core/event-count-notifier/AbstractEventCountNotifier.hpp" -#include "utils/QExifImageHeader.hpp" #include "utils/Utils.hpp" -#include "utils/Constants.hpp" #include "utils/LinphoneEnums.hpp" diff --git a/linphone-app/src/components/content/ContentListModel.cpp b/linphone-app/src/components/content/ContentListModel.cpp index e86941bcb..fa8ebb1a6 100644 --- a/linphone-app/src/components/content/ContentListModel.cpp +++ b/linphone-app/src/components/content/ContentListModel.cpp @@ -88,7 +88,6 @@ void ContentListModel::remove(ContentModel * model){ int count = 0; for(auto it = mList.begin() ; it != mList.end() ; ++count, ++it) { if( it->get() == model) { - model->removeThumbnail(); removeRow(count, QModelIndex()); return; } @@ -96,10 +95,6 @@ void ContentListModel::remove(ContentModel * model){ } void ContentListModel::clear(){ -// Delete thumbnails - for(auto contentModel : mList){ - contentModel.objectCast()->removeThumbnail(); - } resetData(); } @@ -107,7 +102,6 @@ void ContentListModel::removeDownloadedFiles(){ for(auto model : mList){ auto contentModel = model.objectCast(); contentModel->removeDownloadedFile(); - contentModel->removeThumbnail(); } } diff --git a/linphone-app/src/components/content/ContentModel.cpp b/linphone-app/src/components/content/ContentModel.cpp index 995e9ab2c..3317dbaf6 100644 --- a/linphone-app/src/components/content/ContentModel.cpp +++ b/linphone-app/src/components/content/ContentModel.cpp @@ -22,20 +22,18 @@ #include #include -#include #include #include #include "app/App.hpp" -#include "app/paths/Paths.hpp" #include "app/providers/ThumbnailProvider.hpp" #include "components/chat-events/ChatMessageModel.hpp" +#include "components/conferenceInfo/ConferenceInfoModel.hpp" +#include "components/core/CoreManager.hpp" +#include "components/settings/SettingsModel.hpp" -#include "utils/QExifImageHeader.hpp" #include "utils/Utils.hpp" -#include "utils/Constants.hpp" -#include "components/Components.hpp" // ============================================================================= @@ -158,91 +156,13 @@ void ContentModel::createThumbnail (const bool& force) { if(force || isFile() || isFileEncrypted() || isFileTransfer()){ QString id; QString path = getFilePath(); - - auto appdata = ChatMessageModel::AppDataManager(mChatMessageModel ? QString::fromStdString(mChatMessageModel->getChatMessage()->getAppdata()) : ""); - - if(!appdata.mData.contains(path) - || !QFileInfo(QString::fromStdString(Paths::getThumbnailsDirPath())+appdata.mData[path]).isFile()){ - // File don't exist. Create the thumbnail - QImage originalImage(path); - - if( originalImage.isNull()){// Try to determine format from headers - QImageReader reader(path); - reader.setDecideFormatFromContent(true); - QByteArray format = reader.format(); - if(!format.isEmpty()) - originalImage = QImage(path, format); - } - if (!originalImage.isNull()){ - int rotation = 0; - QExifImageHeader exifImageHeader; - if (exifImageHeader.loadFromJpeg(path)) - rotation = int(exifImageHeader.value(QExifImageHeader::ImageTag::Orientation).toShort()); -// Fill with color to replace transparency with white color instead of black (default). - QImage image(originalImage.size(), originalImage.format()); - image.fill(QColor(Qt::white).rgb()); - QPainter painter(&image); - painter.drawImage(0, 0, originalImage); -//-------------------- - double factor = image.width() / (double)image.height(); - if(factor < 0.2 || factor > 5){ - qInfo() << QStringLiteral("Cannot create thumbnails because size factor (%1) is too low/much of: `%2`.").arg(factor).arg(path); - }else { - QImage thumbnail = image.scaled( - Constants::ThumbnailImageFileWidth, Constants::ThumbnailImageFileHeight, - Qt::KeepAspectRatio, Qt::SmoothTransformation - ); - - if (rotation != 0) { - QTransform transform; - if (rotation == 3 || rotation == 4) - transform.rotate(180); - else if (rotation == 5 || rotation == 6) - transform.rotate(90); - else if (rotation == 7 || rotation == 8) - transform.rotate(-90); - thumbnail = thumbnail.transformed(transform); - if (rotation == 2 || rotation == 4 || rotation == 5 || rotation == 7) - thumbnail = thumbnail.mirrored(true, false); - } - QString uuid = QUuid::createUuid().toString(); - id = QStringLiteral("%1.jpg").arg(uuid.mid(1, uuid.length() - 2)); - - if (!thumbnail.save(QString::fromStdString(Paths::getThumbnailsDirPath()) + id , "jpg", 100)) { - qWarning() << QStringLiteral("Unable to create thumbnail of: `%1`.").arg(path); - }else{ - appdata.mData[path] = id; - mAppData.mData[path] = id; - if(mChatMessageModel) - mChatMessageModel->getChatMessage()->setAppdata(appdata.toString().toStdString()); - } - } - } - } - if( path != ""){ setWasDownloaded( !path.isEmpty() && QFileInfo(path).isFile()); - if(appdata.mData.contains(path) && !appdata.mData[path].isEmpty()) - setThumbnail(QStringLiteral("image://%1/%2").arg(ThumbnailProvider::ProviderId).arg(appdata.mData[path])); + setThumbnail(QStringLiteral("image://%1/%2").arg(ThumbnailProvider::ProviderId).arg(path)); } } } -void ContentModel::removeThumbnail(){ - for(QMap::iterator itData = mAppData.mData.begin() ; itData != mAppData.mData.end() ; ++itData){ - QString thumbnailPath = QString::fromStdString(Paths::getThumbnailsDirPath()) +itData.value(); - if( QFileInfo(thumbnailPath).isFile()){ - QFile(thumbnailPath).remove(); - } - } - QString backup; - if( mAppData.mData.contains("receivedTime")) - backup = mAppData.mData["receivedTime"]; - mAppData.mData.clear(); - if( !backup.isEmpty()) - mAppData.mData["receivedTime"] = backup; -} - void ContentModel::removeDownloadedFile(){ QString path = getFilePath(); if( path != ""){ diff --git a/linphone-app/src/components/content/ContentModel.hpp b/linphone-app/src/components/content/ContentModel.hpp index 21649a7cd..750603e69 100644 --- a/linphone-app/src/components/content/ContentModel.hpp +++ b/linphone-app/src/components/content/ContentModel.hpp @@ -76,7 +76,6 @@ public: Q_INVOKABLE int getFileDuration() const; void createThumbnail (const bool& force = false); - void removeThumbnail (); void removeDownloadedFile(); Q_INVOKABLE void downloadFile(); diff --git a/linphone-app/src/components/history/HistoryModel.cpp b/linphone-app/src/components/history/HistoryModel.cpp index e4be99edf..24be4ab80 100644 --- a/linphone-app/src/components/history/HistoryModel.cpp +++ b/linphone-app/src/components/history/HistoryModel.cpp @@ -31,14 +31,8 @@ #include #include -#include "app/App.hpp" -#include "app/paths/Paths.hpp" -#include "app/providers/ThumbnailProvider.hpp" #include "components/core/CoreHandlers.hpp" #include "components/core/CoreManager.hpp" -#include "components/notifier/Notifier.hpp" -#include "components/settings/SettingsModel.hpp" -#include "utils/QExifImageHeader.hpp" #include "utils/Utils.hpp" #include "HistoryModel.hpp" diff --git a/linphone-app/src/components/other/images/ImageModel.cpp b/linphone-app/src/components/other/images/ImageModel.cpp index aefa737c2..a9601eec4 100644 --- a/linphone-app/src/components/other/images/ImageModel.cpp +++ b/linphone-app/src/components/other/images/ImageModel.cpp @@ -20,12 +20,15 @@ #include "ImageModel.hpp" #include +#include +#include #include "app/App.hpp" #include "utils/Utils.hpp" #include "components/Components.hpp" #include "components/core/CoreManager.hpp" +#include "utils/QExifImageHeader.hpp" // ============================================================================= @@ -73,3 +76,53 @@ void ImageModel::setDescription(const QString& data){ void ImageModel::setUrl(const QUrl& url){ setPath(url.toString(QUrl::RemoveScheme)); } + +QImage ImageModel::createThumbnail(const QString& path){ + QImage thumbnail; + if(QFileInfo(path).isFile()){ + QImage originalImage(path); + + if( originalImage.isNull()){// Try to determine format from headers + QImageReader reader(path); + reader.setDecideFormatFromContent(true); + QByteArray format = reader.format(); + if(!format.isEmpty()) + originalImage = QImage(path, format); + } + if (!originalImage.isNull()){ + int rotation = 0; + QExifImageHeader exifImageHeader; + if (exifImageHeader.loadFromJpeg(path)) + rotation = int(exifImageHeader.value(QExifImageHeader::ImageTag::Orientation).toShort()); +// Fill with color to replace transparency with white color instead of black (default). + QImage image(originalImage.size(), originalImage.format()); + image.fill(QColor(Qt::white).rgb()); + QPainter painter(&image); + painter.drawImage(0, 0, originalImage); +//-------------------- + double factor = image.width() / (double)image.height(); + if(factor < 0.2 || factor > 5){ + qInfo() << QStringLiteral("Cannot create thumbnails because size factor (%1) is too low/much of: `%2`.").arg(factor).arg(path); + }else { + thumbnail = image.scaled( + Constants::ThumbnailImageFileWidth, Constants::ThumbnailImageFileHeight, + Qt::KeepAspectRatio, Qt::SmoothTransformation + ); + + if (rotation != 0) { + QTransform transform; + if (rotation == 3 || rotation == 4) + transform.rotate(180); + else if (rotation == 5 || rotation == 6) + transform.rotate(90); + else if (rotation == 7 || rotation == 8) + transform.rotate(-90); + thumbnail = thumbnail.transformed(transform); + if (rotation == 2 || rotation == 4 || rotation == 5 || rotation == 7) + thumbnail = thumbnail.mirrored(true, false); + } + } + } + } + return thumbnail; +} \ No newline at end of file diff --git a/linphone-app/src/components/other/images/ImageModel.hpp b/linphone-app/src/components/other/images/ImageModel.hpp index 259eb8776..a16c6e00a 100644 --- a/linphone-app/src/components/other/images/ImageModel.hpp +++ b/linphone-app/src/components/other/images/ImageModel.hpp @@ -43,11 +43,12 @@ public: QString getDescription() const; QString getId() const; - void setPath(const QString& path); void setDescription(const QString& description); Q_INVOKABLE void setUrl(const QUrl& url); + static QImage createThumbnail(const QString& path); + signals: void pathChanged(); void descriptionChanged(); diff --git a/linphone-app/src/utils/Constants.cpp b/linphone-app/src/utils/Constants.cpp index c3e651d8a..67d39fd14 100644 --- a/linphone-app/src/utils/Constants.cpp +++ b/linphone-app/src/utils/Constants.cpp @@ -44,7 +44,6 @@ constexpr char Constants::PathPlugins[]; #endif constexpr char Constants::PathPluginsApp[]; constexpr char Constants::PathSounds[]; -constexpr char Constants::PathThumbnails[]; constexpr char Constants::PathUserCertificates[]; constexpr char Constants::PathCallHistoryList[]; diff --git a/linphone-app/src/utils/Constants.hpp b/linphone-app/src/utils/Constants.hpp index dadcdac5b..471b374a7 100644 --- a/linphone-app/src/utils/Constants.hpp +++ b/linphone-app/src/utils/Constants.hpp @@ -125,7 +125,6 @@ public: #endif static constexpr char PathPluginsApp[] = "app/"; static constexpr char PathSounds[] = "/sounds/" EXECUTABLE_NAME; - static constexpr char PathThumbnails[] = "/thumbnails/"; static constexpr char PathUserCertificates[] = "/usr-crt/"; static constexpr char PathCallHistoryList[] = "/call-history.db"; diff --git a/linphone-app/ui/modules/Common/Picker/EmojiPicker.qml b/linphone-app/ui/modules/Common/Picker/EmojiPicker.qml index 5382076ad..14242de66 100644 --- a/linphone-app/ui/modules/Common/Picker/EmojiPicker.qml +++ b/linphone-app/ui/modules/Common/Picker/EmojiPicker.qml @@ -225,7 +225,7 @@ Item{ running: grid.auxItemDisplayed