From 2daa704c968c8aa7a4b209450f228b41e9d42d85 Mon Sep 17 00:00:00 2001 From: Show Liu Date: Fri, 11 Sep 2020 16:55:12 +0800 Subject: qcam: New viewfinder hierarchy Create ViewFinder base class and rename the original ViewFinder as QPainter-based ViewFinder. Signed-off-by: Show Liu Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart --- src/qcam/main_window.cpp | 8 +- src/qcam/meson.build | 4 +- src/qcam/viewfinder.cpp | 181 --------------------------------------------- src/qcam/viewfinder.h | 57 +++----------- src/qcam/viewfinder_qt.cpp | 181 +++++++++++++++++++++++++++++++++++++++++++++ src/qcam/viewfinder_qt.h | 64 ++++++++++++++++ 6 files changed, 263 insertions(+), 232 deletions(-) delete mode 100644 src/qcam/viewfinder.cpp create mode 100644 src/qcam/viewfinder_qt.cpp create mode 100644 src/qcam/viewfinder_qt.h diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp index 612d978a..7406f0bd 100644 --- a/src/qcam/main_window.cpp +++ b/src/qcam/main_window.cpp @@ -28,6 +28,7 @@ #include #include "dng_writer.h" +#include "viewfinder_qt.h" using namespace libcamera; @@ -105,10 +106,11 @@ MainWindow::MainWindow(CameraManager *cm, const OptionsParser::Options &options) setWindowTitle(title_); connect(&titleTimer_, SIGNAL(timeout()), this, SLOT(updateTitle())); - viewfinder_ = new ViewFinder(this); - connect(viewfinder_, &ViewFinder::renderComplete, + ViewFinderQt *viewfinder = new ViewFinderQt(this); + connect(viewfinder, &ViewFinderQt::renderComplete, this, &MainWindow::queueRequest); - setCentralWidget(viewfinder_); + viewfinder_ = viewfinder; + setCentralWidget(viewfinder); adjustSize(); /* Hotplug/unplug support */ diff --git a/src/qcam/meson.build b/src/qcam/meson.build index e0c6f26d..a4bad0a0 100644 --- a/src/qcam/meson.build +++ b/src/qcam/meson.build @@ -6,12 +6,12 @@ qcam_sources = files([ 'format_converter.cpp', 'main.cpp', 'main_window.cpp', - 'viewfinder.cpp', + 'viewfinder_qt.cpp', ]) qcam_moc_headers = files([ 'main_window.h', - 'viewfinder.h', + 'viewfinder_qt.h', ]) qcam_resources = files([ diff --git a/src/qcam/viewfinder.cpp b/src/qcam/viewfinder.cpp deleted file mode 100644 index dcf8a15d..00000000 --- a/src/qcam/viewfinder.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2019, Google Inc. - * - * viewfinder.cpp - qcam - Viewfinder - */ - -#include "viewfinder.h" - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -#include "format_converter.h" - -static const QMap nativeFormats -{ -#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) - { libcamera::formats::ABGR8888, QImage::Format_RGBA8888 }, -#endif - { libcamera::formats::ARGB8888, QImage::Format_RGB32 }, -#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) - { libcamera::formats::RGB888, QImage::Format_BGR888 }, -#endif - { libcamera::formats::BGR888, QImage::Format_RGB888 }, -}; - -ViewFinder::ViewFinder(QWidget *parent) - : QWidget(parent), buffer_(nullptr) -{ - icon_ = QIcon(":camera-off.svg"); -} - -ViewFinder::~ViewFinder() -{ -} - -const QList &ViewFinder::nativeFormats() const -{ - static const QList formats = ::nativeFormats.keys(); - return formats; -} - -int ViewFinder::setFormat(const libcamera::PixelFormat &format, - const QSize &size) -{ - image_ = QImage(); - - /* - * If format conversion is needed, configure the converter and allocate - * the destination image. - */ - if (!::nativeFormats.contains(format)) { - int ret = converter_.configure(format, size); - if (ret < 0) - return ret; - - image_ = QImage(size, QImage::Format_RGB32); - - qInfo() << "Using software format conversion from" - << format.toString().c_str(); - } else { - qInfo() << "Zero-copy enabled"; - } - - format_ = format; - size_ = size; - - updateGeometry(); - return 0; -} - -void ViewFinder::render(libcamera::FrameBuffer *buffer, MappedBuffer *map) -{ - if (buffer->planes().size() != 1) { - qWarning() << "Multi-planar buffers are not supported"; - return; - } - - unsigned char *memory = static_cast(map->memory); - size_t size = buffer->metadata().planes[0].bytesused; - - { - QMutexLocker locker(&mutex_); - - if (::nativeFormats.contains(format_)) { - /* - * If the frame format is identical to the display - * format, create a QImage that references the frame - * and store a reference to the frame buffer. The - * previously stored frame buffer, if any, will be - * released. - * - * \todo Get the stride from the buffer instead of - * computing it naively - */ - image_ = QImage(memory, size_.width(), size_.height(), - size / size_.height(), - ::nativeFormats[format_]); - std::swap(buffer, buffer_); - } else { - /* - * Otherwise, convert the format and release the frame - * buffer immediately. - */ - converter_.convert(memory, size, &image_); - } - } - - update(); - - if (buffer) - renderComplete(buffer); -} - -void ViewFinder::stop() -{ - image_ = QImage(); - - if (buffer_) { - renderComplete(buffer_); - buffer_ = nullptr; - } - - update(); -} - -QImage ViewFinder::getCurrentImage() -{ - QMutexLocker locker(&mutex_); - - return image_.copy(); -} - -void ViewFinder::paintEvent(QPaintEvent *) -{ - QPainter painter(this); - - /* If we have an image, draw it. */ - if (!image_.isNull()) { - painter.drawImage(rect(), image_, image_.rect()); - return; - } - - /* - * Otherwise, draw the camera stopped icon. Render it to the pixmap if - * the size has changed. - */ - constexpr int margin = 20; - - if (vfSize_ != size() || pixmap_.isNull()) { - QSize vfSize = size() - QSize{ 2 * margin, 2 * margin }; - QSize pixmapSize{ 1, 1 }; - pixmapSize.scale(vfSize, Qt::KeepAspectRatio); - pixmap_ = icon_.pixmap(pixmapSize); - - vfSize_ = size(); - } - - QPoint point{ margin, margin }; - if (pixmap_.width() < width() - 2 * margin) - point.setX((width() - pixmap_.width()) / 2); - else - point.setY((height() - pixmap_.height()) / 2); - - painter.setBackgroundMode(Qt::OpaqueMode); - painter.drawPixmap(point, pixmap_); -} - -QSize ViewFinder::sizeHint() const -{ - return size_.isValid() ? size_ : QSize(640, 480); -} diff --git a/src/qcam/viewfinder.h b/src/qcam/viewfinder.h index 26a13205..67da1df2 100644 --- a/src/qcam/viewfinder.h +++ b/src/qcam/viewfinder.h @@ -2,70 +2,35 @@ /* * Copyright (C) 2019, Google Inc. * - * viewfinder.h - qcam - Viewfinder + * viewfinder.h - qcam - Viewfinder base class */ #ifndef __QCAM_VIEWFINDER_H__ #define __QCAM_VIEWFINDER_H__ -#include - -#include -#include #include -#include +#include #include -#include #include -#include - -#include "format_converter.h" - -class QImage; +#include struct MappedBuffer { void *memory; size_t size; }; -class ViewFinder : public QWidget +class ViewFinder { - Q_OBJECT - public: - ViewFinder(QWidget *parent); - ~ViewFinder(); - - const QList &nativeFormats() const; - - int setFormat(const libcamera::PixelFormat &format, const QSize &size); - void render(libcamera::FrameBuffer *buffer, MappedBuffer *map); - void stop(); - - QImage getCurrentImage(); - -Q_SIGNALS: - void renderComplete(libcamera::FrameBuffer *buffer); - -protected: - void paintEvent(QPaintEvent *) override; - QSize sizeHint() const override; - -private: - FormatConverter converter_; + virtual ~ViewFinder() {} - libcamera::PixelFormat format_; - QSize size_; + virtual const QList &nativeFormats() const = 0; - /* Camera stopped icon */ - QSize vfSize_; - QIcon icon_; - QPixmap pixmap_; + virtual int setFormat(const libcamera::PixelFormat &format, const QSize &size) = 0; + virtual void render(libcamera::FrameBuffer *buffer, MappedBuffer *map) = 0; + virtual void stop() = 0; - /* Buffer and render image */ - libcamera::FrameBuffer *buffer_; - QImage image_; - QMutex mutex_; /* Prevent concurrent access to image_ */ + virtual QImage getCurrentImage() = 0; }; -#endif /* __QCAM_VIEWFINDER__ */ +#endif /* __QCAM_VIEWFINDER_H__ */ diff --git a/src/qcam/viewfinder_qt.cpp b/src/qcam/viewfinder_qt.cpp new file mode 100644 index 00000000..e436714c --- /dev/null +++ b/src/qcam/viewfinder_qt.cpp @@ -0,0 +1,181 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * viewfinder_qt.cpp - qcam - QPainter-based ViewFinder + */ + +#include "viewfinder_qt.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "format_converter.h" + +static const QMap nativeFormats +{ +#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) + { libcamera::formats::ABGR8888, QImage::Format_RGBA8888 }, +#endif + { libcamera::formats::ARGB8888, QImage::Format_RGB32 }, +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + { libcamera::formats::RGB888, QImage::Format_BGR888 }, +#endif + { libcamera::formats::BGR888, QImage::Format_RGB888 }, +}; + +ViewFinderQt::ViewFinderQt(QWidget *parent) + : QWidget(parent), buffer_(nullptr) +{ + icon_ = QIcon(":camera-off.svg"); +} + +ViewFinderQt::~ViewFinderQt() +{ +} + +const QList &ViewFinderQt::nativeFormats() const +{ + static const QList formats = ::nativeFormats.keys(); + return formats; +} + +int ViewFinderQt::setFormat(const libcamera::PixelFormat &format, + const QSize &size) +{ + image_ = QImage(); + + /* + * If format conversion is needed, configure the converter and allocate + * the destination image. + */ + if (!::nativeFormats.contains(format)) { + int ret = converter_.configure(format, size); + if (ret < 0) + return ret; + + image_ = QImage(size, QImage::Format_RGB32); + + qInfo() << "Using software format conversion from" + << format.toString().c_str(); + } else { + qInfo() << "Zero-copy enabled"; + } + + format_ = format; + size_ = size; + + updateGeometry(); + return 0; +} + +void ViewFinderQt::render(libcamera::FrameBuffer *buffer, MappedBuffer *map) +{ + if (buffer->planes().size() != 1) { + qWarning() << "Multi-planar buffers are not supported"; + return; + } + + unsigned char *memory = static_cast(map->memory); + size_t size = buffer->metadata().planes[0].bytesused; + + { + QMutexLocker locker(&mutex_); + + if (::nativeFormats.contains(format_)) { + /* + * If the frame format is identical to the display + * format, create a QImage that references the frame + * and store a reference to the frame buffer. The + * previously stored frame buffer, if any, will be + * released. + * + * \todo Get the stride from the buffer instead of + * computing it naively + */ + image_ = QImage(memory, size_.width(), size_.height(), + size / size_.height(), + ::nativeFormats[format_]); + std::swap(buffer, buffer_); + } else { + /* + * Otherwise, convert the format and release the frame + * buffer immediately. + */ + converter_.convert(memory, size, &image_); + } + } + + update(); + + if (buffer) + renderComplete(buffer); +} + +void ViewFinderQt::stop() +{ + image_ = QImage(); + + if (buffer_) { + renderComplete(buffer_); + buffer_ = nullptr; + } + + update(); +} + +QImage ViewFinderQt::getCurrentImage() +{ + QMutexLocker locker(&mutex_); + + return image_.copy(); +} + +void ViewFinderQt::paintEvent(QPaintEvent *) +{ + QPainter painter(this); + + /* If we have an image, draw it. */ + if (!image_.isNull()) { + painter.drawImage(rect(), image_, image_.rect()); + return; + } + + /* + * Otherwise, draw the camera stopped icon. Render it to the pixmap if + * the size has changed. + */ + constexpr int margin = 20; + + if (vfSize_ != size() || pixmap_.isNull()) { + QSize vfSize = size() - QSize{ 2 * margin, 2 * margin }; + QSize pixmapSize{ 1, 1 }; + pixmapSize.scale(vfSize, Qt::KeepAspectRatio); + pixmap_ = icon_.pixmap(pixmapSize); + + vfSize_ = size(); + } + + QPoint point{ margin, margin }; + if (pixmap_.width() < width() - 2 * margin) + point.setX((width() - pixmap_.width()) / 2); + else + point.setY((height() - pixmap_.height()) / 2); + + painter.setBackgroundMode(Qt::OpaqueMode); + painter.drawPixmap(point, pixmap_); +} + +QSize ViewFinderQt::sizeHint() const +{ + return size_.isValid() ? size_ : QSize(640, 480); +} diff --git a/src/qcam/viewfinder_qt.h b/src/qcam/viewfinder_qt.h new file mode 100644 index 00000000..d7554288 --- /dev/null +++ b/src/qcam/viewfinder_qt.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * viewfinder_qt.h - qcam - QPainter-based ViewFinder + */ +#ifndef __QCAM_VIEWFINDER_QT_H__ +#define __QCAM_VIEWFINDER_QT_H__ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "format_converter.h" +#include "viewfinder.h" + +class ViewFinderQt : public QWidget, public ViewFinder +{ + Q_OBJECT + +public: + ViewFinderQt(QWidget *parent); + ~ViewFinderQt(); + + const QList &nativeFormats() const override; + + int setFormat(const libcamera::PixelFormat &format, const QSize &size) override; + void render(libcamera::FrameBuffer *buffer, MappedBuffer *map) override; + void stop() override; + + QImage getCurrentImage() override; + +Q_SIGNALS: + void renderComplete(libcamera::FrameBuffer *buffer); + +protected: + void paintEvent(QPaintEvent *) override; + QSize sizeHint() const override; + +private: + FormatConverter converter_; + + libcamera::PixelFormat format_; + QSize size_; + + /* Camera stopped icon */ + QSize vfSize_; + QIcon icon_; + QPixmap pixmap_; + + /* Buffer and render image */ + libcamera::FrameBuffer *buffer_; + QImage image_; + QMutex mutex_; /* Prevent concurrent access to image_ */ +}; + +#endif /* __QCAM_VIEWFINDER_QT_H__ */ -- cgit v1.2.1