summaryrefslogtreecommitdiff
path: root/src/qcam/viewfinder_qt.cpp
blob: efa1d412584b3d8b359ffb223bbd0aa791adc253 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
/* 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,
			  libcamera::Span<uint8_t> mem)
{
	if (buffer->planes().size() != 1) {
		qWarning() << "Multi-planar buffers are not supported";
		return;
	}

	unsigned char *memory = mem.data();
	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);
}
s="hl opt">> &files) { struct dirent *ent; DIR *dir; dir = opendir(libDir); if (!dir) return; while ((ent = readdir(dir)) != nullptr) { if (ent->d_type == DT_DIR && maxDepth) { if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) continue; std::string subdir = std::string(libDir) + "/" + ent->d_name; /* Recursion is limited to maxDepth. */ parseDir(subdir.c_str(), maxDepth - 1, files); continue; } int offset = strlen(ent->d_name) - 3; if (offset < 0) continue; if (strcmp(&ent->d_name[offset], ".so")) continue; files.push_back(std::string(libDir) + "/" + ent->d_name); } closedir(dir); } /** * \brief Load IPA modules from a directory * \param[in] libDir The directory to search for IPA modules * \param[in] maxDepth The maximum depth of sub-directories to search * * This function tries to create an IPAModule instance for every shared object * found in \a libDir, and skips invalid IPA modules. * * Sub-directories are searched up to a depth of \a maxDepth. A \a maxDepth * value of 0 only searches the directory specified in \a libDir. * * \return Number of modules loaded by this call */ unsigned int IPAManager::addDir(const char *libDir, unsigned int maxDepth) { std::vector<std::string> files; parseDir(libDir, maxDepth, files); /* Ensure a stable ordering of modules. */ std::sort(files.begin(), files.end()); unsigned int count = 0; for (const std::string &file : files) { IPAModule *ipaModule = new IPAModule(file); if (!ipaModule->isValid()) { delete ipaModule; continue; } LOG(IPAManager, Debug) << "Loaded IPA module '" << file << "'"; modules_.push_back(ipaModule); count++; } return count; } /** * \brief Retrieve an IPA module that matches a given pipeline handler * \param[in] pipe The pipeline handler * \param[in] minVersion Minimum acceptable version of IPA module * \param[in] maxVersion Maximum acceptable version of IPA module */ IPAModule *IPAManager::module(PipelineHandler *pipe, uint32_t minVersion, uint32_t maxVersion) { for (IPAModule *module : modules_) { if (module->match(pipe, minVersion, maxVersion)) return module; } return nullptr; } /** * \fn IPAManager::createIPA() * \brief Create an IPA proxy that matches a given pipeline handler * \param[in] pipe The pipeline handler that wants a matching IPA proxy * \param[in] minVersion Minimum acceptable version of IPA module * \param[in] maxVersion Maximum acceptable version of IPA module * * \return A newly created IPA proxy, or nullptr if no matching IPA module is * found or if the IPA proxy fails to initialize */ #if HAVE_IPA_PUBKEY /** * \fn IPAManager::pubKey() * \brief Retrieve the IPA module signing public key * * IPA module signature verification is normally handled internally by the * IPAManager class. This function is meant to be used by utilities that need to * verify signatures externally. * * \return The IPA module signing public key */ #endif bool IPAManager::isSignatureValid([[maybe_unused]] IPAModule *ipa) const { #if HAVE_IPA_PUBKEY char *force = utils::secure_getenv("LIBCAMERA_IPA_FORCE_ISOLATION"); if (force && force[0] != '\0') { LOG(IPAManager, Debug) << "Isolation of IPA module " << ipa->path() << " forced through environment variable"; return false; } File file{ ipa->path() }; if (!file.open(File::OpenModeFlag::ReadOnly)) return false; Span<uint8_t> data = file.map(); if (data.empty()) return false; bool valid = pubKey_.verify(data, ipa->signature()); LOG(IPAManager, Debug) << "IPA module " << ipa->path() << " signature is " << (valid ? "valid" : "not valid"); return valid; #else return false; #endif } } /* namespace libcamera */