From 87ff1525f682105fa4e5a9a75dc7d844b19cfdbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be> Date: Tue, 8 Mar 2011 23:00:09 +0100 Subject: Cache rounded avatars. Resolves: #770 diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp index 998912a..1a909fd 100644 --- a/Swift/QtUI/QtChatWindow.cpp +++ b/Swift/QtUI/QtChatWindow.cpp @@ -13,6 +13,7 @@ #include "MessageSnippet.h" #include "SystemMessageSnippet.h" #include "QtTextEdit.h" +#include "QtScaledAvatarCache.h" #include "SwifTools/TabComplete.h" @@ -20,6 +21,7 @@ #include <QBoxLayout> #include <QCloseEvent> #include <QComboBox> +#include <QFileInfo> #include <QLineEdit> #include <QSplitter> #include <QString> @@ -264,6 +266,8 @@ std::string QtChatWindow::addMessage(const std::string &message, const std::stri onAllMessagesRead(); } + QString scaledAvatarPath = QtScaledAvatarCache(32).getScaledAvatarPath(avatarPath.c_str()); + QString htmlString; if (label) { htmlString = QString("<span style=\"border: thin dashed grey; padding-left: .5em; padding-right: .5em; color: %1; background-color: %2; font-size: 90%; margin-right: .5em; \">").arg(Qt::escape(P2QSTRING(label->getForegroundColor()))).arg(Qt::escape(P2QSTRING(label->getBackgroundColor()))); @@ -277,7 +281,7 @@ std::string QtChatWindow::addMessage(const std::string &message, const std::stri htmlString += styleSpanStart + messageHTML + styleSpanEnd; bool appendToPrevious = !previousMessageWasSystem_ && !previousMessageWasPresence_ && ((senderIsSelf && previousMessageWasSelf_) || (!senderIsSelf && !previousMessageWasSelf_ && previousSenderName_ == P2QSTRING(senderName))); - QString qAvatarPath = avatarPath.empty() ? "qrc:/icons/avatar.png" : QUrl::fromLocalFile(P2QSTRING(avatarPath)).toEncoded(); + QString qAvatarPath = scaledAvatarPath.isEmpty() ? "qrc:/icons/avatar.png" : QUrl::fromLocalFile(scaledAvatarPath).toEncoded(); std::string id = id_.generateID(); messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(time), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id)))); diff --git a/Swift/QtUI/QtRosterHeader.cpp b/Swift/QtUI/QtRosterHeader.cpp index 18bb980..5fb4d1a 100644 --- a/Swift/QtUI/QtRosterHeader.cpp +++ b/Swift/QtUI/QtRosterHeader.cpp @@ -8,6 +8,7 @@ #include <QHBoxLayout> #include <QVBoxLayout> +#include <QFileInfo> #include <QIcon> #include <QSizePolicy> #include <qdebug.h> @@ -19,6 +20,7 @@ #include <Swift/QtUI/QtElidingLabel.h> #include <Swift/QtUI/QtClickableLabel.h> #include <Swift/QtUI/QtNameWidget.h> +#include "QtScaledAvatarCache.h" namespace Swift { QtRosterHeader::QtRosterHeader(QtSettingsProvider* settings, QWidget* parent) : QWidget(parent) { @@ -71,28 +73,15 @@ void QtRosterHeader::setConnecting() { } void QtRosterHeader::setAvatar(const QString& path) { - QIcon avatar(path); - if (avatar.isNull()) { - //qDebug() << "Setting null avatar"; - avatar = QIcon(":/icons/avatar.png"); - } - - // Apply a rounded rectangle mask - // FIXME: - // - We shouldn't go via a 128x128 pixmap - // - Something tells me we can do this with clever composition mode + - // 1 drawRectangle on the avatarPixmap, but i haven't figured it out yet. - QPixmap avatarPixmap = avatar.pixmap(128, 128); - QPixmap maskedAvatar(avatarPixmap.size()); - maskedAvatar.fill(QColor(0, 0, 0, 0)); - QPainter maskPainter(&maskedAvatar); - maskPainter.setBrush(Qt::black); - maskPainter.drawRoundedRect(maskedAvatar.rect(), 13, 13); - maskPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); - maskPainter.drawPixmap(0, 0, avatarPixmap); - maskPainter.end(); - - avatarLabel_->setPixmap(maskedAvatar.scaled(avatarSize_, avatarSize_, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + QString scaledAvatarPath = QtScaledAvatarCache(avatarSize_).getScaledAvatarPath(path); + QPixmap avatar; + if (QFileInfo(scaledAvatarPath).exists()) { + avatar.load(scaledAvatarPath); + } + else { + avatar = QPixmap(":/icons/avatar.png").scaled(avatarSize_, avatarSize_, Qt::KeepAspectRatio, Qt::SmoothTransformation); + } + avatarLabel_->setPixmap(avatar); } void QtRosterHeader::setNick(const QString& nick) { diff --git a/Swift/QtUI/QtScaledAvatarCache.cpp b/Swift/QtUI/QtScaledAvatarCache.cpp new file mode 100644 index 0000000..6abff87 --- /dev/null +++ b/Swift/QtUI/QtScaledAvatarCache.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "QtScaledAvatarCache.h" + +#include <QFileInfo> +#include <QDir> +#include <QPixmap> +#include <QImage> +#include <QImageReader> +#include <QPainter> +#include <QByteArray> + +namespace Swift { + +QtScaledAvatarCache::QtScaledAvatarCache(int size) : size(size) { +} + +QString QtScaledAvatarCache::getScaledAvatarPath(const QString& path) { + QFileInfo avatarFile(path); + if (avatarFile.exists()) { + if (!avatarFile.dir().exists(QString::number(size))) { + if (!avatarFile.dir().mkdir(QString::number(size))) { + return path; + } + } + QDir targetDir(avatarFile.dir().absoluteFilePath(QString::number(size))); + QString targetFile = targetDir.absoluteFilePath(avatarFile.baseName()); + if (!QFileInfo(targetFile).exists()) { + QPixmap avatarPixmap; + avatarPixmap.load(path); + QPixmap maskedAvatar(avatarPixmap.size()); + maskedAvatar.fill(QColor(0, 0, 0, 0)); + QPainter maskPainter(&maskedAvatar); + maskPainter.setBrush(Qt::black); + maskPainter.drawRoundedRect(maskedAvatar.rect(), 25.0, 25.0, Qt::RelativeSize); + maskPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); + maskPainter.drawPixmap(0, 0, avatarPixmap); + maskPainter.end(); + + if (!maskedAvatar.scaled(size, size, Qt::KeepAspectRatio, Qt::SmoothTransformation).save(targetFile, "PNG")) { + return path; + } + } + return targetFile; + } + else { + return path; + } +} + + +} diff --git a/Swift/QtUI/QtScaledAvatarCache.h b/Swift/QtUI/QtScaledAvatarCache.h new file mode 100644 index 0000000..cc3ac07 --- /dev/null +++ b/Swift/QtUI/QtScaledAvatarCache.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <string> +#include <QString> + +namespace Swift { + class QtScaledAvatarCache { + public: + QtScaledAvatarCache(int size); + + QString getScaledAvatarPath(const QString& path); + + private: + int size; + }; +} diff --git a/Swift/QtUI/Roster/RosterDelegate.cpp b/Swift/QtUI/Roster/RosterDelegate.cpp index 47ae948..aaa6236 100644 --- a/Swift/QtUI/Roster/RosterDelegate.cpp +++ b/Swift/QtUI/Roster/RosterDelegate.cpp @@ -12,6 +12,7 @@ #include <QBrush> #include <QFontMetrics> #include <QPainterPath> +#include <QFileInfo> #include <QPolygon> #include <qdebug.h> #include <QBitmap> @@ -21,6 +22,7 @@ #include "QtTreeWidget.h" #include "RosterModel.h" +#include "QtScaledAvatarCache.h" namespace Swift { @@ -86,23 +88,18 @@ void RosterDelegate::paintContact(QPainter* painter, const QStyleOptionViewItem& int calculatedAvatarSize = presenceIconRegion.height(); //This overlaps the presenceIcon, so must be painted first QRect avatarRegion(QPoint(presenceIconRegion.right() - presenceIconWidth_ / 2, presenceIconRegion.top()), QSize(calculatedAvatarSize, calculatedAvatarSize)); - QIcon avatar = index.data(AvatarRole).isValid() && !index.data(AvatarRole).value<QIcon>().isNull() - ? index.data(AvatarRole).value<QIcon>() - : QIcon(":/icons/avatar.png"); - - // Apply a rounded rectangle mask - // FIXME: We shouldn't go via a 128x128 pixmap - QPixmap avatarPixmap = avatar.pixmap(128, 128); - QPixmap maskedAvatar(avatarPixmap.size()); - maskedAvatar.fill(QColor(0, 0, 0, 0)); - QPainter maskPainter(&maskedAvatar); - maskPainter.setBrush(Qt::black); - maskPainter.drawRoundedRect(maskedAvatar.rect(), 13, 13); - maskPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); - maskPainter.drawPixmap(0, 0, avatarPixmap); - maskPainter.end(); - - avatarPixmap = maskedAvatar.scaled(avatarRegion.height(), avatarRegion.width(), Qt::KeepAspectRatio, Qt::SmoothTransformation); + + QPixmap avatarPixmap; + if (index.data(AvatarRole).isValid() && !index.data(AvatarRole).value<QString>().isNull()) { + QString avatarPath = index.data(AvatarRole).value<QString>(); + QString scaledAvatarPath = QtScaledAvatarCache(avatarRegion.height()).getScaledAvatarPath(avatarPath); + if (QFileInfo(scaledAvatarPath).exists()) { + avatarPixmap.load(scaledAvatarPath); + } + } + if (avatarPixmap.isNull()) { + avatarPixmap = QPixmap(":/icons/avatar.png").scaled(avatarRegion.height(), avatarRegion.width(), Qt::KeepAspectRatio, Qt::SmoothTransformation); + } painter->drawPixmap(avatarRegion.topLeft() + QPoint(((avatarRegion.width() - avatarPixmap.width()) / 2), (avatarRegion.height() - avatarPixmap.height()) / 2), avatarPixmap); diff --git a/Swift/QtUI/Roster/RosterModel.cpp b/Swift/QtUI/Roster/RosterModel.cpp index 2399a21..1fc20dd 100644 --- a/Swift/QtUI/Roster/RosterModel.cpp +++ b/Swift/QtUI/Roster/RosterModel.cpp @@ -135,12 +135,12 @@ QString RosterModel::getToolTip(RosterItem* item) const { return tip; } -QIcon RosterModel::getAvatar(RosterItem* item) const { +QString RosterModel::getAvatar(RosterItem* item) const { ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item); - if (!contact) return QIcon(); - std::string path = contact->getAvatarPath(); - - return path.empty() ? QIcon() : QIcon(P2QSTRING(path)); + if (!contact) { + return ""; + } + return QString(contact->getAvatarPath().c_str()); } QString RosterModel::getStatusText(RosterItem* item) const { diff --git a/Swift/QtUI/Roster/RosterModel.h b/Swift/QtUI/Roster/RosterModel.h index 964f680..bd34e9c 100644 --- a/Swift/QtUI/Roster/RosterModel.h +++ b/Swift/QtUI/Roster/RosterModel.h @@ -44,7 +44,7 @@ namespace Swift { QColor getTextColor(RosterItem* item) const; QColor getBackgroundColor(RosterItem* item) const; QString getToolTip(RosterItem* item) const; - QIcon getAvatar(RosterItem* item) const; + QString getAvatar(RosterItem* item) const; QString getStatusText(RosterItem* item) const; QIcon getPresenceIcon(RosterItem* item) const; int getChildCount(RosterItem* item) const; diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript index b8c3ebe..a924960 100644 --- a/Swift/QtUI/SConscript +++ b/Swift/QtUI/SConscript @@ -75,6 +75,7 @@ sources = [ "QtNameWidget.cpp", "QtSettingsProvider.cpp", "QtStatusWidget.cpp", + "QtScaledAvatarCache.cpp", "QtSwift.cpp", "QtChatView.cpp", "QtChatTheme.cpp", -- cgit v0.10.2-6-g49f6