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