#include "source-preview-widget.hpp" #include #include #include namespace advss { static int32_t clampToSource(int32_t v, uint32_t max) { if (v < 0) { return 0; } if (v > (int32_t)max) { return (int32_t)max; } return v; } SourcePreviewWidget::SourcePreviewWidget(QWidget *parent, obs_weak_source_t *source) : QWidget(parent), _source(source) { setAttribute(Qt::WA_PaintOnScreen); setAttribute(Qt::WA_NoSystemBackground); setMinimumSize(320, 240); } SourcePreviewWidget::~SourcePreviewWidget() { if (_display) { obs_display_remove_draw_callback( _display, SourcePreviewWidget::DrawCallback, this); obs_display_destroy(_display); _display = nullptr; } if (_showing) { OBSSourceAutoRelease source = obs_weak_source_get_source(_source); if (source) { obs_source_dec_showing(source); } _showing = false; } } void SourcePreviewWidget::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); if (!_display) { WId wid = winId(); if (!wid) { return; } gs_init_data info{}; #if defined(_WIN32) info.window.hwnd = reinterpret_cast(wid); #elif defined(__APPLE__) info.window.view = reinterpret_cast(wid); #else info.window.id = static_cast(wid); info.window.display = nullptr; #endif info.format = GS_BGRA; info.zsformat = GS_ZS_NONE; info.cx = static_cast(width()); info.cy = static_cast(height()); _display = obs_display_create(&info, 0x000000); if (_display) { obs_display_add_draw_callback( _display, SourcePreviewWidget::DrawCallback, this); OBSSourceAutoRelease source = obs_weak_source_get_source(_source); if (source) { obs_source_inc_showing(source); _showing = true; } } } else { obs_display_resize(_display, static_cast(width()), static_cast(height())); } } void SourcePreviewWidget::DrawCallback(void *param, uint32_t cx, uint32_t cy) { auto self = static_cast(param); OBSSourceAutoRelease source = obs_weak_source_get_source(self->_source); if (!source) { return; } uint32_t srcW = std::max(obs_source_get_width(source), 1u); uint32_t srcH = std::max(obs_source_get_height(source), 1u); float scaleX = static_cast(cx) / srcW; float scaleY = static_cast(cy) / srcH; float scale = std::min(scaleX, scaleY); int newW = static_cast(scale * srcW); int newH = static_cast(scale * srcH); int offX = (static_cast(cx) - newW) / 2; int offY = (static_cast(cy) - newH) / 2; self->_offsetX = offX; self->_offsetY = offY; self->_scale = scale; gs_viewport_push(); gs_projection_push(); gs_ortho(0.0f, static_cast(srcW), 0.0f, static_cast(srcH), -100.0f, 100.0f); gs_set_viewport(offX, offY, newW, newH); obs_source_video_render(source); gs_projection_pop(); gs_viewport_pop(); } bool SourcePreviewWidget::GetSourceRelativeXY(int widgetX, int widgetY, int &srcX, int &srcY) const { OBSSourceAutoRelease source = obs_weak_source_get_source(_source); if (!source) { return false; } uint32_t srcW = std::max(obs_source_get_width(source), 1u); uint32_t srcH = std::max(obs_source_get_height(source), 1u); srcX = static_cast((widgetX - _offsetX) / _scale); srcY = static_cast((widgetY - _offsetY) / _scale); srcX = static_cast( clampToSource(static_cast(srcX), srcW)); srcY = static_cast( clampToSource(static_cast(srcY), srcH)); return true; } } // namespace advss