diff --git a/CHANGELOG.md b/CHANGELOG.md index 33a1eca28..29d2b84aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ desktop: fix gas switches in UDDF exports core: allow of up to 6 O2 sensors (and corresponding voting logic) desktop: add divemode as a possible dive list column profile-widget: Now zomed in profiles can be panned with horizontal scroll. +media: support raw files if libraw is installed desktop: hide only events with the same severity when 'Hide similar events' is used equipment: mark gas mixes reported by the dive computer as 'inactive' as 'not used' equipment: include unused cylinders in merged dive if the preference is enabled diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e63fdcad..55af6d022 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -166,6 +166,11 @@ if(NOT ANDROID) endif() pkg_config_library(LIBUSB libusb-1.0 QUIET) pkg_config_library(LIBMTP libmtp QUIET) + pkg_config_library(LIBRAW libraw QUIET) +endif() + +if(LIBRAW_FOUND) + add_definitions(-DLIBRAW_SUPPORT) endif() include_directories(. diff --git a/INSTALL b/INSTALL index 0a08f606c..e1f811000 100644 --- a/INSTALL +++ b/INSTALL @@ -146,7 +146,7 @@ sudo dnf install autoconf automake bluez-libs-devel cmake gcc-c++ git \ qt5-qtbase-devel qt5-qtconnectivity-devel qt5-qtdeclarative-devel \ qt5-qtlocation-devel qt5-qtscript-devel qt5-qtsvg-devel \ qt5-qttools-devel qt5-qtwebkit-devel redhat-rpm-config \ - bluez-libs-devel libgit2-devel libzip-devel libmtp-devel + bluez-libs-devel libgit2-devel libzip-devel libmtp-devel libraw-devel Package names are sadly different on OpenSUSE @@ -157,7 +157,7 @@ sudo zypper install git gcc-c++ make autoconf automake libtool cmake libzip-deve libqt5-qtbase-devel libQt5WebKit5-devel libqt5-qtsvg-devel \ libqt5-qtscript-devel libqt5-qtdeclarative-devel \ libqt5-qtconnectivity-devel libqt5-qtlocation-devel libcurl-devel \ - bluez-devel libgit2-devel libmtp-devel + bluez-devel libgit2-devel libmtp-devel libraw-devel On Debian Bookworm this seems to work @@ -169,7 +169,7 @@ sudo apt install \ qml-module-qtlocation qml-module-qtpositioning qml-module-qtquick2 \ qt5-qmake qtchooser qtconnectivity5-dev qtdeclarative5-dev \ qtdeclarative5-private-dev qtlocation5-dev qtpositioning5-dev \ - qtscript5-dev qttools5-dev qttools5-dev-tools libmtp-dev + qtscript5-dev qttools5-dev qttools5-dev-tools libmtp-dev libraw-dev In order to build and run mobile-on-desktop, you also need @@ -189,7 +189,7 @@ sudo apt install \ qml-module-qtlocation qml-module-qtpositioning qml-module-qtquick2 \ qt5-qmake qtchooser qtconnectivity5-dev qtdeclarative5-dev \ qtdeclarative5-private-dev qtlocation5-dev qtpositioning5-dev \ - qtscript5-dev qttools5-dev qttools5-dev-tools libmtp-dev + qtscript5-dev qttools5-dev qttools5-dev-tools libmtp-dev libraw-dev In order to build and run mobile-on-desktop, you also need @@ -209,7 +209,7 @@ sudo apt install \ qml-module-qtlocation qml-module-qtpositioning qml-module-qtquick2 \ qt5-qmake qtchooser qtconnectivity5-dev qtdeclarative5-dev \ qtdeclarative5-private-dev qtlocation5-dev qtpositioning5-dev \ - qtscript5-dev qttools5-dev qttools5-dev-tools libmtp-dev + qtscript5-dev qttools5-dev qttools5-dev-tools libmtp-dev libraw-dev In order to build and run mobile-on-desktop, you also need @@ -234,7 +234,7 @@ sudo apt install \ qml-module-qtlocation qml-module-qtpositioning qml-module-qtquick2 \ qt5-qmake qtchooser qtconnectivity5-dev qtdeclarative5-dev \ qtdeclarative5-private-dev qtlocation5-dev qtpositioning5-dev \ - qtscript5-dev qttools5-dev qttools5-dev-tools libmtp-dev + qtscript5-dev qttools5-dev qttools5-dev-tools libmtp-dev libraw-dev Note that you'll need to increase the swap space as the default of 100MB doesn't seem to be enough. 1024MB worked on a 3B+. diff --git a/core/imagedownloader.cpp b/core/imagedownloader.cpp index decc7e56d..3c5a85d32 100644 --- a/core/imagedownloader.cpp +++ b/core/imagedownloader.cpp @@ -13,6 +13,9 @@ #include #include #include +#ifdef LIBRAW_SUPPORT +#include +#endif #include @@ -78,12 +81,40 @@ static bool hasVideoFileExtension(const QString &filename) return false; } -// Fetch a picture from the given filename and determine its type (picture of video). +#ifdef LIBRAW_SUPPORT +QImage fetchRawThumbnail(const QString &filename) +{ + LibRaw raw; // Might think about reusing that, one instance per thread + + // TODO: Convert filename to UTF-16 for windows + if (raw.open_file(qPrintable(filename)) != LIBRAW_SUCCESS || + raw.unpack_thumb() != LIBRAW_SUCCESS) { + return QImage(); + } + + switch (raw.imgdata.thumbnail.tformat) { + case LIBRAW_THUMBNAIL_JPEG: { + QImage res; + res.loadFromData(reinterpret_cast(raw.imgdata.thumbnail.thumb), + raw.imgdata.thumbnail.tlength); + return res; + } + case LIBRAW_THUMBNAIL_BITMAP: + return QImage(reinterpret_cast(raw.imgdata.thumbnail.thumb), + raw.imgdata.thumbnail.twidth, raw.imgdata.thumbnail.theight, + QImage::Format_RGB888); + default: // Unsupported + return QImage(); + } +} + +#endif + +// Fetch a picture from the given filename and determine its type (picture or video). // If this is a non-remote file, fetch it from disk. Remote files are fetched from the // net in a background thread. In such a case, the output-type is set to MEDIATYPE_STILL_LOADING. // If the input-flag "tryDownload" is set to false, no download attempt is made. This is to // prevent infinite loops, where failed image downloads would be repeated ad infinitum. -// Returns: fetched image, type Thumbnailer::Thumbnail Thumbnailer::fetchImage(const QString &urlfilename, const QString &originalFilename, bool tryDownload) { QUrl url = QUrl::fromUserInput(urlfilename); @@ -101,6 +132,12 @@ Thumbnailer::Thumbnail Thumbnailer::fetchImage(const QString &urlfilename, const // Try if Qt can parse this image. If it does, use this as a thumbnail. QImage thumb(filename); + +#ifdef LIBRAW_SUPPORT + // If note, perhaps a raw image? + if (thumb.isNull()) + thumb = fetchRawThumbnail(filename); +#endif if (!thumb.isNull()) { int size = maxThumbnailSize(); thumb = thumb.scaled(size, size, Qt::KeepAspectRatio); diff --git a/core/metadata.cpp b/core/metadata.cpp index 2596276c6..4892876a2 100644 --- a/core/metadata.cpp +++ b/core/metadata.cpp @@ -7,6 +7,9 @@ #include #include #include +#ifdef LIBRAW_SUPPORT +#include +#endif // Weirdly, android builds fail owing to undefined UINT64_MAX #ifndef UINT64_MAX @@ -528,6 +531,33 @@ static bool parseASF(QFile &f, metadata *metadata) return false; } +// Transform a (deg, min, sec) float triple into microdegrees +degrees_t degminsec_to_udeg(float a[3]) +{ + if (a[0] == 0.0 && a[1] == 0.0 && a[2] == 0.0) + return { 0 }; + return { static_cast(round(a[0] * 1'000'000.0 + + a[1] * (1'000'000.0/60.0) + + a[2] * (1'000'000.0/3600.0))) }; +} + +#ifdef LIBRAW_SUPPORT +static bool parseRaw(const char *fn, metadata *metadata) +{ + LibRaw raw; // Might think about reusing that + + // TODO: Convert filename to UTF-16 for windows + if (raw.open_file(fn) != LIBRAW_SUCCESS) + return false; + + metadata->timestamp = raw.imgdata.other.timestamp; + metadata->location.lat = degminsec_to_udeg(raw.imgdata.other.parsed_gps.latitude); + metadata->location.lon = degminsec_to_udeg(raw.imgdata.other.parsed_gps.longitude); + + return true; +} +#endif + extern "C" mediatype_t get_metadata(const char *filename_in, metadata *data) { data->timestamp = 0; @@ -535,6 +565,11 @@ extern "C" mediatype_t get_metadata(const char *filename_in, metadata *data) data->location.lat.udeg = 0; data->location.lon.udeg = 0; +#ifdef LIBRAW_SUPPORT + if (parseRaw(filename_in, data)) + return MEDIATYPE_PICTURE; +#endif + QString filename = localFilePath(QString(filename_in)); QFile f(filename); if (!f.open(QIODevice::ReadOnly)) diff --git a/core/qthelper.cpp b/core/qthelper.cpp index 8bde6a53d..a7cec5cc1 100644 --- a/core/qthelper.cpp +++ b/core/qthelper.cpp @@ -1168,6 +1168,17 @@ const QStringList videoExtensionsList = { ".avi", ".mp4", ".mov", ".mpeg", ".mpg", ".wmv" }; +// Raw extensions according to https://en.wikipedia.org/wiki/Raw_image_format +static const QStringList rawExtensionsList = { +#ifdef LIBRAW_SUPPORT + "*.3fr", "*.ari", "*.arw", "*.bay", "*.braw", "*.crw", "*.cr2", "*.cr3", "*.cap", + "*.data", "*.dcs", "*.dcr", "*.dng", "*.drf", "*.eip", "*.erf", "*.fff", "*.gpr", + "*.iiq", "*.k25", "*.kdc", "*.mdc", "*.mef", "*.mos", "*.mrw", "*.nef", "*.nrw", + "*.obm", "*.orf", "*.pef", "*.ptx", "*.pxn", "*.r3d", "*.raf", "*.raw", "*.rwl", + "*.rw2", "*.rwz", "*.sr2", "*.srf", "*.srw", "*.x3f" +#endif +}; + QStringList mediaExtensionFilters() { return imageExtensionFilters() + videoExtensionFilters(); @@ -1178,7 +1189,7 @@ QStringList imageExtensionFilters() QStringList filters; for (QString format: QImageReader::supportedImageFormats()) filters.append("*." + format); - return filters; + return filters + rawExtensionsList; } QStringList videoExtensionFilters() diff --git a/scripts/docker/trusty-qt512/Dockerfile b/scripts/docker/trusty-qt512/Dockerfile index 0826e9ee1..6990dd789 100644 --- a/scripts/docker/trusty-qt512/Dockerfile +++ b/scripts/docker/trusty-qt512/Dockerfile @@ -17,7 +17,7 @@ RUN apt-get -y update && apt-get install -y \ gcc-7 g++-7 make git autoconf automake libtool pkg-config \ curl libdbus-1-3 libexpat1 libfontconfig1 libfreetype6 \ libexpat1-dev libgl1-mesa-dev libgl1-mesa-glx \ - ruby gperf bison libx11-6 libx11-xcb1 libjpeg-dev libpng-dev \ + ruby gperf bison libx11-6 libx11-xcb1 libjpeg-dev libpng-dev libraw-dev \ libicu-dev libXcomposite-dev libXrender-dev libgstreamer-plugins-base1.0 \ libxml2-dev libxslt1-dev libzip-dev libsqlite3-dev libusb-1.0-0-dev \ libssl-dev libssh2-1-dev libcurl4-openssl-dev mesa-common-dev libqt5gui5 \