summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Robert C. Helling <helling@atdotde.de>2015-02-26 14:39:42 +0100
committerGravatar Dirk Hohndel <dirk@hohndel.org>2015-02-27 06:29:44 +0100
commitb02bf002a677e45dbaa4d28edadbaafc89a7badc (patch)
tree86f26c0dc8edbf66999e602277a15bb6a09dc38e
parentea00fdb36ab727b2f403a9feed5e8d96fb6659fc (diff)
downloadsubsurface-b02bf002a677e45dbaa4d28edadbaafc89a7badc.tar.gz
Add hashes to images
Upon successfull reading an image file, this computes a SHA1 hash of the image and saves it with the picture tag in the log file. When a file is not successfully loaded (for example because the log was created on a different computer) we look up the hash in a dictionary that maps hashes to local file names. That dictionary (actually two for both directions), is loaded on startup and saved upon destruction of the main window. Signed-off-by: Robert C. Helling <helling@atdotde.de> Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
-rw-r--r--dive.c2
-rw-r--r--dive.h1
-rw-r--r--load-git.c8
-rw-r--r--parse-xml.c2
-rw-r--r--qt-ui/divepicturewidget.cpp46
-rw-r--r--qt-ui/divepicturewidget.h7
-rw-r--r--qt-ui/mainwindow.cpp4
-rw-r--r--qthelper.cpp69
-rw-r--r--qthelper.h9
-rw-r--r--save-git.c1
-rw-r--r--save-xml.c5
11 files changed, 139 insertions, 15 deletions
diff --git a/dive.c b/dive.c
index 758368615..fc0fbb855 100644
--- a/dive.c
+++ b/dive.c
@@ -392,6 +392,7 @@ static void copy_pl(struct picture *sp, struct picture *dp)
{
*dp = *sp;
dp->filename = copy_string(sp->filename);
+ dp->hash = copy_string(sp->hash);
}
/* copy an element in a list of tags */
@@ -2936,6 +2937,7 @@ static void picture_free(struct picture *p)
if (!p)
return;
free(p->filename);
+ free(p->hash);
free(p);
}
void dive_remove_picture(char *filename)
diff --git a/dive.h b/dive.h
index 91c160e1f..e189b860d 100644
--- a/dive.h
+++ b/dive.h
@@ -366,6 +366,7 @@ struct dive_components {
/* picture list and methods related to dive picture handling */
struct picture {
char *filename;
+ char *hash;
offset_t offset;
degrees_t latitude;
degrees_t longitude;
diff --git a/load-git.c b/load-git.c
index 053a7140e..be25e8e94 100644
--- a/load-git.c
+++ b/load-git.c
@@ -828,6 +828,12 @@ static void parse_picture_gps(char *line, struct membuffer *str, void *_pic)
pic->longitude = parse_degrees(line, &line);
}
+static void parse_picture_hash(char *line, struct membuffer *str, void *_pic)
+{
+ struct picture *pic = _pic;
+ pic->hash = get_utf8(str);
+}
+
/* These need to be sorted! */
struct keyword_action dc_action[] = {
#undef D
@@ -900,7 +906,7 @@ static void settings_parser(char *line, struct membuffer *str, void *_unused)
static struct keyword_action picture_action[] = {
#undef D
#define D(x) { #x, parse_picture_ ## x }
- D(filename), D(gps)
+ D(filename), D(gps), D(hash)
};
static void picture_parser(char *line, struct membuffer *str, void *_pic)
diff --git a/parse-xml.c b/parse-xml.c
index 90559d6c4..a42f8c854 100644
--- a/parse-xml.c
+++ b/parse-xml.c
@@ -1296,6 +1296,8 @@ static void try_to_fill_dive(struct dive *dive, const char *name, char *buf)
return;
if (MATCH("gps.picture", gps_picture_location, cur_picture))
return;
+ if (MATCH("hash.picture", utf8_string, &cur_picture->hash))
+ return;
if (MATCH("cylinderstartpressure", pressure, &dive->cylinder[0].start))
return;
if (MATCH("cylinderendpressure", pressure, &dive->cylinder[0].end))
diff --git a/qt-ui/divepicturewidget.cpp b/qt-ui/divepicturewidget.cpp
index 92695b6a6..a0d209b6d 100644
--- a/qt-ui/divepicturewidget.cpp
+++ b/qt-ui/divepicturewidget.cpp
@@ -3,7 +3,25 @@
#include "dive.h"
#include "divelist.h"
#include <QtConcurrentMap>
+#include <QtConcurrentRun>
#include <QDir>
+#include <QCryptographicHash>
+#include <mainwindow.h>
+#include <qthelper.h>
+
+SHashedImage::SHashedImage(struct picture *picture) : QImage(picture->filename)
+{
+ if (isNull()) {
+ // Hash lookup.
+ load(fileFromHash(picture->hash));
+ if (!isNull())
+ QtConcurrent::run(updateHash, picture);
+ } else {
+ QByteArray hash = hashFile(QString(picture->filename));
+ free(picture->hash);
+ picture->hash = strdup(hash.toHex().data());
+ }
+}
DivePictureModel *DivePictureModel::instance()
{
@@ -15,20 +33,21 @@ DivePictureModel::DivePictureModel() : numberOfPictures(0)
{
}
-typedef QPair<QString, QImage> SPixmap;
-typedef QList<SPixmap> SPixmapList;
+typedef struct picture *picturepointer;
+typedef QPair<picturepointer, QImage> SPixmap;
+typedef QList<struct picture *> SPictureList;
-SPixmap scaleImages(const QString &s)
+SPixmap scaleImages(picturepointer picture)
{
static QHash <QString, QImage > cache;
SPixmap ret;
- ret.first = s;
- if (cache.contains(s)) {
- ret.second = cache.value(s);
+ ret.first = picture;
+ if (cache.contains(picture->filename) && !cache.value(picture->filename).isNull()) {
+ ret.second = cache.value(picture->filename);
} else {
int dim = defaultIconMetrics().sz_pic;
- QImage p = QImage(s).scaled(dim, dim, Qt::KeepAspectRatio);
- cache.insert(s, p);
+ QImage p = SHashedImage(picture).scaled(dim, dim, Qt::KeepAspectRatio);
+ cache.insert(picture->filename, p);
ret.second = p;
}
return ret;
@@ -49,14 +68,15 @@ void DivePictureModel::updateDivePictures()
}
stringPixmapCache.clear();
- QStringList pictures;
+ SPictureList pictures;
FOR_EACH_PICTURE_NON_PTR(displayed_dive) {
stringPixmapCache[QString(picture->filename)].offsetSeconds = picture->offset.seconds;
- pictures.push_back(QString(picture->filename));
+ pictures.push_back(picture);
}
- Q_FOREACH (const SPixmap &pixmap, QtConcurrent::blockingMapped<SPixmapList>(pictures, scaleImages))
- stringPixmapCache[pixmap.first].image = pixmap.second;
+ QList<SPixmap> list = QtConcurrent::blockingMapped(pictures, scaleImages);
+ Q_FOREACH (const SPixmap &pixmap, list)
+ stringPixmapCache[pixmap.first->filename].image = pixmap.second;
beginInsertRows(QModelIndex(), 0, numberOfPictures - 1);
endInsertRows();
@@ -121,5 +141,5 @@ DivePictureWidget::DivePictureWidget(QWidget *parent) : QListView(parent)
void DivePictureWidget::doubleClicked(const QModelIndex &index)
{
QString filePath = model()->data(index, Qt::DisplayPropertyRole).toString();
- emit photoDoubleClicked(filePath);
+ emit photoDoubleClicked(localFilePath(filePath));
}
diff --git a/qt-ui/divepicturewidget.h b/qt-ui/divepicturewidget.h
index aa524e1a6..e8104a1db 100644
--- a/qt-ui/divepicturewidget.h
+++ b/qt-ui/divepicturewidget.h
@@ -5,11 +5,18 @@
#include <QListView>
#include <QThread>
+typedef QPair<QString, QByteArray> SHashedFilename;
+
struct PhotoHelper {
QImage image;
int offsetSeconds;
};
+class SHashedImage : public QImage {
+public:
+ SHashedImage(struct picture *picture);
+};
+
class DivePictureModel : public QAbstractTableModel {
Q_OBJECT
public:
diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp
index 7b204626c..8c3b44c48 100644
--- a/qt-ui/mainwindow.cpp
+++ b/qt-ui/mainwindow.cpp
@@ -37,6 +37,8 @@
#endif
#include <QNetworkProxy>
#include <QUndoStack>
+#include <qthelper.h>
+#include <QtConcurrentRun>
MainWindow *MainWindow::m_Instance = NULL;
@@ -50,6 +52,7 @@ MainWindow::MainWindow() : QMainWindow(),
Q_ASSERT_X(m_Instance == NULL, "MainWindow", "MainWindow recreated!");
m_Instance = this;
ui.setupUi(this);
+ read_hashes();
// Define the States of the Application Here, Currently the states are situations where the different
// widgets will change on the mainwindow.
@@ -201,6 +204,7 @@ MainWindow::MainWindow() : QMainWindow(),
MainWindow::~MainWindow()
{
+ write_hashes();
m_Instance = NULL;
}
diff --git a/qthelper.cpp b/qthelper.cpp
index 4cedc5597..74c67d769 100644
--- a/qthelper.cpp
+++ b/qthelper.cpp
@@ -27,6 +27,11 @@
#include <QUrlQuery>
#include <QEventLoop>
#include <QDateTime>
+#include <QSaveFile>
+#include <QDir>
+#include <QImageReader>
+#include <QtConcurrent>
+#include "divepicturewidget.h"
#include <libxslt/documents.h>
@@ -780,3 +785,67 @@ extern "C" void reverseGeoLookup(degrees_t latitude, degrees_t longitude, uint32
ds->notes = add_to_string(ds->notes, "countrytag: %s", address.value("country").toString().toUtf8().data());
}
}
+
+QHash<QString, QByteArray> hashOf;
+QHash<QByteArray, QString> localFilenameOf;
+
+extern "C" char * hashstring(char * filename)
+{
+ return hashOf[QString(filename)].toHex().data();
+}
+
+void read_hashes()
+{
+ QFile hashfile(QString(system_default_directory()).append("/hashes"));
+ if (hashfile.open(QIODevice::ReadOnly)) {
+ QDataStream stream(&hashfile);
+ stream >> localFilenameOf;
+ hashfile.close();
+ }
+}
+
+void write_hashes()
+{
+ QSaveFile hashfile(QString(system_default_directory()).append("/hashes"));
+ if (hashfile.open(QIODevice::WriteOnly)) {
+ QDataStream stream(&hashfile);
+ stream << localFilenameOf;
+ hashfile.commit();
+ } else {
+ qDebug() << "cannot open" << hashfile.fileName();
+ }
+}
+
+void add_hash(const QString filename, QByteArray hash)
+{
+ hashOf[filename] = hash;
+ localFilenameOf[hash] = filename;
+}
+
+QByteArray hashFile(const QString filename)
+{
+ QCryptographicHash hash(QCryptographicHash::Sha1);
+ QFile imagefile(filename);
+ imagefile.open(QIODevice::ReadOnly);
+ hash.addData(&imagefile);
+ add_hash(filename, hash.result());
+ return hash.result();
+}
+
+QString localFilePath(const QString originalFilename)
+{
+ return localFilenameOf[hashOf[originalFilename]];
+}
+
+QString fileFromHash(char *hash)
+{
+ return localFilenameOf[QByteArray::fromHex(hash)];
+}
+
+void updateHash(struct picture *picture) {
+ QByteArray hash = hashFile(fileFromHash(picture->hash));
+ hashOf[QString(picture->filename)] = hash;
+ char *old = picture->hash;
+ picture->hash = strdup(hash.toHex());
+ free(old);
+}
diff --git a/qthelper.h b/qthelper.h
index a367a9dac..e164a41b6 100644
--- a/qthelper.h
+++ b/qthelper.h
@@ -7,6 +7,7 @@
#include "dive.h"
#include "divelist.h"
#include <QTranslator>
+#include <QDir>
// global pointers for our translation
extern QTranslator *qtTranslator, *ssrfTranslator;
@@ -16,5 +17,11 @@ bool gpsHasChanged(struct dive *dive, struct dive *master, const QString &gps_te
extern "C" const char *printGPSCoords(int lat, int lon);
QList<int> getDivesInTrip(dive_trip_t *trip);
QString gasToStr(struct gasmix gas);
-
+void read_hashes();
+void write_hashes();
+void updateHash(struct picture *picture);
+QByteArray hashFile(const QString filename);
+void add_hash(const QString filename, QByteArray &hash);
+QString localFilePath(const QString originalFilename);
+QString fileFromHash(char *hash);
#endif // QTHELPER_H
diff --git a/save-git.c b/save-git.c
index 0125f64d7..a7b51446a 100644
--- a/save-git.c
+++ b/save-git.c
@@ -585,6 +585,7 @@ static int save_one_picture(git_repository *repo, struct dir *dir, struct pictur
show_utf8(&buf, "filename ", pic->filename, "\n");
show_gps(&buf, pic->latitude, pic->longitude);
+ show_utf8(&buf, "hash ", pic->hash, "\n");
/* Picture loading will load even negative offsets.. */
if (offset < 0) {
diff --git a/save-xml.c b/save-xml.c
index 8366f87ff..c68b5b633 100644
--- a/save-xml.c
+++ b/save-xml.c
@@ -359,6 +359,8 @@ static void save_dc(struct membuffer *b, struct dive *dive, struct divecomputer
put_format(b, " </divecomputer>\n");
}
+extern char * hashstring(char * filename);
+
static void save_picture(struct membuffer *b, struct picture *pic)
{
put_string(b, " <picture filename='");
@@ -377,6 +379,9 @@ static void save_picture(struct membuffer *b, struct picture *pic)
put_degrees(b, pic->latitude, " gps='", " ");
put_degrees(b, pic->longitude, "", "'");
}
+ if (hashstring(pic->filename))
+ put_format(b, " hash='%s'", hashstring(pic->filename));
+
put_string(b, "/>\n");
}