Create thumbnails into memory instead of disk.

Clean unused headers.
This commit is contained in:
Julien Wadel 2023-04-11 10:22:05 +02:00
parent d3b3a5ac37
commit c8337d9dda
20 changed files with 71 additions and 181 deletions

View file

@ -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.

View file

@ -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);
}

View file

@ -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 ();

View file

@ -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;
}

View file

@ -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_

View file

@ -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);

View file

@ -26,19 +26,7 @@
#include <QDateTime>
// =============================================================================
/*
class Thumbnail{
public:
Thumbnail();
QString mId;
QString mPath;
QString toString()const;
void fromString(const QString& );
static QString toString(const QVector<Thumbnail>& );
static QVector<Thumbnail> 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<linphone::ChatMessage> 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<QString, QString> mData;// Path / ID

View file

@ -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"

View file

@ -26,15 +26,7 @@
#include <QMessageBox>
#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"
// =============================================================================

View file

@ -27,14 +27,9 @@
#include <QMessageBox>
#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);

View file

@ -35,34 +35,12 @@
#include <qqmlapplicationengine.h>
#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"

View file

@ -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<ContentModel>()->removeThumbnail();
}
resetData();
}
@ -107,7 +102,6 @@ void ContentListModel::removeDownloadedFiles(){
for(auto model : mList){
auto contentModel = model.objectCast<ContentModel>();
contentModel->removeDownloadedFile();
contentModel->removeThumbnail();
}
}

View file

@ -22,20 +22,18 @@
#include <QQmlApplicationEngine>
#include <QDesktopServices>
#include <QImageReader>
#include <QMessageBox>
#include <QPainter>
#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<QString, QString>::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 != ""){

View file

@ -76,7 +76,6 @@ public:
Q_INVOKABLE int getFileDuration() const;
void createThumbnail (const bool& force = false);
void removeThumbnail ();
void removeDownloadedFile();
Q_INVOKABLE void downloadFile();

View file

@ -31,14 +31,8 @@
#include <QUrlQuery>
#include <QImageReader>
#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"

View file

@ -20,12 +20,15 @@
#include "ImageModel.hpp"
#include <QQmlApplicationEngine>
#include <QImageReader>
#include <QPainter>
#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;
}

View file

@ -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();

View file

@ -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[];

View file

@ -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";

View file

@ -225,7 +225,7 @@ Item{
running: grid.auxItemDisplayed<mainItem.maxIndex-grid.auxItemsCount
property int lastCount: 0
onTriggered: {
console.log(grid.auxItemDisplayed - lastCount)
console.log("Loaded emojis : " +(grid.auxItemDisplayed - lastCount))
lastCount = grid.auxItemDisplayed
}
}