diff options
author | Robert C. Helling <helling@atdotde.de> | 2015-02-26 14:39:42 +0100 |
---|---|---|
committer | Dirk Hohndel <dirk@hohndel.org> | 2015-02-27 06:29:44 +0100 |
commit | b02bf002a677e45dbaa4d28edadbaafc89a7badc (patch) | |
tree | 86f26c0dc8edbf66999e602277a15bb6a09dc38e | |
parent | ea00fdb36ab727b2f403a9feed5e8d96fb6659fc (diff) | |
download | subsurface-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.c | 2 | ||||
-rw-r--r-- | dive.h | 1 | ||||
-rw-r--r-- | load-git.c | 8 | ||||
-rw-r--r-- | parse-xml.c | 2 | ||||
-rw-r--r-- | qt-ui/divepicturewidget.cpp | 46 | ||||
-rw-r--r-- | qt-ui/divepicturewidget.h | 7 | ||||
-rw-r--r-- | qt-ui/mainwindow.cpp | 4 | ||||
-rw-r--r-- | qthelper.cpp | 69 | ||||
-rw-r--r-- | qthelper.h | 9 | ||||
-rw-r--r-- | save-git.c | 1 | ||||
-rw-r--r-- | save-xml.c | 5 |
11 files changed, 139 insertions, 15 deletions
@@ -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) @@ -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"); } |