diff options
Diffstat (limited to 'src/apps')
-rw-r--r-- | src/apps/cam/camera_session.cpp | 45 | ||||
-rw-r--r-- | src/apps/cam/file_sink.cpp | 60 | ||||
-rw-r--r-- | src/apps/cam/file_sink.h | 18 | ||||
-rw-r--r-- | src/apps/common/dng_writer.cpp | 39 | ||||
-rw-r--r-- | src/apps/common/dng_writer.h | 1 | ||||
-rw-r--r-- | src/apps/common/options.cpp | 8 | ||||
-rw-r--r-- | src/apps/qcam/assets/shader/bayer_8.vert | 4 | ||||
-rw-r--r-- | src/apps/qcam/assets/shader/identity.vert | 3 | ||||
-rw-r--r-- | src/apps/qcam/cam_select_dialog.cpp | 10 | ||||
-rw-r--r-- | src/apps/qcam/main_window.cpp | 14 | ||||
-rw-r--r-- | src/apps/qcam/meson.build | 49 | ||||
-rw-r--r-- | src/apps/qcam/viewfinder_gl.cpp | 52 | ||||
-rw-r--r-- | src/apps/qcam/viewfinder_gl.h | 2 | ||||
-rw-r--r-- | src/apps/qcam/viewfinder_qt.cpp | 51 | ||||
-rw-r--r-- | src/apps/qcam/viewfinder_qt.h | 2 |
15 files changed, 234 insertions, 124 deletions
diff --git a/src/apps/cam/camera_session.cpp b/src/apps/cam/camera_session.cpp index 097dc479..edc49b87 100644 --- a/src/apps/cam/camera_session.cpp +++ b/src/apps/cam/camera_session.cpp @@ -159,8 +159,34 @@ CameraSession::~CameraSession() void CameraSession::listControls() const { for (const auto &[id, info] : camera_->controls()) { - std::cout << "Control: " << id->name() << ": " - << info.toString() << std::endl; + if (info.values().empty()) { + std::cout << "Control: " << id->name() << ": " + << info.toString() << std::endl; + } else { + std::cout << "Control: " << id->name() << ":" << std::endl; + for (const auto &value : info.values()) { + int32_t val = value.get<int32_t>(); + const auto &it = id->enumerators().find(val); + + std::cout << " - "; + if (it == id->enumerators().end()) + std::cout << "UNKNOWN"; + else + std::cout << it->second; + std::cout << " (" << val << ")" << std::endl; + } + } + + if (id->isArray()) { + std::size_t size = id->size(); + + std::cout << " Size: "; + if (size == std::numeric_limits<std::size_t>::max()) + std::cout << "n"; + else + std::cout << std::to_string(size); + std::cout << std::endl; + } } } @@ -230,11 +256,16 @@ int CameraSession::start() #endif if (options_.isSet(OptFile)) { - if (!options_[OptFile].toString().empty()) - sink_ = std::make_unique<FileSink>(camera_.get(), streamNames_, - options_[OptFile]); - else - sink_ = std::make_unique<FileSink>(camera_.get(), streamNames_); + std::unique_ptr<FileSink> sink = + std::make_unique<FileSink>(camera_.get(), streamNames_); + + if (!options_[OptFile].toString().empty()) { + ret = sink->setFilePattern(options_[OptFile]); + if (ret) + return ret; + } + + sink_ = std::move(sink); } if (sink_) { diff --git a/src/apps/cam/file_sink.cpp b/src/apps/cam/file_sink.cpp index 3e000d2f..76e21db9 100644 --- a/src/apps/cam/file_sink.cpp +++ b/src/apps/cam/file_sink.cpp @@ -5,6 +5,7 @@ * File Sink */ +#include <array> #include <assert.h> #include <fcntl.h> #include <iomanip> @@ -12,6 +13,7 @@ #include <sstream> #include <string.h> #include <unistd.h> +#include <utility> #include <libcamera/camera.h> @@ -24,13 +26,13 @@ using namespace libcamera; FileSink::FileSink([[maybe_unused]] const libcamera::Camera *camera, - const std::map<const libcamera::Stream *, std::string> &streamNames, - const std::string &pattern) + const std::map<const libcamera::Stream *, std::string> &streamNames) : #ifdef HAVE_TIFF camera_(camera), #endif - streamNames_(streamNames), pattern_(pattern) + pattern_(kDefaultFilePattern), fileType_(FileType::Binary), + streamNames_(streamNames) { } @@ -38,6 +40,41 @@ FileSink::~FileSink() { } +int FileSink::setFilePattern(const std::string &pattern) +{ + static const std::array<std::pair<std::string, FileType>, 2> types{{ + { ".dng", FileType::Dng }, + { ".ppm", FileType::Ppm }, + }}; + + pattern_ = pattern; + + if (pattern_.empty() || pattern_.back() == '/') + pattern_ += kDefaultFilePattern; + + fileType_ = FileType::Binary; + + for (const auto &type : types) { + if (pattern_.size() < type.first.size()) + continue; + + if (pattern_.find(type.first, pattern_.size() - type.first.size()) != + std::string::npos) { + fileType_ = type.second; + break; + } + } + +#ifndef HAVE_TIFF + if (fileType_ == FileType::Dng) { + std::cerr << "DNG support not available" << std::endl; + return -EINVAL; + } +#endif /* HAVE_TIFF */ + + return 0; +} + int FileSink::configure(const libcamera::CameraConfiguration &config) { int ret = FrameSink::configure(config); @@ -67,21 +104,10 @@ bool FileSink::processRequest(Request *request) void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer, [[maybe_unused]] const ControlList &metadata) { - std::string filename; + std::string filename = pattern_; size_t pos; int fd, ret = 0; - if (!pattern_.empty()) - filename = pattern_; - -#ifdef HAVE_TIFF - bool dng = filename.find(".dng", filename.size() - 4) != std::string::npos; -#endif /* HAVE_TIFF */ - bool ppm = filename.find(".ppm", filename.size() - 4) != std::string::npos; - - if (filename.empty() || filename.back() == '/') - filename += "frame-#.bin"; - pos = filename.find_first_of('#'); if (pos != std::string::npos) { std::stringstream ss; @@ -93,7 +119,7 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer, Image *image = mappedBuffers_[buffer].get(); #ifdef HAVE_TIFF - if (dng) { + if (fileType_ == FileType::Dng) { ret = DNGWriter::write(filename.c_str(), camera_, stream->configuration(), metadata, buffer, image->data(0).data()); @@ -104,7 +130,7 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer, return; } #endif /* HAVE_TIFF */ - if (ppm) { + if (fileType_ == FileType::Ppm) { ret = PPMWriter::write(filename.c_str(), stream->configuration(), image->data(0)); if (ret < 0) diff --git a/src/apps/cam/file_sink.h b/src/apps/cam/file_sink.h index 9d560783..71b7fe0f 100644 --- a/src/apps/cam/file_sink.h +++ b/src/apps/cam/file_sink.h @@ -21,10 +21,11 @@ class FileSink : public FrameSink { public: FileSink(const libcamera::Camera *camera, - const std::map<const libcamera::Stream *, std::string> &streamNames, - const std::string &pattern = ""); + const std::map<const libcamera::Stream *, std::string> &streamNames); ~FileSink(); + int setFilePattern(const std::string &pattern); + int configure(const libcamera::CameraConfiguration &config) override; void mapBuffer(libcamera::FrameBuffer *buffer) override; @@ -32,6 +33,14 @@ public: bool processRequest(libcamera::Request *request) override; private: + static constexpr const char *kDefaultFilePattern = "frame-#.bin"; + + enum class FileType { + Binary, + Dng, + Ppm, + }; + void writeBuffer(const libcamera::Stream *stream, libcamera::FrameBuffer *buffer, const libcamera::ControlList &metadata); @@ -39,7 +48,10 @@ private: #ifdef HAVE_TIFF const libcamera::Camera *camera_; #endif - std::map<const libcamera::Stream *, std::string> streamNames_; + std::string pattern_; + FileType fileType_; + + std::map<const libcamera::Stream *, std::string> streamNames_; std::map<libcamera::FrameBuffer *, std::unique_ptr<Image>> mappedBuffers_; }; diff --git a/src/apps/common/dng_writer.cpp b/src/apps/common/dng_writer.cpp index 9241f23f..ac461951 100644 --- a/src/apps/common/dng_writer.cpp +++ b/src/apps/common/dng_writer.cpp @@ -8,8 +8,10 @@ #include "dng_writer.h" #include <algorithm> +#include <endian.h> #include <iostream> #include <map> +#include <vector> #include <tiffio.h> @@ -138,29 +140,29 @@ void packScanlineRaw8(void *output, const void *input, unsigned int width) void packScanlineRaw10(void *output, const void *input, unsigned int width) { - const uint16_t *in = static_cast<const uint16_t *>(input); + const uint8_t *in = static_cast<const uint8_t *>(input); uint8_t *out = static_cast<uint8_t *>(output); for (unsigned int i = 0; i < width; i += 4) { - *out++ = (in[0] & 0x3fc) >> 2; - *out++ = (in[0] & 0x003) << 6 | (in[1] & 0x3f0) >> 4; - *out++ = (in[1] & 0x00f) << 4 | (in[2] & 0x3c0) >> 6; - *out++ = (in[2] & 0x03f) << 2 | (in[3] & 0x300) >> 8; - *out++ = (in[3] & 0x0ff); - in += 4; + *out++ = in[1] << 6 | in[0] >> 2; + *out++ = in[0] << 6 | (in[3] & 0x03) << 4 | in[2] >> 4; + *out++ = in[2] << 4 | (in[5] & 0x03) << 2 | in[4] >> 6; + *out++ = in[4] << 2 | (in[7] & 0x03) << 0; + *out++ = in[6]; + in += 8; } } void packScanlineRaw12(void *output, const void *input, unsigned int width) { - const uint16_t *in = static_cast<const uint16_t *>(input); + const uint8_t *in = static_cast<const uint8_t *>(input); uint8_t *out = static_cast<uint8_t *>(output); for (unsigned int i = 0; i < width; i += 2) { - *out++ = (in[0] & 0xff0) >> 4; - *out++ = (in[0] & 0x00f) << 4 | (in[1] & 0xf00) >> 8; - *out++ = (in[1] & 0x0ff); - in += 2; + *out++ = in[1] << 4 | in[0] >> 4; + *out++ = in[0] << 4 | (in[3] & 0x0f); + *out++ = in[2]; + in += 4; } } @@ -185,7 +187,8 @@ void thumbScanlineRaw(const FormatInfo &info, void *output, const void *input, /* Simple averaging that produces greyscale RGB values. */ for (unsigned int x = 0; x < width; x++) { - uint16_t value = (in[0] + in[1] + in2[0] + in2[1]) >> 2; + uint16_t value = (le16toh(in[0]) + le16toh(in[1]) + + le16toh(in2[0]) + le16toh(in2[1])) >> 2; value = value >> shift; *out++ = value; *out++ = value; @@ -542,7 +545,7 @@ int DNGWriter::write(const char *filename, const Camera *camera, * or a thumbnail scanline. The latter will always be much smaller than * the former as we downscale by 16 in both directions. */ - uint8_t scanline[(config.size.width * info->bitsPerSample + 7) / 8]; + std::vector<uint8_t> scanline((config.size.width * info->bitsPerSample + 7) / 8); toff_t rawIFDOffset = 0; toff_t exifIFDOffset = 0; @@ -642,10 +645,10 @@ int DNGWriter::write(const char *filename, const Camera *camera, /* Write the thumbnail. */ const uint8_t *row = static_cast<const uint8_t *>(data); for (unsigned int y = 0; y < config.size.height / 16; y++) { - info->thumbScanline(*info, &scanline, row, + info->thumbScanline(*info, scanline.data(), row, config.size.width / 16, config.stride); - if (TIFFWriteScanline(tif, &scanline, y, 0) != 1) { + if (TIFFWriteScanline(tif, scanline.data(), y, 0) != 1) { std::cerr << "Failed to write thumbnail scanline" << std::endl; TIFFClose(tif); @@ -745,9 +748,9 @@ int DNGWriter::write(const char *filename, const Camera *camera, /* Write RAW content. */ row = static_cast<const uint8_t *>(data); for (unsigned int y = 0; y < config.size.height; y++) { - info->packScanline(&scanline, row, config.size.width); + info->packScanline(scanline.data(), row, config.size.width); - if (TIFFWriteScanline(tif, &scanline, y, 0) != 1) { + if (TIFFWriteScanline(tif, scanline.data(), y, 0) != 1) { std::cerr << "Failed to write RAW scanline" << std::endl; TIFFClose(tif); diff --git a/src/apps/common/dng_writer.h b/src/apps/common/dng_writer.h index 917713e6..aaa8a852 100644 --- a/src/apps/common/dng_writer.h +++ b/src/apps/common/dng_writer.h @@ -8,7 +8,6 @@ #pragma once #ifdef HAVE_TIFF -#define HAVE_DNG #include <libcamera/camera.h> #include <libcamera/controls.h> diff --git a/src/apps/common/options.cpp b/src/apps/common/options.cpp index ab19aa3d..ece268d0 100644 --- a/src/apps/common/options.cpp +++ b/src/apps/common/options.cpp @@ -10,6 +10,7 @@ #include <iomanip> #include <iostream> #include <string.h> +#include <vector> #include "options.h" @@ -879,8 +880,8 @@ OptionsParser::Options OptionsParser::parse(int argc, char **argv) * Allocate short and long options arrays large enough to contain all * options. */ - char shortOptions[optionsMap_.size() * 3 + 2]; - struct option longOptions[optionsMap_.size() + 1]; + std::vector<char> shortOptions(optionsMap_.size() * 3 + 2); + std::vector<struct option> longOptions(optionsMap_.size() + 1); unsigned int ids = 0; unsigned int idl = 0; @@ -922,7 +923,8 @@ OptionsParser::Options OptionsParser::parse(int argc, char **argv) opterr = 0; while (true) { - int c = getopt_long(argc, argv, shortOptions, longOptions, nullptr); + int c = getopt_long(argc, argv, shortOptions.data(), + longOptions.data(), nullptr); if (c == -1) break; diff --git a/src/apps/qcam/assets/shader/bayer_8.vert b/src/apps/qcam/assets/shader/bayer_8.vert index 3695a5e9..fb5109ee 100644 --- a/src/apps/qcam/assets/shader/bayer_8.vert +++ b/src/apps/qcam/assets/shader/bayer_8.vert @@ -19,6 +19,8 @@ Copyright (C) 2021, Linaro attribute vec4 vertexIn; attribute vec2 textureIn; +uniform mat4 proj_matrix; + uniform vec2 tex_size; /* The texture size in pixels */ uniform vec2 tex_step; @@ -47,5 +49,5 @@ void main(void) { yCoord = center.y + vec4(-2.0 * tex_step.y, -tex_step.y, tex_step.y, 2.0 * tex_step.y); - gl_Position = vertexIn; + gl_Position = proj_matrix * vertexIn; } diff --git a/src/apps/qcam/assets/shader/identity.vert b/src/apps/qcam/assets/shader/identity.vert index 12c41377..907e8741 100644 --- a/src/apps/qcam/assets/shader/identity.vert +++ b/src/apps/qcam/assets/shader/identity.vert @@ -9,10 +9,11 @@ attribute vec4 vertexIn; attribute vec2 textureIn; varying vec2 textureOut; +uniform mat4 proj_matrix; uniform float stride_factor; void main(void) { - gl_Position = vertexIn; + gl_Position = proj_matrix * vertexIn; textureOut = vec2(textureIn.x * stride_factor, textureIn.y); } diff --git a/src/apps/qcam/cam_select_dialog.cpp b/src/apps/qcam/cam_select_dialog.cpp index c51f5974..6b6d0713 100644 --- a/src/apps/qcam/cam_select_dialog.cpp +++ b/src/apps/qcam/cam_select_dialog.cpp @@ -15,7 +15,9 @@ #include <QComboBox> #include <QDialogButtonBox> #include <QFormLayout> +#include <QGuiApplication> #include <QLabel> +#include <QScreen> #include <QString> CameraSelectorDialog::CameraSelectorDialog(libcamera::CameraManager *cameraManager, @@ -53,6 +55,14 @@ CameraSelectorDialog::CameraSelectorDialog(libcamera::CameraManager *cameraManag layout->addRow("Location:", cameraLocation_); layout->addRow("Model:", cameraModel_); layout->addWidget(buttonBox); + + /* + * Decrease the minimum width of dialog to fit on narrow screens, with a + * 20 pixels margin. + */ + QRect screenGeometry = qGuiApp->primaryScreen()->availableGeometry(); + if (screenGeometry.width() < minimumWidth()) + setMinimumWidth(screenGeometry.width() - 20); } CameraSelectorDialog::~CameraSelectorDialog() = default; diff --git a/src/apps/qcam/main_window.cpp b/src/apps/qcam/main_window.cpp index d515beed..5144c6b3 100644 --- a/src/apps/qcam/main_window.cpp +++ b/src/apps/qcam/main_window.cpp @@ -37,16 +37,6 @@ using namespace libcamera; -#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) -/* - * Qt::fixed was introduced in v5.14, and ::fixed deprecated in v5.15. Allow - * usage of Qt::fixed unconditionally. - */ -namespace Qt { -constexpr auto fixed = ::fixed; -} /* namespace Qt */ -#endif - /** * \brief Custom QEvent to signal capture completion */ @@ -221,7 +211,7 @@ int MainWindow::createToolbars() action->setShortcut(QKeySequence::SaveAs); connect(action, &QAction::triggered, this, &MainWindow::saveImageAs); -#ifdef HAVE_DNG +#ifdef HAVE_TIFF /* Save Raw action. */ action = toolbar_->addAction(QIcon::fromTheme("camera-photo", QIcon(":aperture.svg")), @@ -656,7 +646,7 @@ void MainWindow::captureRaw() void MainWindow::processRaw(FrameBuffer *buffer, [[maybe_unused]] const ControlList &metadata) { -#ifdef HAVE_DNG +#ifdef HAVE_TIFF QString defaultPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation); QString filename = QFileDialog::getSaveFileName(this, "Save DNG", defaultPath, "DNG Files (*.dng)"); diff --git a/src/apps/qcam/meson.build b/src/apps/qcam/meson.build index 6cf4c171..f7c14064 100644 --- a/src/apps/qcam/meson.build +++ b/src/apps/qcam/meson.build @@ -1,13 +1,13 @@ # SPDX-License-Identifier: CC0-1.0 -qt5 = import('qt5') -qt5_dep = dependency('qt5', +qt6 = import('qt6') +qt6_dep = dependency('qt6', method : 'pkg-config', - modules : ['Core', 'Gui', 'Widgets'], + modules : ['Core', 'Gui', 'OpenGL', 'OpenGLWidgets', 'Widgets'], required : get_option('qcam'), - version : '>=5.4') + version : '>=6.2') -if not qt5_dep.found() +if not qt6_dep.found() qcam_enabled = false subdir_done() endif @@ -20,46 +20,31 @@ qcam_sources = files([ 'main.cpp', 'main_window.cpp', 'message_handler.cpp', + 'viewfinder_gl.cpp', 'viewfinder_qt.cpp', ]) qcam_moc_headers = files([ 'cam_select_dialog.h', 'main_window.h', + 'viewfinder_gl.h', 'viewfinder_qt.h', ]) qcam_resources = files([ 'assets/feathericons/feathericons.qrc', + 'assets/shader/shaders.qrc', ]) -qt5_cpp_args = [apps_cpp_args, '-DQT_NO_KEYWORDS'] +qt6_cpp_args = [ + apps_cpp_args, + '-DQT_NO_KEYWORDS', + '-Wno-extra-semi', +] -if cxx.has_header_symbol('QOpenGLWidget', 'QOpenGLWidget', - dependencies : qt5_dep, args : '-fPIC') - qcam_sources += files([ - 'viewfinder_gl.cpp', - ]) - qcam_moc_headers += files([ - 'viewfinder_gl.h', - ]) - qcam_resources += files([ - 'assets/shader/shaders.qrc' - ]) -endif - -# gcc 9 introduced a deprecated-copy warning that is triggered by Qt until -# Qt 5.13. clang 10 introduced the same warning, but detects more issues -# that are not fixed in Qt yet. Disable the warning manually in both cases. -if ((cc.get_id() == 'gcc' and cc.version().version_compare('>=9.0') and - qt5_dep.version().version_compare('<5.13')) or - (cc.get_id() == 'clang' and cc.version().version_compare('>=10.0'))) - qt5_cpp_args += ['-Wno-deprecated-copy'] -endif - -resources = qt5.preprocess(moc_headers : qcam_moc_headers, +resources = qt6.preprocess(moc_headers : qcam_moc_headers, qresources : qcam_resources, - dependencies : qt5_dep) + dependencies : qt6_dep) qcam = executable('qcam', qcam_sources, resources, install : true, @@ -69,6 +54,6 @@ qcam = executable('qcam', qcam_sources, resources, libatomic, libcamera_public, libtiff, - qt5_dep, + qt6_dep, ], - cpp_args : qt5_cpp_args) + cpp_args : qt6_cpp_args) diff --git a/src/apps/qcam/viewfinder_gl.cpp b/src/apps/qcam/viewfinder_gl.cpp index 9d2a6960..f31956ff 100644 --- a/src/apps/qcam/viewfinder_gl.cpp +++ b/src/apps/qcam/viewfinder_gl.cpp @@ -12,6 +12,7 @@ #include <QByteArray> #include <QFile> #include <QImage> +#include <QMatrix4x4> #include <QStringList> #include <libcamera/formats.h> @@ -443,15 +444,11 @@ bool ViewFinderGL::createFragmentShader() close(); } - /* Bind shader pipeline for use */ - if (!shaderProgram_.bind()) { - qWarning() << "[ViewFinderGL]:" << shaderProgram_.log(); - close(); - } - attributeVertex = shaderProgram_.attributeLocation("vertexIn"); attributeTexture = shaderProgram_.attributeLocation("textureIn"); + vertexBuffer_.bind(); + shaderProgram_.enableAttributeArray(attributeVertex); shaderProgram_.setAttributeBuffer(attributeVertex, GL_FLOAT, @@ -466,6 +463,9 @@ bool ViewFinderGL::createFragmentShader() 2, 2 * sizeof(GLfloat)); + vertexBuffer_.release(); + + projMatrixUniform_ = shaderProgram_.uniformLocation("proj_matrix"); textureUniformY_ = shaderProgram_.uniformLocation("tex_y"); textureUniformU_ = shaderProgram_.uniformLocation("tex_u"); textureUniformV_ = shaderProgram_.uniformLocation("tex_v"); @@ -511,7 +511,7 @@ void ViewFinderGL::initializeGL() glEnable(GL_TEXTURE_2D); glDisable(GL_DEPTH_TEST); - static const GLfloat coordinates[2][4][2]{ + const GLfloat coordinates[2][4][2]{ { /* Vertex coordinates */ { -1.0f, -1.0f }, @@ -535,8 +535,6 @@ void ViewFinderGL::initializeGL() /* Create Vertex Shader */ if (!createVertexShader()) qWarning() << "[ViewFinderGL]: create vertex shader failed."; - - glClearColor(1.0f, 1.0f, 1.0f, 0.0f); } void ViewFinderGL::doRender() @@ -805,28 +803,42 @@ void ViewFinderGL::doRender() shaderProgram_.setUniformValue(textureUniformStrideFactor_, static_cast<float>(size_.width() - 1) / (stridePixels - 1)); + + /* + * Place the viewfinder in the centre of the widget, preserving the + * aspect ratio of the image. + */ + QMatrix4x4 projMatrix; + QSizeF scaledSize = size_.scaled(size(), Qt::KeepAspectRatio); + projMatrix.scale(scaledSize.width() / size().width(), + scaledSize.height() / size().height()); + + shaderProgram_.setUniformValue(projMatrixUniform_, projMatrix); } void ViewFinderGL::paintGL() { - if (!fragmentShader_) + if (!fragmentShader_) { if (!createFragmentShader()) { qWarning() << "[ViewFinderGL]:" << "create fragment shader failed."; } + } - if (image_) { - glClearColor(0.0, 0.0, 0.0, 1.0); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - doRender(); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + /* Bind shader pipeline for use. */ + if (!shaderProgram_.bind()) { + qWarning() << "[ViewFinderGL]:" << shaderProgram_.log(); + close(); } -} -void ViewFinderGL::resizeGL(int w, int h) -{ - glViewport(0, 0, w, h); + if (!image_) + return; + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + doRender(); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } QSize ViewFinderGL::sizeHint() const diff --git a/src/apps/qcam/viewfinder_gl.h b/src/apps/qcam/viewfinder_gl.h index 23744b41..23c657bc 100644 --- a/src/apps/qcam/viewfinder_gl.h +++ b/src/apps/qcam/viewfinder_gl.h @@ -51,7 +51,6 @@ Q_SIGNALS: protected: void initializeGL() override; void paintGL() override; - void resizeGL(int w, int h) override; QSize sizeHint() const override; private: @@ -88,6 +87,7 @@ private: /* Common texture parameters */ GLuint textureMinMagFilters_; + GLuint projMatrixUniform_; /* YUV texture parameters */ GLuint textureUniformU_; diff --git a/src/apps/qcam/viewfinder_qt.cpp b/src/apps/qcam/viewfinder_qt.cpp index 4821c27d..1a238922 100644 --- a/src/apps/qcam/viewfinder_qt.cpp +++ b/src/apps/qcam/viewfinder_qt.cpp @@ -18,6 +18,7 @@ #include <QMap> #include <QMutexLocker> #include <QPainter> +#include <QResizeEvent> #include <QtDebug> #include "../common/image.h" @@ -26,23 +27,23 @@ static const QMap<libcamera::PixelFormat, QImage::Format> nativeFormats { -#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) { libcamera::formats::ABGR8888, QImage::Format_RGBX8888 }, { libcamera::formats::XBGR8888, QImage::Format_RGBX8888 }, -#endif { libcamera::formats::ARGB8888, QImage::Format_RGB32 }, { libcamera::formats::XRGB8888, 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 }, { libcamera::formats::RGB565, QImage::Format_RGB16 }, }; ViewFinderQt::ViewFinderQt(QWidget *parent) - : QWidget(parent), buffer_(nullptr) + : QWidget(parent), place_(rect()), buffer_(nullptr) { icon_ = QIcon(":camera-off.svg"); + + QPalette pal = palette(); + pal.setColor(QPalette::Window, Qt::black); + setPalette(pal); } ViewFinderQt::~ViewFinderQt() @@ -117,6 +118,11 @@ void ViewFinderQt::render(libcamera::FrameBuffer *buffer, Image *image) } } + /* + * Indicate the widget paints all its pixels, to optimize rendering by + * skipping erasing the widget before painting. + */ + setAttribute(Qt::WA_OpaquePaintEvent, true); update(); if (buffer) @@ -132,6 +138,11 @@ void ViewFinderQt::stop() buffer_ = nullptr; } + /* + * The logo has a transparent background, reenable erasing the widget + * before painting. + */ + setAttribute(Qt::WA_OpaquePaintEvent, false); update(); } @@ -146,9 +157,23 @@ void ViewFinderQt::paintEvent(QPaintEvent *) { QPainter painter(this); - /* If we have an image, draw it. */ + painter.setBrush(palette().window()); + + /* If we have an image, draw it, with black letterbox rectangles. */ if (!image_.isNull()) { - painter.drawImage(rect(), image_, image_.rect()); + if (place_.width() < width()) { + QRect rect{ 0, 0, (width() - place_.width()) / 2, height() }; + painter.drawRect(rect); + rect.moveLeft(place_.right()); + painter.drawRect(rect); + } else { + QRect rect{ 0, 0, width(), (height() - place_.height()) / 2 }; + painter.drawRect(rect); + rect.moveTop(place_.bottom()); + painter.drawRect(rect); + } + + painter.drawImage(place_, image_, image_.rect()); return; } @@ -173,7 +198,6 @@ void ViewFinderQt::paintEvent(QPaintEvent *) else point.setY((height() - pixmap_.height()) / 2); - painter.setBackgroundMode(Qt::OpaqueMode); painter.drawPixmap(point, pixmap_); } @@ -181,3 +205,14 @@ QSize ViewFinderQt::sizeHint() const { return size_.isValid() ? size_ : QSize(640, 480); } + +void ViewFinderQt::resizeEvent(QResizeEvent *event) +{ + if (!size_.isValid()) + return; + + place_.setSize(size_.scaled(event->size(), Qt::KeepAspectRatio)); + place_.moveCenter(rect().center()); + + QWidget::resizeEvent(event); +} diff --git a/src/apps/qcam/viewfinder_qt.h b/src/apps/qcam/viewfinder_qt.h index 4f4b9f11..50fde88e 100644 --- a/src/apps/qcam/viewfinder_qt.h +++ b/src/apps/qcam/viewfinder_qt.h @@ -44,6 +44,7 @@ Q_SIGNALS: protected: void paintEvent(QPaintEvent *) override; + void resizeEvent(QResizeEvent *) override; QSize sizeHint() const override; private: @@ -51,6 +52,7 @@ private: libcamera::PixelFormat format_; QSize size_; + QRect place_; /* Camera stopped icon */ QSize vfSize_; |