[calligra/calligra/2.9] krita: [FEATURE] Added a reporting functional for memory consumption in Krita
Dmitry Kazakov
dimula73 at gmail.com
Tue Jun 2 10:55:28 UTC 2015
Git commit 38d77e007d7704795a87d1a1f5d8965260450536 by Dmitry Kazakov.
Committed on 02/06/2015 at 10:55.
Pushed by dkazakov into branch 'calligra/2.9'.
[FEATURE] Added a reporting functional for memory consumption in Krita
Still TODO: add a configuration page to the settings dialog
CCMAIL:kimageshop at kde.org
M +1 -0 krita/image/CMakeLists.txt
M +2 -0 krita/image/kis_image.cc
A +127 -0 krita/image/kis_memory_statistics_server.cpp [License: GPL (v2+)]
A +90 -0 krita/image/kis_memory_statistics_server.h [License: GPL (v2+)]
M +4 -0 krita/image/tiles3/kis_tile_data.h
M +7 -0 krita/image/tiles3/kis_tile_data_interface.h
M +43 -4 krita/image/tiles3/kis_tile_data_pooler.cc
M +12 -2 krita/image/tiles3/kis_tile_data_pooler.h
M +19 -0 krita/image/tiles3/kis_tile_data_store.cc
M +12 -0 krita/image/tiles3/kis_tile_data_store.h
M +12 -0 krita/image/tiles3/swap/kis_swapped_data_store.cpp
M +8 -0 krita/image/tiles3/swap/kis_swapped_data_store.h
M +1 -1 krita/image/tiles3/swap/kis_tile_data_swapper.cpp
M +121 -13 krita/ui/kis_statusbar.cc
M +11 -1 krita/ui/kis_statusbar.h
http://commits.kde.org/calligra/38d77e007d7704795a87d1a1f5d8965260450536
diff --git a/krita/image/CMakeLists.txt b/krita/image/CMakeLists.txt
index a351780..2ce5684 100644
--- a/krita/image/CMakeLists.txt
+++ b/krita/image/CMakeLists.txt
@@ -192,6 +192,7 @@ set(kritaimage_LIB_SRCS
kis_curve_rect_mask_generator.cpp
kis_math_toolbox.cpp
kis_memory_leak_tracker.cpp
+ kis_memory_statistics_server.cpp
kis_name_server.cpp
kis_node.cpp
kis_node_facade.cpp
diff --git a/krita/image/kis_image.cc b/krita/image/kis_image.cc
index 35ecf9d..216b97f 100644
--- a/krita/image/kis_image.cc
+++ b/krita/image/kis_image.cc
@@ -59,6 +59,7 @@
#include "kis_transaction.h"
#include "kis_types.h"
#include "kis_meta_data_merge_strategy.h"
+#include "kis_memory_statistics_server.h"
#include "kis_image_config.h"
#include "kis_update_scheduler.h"
@@ -187,6 +188,7 @@ KisImage::KisImage(KisUndoStore *undoStore, qint32 width, qint32 height, const K
m_d->scheduler->setProgressProxy(m_d->compositeProgressProxy);
}
+ connect(this, SIGNAL(sigImageModified()), KisMemoryStatisticsServer::instance(), SLOT(notifyImageChanged()));
}
KisImage::~KisImage()
diff --git a/krita/image/kis_memory_statistics_server.cpp b/krita/image/kis_memory_statistics_server.cpp
new file mode 100644
index 0000000..5ee525e
--- /dev/null
+++ b/krita/image/kis_memory_statistics_server.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2015 Dmitry Kazakov <dimula73 at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "kis_memory_statistics_server.h"
+
+#include <kglobal.h>
+
+#include "kis_image.h"
+#include "kis_image_config.h"
+#include "kis_signal_compressor.h"
+
+#include "tiles3/kis_tile_data_store.h"
+
+
+struct KisMemoryStatisticsServer::Private
+{
+ Private()
+ : updateCompressor(1000 /* ms */, KisSignalCompressor::POSTPONE)
+ {
+ }
+
+ KisSignalCompressor updateCompressor;
+};
+
+
+KisMemoryStatisticsServer::KisMemoryStatisticsServer()
+ : m_d(new Private)
+{
+ connect(&m_d->updateCompressor, SIGNAL(timeout()), SIGNAL(sigUpdateMemoryStatistics()));
+}
+
+KisMemoryStatisticsServer::~KisMemoryStatisticsServer()
+{
+}
+
+KisMemoryStatisticsServer* KisMemoryStatisticsServer::instance()
+{
+ K_GLOBAL_STATIC(KisMemoryStatisticsServer, s_instance);
+ return s_instance;
+}
+
+inline void addDevice(KisPaintDeviceSP dev,
+ QSet<KisPaintDevice*> &devices,
+ qint64 &memBound)
+{
+ if (dev && !devices.contains(dev.data())) {
+ devices.insert(dev.data());
+ memBound +=
+ dev->extent().width() *
+ dev->extent().height() *
+ dev->pixelSize();
+ }
+}
+
+qint64 calculateNodeMemoryHiBoundStep(KisNodeSP node,
+ QSet<KisPaintDevice*> &devices)
+{
+ qint64 memBound = 0;
+
+ addDevice(node->paintDevice(), devices, memBound);
+ addDevice(node->original(), devices, memBound);
+ addDevice(node->projection(), devices, memBound);
+
+ node = node->firstChild();
+ while (node) {
+ memBound += calculateNodeMemoryHiBoundStep(node, devices);
+ node = node->nextSibling();
+ }
+
+ return memBound;
+}
+
+qint64 calculateNodeMemoryHiBound(KisNodeSP node)
+{
+ QSet<KisPaintDevice*> devices;
+ return calculateNodeMemoryHiBoundStep(node, devices);
+}
+
+
+KisMemoryStatisticsServer::Statistics
+KisMemoryStatisticsServer::fetchMemoryStatistics(KisImageSP image) const
+{
+ KisTileDataStore::MemoryStatistics tileStats =
+ KisTileDataStore::instance()->memoryStatistics();
+
+ Statistics stats;
+ if (image) {
+ stats.imageSize = calculateNodeMemoryHiBound(image->root());
+ }
+ stats.totalMemorySize = tileStats.totalMemorySize;
+ stats.realMemorySize = tileStats.realMemorySize;
+ stats.historicalMemorySize = tileStats.historicalMemorySize;
+ stats.poolSize = tileStats.poolSize;
+
+ stats.swapSize = tileStats.swapSize;
+
+ KisImageConfig cfg;
+
+ stats.tilesHardLimit = cfg.tilesHardLimit() * MiB;
+ stats.tilesSoftLimit = cfg.tilesSoftLimit() * MiB;
+ stats.tilesPoolLimit = cfg.poolLimit() * MiB;
+ stats.totalMemoryLimit = stats.tilesHardLimit + stats.tilesPoolLimit;
+
+ return stats;
+}
+
+void KisMemoryStatisticsServer::notifyImageChanged()
+{
+ m_d->updateCompressor.start();
+}
+
+
diff --git a/krita/image/kis_memory_statistics_server.h b/krita/image/kis_memory_statistics_server.h
new file mode 100644
index 0000000..cb7e734
--- /dev/null
+++ b/krita/image/kis_memory_statistics_server.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2015 Dmitry Kazakov <dimula73 at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __KIS_MEMORY_STATISTICS_SERVER_H
+#define __KIS_MEMORY_STATISTICS_SERVER_H
+
+#include <QtGlobal>
+#include <QObject>
+#include <QScopedPointer>
+
+#include "krita_export.h"
+#include "kis_types.h"
+
+
+class KRITAIMAGE_EXPORT KisMemoryStatisticsServer : public QObject
+{
+ Q_OBJECT
+public:
+ struct Statistics
+ {
+ Statistics()
+ : imageSize(0),
+
+ totalMemorySize(0),
+ realMemorySize(0),
+ historicalMemorySize(0),
+ poolSize(0),
+
+ swapSize(0),
+
+ totalMemoryLimit(0),
+ tilesHardLimit(0),
+ tilesSoftLimit(0),
+ tilesPoolLimit(0)
+ {
+ }
+
+ qint64 imageSize;
+
+ qint64 totalMemorySize;
+ qint64 realMemorySize;
+ qint64 historicalMemorySize;
+ qint64 poolSize;
+
+ qint64 swapSize;
+
+ qint64 totalMemoryLimit;
+ qint64 tilesHardLimit;
+ qint64 tilesSoftLimit;
+ qint64 tilesPoolLimit;
+ };
+
+
+
+public:
+ ~KisMemoryStatisticsServer();
+ static KisMemoryStatisticsServer* instance();
+
+ Statistics fetchMemoryStatistics(KisImageSP image) const;
+
+public Q_SLOTS:
+ void notifyImageChanged();
+
+Q_SIGNALS:
+ void sigUpdateMemoryStatistics();
+
+private:
+ KisMemoryStatisticsServer();
+
+private:
+ struct Private;
+ const QScopedPointer<Private> m_d;
+};
+
+#endif /* __KIS_MEMORY_STATISTICS_SERVER_H */
diff --git a/krita/image/tiles3/kis_tile_data.h b/krita/image/tiles3/kis_tile_data.h
index 98c51c5..dee8a37 100644
--- a/krita/image/tiles3/kis_tile_data.h
+++ b/krita/image/tiles3/kis_tile_data.h
@@ -115,6 +115,10 @@ inline void KisTileData::setMementoed(bool value) {
m_mementoFlag += value ? 1 : -1;
}
+inline bool KisTileData::historical() const {
+ return mementoed() && numUsers() <= 1;
+}
+
inline int KisTileData::age() const {
return m_age;
}
diff --git a/krita/image/tiles3/kis_tile_data_interface.h b/krita/image/tiles3/kis_tile_data_interface.h
index fc2aad4..8ce9c7c 100644
--- a/krita/image/tiles3/kis_tile_data_interface.h
+++ b/krita/image/tiles3/kis_tile_data_interface.h
@@ -132,6 +132,13 @@ public:
*/
inline qint32 numUsers() const;
+ /**
+ * Conveniece method. Returns true iff the tile data is linked to
+ * information only and therefore can be swapped out easily.
+ *
+ * Effectively equivalent to: (mementoed() && numUsers() <= 1)
+ */
+ inline bool historical() const;
/**
* Used for swapping purposes only.
diff --git a/krita/image/tiles3/kis_tile_data_pooler.cc b/krita/image/tiles3/kis_tile_data_pooler.cc
index 3984526..11f8dfa 100644
--- a/krita/image/tiles3/kis_tile_data_pooler.cc
+++ b/krita/image/tiles3/kis_tile_data_pooler.cc
@@ -91,6 +91,9 @@ KisTileDataPooler::KisTileDataPooler(KisTileDataStore *store, qint32 memoryLimit
m_store = store;
m_timeout = MIN_TIMEOUT;
m_lastCycleHadWork = false;
+ m_lastPoolMemoryMetric = 0;
+ m_lastRealMemoryMetric = 0;
+ m_lastHistoricalMemoryMetric = 0;
if(memoryLimit >= 0) {
m_memoryLimit = memoryLimit;
@@ -195,19 +198,44 @@ void KisTileDataPooler::run()
QList<KisTileData*> donors;
qint32 memoryOccupied;
- getLists(iter, beggers, donors, memoryOccupied);
+ qint32 statRealMemory;
+ qint32 statHistoricalMemory;
+
+
+ getLists(iter, beggers, donors,
+ memoryOccupied,
+ statRealMemory,
+ statHistoricalMemory);
m_lastCycleHadWork =
processLists(beggers, donors, memoryOccupied);
- m_store->endIteration(iter);
+ m_lastPoolMemoryMetric = memoryOccupied;
+ m_lastRealMemoryMetric = statRealMemory;
+ m_lastHistoricalMemoryMetric = statHistoricalMemory;
+ m_store->endIteration(iter);
DEBUG_TILE_STATISTICS();
DEBUG_SIMPLE_ACTION("cycle finished");
}
}
+qint64 KisTileDataPooler::lastPoolMemoryMetric() const
+{
+ return m_lastPoolMemoryMetric;
+}
+
+qint64 KisTileDataPooler::lastRealMemoryMetric() const
+{
+ return m_lastRealMemoryMetric;
+}
+
+qint64 KisTileDataPooler::lastHistoricalMemoryMetric() const
+{
+ return m_lastHistoricalMemoryMetric;
+}
+
inline int KisTileDataPooler::clonesMetric(KisTileData *td, int numClones) {
return numClones * td->pixelSize();
}
@@ -240,9 +268,13 @@ template<class Iter>
void KisTileDataPooler::getLists(Iter *iter,
QList<KisTileData*> &beggers,
QList<KisTileData*> &donors,
- qint32 &memoryOccupied)
+ qint32 &memoryOccupied,
+ qint32 &statRealMemory,
+ qint32 &statHistoricalMemory)
{
memoryOccupied = 0;
+ statRealMemory = 0;
+ statHistoricalMemory = 0;
qint32 needMemoryTotal = 0;
qint32 canDonorMemoryTotal = 0;
@@ -267,6 +299,13 @@ void KisTileDataPooler::getLists(Iter *iter,
}
memoryOccupied += clonesMetric(item);
+
+ // statistics gathering
+ if (item->historical()) {
+ statHistoricalMemory += item->pixelSize();
+ } else {
+ statRealMemory += item->pixelSize();
+ }
}
DEBUG_LISTS(memoryOccupied,
@@ -297,7 +336,7 @@ qint32 KisTileDataPooler::tryGetMemory(QList<KisTileData*> &donors,
bool KisTileDataPooler::processLists(QList<KisTileData*> &beggers,
QList<KisTileData*> &donors,
- qint32 memoryOccupied)
+ qint32 &memoryOccupied)
{
bool hadWork = false;
diff --git a/krita/image/tiles3/kis_tile_data_pooler.h b/krita/image/tiles3/kis_tile_data_pooler.h
index e4ef394..4f4ce37 100644
--- a/krita/image/tiles3/kis_tile_data_pooler.h
+++ b/krita/image/tiles3/kis_tile_data_pooler.h
@@ -40,6 +40,10 @@ public:
void testingRereadConfig();
+ qint64 lastPoolMemoryMetric() const;
+ qint64 lastRealMemoryMetric() const;
+ qint64 lastHistoricalMemoryMetric() const;
+
protected:
static const qint32 MAX_NUM_CLONES;
static const qint32 MAX_TIMEOUT;
@@ -61,11 +65,14 @@ protected:
template<class Iter>
void getLists(Iter *iter, QList<KisTileData*> &beggers,
- QList<KisTileData*> &donors, qint32 &memoryOccupied);
+ QList<KisTileData*> &donors,
+ qint32 &memoryOccupied,
+ qint32 &statRealMemory,
+ qint32 &statHistoricalMemory);
bool processLists(QList<KisTileData*> &beggers,
QList<KisTileData*> &donors,
- qint32 memoryOccupied);
+ qint32 &memoryOccupied);
private:
void debugTileStatistics();
@@ -76,6 +83,9 @@ protected:
qint32 m_timeout;
bool m_lastCycleHadWork;
qint32 m_memoryLimit;
+ qint32 m_lastPoolMemoryMetric;
+ qint32 m_lastRealMemoryMetric;
+ qint32 m_lastHistoricalMemoryMetric;
};
diff --git a/krita/image/tiles3/kis_tile_data_store.cc b/krita/image/tiles3/kis_tile_data_store.cc
index 06775c9..53d5fcc 100644
--- a/krita/image/tiles3/kis_tile_data_store.cc
+++ b/krita/image/tiles3/kis_tile_data_store.cc
@@ -95,6 +95,25 @@ KisTileDataStore* KisTileDataStore::instance()
return s_instance;
}
+KisTileDataStore::MemoryStatistics KisTileDataStore::memoryStatistics()
+{
+ QMutexLocker lock(&m_listLock);
+
+ MemoryStatistics stats;
+
+ const qint64 metricCoeff = KisTileData::WIDTH * KisTileData::HEIGHT;
+
+ stats.realMemorySize = m_pooler.lastRealMemoryMetric() * metricCoeff;
+ stats.historicalMemorySize = m_pooler.lastHistoricalMemoryMetric() * metricCoeff;
+ stats.poolSize = m_pooler.lastPoolMemoryMetric() * metricCoeff;
+
+ stats.totalMemorySize = memoryMetric() * metricCoeff + stats.poolSize;
+
+ stats.swapSize = m_swappedStore.totalMemoryMetric() * metricCoeff;
+
+ return stats;
+}
+
inline void KisTileDataStore::registerTileDataImp(KisTileData *td)
{
td->m_listIterator = m_tileDataList.insert(m_tileDataList.end(), td);
diff --git a/krita/image/tiles3/kis_tile_data_store.h b/krita/image/tiles3/kis_tile_data_store.h
index 67efcb9..3968296 100644
--- a/krita/image/tiles3/kis_tile_data_store.h
+++ b/krita/image/tiles3/kis_tile_data_store.h
@@ -46,6 +46,18 @@ public:
void debugPrintList();
+ struct MemoryStatistics {
+ qint64 totalMemorySize;
+ qint64 realMemorySize;
+ qint64 historicalMemorySize;
+
+ qint64 poolSize;
+
+ qint64 swapSize;
+ };
+
+ MemoryStatistics memoryStatistics();
+
/**
* Returns total number of tiles present: in memory
* or in a swap file
diff --git a/krita/image/tiles3/swap/kis_swapped_data_store.cpp b/krita/image/tiles3/swap/kis_swapped_data_store.cpp
index 0a0aec4..6e82b6c 100644
--- a/krita/image/tiles3/swap/kis_swapped_data_store.cpp
+++ b/krita/image/tiles3/swap/kis_swapped_data_store.cpp
@@ -26,6 +26,7 @@
//#define COMPRESSOR_VERSION 2
KisSwappedDataStore::KisSwappedDataStore()
+ : m_memoryMetric(0)
{
KisImageConfig config;
const quint64 maxSwapSize = config.maxSwapSize() * MiB;
@@ -78,6 +79,8 @@ void KisSwappedDataStore::swapOutTileData(KisTileData *td)
td->releaseMemory();
td->setSwapChunk(chunk);
+
+ m_memoryMetric += td->pixelSize();
}
void KisSwappedDataStore::swapInTileData(KisTileData *td)
@@ -95,6 +98,8 @@ void KisSwappedDataStore::swapInTileData(KisTileData *td)
quint8 *ptr = m_swapSpace->getReadChunkPtr(chunk);
m_compressor->decompressTileData(ptr, chunk.size(), td);
m_allocator->freeChunk(chunk);
+
+ m_memoryMetric -= td->pixelSize();
}
void KisSwappedDataStore::forgetTileData(KisTileData *td)
@@ -103,6 +108,13 @@ void KisSwappedDataStore::forgetTileData(KisTileData *td)
m_allocator->freeChunk(td->swapChunk());
td->setSwapChunk(KisChunk());
+
+ m_memoryMetric -= td->pixelSize();
+}
+
+qint64 KisSwappedDataStore::totalMemoryMetric() const
+{
+ return m_memoryMetric;
}
void KisSwappedDataStore::debugStatistics()
diff --git a/krita/image/tiles3/swap/kis_swapped_data_store.h b/krita/image/tiles3/swap/kis_swapped_data_store.h
index c980b0e..fd416b8 100644
--- a/krita/image/tiles3/swap/kis_swapped_data_store.h
+++ b/krita/image/tiles3/swap/kis_swapped_data_store.h
@@ -66,6 +66,12 @@ public:
void forgetTileData(KisTileData *td);
/**
+ * Retorns the metric of the total memory stored in the swap
+ * in *uncompressed* form!
+ */
+ qint64 totalMemoryMetric() const;
+
+ /**
* Some debugging output
*/
void debugStatistics();
@@ -78,6 +84,8 @@ private:
KisMemoryWindow *m_swapSpace;
QMutex m_lock;
+
+ qint64 m_memoryMetric;
};
#endif /* __KIS_SWAPPED_DATA_STORE_H */
diff --git a/krita/image/tiles3/swap/kis_tile_data_swapper.cpp b/krita/image/tiles3/swap/kis_tile_data_swapper.cpp
index be40734..7623249 100644
--- a/krita/image/tiles3/swap/kis_tile_data_swapper.cpp
+++ b/krita/image/tiles3/swap/kis_tile_data_swapper.cpp
@@ -159,7 +159,7 @@ public:
static inline bool isInteresting(KisTileData *td) {
// We are working with mementoed tiles only...
- return td->mementoed() && td->numUsers() <= 1;
+ return td->historical();
}
static inline bool swapOutFirst(KisTileData *td) {
diff --git a/krita/ui/kis_statusbar.cc b/krita/ui/kis_statusbar.cc
index c7cce56..0e8cfc7 100644
--- a/krita/ui/kis_statusbar.cc
+++ b/krita/ui/kis_statusbar.cc
@@ -22,7 +22,10 @@
#include <QLabel>
#include <QFontMetrics>
#include <QToolButton>
+#include <QPushButton>
#include <QAction>
+#include <QToolTip>
+
#include <ksqueezedtextlabel.h>
#include <kstatusbar.h>
@@ -37,6 +40,7 @@
#include <kis_selection.h>
#include <kis_paint_device.h>
#include <kis_selection_manager.h>
+#include "kis_memory_statistics_server.h"
#include <KisView.h>
#include "KisViewManager.h"
@@ -85,11 +89,13 @@ KisStatusBar::KisStatusBar(KisViewManager * view)
m_progress = new KisProgressWidget();
view->addStatusBarItem(m_progress);
- m_imageSizeLabel = new QLabel(QString());
- m_imageSizeLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
- m_imageSizeLabel->setContentsMargins(5,5, 5, 5);
- m_imageSizeLabel->setMinimumWidth(100);
- view->addStatusBarItem(m_imageSizeLabel);
+ m_memoryReportBox = new QPushButton();
+ m_memoryReportBox->setFlat(true);
+ m_memoryReportBox->setContentsMargins(5, 5, 5, 5);
+ m_memoryReportBox->setMinimumWidth(120);
+ view->addStatusBarItem(m_memoryReportBox);
+
+ connect(m_memoryReportBox, SIGNAL(clicked()), SLOT(showMemoryInfoToolTip()));
m_pointerPositionLabel = new QLabel(QString());
m_pointerPositionLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
@@ -98,6 +104,9 @@ KisStatusBar::KisStatusBar(KisViewManager * view)
view->addStatusBarItem(m_pointerPositionLabel);
m_pointerPositionLabel->setVisible(false);
+ connect(KisMemoryStatisticsServer::instance(),
+ SIGNAL(sigUpdateMemoryStatistics()),
+ SLOT(imageSizeChanged()));
}
KisStatusBar::~KisStatusBar()
@@ -105,7 +114,7 @@ KisStatusBar::~KisStatusBar()
m_view->removeStatusBarItem(m_selectionStatus);
m_view->removeStatusBarItem(m_statusBarStatusLabel);
m_view->removeStatusBarItem(m_statusBarProfileLabel);
- m_view->removeStatusBarItem(m_imageSizeLabel);
+ m_view->removeStatusBarItem(m_memoryReportBox);
m_view->removeStatusBarItem(m_pointerPositionLabel);
m_view->removeStatusBarItem(m_progress);
}
@@ -118,6 +127,7 @@ void KisStatusBar::setView(QPointer<KisView> imageView)
if (m_imageView) {
m_imageView->disconnect(this);
m_view->removeStatusBarItem(m_imageView->zoomManager()->zoomActionWidget());
+ m_imageView = 0;
}
if (imageView) {
m_imageView = imageView;
@@ -129,14 +139,17 @@ void KisStatusBar::setView(QPointer<KisView> imageView)
connect(m_imageView, SIGNAL(sigSizeChanged(const QPointF&, const QPointF&)),
this, SLOT(imageSizeChanged()));
updateStatusBarProfileLabel();
- imageSizeChanged();
m_view->addStatusBarItem(m_imageView->zoomManager()->zoomActionWidget());
}
+
+ imageSizeChanged();
}
void KisStatusBar::documentMousePositionChanged(const QPointF &pos)
{
- QPoint pixelPos = m_view->image()->documentToIntPixel(pos);
+ if (!m_imageView) return;
+
+ QPoint pixelPos = m_imageView->image()->documentToIntPixel(pos);
pixelPos.setX(qBound(0, pixelPos.x(), m_view->image()->width() - 1));
pixelPos.setY(qBound(0, pixelPos.y(), m_view->image()->height() - 1));
@@ -145,11 +158,21 @@ void KisStatusBar::documentMousePositionChanged(const QPointF &pos)
void KisStatusBar::imageSizeChanged()
{
- KisImageWSP image = m_view->image();
- qint32 w = image->width();
- qint32 h = image->height();
+ updateMemoryStatus();
+
+ QString sizeText;
+ KisImageWSP image = m_imageView ? m_imageView->image() : 0;
+ if (image) {
+ qint32 w = image->width();
+ qint32 h = image->height();
+ sizeText = QString("%1 x %2 (%3)").arg(w).arg(h).arg(m_shortMemoryTag);
+ } else {
+ sizeText = m_shortMemoryTag;
+ }
- m_imageSizeLabel->setText(QString("%1 x %2").arg(w).arg(h));
+ m_memoryReportBox->setIcon(m_memoryStatusIcon);
+ m_memoryReportBox->setText(sizeText);
+ m_memoryReportBox->setToolTip(m_longMemoryTag);
}
void KisStatusBar::updateSelectionIcon()
@@ -165,6 +188,89 @@ void KisStatusBar::updateSelectionIcon()
m_selectionStatus->setIcon(icon);
}
+inline QString formatSize(qint64 size)
+{
+ qint64 K = 1024;
+ QString suffix = i18nc("very shortened \'byte\' suffix (for statusbar)", "b");
+
+ if (size > K) {
+ size /= K;
+ suffix = i18nc("very shortened KiB suffix (for statusbar)", "K");
+ }
+
+ if (size > K) {
+ size /= K;
+ suffix = i18nc("very shortened MiB suffix (for statusbar)", "M");
+ }
+
+ if (size > K) {
+ size /= K;
+ suffix = i18nc("very shortened GiB suffix (for statusbar)", "G");
+ }
+
+ if (size > K) {
+ size /= K;
+ suffix = i18nc("very shortened TiB suffix (for statusbar)", "T");
+ }
+
+ return QString("%2%3").arg(size).arg(suffix);
+}
+
+void KisStatusBar::updateMemoryStatus()
+{
+ KisMemoryStatisticsServer::Statistics stats =
+ KisMemoryStatisticsServer::instance()
+ ->fetchMemoryStatistics(m_imageView ? m_imageView->image() : 0);
+
+ QString longStats =
+ i18nc("tooltip on statusbar memory reporting button",
+ "Image size:\t %1\n"
+ "\n"
+ "Memory used:\t %2 / %3\n"
+ " image data:\t %4 / %5\n"
+ " pool:\t\t %6 / %7\n"
+ " undo data:\t %8\n"
+ "\n"
+ "Swap used:\t %9",
+ formatSize(stats.imageSize),
+
+ formatSize(stats.totalMemorySize),
+ formatSize(stats.totalMemoryLimit),
+
+ formatSize(stats.realMemorySize),
+ formatSize(stats.tilesHardLimit),
+
+ formatSize(stats.poolSize),
+ formatSize(stats.tilesPoolLimit),
+
+ formatSize(stats.historicalMemorySize),
+ formatSize(stats.swapSize));
+
+ QString shortStats = formatSize(stats.imageSize);
+ QIcon icon;
+ qint64 warnLevel = stats.tilesHardLimit - stats.tilesHardLimit / 8;
+
+ if (stats.imageSize > warnLevel ||
+ stats.realMemorySize > warnLevel) {
+
+ icon = QIcon::fromTheme("dialog-warning");
+ QString suffix =
+ i18nc("tooltip on statusbar memory reporting button",
+ "\n\nWARNING:\tOut of memory! Swapping has been started.\n"
+ "\t\tPlease configure more RAM for Krita in Settings dialog");
+ longStats += suffix;
+ }
+
+ m_shortMemoryTag = shortStats;
+ m_longMemoryTag = longStats;
+ m_memoryStatusIcon = icon;
+}
+
+void KisStatusBar::showMemoryInfoToolTip()
+{
+ QToolTip::showText(QCursor::pos(), m_memoryReportBox->toolTip(), m_memoryReportBox);
+}
+
void KisStatusBar::updateSelectionToolTip()
{
updateSelectionIcon();
@@ -220,7 +326,9 @@ void KisStatusBar::setHelp(const QString &t)
void KisStatusBar::updateStatusBarProfileLabel()
{
- setProfile(m_view->image());
+ if (!m_imageView) return;
+
+ setProfile(m_imageView->image());
}
diff --git a/krita/ui/kis_statusbar.h b/krita/ui/kis_statusbar.h
index 5ed52dc..92a34af 100644
--- a/krita/ui/kis_statusbar.h
+++ b/krita/ui/kis_statusbar.h
@@ -21,12 +21,14 @@
#include <QObject>
#include <QPointer>
+#include <QIcon>
#include <kis_types.h>
#include "KisView.h"
class QLabel;
class QToolButton;
+class QPushButton;
class KSqueezedTextLabel;
class KisViewManager;
class KisProgressWidget;
@@ -58,6 +60,10 @@ public Q_SLOTS:
private Q_SLOTS:
void updateSelectionIcon();
+ void showMemoryInfoToolTip();
+
+private:
+ void updateMemoryStatus();
private:
@@ -66,12 +72,16 @@ private:
KisProgressWidget * m_progress;
QToolButton *m_selectionStatus;
- QLabel *m_imageSizeLabel;
+ QPushButton *m_memoryReportBox;
QLabel *m_pointerPositionLabel;
KSqueezedTextLabel *m_statusBarStatusLabel;
KSqueezedTextLabel *m_statusBarProfileLabel;
+
+ QString m_shortMemoryTag;
+ QString m_longMemoryTag;
+ QIcon m_memoryStatusIcon;
};
#endif
More information about the kimageshop
mailing list