diff --git a/app/widget/viewer/CMakeLists.txt b/app/widget/viewer/CMakeLists.txt index d010f11d8..5b457a802 100644 --- a/app/widget/viewer/CMakeLists.txt +++ b/app/widget/viewer/CMakeLists.txt @@ -26,6 +26,7 @@ set(OLIVE_SOURCES widget/viewer/viewer.cpp widget/viewer/viewerglwidget.h widget/viewer/viewerglwidget.cpp + widget/viewer/viewersafemargininfo.h widget/viewer/viewersizer.h widget/viewer/viewersizer.cpp widget/viewer/viewerwindow.h diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 442691e08..25f05a0f1 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -22,7 +22,9 @@ #include #include +#include #include +#include #include #include #include @@ -421,6 +423,60 @@ void ViewerWidget::ContextMenuSetFullScreen(QAction *action) SetFullScreen(QGuiApplication::screens().at(action->data().toInt())); } +void ViewerWidget::ContextMenuDisableSafeMargins() +{ + context_menu_widget_->SetSafeMargins(ViewerSafeMarginInfo(false)); +} + +void ViewerWidget::ContextMenuSetSafeMargins() +{ + context_menu_widget_->SetSafeMargins(ViewerSafeMarginInfo(true)); +} + +void ViewerWidget::ContextMenuSetCustomSafeMargins() +{ + QString s; + + forever { + bool ok; + + s = QInputDialog::getText(this, + tr("Safe Margins"), + tr("Enter custom ratio (e.g. \"4:3\", \"16/9\", etc.):"), + QLineEdit::Normal, + s, + &ok); + + if (!ok) { + // User cancelled dialog, do nothing + return; + } + + QStringList ratio_components = s.split(QRegExp(QStringLiteral(":|;|\\/"))); + + if (ratio_components.size() == 2) { + bool numer_ok, denom_ok; + + // FIXME: Won't accept decimals like 2.39:1 + double num = ratio_components.at(0).toDouble(&numer_ok); + double den = ratio_components.at(1).toDouble(&denom_ok); + + if (numer_ok + && denom_ok + && num > 0) { + // Exit loop and set this ratio + context_menu_widget_->SetSafeMargins(ViewerSafeMarginInfo(true, num / den)); + return; + } + } + + QMessageBox::warning(this, + tr("Invalid custom ratio"), + tr("Failed to parse \"%1\" into an aspect ratio. Please format a rational fraction with a ':' or a '/' separator.").arg(s), + QMessageBox::Ok); + } +} + void ViewerWidget::WindowAboutToClose() { ViewerWindow* vw = static_cast(sender()); @@ -544,6 +600,29 @@ void ViewerWidget::ShowContextMenu(const QPoint &pos) } connect(full_screen_menu, &QMenu::triggered, this, &ViewerWidget::ContextMenuSetFullScreen); + menu.addSeparator(); + + { + // Safe Margins + Menu* safe_margin_menu = new Menu(tr("Safe Margins")); + menu.addMenu(safe_margin_menu); + + QAction* safe_margin_off = safe_margin_menu->addAction(tr("Off")); + safe_margin_off->setCheckable(true); + safe_margin_off->setChecked(!context_menu_widget_->GetSafeMargin().is_enabled()); + connect(safe_margin_off, &QAction::triggered, this, &ViewerWidget::ContextMenuDisableSafeMargins); + + QAction* safe_margin_on = safe_margin_menu->addAction(tr("On")); + safe_margin_on->setCheckable(true); + safe_margin_on->setChecked(context_menu_widget_->GetSafeMargin().is_enabled() && !context_menu_widget_->GetSafeMargin().custom_ratio()); + connect(safe_margin_on, &QAction::triggered, this, &ViewerWidget::ContextMenuSetSafeMargins); + + QAction* safe_margin_custom = safe_margin_menu->addAction(tr("Custom Aspect")); + safe_margin_custom->setCheckable(true); + safe_margin_custom->setChecked(context_menu_widget_->GetSafeMargin().is_enabled() && context_menu_widget_->GetSafeMargin().custom_ratio()); + connect(safe_margin_custom, &QAction::triggered, this, &ViewerWidget::ContextMenuSetCustomSafeMargins); + } + menu.exec(static_cast(sender())->mapToGlobal(pos)); } diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index 87c15a935..892c5b5f0 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -232,6 +232,12 @@ private slots: void ContextMenuSetFullScreen(QAction* action); + void ContextMenuDisableSafeMargins(); + + void ContextMenuSetSafeMargins(); + + void ContextMenuSetCustomSafeMargins(); + void WindowAboutToClose(); }; diff --git a/app/widget/viewer/viewerglwidget.cpp b/app/widget/viewer/viewerglwidget.cpp index 6767ff918..e5dcf7d98 100644 --- a/app/widget/viewer/viewerglwidget.cpp +++ b/app/widget/viewer/viewerglwidget.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include "common/define.h" #include "render/backend/opengl/openglrenderfunctions.h" @@ -223,6 +224,18 @@ void ViewerGLWidget::ConnectSibling(ViewerGLWidget *sibling) sibling->SetImageFromLoadBuffer(&load_buffer_); } +const ViewerSafeMarginInfo &ViewerGLWidget::GetSafeMargin() const +{ + return safe_margin_; +} + +void ViewerGLWidget::SetSafeMargins(const ViewerSafeMarginInfo &safe_margin) +{ + safe_margin_ = safe_margin; + + update(); +} + void ViewerGLWidget::mousePressEvent(QMouseEvent *event) { QOpenGLWidget::mousePressEvent(event); @@ -296,18 +309,48 @@ void ViewerGLWidget::paintGL() f->glClear(GL_COLOR_BUFFER_BIT); // We only draw if we have a pipeline - if (!has_image_ || !color_service_ || !texture_.IsCreated()) { - return; + if (has_image_ && color_service_ && texture_.IsCreated()) { + // Bind retrieved texture + f->glBindTexture(GL_TEXTURE_2D, texture_.texture()); + + // Blit using the color service + color_service_->ProcessOpenGL(true, matrix_); + + // Release retrieved texture + f->glBindTexture(GL_TEXTURE_2D, 0); } - // Bind retrieved texture - f->glBindTexture(GL_TEXTURE_2D, texture_.texture()); + // Draw action/title safe areas + if (safe_margin_.is_enabled()) { + QPainter p(this); + p.setPen(Qt::lightGray); + p.setBrush(Qt::NoBrush); - // Blit using the color service - color_service_->ProcessOpenGL(true, matrix_); + int x = 0, y = 0, w = width(), h = height(); - // Release retrieved texture - f->glBindTexture(GL_TEXTURE_2D, 0); + if (safe_margin_.custom_ratio()) { + double widget_ar = static_cast(width()) / static_cast(height()); + + if (widget_ar > safe_margin_.ratio()) { + // Widget is wider than margins + w = h * safe_margin_.ratio(); + x = width() / 2 - w / 2; + } else { + h = w / safe_margin_.ratio(); + y = height() / 2 - h / 2; + } + } + + p.drawRect(w / 20 + x, h / 20 + y, w / 10 * 9, h / 10 * 9); + p.drawRect(w / 10 + x, h / 10 + y, w / 10 * 8, h / 10 * 8); + + int cross = qMin(w, h) / 32; + + QLine lines[] = {QLine(rect().center().x() - cross, rect().center().y(), rect().center().x() + cross, rect().center().y()), + QLine(rect().center().x(), rect().center().y() - cross, rect().center().x(), rect().center().y() + cross)}; + + p.drawLines(lines, 2); + } } void ViewerGLWidget::RefreshColorPipeline() diff --git a/app/widget/viewer/viewerglwidget.h b/app/widget/viewer/viewerglwidget.h index 046e5c2df..4898db3a8 100644 --- a/app/widget/viewer/viewerglwidget.h +++ b/app/widget/viewer/viewerglwidget.h @@ -29,6 +29,7 @@ #include "render/backend/opengl/opengltexture.h" #include "render/color.h" #include "render/colormanager.h" +#include "viewersafemargininfo.h" OLIVE_NAMESPACE_ENTER @@ -87,6 +88,9 @@ public: void ConnectSibling(ViewerGLWidget* sibling); + const ViewerSafeMarginInfo& GetSafeMargin() const; + void SetSafeMargins(const ViewerSafeMarginInfo& safe_margin); + public slots: /** * @brief Set the texture to draw and draw it @@ -244,6 +248,8 @@ private: bool signal_cursor_color_; + ViewerSafeMarginInfo safe_margin_; + private slots: /** * @brief Slot to connect just before the OpenGL context is destroyed to clean up resources diff --git a/app/widget/viewer/viewersafemargininfo.h b/app/widget/viewer/viewersafemargininfo.h new file mode 100644 index 000000000..4e1e216d0 --- /dev/null +++ b/app/widget/viewer/viewersafemargininfo.h @@ -0,0 +1,65 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef VIEWERSAFEMARGININFO_H +#define VIEWERSAFEMARGININFO_H + +#include "common/define.h" + +OLIVE_NAMESPACE_ENTER + +class ViewerSafeMarginInfo { +public: + ViewerSafeMarginInfo() : + enabled_(false) + { + } + + ViewerSafeMarginInfo(bool enabled, double ratio = 0) : + enabled_(enabled), + ratio_(ratio) + { + } + + bool is_enabled() const + { + return enabled_; + } + + bool custom_ratio() const + { + return (ratio_ > 0); + } + + double ratio() const + { + return ratio_; + } + +private: + bool enabled_; + + double ratio_; + +}; + +OLIVE_NAMESPACE_EXIT + +#endif // VIEWERSAFEMARGININFO_H