From 0e7082fe99f560cc42596c0dcae496f9e543ba04 Mon Sep 17 00:00:00 2001 From: Martin Burchell Date: Thu, 24 Aug 2023 16:19:59 +0100 Subject: [PATCH] Fix iPad photo orientation On iPad the photos from the QML ImageCapture imageSaved signal do not have the correct orientation. We could try to work out the orientation from the image metadata or try to grab the device orientation at the point the photo is taken. The preview image appears to have the correct orientation and same resolution as the saved image. So I've changed the CameraQml class to use this instead. --- tablet_qt/questionnairelib/quphoto.cpp | 39 ----------- tablet_qt/questionnairelib/quphoto.h | 5 -- .../camcops/camera_qml/PhotoPreview.qml | 4 +- .../resources/camcops/camera_qml/camera.qml | 11 ++-- tablet_qt/widgets/cameraqml.cpp | 64 +++++-------------- tablet_qt/widgets/cameraqml.h | 18 ++---- 6 files changed, 27 insertions(+), 114 deletions(-) diff --git a/tablet_qt/questionnairelib/quphoto.cpp b/tablet_qt/questionnairelib/quphoto.cpp index 108d92288..78b5fa6f2 100644 --- a/tablet_qt/questionnairelib/quphoto.cpp +++ b/tablet_qt/questionnairelib/quphoto.cpp @@ -246,8 +246,6 @@ void QuPhoto::takePhoto() qDebug() << "... CameraQml() created"; #endif connect(m_camera, &CameraQml::cancelled, this, &QuPhoto::cameraCancelled); - connect(m_camera, &CameraQml::rawImageCaptured, - this, &QuPhoto::rawImageCaptured); connect(m_camera, &CameraQml::imageCaptured, this, &QuPhoto::imageCaptured); @@ -333,43 +331,6 @@ void QuPhoto::imageCaptured(const QImage& image) } -void QuPhoto::rawImageCaptured(const QByteArray& data, - const QString& extension_without_dot, - const QString& mimetype) -{ -#ifdef DEBUG_CAMERA - qDebug() << Q_FUNC_INFO; -#endif - if (!m_camera) { - qWarning() << Q_FUNC_INFO << "... no camera!"; - return; - } - if (!m_questionnaire) { - qWarning() << Q_FUNC_INFO << "... no questionnaire!"; - return; - } - bool changed = false; - { // guard block - SlowGuiGuard guard = m_questionnaire->app().getSlowGuiGuard( - tr("Saving image..."), - tr("Saving")); -#ifdef DEBUG_CAMERA - qDebug() << "QuPhoto: setting field value to raw image..."; -#endif - changed = m_fieldref->setRawImage(data, - extension_without_dot, - mimetype); -#ifdef DEBUG_CAMERA - qDebug() << "QuPhoto: ... field value set to raw image."; -#endif - m_camera->finish(); // close the camera - } - if (changed) { - emit elementValueChanged(); - } -} - - void QuPhoto::rotate(const int angle_degrees_clockwise) { if (m_fieldref->isNull()) { diff --git a/tablet_qt/questionnairelib/quphoto.h b/tablet_qt/questionnairelib/quphoto.h index 3a6c302b1..1a574aac5 100644 --- a/tablet_qt/questionnairelib/quphoto.h +++ b/tablet_qt/questionnairelib/quphoto.h @@ -72,11 +72,6 @@ protected slots: // "User cancelled taking a photo." void cameraCancelled(); - // "Camera sends you this captured raw image." - void rawImageCaptured(const QByteArray& data, - const QString& extension_without_dot, - const QString& mimetype); - // "Camera sends you this captured QImage." void imageCaptured(const QImage& image); diff --git a/tablet_qt/resources/camcops/camera_qml/PhotoPreview.qml b/tablet_qt/resources/camcops/camera_qml/PhotoPreview.qml index de5d7b4dd..71f586f07 100644 --- a/tablet_qt/resources/camcops/camera_qml/PhotoPreview.qml +++ b/tablet_qt/resources/camcops/camera_qml/PhotoPreview.qml @@ -57,7 +57,7 @@ import QtMultimedia Item { signal closed - signal imageSavedToFile // RNC + signal previewSaved // RNC Image { // http://doc.qt.io/qt-6.2/qml-qtquick-image.html id: preview @@ -85,7 +85,7 @@ Item { text: qsTr("Save") onClicked: { console.log("Save button clicked") - imageSavedToFile() + previewSaved() } } } diff --git a/tablet_qt/resources/camcops/camera_qml/camera.qml b/tablet_qt/resources/camcops/camera_qml/camera.qml index d8805e76d..b63f9e643 100644 --- a/tablet_qt/resources/camcops/camera_qml/camera.qml +++ b/tablet_qt/resources/camcops/camera_qml/camera.qml @@ -58,7 +58,8 @@ import QtMultimedia Rectangle { id : cameraUI - signal imageSavedToFile(string filename) // RNC + signal imageCaptured(variant previewImage) + signal previewSaved signal fileNoLongerNeeded(string filename) // RNC width: 800 @@ -139,8 +140,8 @@ Rectangle { console.log("Image captured") stillControls.previewAvailable = true cameraUI.state = "PhotoPreview" + cameraUI.imageCaptured(previewImage) } - // RNC: onImageSaved: function(requestId, path) { console.log("onImageSaved: ", path) stillControls.fileSaved = true @@ -160,9 +161,9 @@ Rectangle { onClosed: cameraUI.state = "PhotoCapture" visible: (cameraUI.state === "PhotoPreview") focus: visible - onImageSavedToFile: { - console.log("Returning image with filename:", stillControls.filePath) - cameraUI.imageSavedToFile(stillControls.filePath) + onPreviewSaved: { + cameraUI.previewSaved() + cameraUI.fileNoLongerNeeded(stillControls.filePath) } } diff --git a/tablet_qt/widgets/cameraqml.cpp b/tablet_qt/widgets/cameraqml.cpp index db25149e8..cf5fbd0e5 100644 --- a/tablet_qt/widgets/cameraqml.cpp +++ b/tablet_qt/widgets/cameraqml.cpp @@ -166,8 +166,9 @@ void CameraQml::qmlFinishedLoading() Q_ASSERT(root); // It's possible to connect to non-root objects, but it's much cleaner to // route from QML child objects up to the QML root object, and then to C++. - connect(root, SIGNAL(imageSavedToFile(const QString&)), - this, SLOT(cameraHasCapturedImage(const QString&))); + connect(root, SIGNAL(imageCaptured(const QVariant&)), + this, SLOT(copyPreviewImage(const QVariant&))); + connect(root, SIGNAL(previewSaved()), this, SLOT(savePreviewImage())); connect(root, SIGNAL(fileNoLongerNeeded(const QString&)), this, SLOT(deleteSuperfluousFile(const QString&))); // ... we have to use SIGNAL() and SLOT() since C++ has no idea of the @@ -178,6 +179,18 @@ void CameraQml::qmlFinishedLoading() } +void CameraQml::copyPreviewImage(const QVariant& preview) +{ + m_preview = preview.value(); +} + + +void CameraQml::savePreviewImage() +{ + emit imageCaptured(m_preview); +} + + void CameraQml::deleteFile(const QString& filename) const { #ifdef DEBUG_CAMERA @@ -198,50 +211,3 @@ void CameraQml::deleteSuperfluousFile(const QString& filename) const #endif deleteFile(filename); } - - -void CameraQml::cameraHasCapturedImage(const QString& filename) -{ -#ifdef DEBUG_CAMERA - qDebug() << Q_FUNC_INFO; - qDebug() << "Camera image has arrived via temporary file" << filename; -#endif - - const QFileInfo fileinfo(filename); - const QString extension_without_dot = fileinfo.suffix(); - const QMimeDatabase mime_db; - const QMimeType mime_type = mime_db.mimeTypeForFile(filename); - // ... default method is to use filename and contents - // ... it will ALWAYS BE VALID, but may be "application/octet-stream" if - // Qt doesn't know what it is: - // http://doc.qt.io/qt-5/qmimedatabase.html#mimeTypeForFile - const QString mimetype_name = mime_type.name(); - - QFile file(filename); - if (mimetype_name != "application/octet-stream" && - file.open(QIODevice::ReadOnly)) { - - // We know the MIME type (and can read the file), so we can use the - // higher-performance method. - const QByteArray data = file.readAll(); - file.close(); - deleteFile(filename); -#ifdef DEBUG_CAMERA - qDebug() << "Camera image data loaded"; -#endif - emit rawImageCaptured(data, extension_without_dot, mimetype_name); - - } else { - -#ifdef DEBUG_CAMERA - qDebug() << "Camera image loaded"; -#endif - QImage img; - img.load(filename); - deleteFile(filename); - emit imageCaptured(img); - - } - - close(); -} diff --git a/tablet_qt/widgets/cameraqml.h b/tablet_qt/widgets/cameraqml.h index 5ac44f4c9..74b742fc8 100644 --- a/tablet_qt/widgets/cameraqml.h +++ b/tablet_qt/widgets/cameraqml.h @@ -86,16 +86,6 @@ class CameraQml : public OpenableWidget void finish(); signals: - // If possible, we will emit rawImageCaptured, because performance is - // better. Failing that, we will emit imageCaptured. ONE OR THE OTHER will - // be emitted. - - // "We've captured an image." High performance. - void rawImageCaptured(QByteArray data, // QByteArray is copy-on-write - QString extension_without_dot, - QString mimetype); - - // "We've captured an image." Lower performance. void imageCaptured(QImage image); // QImage is copy-on-write // "User has cancelled the operation." @@ -124,14 +114,14 @@ protected slots: // Called from m_qml_view's QQuickWidget::statusChanged. void qmlStatusChanged(QQuickWidget::Status status); + void copyPreviewImage(const QVariant& preview); + void savePreviewImage(); // "The camera QML says a temporary file is no longer needed." // Called from the fileNoLongerNeeded signal defined in camera.qml. void deleteSuperfluousFile(const QString& filename) const; - // "The camera QML has captured an image via a temporary file." - // Called from the imageSavedToFile signal defined in camera.qml. - void cameraHasCapturedImage(const QString& filename); - protected: QPointer m_qml_view; // our QML view widget +private: + QImage m_preview; };