diff --git a/.travis/build-ubuntu-14-04.sh b/.travis/build-ubuntu-14-04.sh index 888ad877c..4b31b693d 100755 --- a/.travis/build-ubuntu-14-04.sh +++ b/.travis/build-ubuntu-14-04.sh @@ -31,6 +31,7 @@ sudo apt-get install -y --force-yes \ build-essential \ check \ checkinstall \ + libexif-dev \ libgdk-pixbuf2.0-dev \ libglib2.0-dev \ libgtk2.0-dev \ diff --git a/INSTALL.md b/INSTALL.md index a519c8ee6..ca803c329 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -266,6 +266,7 @@ sudo apt-get install \ build-essential \ cmake \ ffmpeg \ + libexif-dev \ libgdk-pixbuf2.0-dev \ libglib2.0-dev \ libgtk2.0-dev \ @@ -300,6 +301,7 @@ sudo dnf groupinstall "Development Tools" "C Development Tools and Libraries" sudo dnf install \ ffmpeg-devel \ gtk2-devel \ + libexif-devel \ libXScrnSaver-devel \ libtool \ openal-soft-devel \ @@ -324,6 +326,7 @@ sudo dnf install \ ```bash sudo zypper install \ + libexif-devel \ libQt5Concurrent-devel \ libQt5Network-devel \ libQt5OpenGL-devel \ @@ -358,6 +361,7 @@ sudo apt-get install \ libavdevice-ffmpeg-dev \ libavfilter-ffmpeg-dev \ libavutil-ffmpeg-dev \ + libexif-dev \ libgdk-pixbuf2.0-dev \ libglib2.0-dev \ libgtk2.0-dev \ @@ -387,6 +391,7 @@ sudo apt-get install \ libavdevice-dev \ libavfilter-dev \ libavutil-dev \ + libexif-dev \ libgdk-pixbuf2.0-dev \ libglib2.0-dev \ libgtk2.0-dev \ diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake index a76162ca6..e53d501d7 100644 --- a/cmake/Dependencies.cmake +++ b/cmake/Dependencies.cmake @@ -95,6 +95,7 @@ search_dependency(LIBAVCODEC PACKAGE libavcodec) search_dependency(LIBAVDEVICE PACKAGE libavdevice) search_dependency(LIBAVFORMAT PACKAGE libavformat) search_dependency(LIBAVUTIL PACKAGE libavutil) +search_dependency(LIBEXIF PACKAGE libexif) search_dependency(LIBQRENCODE PACKAGE libqrencode) search_dependency(LIBSODIUM PACKAGE libsodium) search_dependency(LIBSWSCALE PACKAGE libswscale) diff --git a/osx/qTox-Mac-Deployer-ULTIMATE.sh b/osx/qTox-Mac-Deployer-ULTIMATE.sh index 0cae55c39..3bf093cd7 100755 --- a/osx/qTox-Mac-Deployer-ULTIMATE.sh +++ b/osx/qTox-Mac-Deployer-ULTIMATE.sh @@ -167,7 +167,7 @@ install() { else brew install cmake fi - brew install ffmpeg qrencode qt5 sqlcipher openal-soft + brew install ffmpeg libexif qrencode qt5 sqlcipher openal-soft fcho "Cloning filter_audio ... " git clone --branch v0.0.1 --depth=1 https://github.com/irungentoo/filter_audio "$FILTERAUIO_DIR" diff --git a/qtox.pro b/qtox.pro index f8f3ca857..c1726b267 100644 --- a/qtox.pro +++ b/qtox.pro @@ -157,6 +157,7 @@ win32 { -lavformat \ -lavcodec \ -lavutil \ + -lexif \ -lswscale \ -lOpenAL32 \ -lopus \ @@ -214,6 +215,7 @@ win32 { -lavdevice \ -lavcodec \ -lavutil \ + -lexif \ -lswscale \ -lz \ -ljpeg \ @@ -250,6 +252,7 @@ win32 { -lavdevice \ -lavcodec \ -lavutil \ + -lexif \ -lswscale \ -lqrencode \ -lsqlcipher \ diff --git a/src/chatlog/content/filetransferwidget.cpp b/src/chatlog/content/filetransferwidget.cpp index 86031d549..429fe4ba6 100644 --- a/src/chatlog/content/filetransferwidget.cpp +++ b/src/chatlog/content/filetransferwidget.cpp @@ -27,6 +27,8 @@ #include "src/widget/style.h" #include "src/widget/widget.h" +#include + #include #include #include @@ -528,7 +530,18 @@ void FileTransferWidget::showPreview(const QString& filename) // Subtract to make border visible const int size = qMax(ui->previewButton->width(), ui->previewButton->height()) - 4; - const QImage image = QImage(filename); + QFile imageFile(filename); + if (!imageFile.open(QIODevice::ReadOnly)) { + qCritical() << "Failed to open file for preview"; + return; + } + const QByteArray imageFileData = imageFile.readAll(); + QImage image = QImage::fromData(imageFileData); + const int exifOrientation = getExifOrientation(imageFileData.constData(), imageFileData.size()); + if (exifOrientation) { + applyTransformation(exifOrientation, image); + } + const QPixmap iconPixmap = scaleCropIntoSquare(QPixmap::fromImage(image), size); ui->previewButton->setIcon(QIcon(iconPixmap)); @@ -585,3 +598,56 @@ QPixmap FileTransferWidget::scaleCropIntoSquare(const QPixmap& source, const int // Picture was rectangle in the first place, no cropping return result; } + +int FileTransferWidget::getExifOrientation(const char* data, const int size) +{ + ExifData* exifData = exif_data_new_from_data(reinterpret_cast(data), size); + + if (!exifData) + return 0; + + int orientation = 0; + const ExifByteOrder byteOrder = exif_data_get_byte_order(exifData); + const ExifEntry* const exifEntry = exif_data_get_entry(exifData, EXIF_TAG_ORIENTATION); + if (exifEntry) { + orientation = exif_get_short(exifEntry->data, byteOrder); + } + exif_data_free(exifData); + return orientation; +} + +void FileTransferWidget::applyTransformation(const int orientation, QImage& image) +{ + QTransform exifTransform; + switch(static_cast(orientation)) + { + case ExifOrientation::TopLeft: + break; + case ExifOrientation::TopRight: + image = image.mirrored(1,0); + break; + case ExifOrientation::BottomRight: + exifTransform.rotate(180); + break; + case ExifOrientation::BottomLeft: + image = image.mirrored(0, 1); + break; + case ExifOrientation::LeftTop: + exifTransform.rotate(90); + image = image.mirrored(0, 1); + break; + case ExifOrientation::RightTop: + exifTransform.rotate(-90); + break; + case ExifOrientation::RightBottom: + exifTransform.rotate(-90); + image = image.mirrored(0, 1); + break; + case ExifOrientation::LeftBottom: + exifTransform.rotate(90); + break; + default: + qWarning() << "Invalid exif orientation passed to applyTransformation!"; + } + image = image.transformed(exifTransform); +} diff --git a/src/chatlog/content/filetransferwidget.h b/src/chatlog/content/filetransferwidget.h index 7af0799cb..882c5b9f5 100644 --- a/src/chatlog/content/filetransferwidget.h +++ b/src/chatlog/content/filetransferwidget.h @@ -75,6 +75,8 @@ private slots: private: static QPixmap scaleCropIntoSquare(const QPixmap& source, int targetSize); + static int getExifOrientation(const char* data, const int size); + static void applyTransformation(const int oritentation, QImage& image); private: Ui::FileTransferWidget* ui; @@ -92,6 +94,22 @@ private: qreal meanData[TRANSFER_ROLLING_AVG_COUNT] = {0.0}; bool active; + enum class ExifOrientation { + /* do not change values, this is exif spec + * + * name corresponds to where the 0 row and 0 column is in form row-column + * i.e. entry 5 here means that the 0'th row corresponds to the left side of the scene and the 0'th column corresponds + * to the top of the captured scene. This means that the image needs to be mirrored and rotated to be displayed. + */ + TopLeft = 1, + TopRight = 2, + BottomRight = 3, + BottomLeft = 4, + LeftTop = 5, + RightTop = 6, + RightBottom = 7, + LeftBottom = 8 + }; }; #endif // FILETRANSFERWIDGET_H