diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index c3b2de6d4..a2a8230fd 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -57,11 +57,11 @@ ViewerWidget::ViewerWidget(QWidget *parent) : gl_widget_ = new ViewerGLWidget(); connect(gl_widget_, &ViewerGLWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu); + connect(sizer_, &ViewerSizer::RequestMatrix, gl_widget_, &ViewerGLWidget::SetMatrix); sizer_->SetWidget(gl_widget_); // Create waveform view when audio is connected and video isn't waveform_view_ = new WaveformView(); - stack_->addWidget(waveform_view_); // Create time ruler @@ -272,7 +272,7 @@ void ViewerWidget::PlayInternal(int speed) controls_->ShowPauseButton(); - if (stack_->currentWidget() == gl_widget_) { + if (stack_->currentWidget() == sizer_) { connect(gl_widget_, &ViewerGLWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate); } else { connect(AudioManager::instance(), &AudioManager::OutputNotified, this, &ViewerWidget::PlaybackTimerUpdate); @@ -315,7 +315,7 @@ int ViewerWidget::CalculateDivider() void ViewerWidget::UpdateStack() { if (!GetConnectedNode() || GetConnectedNode()->texture_input()->IsConnected()) { - stack_->setCurrentWidget(gl_widget_); + stack_->setCurrentWidget(sizer_); } else { stack_->setCurrentWidget(waveform_view_); } @@ -393,12 +393,21 @@ void ViewerWidget::ShowContextMenu(const QPoint &pos) // Playback resolution QMenu* playback_resolution_menu = menu.addMenu(tr("Resolution")); playback_resolution_menu->addAction(tr("Full"))->setData(1); - playback_resolution_menu->addAction(tr("1/2"))->setData(2); - playback_resolution_menu->addAction(tr("1/4"))->setData(4); - playback_resolution_menu->addAction(tr("1/8"))->setData(8); - playback_resolution_menu->addAction(tr("1/16"))->setData(16); + int dividers[] = {2, 4, 8, 16}; + for (int i=0;i<4;i++) { + playback_resolution_menu->addAction(tr("1/%1").arg(dividers[i]))->setData(dividers[i]); + } connect(playback_resolution_menu, &QMenu::triggered, this, &ViewerWidget::SetDividerFromMenu); + // Viewer Zoom Level + QMenu* zoom_menu = menu.addMenu(tr("Zoom")); + int zoom_levels[] = {10, 25, 50, 75, 100, 150, 200, 400}; + zoom_menu->addAction(tr("Fit"))->setData(0); + for (int i=0;i<8;i++) { + zoom_menu->addAction(tr("%1%").arg(zoom_levels[i]))->setData(zoom_levels[i]); + } + connect(zoom_menu, &QMenu::triggered, this, &ViewerWidget::SetZoomFromMenu); + foreach (QAction* a, playback_resolution_menu->actions()) { a->setCheckable(true); if (a->data() == divider_) { @@ -564,6 +573,11 @@ void ViewerWidget::SetDividerFromMenu(QAction *action) UpdateRendererParameters(); } +void ViewerWidget::SetZoomFromMenu(QAction *action) +{ + sizer_->SetZoom(action->data().toInt()); +} + void ViewerWidget::InvalidateVisible() { video_renderer_->InvalidateCache(TimeRange(GetTime(), GetTime())); diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index 6e52b9fa1..a45a34691 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -187,6 +187,8 @@ private slots: void SetDividerFromMenu(QAction* action); + void SetZoomFromMenu(QAction* action); + void InvalidateVisible(); void UpdateStack(); diff --git a/app/widget/viewer/viewerglwidget.h b/app/widget/viewer/viewerglwidget.h index f2e2bd1ca..4c14fb701 100644 --- a/app/widget/viewer/viewerglwidget.h +++ b/app/widget/viewer/viewerglwidget.h @@ -68,18 +68,17 @@ public: */ void DisconnectColorManager(); - /** - * @brief Set the transformation matrix to draw with - * - * Set this if you want the drawing to pass through some sort of transform (most of the time you won't want this). - */ - void SetMatrix(const QMatrix4x4& mat); - /** * @brief Set an image to load and display on screen */ void SetImage(const QString& fn); + ColorManager* color_manager() const; + + const QString& ocio_display() const; + const QString& ocio_view() const; + const QString& ocio_look() const; + public slots: /** * @brief Set the texture to draw and draw it @@ -113,11 +112,12 @@ public slots: */ void SetOCIOLook(const QString& look); - ColorManager* color_manager() const; - - const QString& ocio_display() const; - const QString& ocio_view() const; - const QString& ocio_look() const; + /** + * @brief Set the transformation matrix to draw with + * + * Set this if you want the drawing to pass through some sort of transform (most of the time you won't want this). + */ + void SetMatrix(const QMatrix4x4& mat); signals: void DragStarted(); diff --git a/app/widget/viewer/viewersizer.cpp b/app/widget/viewer/viewersizer.cpp index a9c3c2fc1..d9d906bc1 100644 --- a/app/widget/viewer/viewersizer.cpp +++ b/app/widget/viewer/viewersizer.cpp @@ -1,9 +1,12 @@ #include "viewersizer.h" +#include + ViewerSizer::ViewerSizer(QWidget *parent) : QWidget(parent), widget_(nullptr), - aspect_ratio_(0) + aspect_ratio_(0), + zoom_(0) { } @@ -23,15 +26,25 @@ void ViewerSizer::SetWidget(QWidget *widget) void ViewerSizer::SetChildSize(int width, int height) { - if (height == 0) { + width_ = width; + height_ = height; + + if (!width_ || !height_) { aspect_ratio_ = 0; } else { - aspect_ratio_ = static_cast(width) / static_cast(height); + aspect_ratio_ = static_cast(width_) / static_cast(height_); } UpdateSize(); } +void ViewerSizer::SetZoom(int percent) +{ + zoom_ = percent; + + UpdateSize(); +} + void ViewerSizer::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); @@ -53,21 +66,51 @@ void ViewerSizer::UpdateSize() widget_->setVisible(true); - double our_aspect_ratio = static_cast(width()) / static_cast(height()); + QSize child_size; + QMatrix4x4 child_matrix; - QPoint child_pos; - QSize child_size = size(); + if (zoom_ <= 0) { + + // If zoom is 0, we auto-fit + double our_aspect_ratio = static_cast(width()) / static_cast(height()); + + child_size = size(); + + if (our_aspect_ratio > aspect_ratio_) { + // This container is wider than the image, scale by height + child_size = QSize(qRound(child_size.height() * aspect_ratio_), height()); + } else { + // This container is taller than the image, scale by width + child_size = QSize(width(), qRound(child_size.width() / aspect_ratio_)); + } - if (our_aspect_ratio > aspect_ratio_) { - // This container is wider than the image, scale by height - child_size.setWidth(qRound(child_size.height() * aspect_ratio_)); - child_pos.setX(width() / 2 - child_size.width() / 2); } else { - // This container is taller than the image, scale by width - child_size.setHeight(qRound(child_size.width() / aspect_ratio_)); - child_pos.setY(height() / 2 - child_size.height() / 2); + + float x_scale = 1.0f; + float y_scale = 1.0f; + + int zoomed_width = qRound(width_ * static_cast(zoom_) * 0.01); + int zoomed_height = qRound(height_ * static_cast(zoom_) * 0.01); + + if (zoomed_width > width()) { + x_scale = static_cast(zoomed_width) / static_cast(width()); + zoomed_width = width(); + } + + if (zoomed_height > height()) { + y_scale = static_cast(zoomed_height) / static_cast(height()); + zoomed_height = height(); + } + + // Rather than make a huge surface, we still crop at our width/height and then signal a matrix + child_matrix.scale(x_scale, y_scale, 1.0F); + + child_size = QSize(zoomed_width, zoomed_height); + } widget_->resize(child_size); - widget_->move(child_pos); + widget_->move(width() / 2 - child_size.width() / 2, height() / 2 - child_size.height() / 2); + + emit RequestMatrix(child_matrix); } diff --git a/app/widget/viewer/viewersizer.h b/app/widget/viewer/viewersizer.h index 182b61164..9b383b01f 100644 --- a/app/widget/viewer/viewersizer.h +++ b/app/widget/viewer/viewersizer.h @@ -52,6 +52,16 @@ public: */ void SetChildSize(int width, int height); + /** + * @brief Set the zoom value of the child widget + * + * The number is an integer percentage (100 = 100%). Set to 0 to auto-fit. + */ + void SetZoom(int percent); + +signals: + void RequestMatrix(const QMatrix4x4& matrix); + protected: /** * @brief Listen for resize events to ensure the child widget remains correctly sized @@ -71,11 +81,22 @@ private: */ QWidget* widget_; + /** + * @brief Internal resolution values + */ + int width_; + int height_; + /** * @brief Aspect ratio calculated from the size provided by SetChildSize() */ double aspect_ratio_; + /** + * @brief Internal zoom value + */ + int zoom_; + }; #endif // VIEWERSIZER_H