diff options
Diffstat (limited to 'src/qcam/viewfinder_qt.cpp')
-rw-r--r-- | src/qcam/viewfinder_qt.cpp | 181 |
1 files changed, 181 insertions, 0 deletions
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 <stdint.h> +#include <utility> + +#include <QImage> +#include <QImageWriter> +#include <QMap> +#include <QMutexLocker> +#include <QPainter> +#include <QtDebug> + +#include <libcamera/formats.h> + +#include "format_converter.h" + +static const QMap<libcamera::PixelFormat, QImage::Format> 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<libcamera::PixelFormat> &ViewFinderQt::nativeFormats() const +{ + static const QList<libcamera::PixelFormat> 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<unsigned char *>(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); +} |