#include "video-widget.hpp" #include "compat/check-visible.hpp" #include "compat/math.hpp" #include #include #ifdef OTR_VIDEO_HAS_OPENCV # include #endif #include void cv_video_widget::init_image_nolock() { texture = QImage(W, H, QImage::Format_ARGB32); texture.setDevicePixelRatio(devicePixelRatioF()); } cv_video_widget::cv_video_widget(QWidget* parent) : QWidget(parent) { W = width(); H = height(); init_image_nolock(); texture.fill(Qt::gray); connect(&timer, &QTimer::timeout, this, &cv_video_widget::update_and_repaint, Qt::DirectConnection); timer.start(65); } #ifdef OTR_VIDEO_HAS_OPENCV void cv_video_widget::update_image(const cv::Mat& frame) { QMutexLocker l(&mtx); if (freshp) return; if (W < 1 || H < 1 || frame.rows < 1 || frame.cols < 1) return; cv::Mat const* __restrict frame_scaled = nullptr; if (frame3.cols != W || frame3.rows != H) { frame3 = cv::Mat(H, W, frame.type()); frame2 = cv::Mat(H, W, CV_8UC4); if (!frame2.isContinuous() || !frame3.isContinuous()) std::abort(); } if (frame.cols != W || frame.rows != H) { cv::resize(frame, frame3, { W, H }, 0, 0, cv::INTER_NEAREST); frame_scaled = &frame3; } else if (!frame.isContinuous()) { frame.copyTo(frame3); frame_scaled = &frame3; } else frame_scaled = &frame; freshp = true; int color_cvt = 0; constexpr int nchannels = 4; switch (frame_scaled->channels()) { case 1: color_cvt = cv::COLOR_GRAY2BGRA; break; case 3: color_cvt = cv::COLOR_BGR2BGRA; break; case nchannels: break; default: unreachable(); break; } cv::Mat const* frame_color; if (color_cvt != cv::COLOR_COLORCVT_MAX) { cv::cvtColor(*frame_scaled, frame2, color_cvt); frame_color = &frame2; } else frame_color = frame_scaled; int stride = frame_color->step.p[0], rows = frame_color->rows; int nbytes = rows * stride; vec.resize(nbytes); vec.shrink_to_fit(); std::memcpy(vec.data(), frame_color->data, nbytes); texture = QImage((const unsigned char*) vec.data(), W, H, stride, QImage::Format_ARGB32); texture.setDevicePixelRatio(devicePixelRatioF()); } #endif void cv_video_widget::update_image(const QImage& img) { QMutexLocker l(&mtx); if (freshp) return; freshp = true; unsigned nbytes = (unsigned)(img.bytesPerLine() * img.height()); vec.resize(nbytes); vec.shrink_to_fit(); std::memcpy(vec.data(), img.constBits(), nbytes); texture = QImage((const unsigned char*) vec.data(), img.width(), img.height(), img.bytesPerLine(), img.format()); texture.setDevicePixelRatio(devicePixelRatioF()); } void cv_video_widget::paintEvent(QPaintEvent*) { QMutexLocker foo(&mtx); if (texture.width() != W || texture.height() != H) { init_image_nolock(); texture.fill(Qt::gray); } QPainter painter(this); painter.drawImage(rect(), texture); } void cv_video_widget::update_and_repaint() { if (!check_is_visible()) return; QMutexLocker l(&mtx); if (freshp) { freshp = false; repaint(); } } void cv_video_widget::resizeEvent(QResizeEvent*) { QMutexLocker l(&mtx); double dpr = devicePixelRatioF(); W = iround(width() * dpr); H = iround(height() * dpr); init_image_nolock(); } void cv_video_widget::get_preview_size(int& w, int& h) { QMutexLocker l(&mtx); w = W; h = H; }