From c18b2c13e923ff50e71e2c84bdaa076ceb7f8ccb Mon Sep 17 00:00:00 2001 From: "pls.153" Date: Tue, 8 Nov 2022 16:55:09 +0100 Subject: [PATCH] example 'camera': fix orientation of both video and saved images --- examples/camera/app.asd | 1 + examples/camera/app.pro | 10 +++++---- examples/camera/cpp/qt.cpp | 33 ++++++++++++++++++++++++++++ examples/camera/cpp/qt.h | 22 +++++++++++++++++++ examples/camera/cpp/qt.pro | 27 +++++++++++++++++++++++ examples/camera/lisp/main.lisp | 2 ++ examples/camera/lisp/qt.lisp | 17 ++++++++++++++ examples/camera/lisp/web-server.lisp | 6 +++-- examples/camera/qml/main.qml | 25 ++++++++++++++++++++- examples/camera/readme.md | 6 +++-- examples/camera/run.lisp | 2 ++ 11 files changed, 142 insertions(+), 9 deletions(-) create mode 100644 examples/camera/cpp/qt.cpp create mode 100644 examples/camera/cpp/qt.h create mode 100644 examples/camera/cpp/qt.pro create mode 100644 examples/camera/lisp/qt.lisp diff --git a/examples/camera/app.asd b/examples/camera/app.asd index cadca72..cda9fc5 100644 --- a/examples/camera/app.asd +++ b/examples/camera/app.asd @@ -2,6 +2,7 @@ :serial t :depends-on (#-:depends-loaded :s-http-server) :components ((:file "lisp/package") + (:file "lisp/qt") (:file "lisp/web-server") (:file "lisp/main"))) diff --git a/examples/camera/app.pro b/examples/camera/app.pro index ed3d7b2..ea7baf2 100644 --- a/examples/camera/app.pro +++ b/examples/camera/app.pro @@ -30,7 +30,7 @@ win32: PRE_TARGETDEPS = tmp/app.lib QT += quick qml multimedia TEMPLATE = app CONFIG += c++17 no_keywords release -DEFINES = DESKTOP_APP INI_LISP INI_ECL_CONTRIB +DEFINES += DESKTOP_APP INI_LISP INI_ECL_CONTRIB QT_EXTENSION INCLUDEPATH = /usr/local/include ECL_VERSION = $$lower($$system(ecl -v)) ECL_VERSION = $$replace(ECL_VERSION, " ", "-") @@ -96,9 +96,11 @@ ios { LIBS += -llqml -llisp } -LIBS += -Ltmp -lapp -HEADERS += ../../src/cpp/main.h -SOURCES += ../../src/cpp/main.cpp +LIBS += -Ltmp -lapp +INCLUDEPATH += ../../../src/cpp + +HEADERS += ../../src/cpp/main.h cpp/qt.h +SOURCES += ../../src/cpp/main.cpp cpp/qt.cpp RESOURCES += $$files(qml/*) RESOURCES += $$files(i18n/*.qm) diff --git a/examples/camera/cpp/qt.cpp b/examples/camera/cpp/qt.cpp new file mode 100644 index 0000000..59b36fb --- /dev/null +++ b/examples/camera/cpp/qt.cpp @@ -0,0 +1,33 @@ +#include "qt.h" +#include +#include + +#ifdef PLUGIN + #include +#else + #include +#endif + +QT_BEGIN_NAMESPACE + +QObject* ini() { + static QObject* qt = nullptr; + if (qt == nullptr) { + qt = new QT; +#ifdef PLUGIN + ini_lisp(); +#endif + } + return qt; +} + +QVariant QT::rotateImage(const QVariant& imagePath, const QVariant& angle) { + // rotates image, replacing it; must be called from the UI thread, see QRUN* + QString path(imagePath.toString()); + QImage img(path); + QImage rotated = img.transformed(QTransform().rotate(angle.toReal())); + rotated.save(path); + return imagePath; +} + +QT_END_NAMESPACE diff --git a/examples/camera/cpp/qt.h b/examples/camera/cpp/qt.h new file mode 100644 index 0000000..991ee73 --- /dev/null +++ b/examples/camera/cpp/qt.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#ifdef Q_CC_MSVC +#define LIB_EXPORT __declspec(dllexport) +#else +#define LIB_EXPORT +#endif + +QT_BEGIN_NAMESPACE + +extern "C" { LIB_EXPORT QObject* ini(); } + +class QT : public QObject { + Q_OBJECT + +public: + Q_INVOKABLE QVariant rotateImage(const QVariant&, const QVariant&); +}; + +QT_END_NAMESPACE diff --git a/examples/camera/cpp/qt.pro b/examples/camera/cpp/qt.pro new file mode 100644 index 0000000..a45275f --- /dev/null +++ b/examples/camera/cpp/qt.pro @@ -0,0 +1,27 @@ +QT += gui +TEMPLATE = lib +CONFIG += c++17 plugin release no_keywords +DEFINES += PLUGIN +INCLUDEPATH = /usr/local/include ../../../src/cpp +LIBS = -L/usr/local/lib -lecl +DESTDIR = ./ +TARGET = qt +OBJECTS_DIR = ./tmp/ +MOC_DIR = ./tmp/ + +HEADERS += qt.h +SOURCES += qt.cpp + +linux { + LIBS += -L../../../platforms/linux/lib +} + +macx { + LIBS += -L../../../platforms/macos/lib +} + +win32 { + include(../../../src/windows.pri) + + LIBS += -L../../../platforms/windows/lib +} diff --git a/examples/camera/lisp/main.lisp b/examples/camera/lisp/main.lisp index 73d134e..e964362 100644 --- a/examples/camera/lisp/main.lisp +++ b/examples/camera/lisp/main.lisp @@ -1,2 +1,4 @@ (in-package :camera) +(qlater 'qt:ini) + diff --git a/examples/camera/lisp/qt.lisp b/examples/camera/lisp/qt.lisp new file mode 100644 index 0000000..fa62ab7 --- /dev/null +++ b/examples/camera/lisp/qt.lisp @@ -0,0 +1,17 @@ +(defpackage :qt + (:use :cl :qml) + (:export + #:*cpp* + #:ini + #:rotate-image)) + +(in-package :qt) + +(defvar *cpp* nil) + +(defun ini () + (setf *cpp* + #+qt-plugin (qload-c++ "cpp/qt") + #-qt-plugin (qfind-child nil "QT")) + (let ((*package* (find-package :qt))) + (define-qt-wrappers *cpp*))) diff --git a/examples/camera/lisp/web-server.lisp b/examples/camera/lisp/web-server.lisp index 5ff7894..baae969 100644 --- a/examples/camera/lisp/web-server.lisp +++ b/examples/camera/lisp/web-server.lisp @@ -14,7 +14,7 @@ @@ -30,8 +30,10 @@ img { width: 100px; border-width: 10px; border-style: solid; border-color: white (register-context-handler *web-server* "/" 'static-resource-handler :arguments (list image-path))) -(defun create-index.html (image-path) ; called from QML +(defun create-index.html (image-path rotation) ; called from QML "Creates 'index.html' for local web-server." + #+ios ; on iOS the image must be rotated + (qt:rotate-image qt:*cpp* image-path rotation) (unless *image-path* (ini image-path)) (setf *image-path* image-path) diff --git a/examples/camera/qml/main.qml b/examples/camera/qml/main.qml index 9f0737a..4478f75 100644 --- a/examples/camera/qml/main.qml +++ b/examples/camera/qml/main.qml @@ -1,5 +1,6 @@ import QtQuick 2.15 import QtQuick.Controls 2.15 +import QtQuick.Window 2.15 import QtMultimedia 5.15 Rectangle { @@ -7,27 +8,49 @@ Rectangle { height: 360 color: "black" + property int rotation: 0 // iOS: saved image will be rotated by this angle + Camera { id: camera objectName: "camera" imageCapture { onImageSaved: { + Lisp.call("camera:create-index.html", path, rotation) imagePaths.append({"path": "file://" + path}) listView.positionViewAtEnd() - Lisp.call("camera:create-index.html", path) } } } VideoOutput { + id: videoOutput objectName: "output" source: camera anchors.fill: parent anchors.bottomMargin: listView.height + 10 focus: visible // to receive focus and capture key events when visible + autoOrientation: (Qt.platform.os === "android") + + Component.onCompleted: adaptOrientation(Screen.orientation) } + // for iOS + function adaptOrientation(orientation) { + if (Qt.platform.os === "ios") { + var angle = 0 + switch (orientation) { + case Qt.PortraitOrientation: angle = -90; break + case Qt.InvertedLandscapeOrientation: angle = 180; break + case Qt.InvertedPortraitOrientation: angle = 90; break + } + videoOutput.orientation = angle + rotation = (Math.abs(angle) === 90) ? -angle : angle + } + } + + Screen.onOrientationChanged: adaptOrientation(Screen.orientation) + // menu buttons Column { diff --git a/examples/camera/readme.md b/examples/camera/readme.md index 97187c6..865fee0 100644 --- a/examples/camera/readme.md +++ b/examples/camera/readme.md @@ -29,8 +29,10 @@ http://192.168.1.x:1701/ **1701** is the default port of `:s-http-server` (from Quicklisp), which was chosen here because it's both relatively small and works well on mobile. -You may wonder about the "wrong" orientation of the images in the browser: this -depends both on the camera orientation and on the mobile OS. +This example needs a small Qt extension for rotating images. That's necessary +here because different devices may have different camera orientations, so the +saved images would be displayed with the wrong orientation in the desktop +browser. diff --git a/examples/camera/run.lisp b/examples/camera/run.lisp index 7bd2360..4787fa5 100644 --- a/examples/camera/run.lisp +++ b/examples/camera/run.lisp @@ -1,5 +1,7 @@ (in-package :qml-user) +(pushnew :qt-plugin *features*) + (require :asdf) (asdf:load-system :s-http-server)