Files
oak-editor/app/widget/viewer/viewerdisplay.cpp
T
itsmattkc 75d4cd899b use hardcoded namespace
The macro defined namespaces confused the hell out of lupdate and more or less broke translations permanently. Looks like the only way we can do it is to have a hardcoded namespace, which goes against my instinct, but honestly how likely is it that we'll change the namespace anyway (I guess forks might want to do it, but that's their problem ;) )
2020-11-17 20:24:42 +11:00

425 lines
10 KiB
C++

/***
Olive - Non-Linear Video Editor
Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.
***/
#include "viewerdisplay.h"
#include <OpenImageIO/imagebuf.h>
#include <QFileInfo>
#include <QMessageBox>
#include <QMouseEvent>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QOpenGLTexture>
#include <QPainter>
#include "common/define.h"
#include "common/functiontimer.h"
#include "config/config.h"
#include "core.h"
#include "gizmotraverser.h"
namespace olive {
ViewerDisplayWidget::ViewerDisplayWidget(QWidget *parent) :
ManagedDisplayWidget(parent),
signal_cursor_color_(false),
gizmos_(nullptr),
gizmo_click_(false),
last_loaded_buffer_(nullptr),
hand_dragging_(false),
deinterlace_(false)
{
connect(Core::instance(), &Core::ToolChanged, this, &ViewerDisplayWidget::UpdateCursor);
// Initializes cursor based on tool
UpdateCursor();
}
ViewerDisplayWidget::~ViewerDisplayWidget()
{
OnDestroy();
}
void ViewerDisplayWidget::SetMatrixTranslate(const QMatrix4x4 &mat)
{
translate_matrix_ = mat;
UpdateMatrix();
}
void ViewerDisplayWidget::SetMatrixZoom(const QMatrix4x4 &mat)
{
scale_matrix_ = mat;
UpdateMatrix();
}
void ViewerDisplayWidget::UpdateCursor()
{
if (Core::instance()->tool() == Tool::kHand) {
setCursor(Qt::OpenHandCursor);
} else {
unsetCursor();
}
}
QMatrix4x4 ViewerDisplayWidget::GetCompleteMatrixFlippedYTranslation()
{
QMatrix4x4 mat = combined_matrix_;
mat.scale(1, -1, 1);
return mat;
}
void ViewerDisplayWidget::SetSignalCursorColorEnabled(bool e)
{
signal_cursor_color_ = e;
setMouseTracking(e);
}
void ViewerDisplayWidget::SetImage(FramePtr in_buffer)
{
last_loaded_buffer_ = in_buffer;
if (last_loaded_buffer_) {
makeCurrent();
if (!texture_
|| texture_->width() != in_buffer->width()
|| texture_->height() != in_buffer->height()
|| texture_->format() != in_buffer->format()
|| texture_->channel_count() != in_buffer->channel_count()) {
texture_ = renderer()->CreateTexture(in_buffer->video_params(), in_buffer->data(), in_buffer->linesize_pixels());
} else {
texture_->Upload(in_buffer->data(), in_buffer->linesize_pixels());
}
doneCurrent();
}
update();
}
void ViewerDisplayWidget::SetDeinterlacing(bool e)
{
deinterlace_ = e;
update();
}
const ViewerSafeMarginInfo &ViewerDisplayWidget::GetSafeMargin() const
{
return safe_margin_;
}
void ViewerDisplayWidget::SetSafeMargins(const ViewerSafeMarginInfo &safe_margin)
{
if (safe_margin_ != safe_margin) {
safe_margin_ = safe_margin;
update();
}
}
void ViewerDisplayWidget::SetGizmos(Node *node)
{
if (gizmos_ != node) {
gizmos_ = node;
update();
}
}
void ViewerDisplayWidget::SetVideoParams(const VideoParams &params)
{
gizmo_params_ = params;
if (gizmos_) {
update();
}
}
void ViewerDisplayWidget::SetTime(const rational &time)
{
time_ = time;
if (gizmos_) {
update();
}
}
FramePtr ViewerDisplayWidget::last_loaded_buffer() const
{
return last_loaded_buffer_;
}
QPoint ViewerDisplayWidget::TransformViewerSpaceToBufferSpace(QPoint pos)
{
/*
* Inversion will only fail if the viewer has been scaled by 0 in any direction
* which I think should never happen.
*/
return pos * GenerateWorldTransform().inverted();
}
void ViewerDisplayWidget::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton && gizmos_
&& gizmos_->GizmoPress(gizmo_db_, TransformViewerSpaceToBufferSpace(event->pos()),
QVector2D(GetTexturePosition(size())), size())) {
// Handle gizmo click
gizmo_click_ = true;
gizmo_drag_time_ = GetGizmoTime();
} else if (IsHandDrag(event)) {
// Handle hand drag
hand_last_drag_pos_ = event->pos();
hand_dragging_ = true;
emit HandDragStarted();
setCursor(Qt::ClosedHandCursor);
} else {
if (event->button() == Qt::LeftButton) {
// Handle standard drag
emit DragStarted();
}
ManagedDisplayWidget::mousePressEvent(event);
}
}
void ViewerDisplayWidget::mouseMoveEvent(QMouseEvent *event)
{
// Do this no matter what, emits signal to any pixel samplers
if (signal_cursor_color_) {
Color reference, display;
if (last_loaded_buffer_) {
QVector3D pixel_pos(static_cast<float>(event->x()) / static_cast<float>(width()) * 2.0f - 1.0f,
static_cast<float>(event->y()) / static_cast<float>(height()) * 2.0f - 1.0f,
0);
pixel_pos = GenerateWorldTransform().inverted() * pixel_pos;
int frame_x = qRound((pixel_pos.x() + 1.0f) * 0.5f * last_loaded_buffer_->width());
int frame_y = qRound((pixel_pos.y() + 1.0f) * 0.5f * last_loaded_buffer_->height());
reference = last_loaded_buffer_->get_pixel(frame_x, frame_y);
display = color_service()->ConvertColor(reference);
}
emit CursorColor(reference, display);
}
// Handle hand dragging
if (hand_dragging_) {
// Emit movement
emit HandDragMoved(event->x() - hand_last_drag_pos_.x(),
event->y() - hand_last_drag_pos_.y());
hand_last_drag_pos_ = event->pos();
} else if (gizmo_click_) {
// Signal movement
gizmos_->GizmoMove(TransformViewerSpaceToBufferSpace(event->pos()),
QVector2D(GetTexturePosition(size())), gizmo_drag_time_);
update();
} else {
// Default behavior
ManagedDisplayWidget::mouseMoveEvent(event);
}
}
void ViewerDisplayWidget::mouseReleaseEvent(QMouseEvent *event)
{
if (hand_dragging_) {
// Handle hand drag
emit HandDragEnded();
hand_dragging_ = false;
UpdateCursor();
} else if (gizmo_click_) {
// Handle gizmo
gizmos_->GizmoRelease();
gizmo_click_ = false;
} else {
// Default behavior
ManagedDisplayWidget::mouseReleaseEvent(event);
}
}
void ViewerDisplayWidget::OnInit()
{
ManagedDisplayWidget::OnInit();
}
void ViewerDisplayWidget::OnPaint()
{
// Clear background to empty
renderer()->ClearDestination();
// We only draw if we have a pipeline
if (last_loaded_buffer_ && color_service()) {
if (deinterlace_) {
qDebug() << "FIXME: Deinterlacing is currently broken, we're working on this...";
//color_service()->pipeline()->setUniformValue("ove_resolution", texture_.width(), texture_.height());
//color_service()->pipeline()->setUniformValue("ove_deinterlace", deinterlace_);
}
// Draw texture through color transform
renderer()->BlitColorManaged(color_service(), texture_, true,
VideoParams(width(), height(), static_cast<VideoParams::Format>(Config::Current()["OfflinePixelFormat"].toInt()), VideoParams::kInternalChannelCount),
GetCompleteMatrixFlippedYTranslation());
}
QTransform world_transform = GenerateWorldTransform();
// Draw gizmos if we have any
if (gizmos_) {
GizmoTraverser gt(QSize(gizmo_params_.width(), gizmo_params_.height()));
rational node_time = GetGizmoTime();
gizmo_db_ = gt.GenerateDatabase(gizmos_, TimeRange(node_time, node_time));
QPainter p(inner_widget());
p.setWorldTransform(world_transform);
gizmos_->DrawGizmos(gizmo_db_, &p, QVector2D(GetTexturePosition(size())), size());
}
// Draw action/title safe areas
if (safe_margin_.is_enabled()) {
QPainter p(inner_widget());
p.setWorldTransform(world_transform);
p.setPen(Qt::lightGray);
p.setBrush(Qt::NoBrush);
int x = 0, y = 0, w = width(), h = height();
if (safe_margin_.custom_ratio()) {
double widget_ar = static_cast<double>(width()) / static_cast<double>(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 ViewerDisplayWidget::OnDestroy()
{
ManagedDisplayWidget::OnDestroy();
texture_ = nullptr;
}
QMatrix4x4 ViewerDisplayWidget::GetMatrixTranslate()
{
return translate_matrix_;
}
QPointF ViewerDisplayWidget::GetTexturePosition(const QPoint &screen_pos)
{
return GetTexturePosition(screen_pos.x(), screen_pos.y());
}
QPointF ViewerDisplayWidget::GetTexturePosition(const QSize &size)
{
return GetTexturePosition(size.width(), size.height());
}
QPointF ViewerDisplayWidget::GetTexturePosition(const double &x, const double &y)
{
return QPointF(x / gizmo_params_.width(),
y / gizmo_params_.height());
}
rational ViewerDisplayWidget::GetGizmoTime()
{
return GetAdjustedTime(GetTimeTarget(), gizmos_, time_, NodeParam::kInput);
}
bool ViewerDisplayWidget::IsHandDrag(QMouseEvent *event) const
{
return event->button() == Qt::MiddleButton || Core::instance()->tool() == Tool::kHand;
}
void ViewerDisplayWidget::UpdateMatrix()
{
combined_matrix_ = scale_matrix_ * translate_matrix_;
update();
}
QTransform ViewerDisplayWidget::GenerateWorldTransform()
{
/*
* Get matrix elements (roughly) as below in column major order
*
* | Sx 0 0 Tx |
* | 0 Sy 0 Ty |
* | 0 0 Sz Tz |
* | 0 0 0 1 |
*/
float *d = combined_matrix_.data();
QTransform world;
// Move corner of canvas to correct point
world.translate(width() * 0.5 - width() * *(d)*0.5, height() * 0.5 - height() * *(d + 5) * 0.5);
// Scale
world.scale(*(d), *(d + 5));
// Translate for mouse movement
world.translate(*(d + 12) * width() * 0.5 / *(d), *(d + 13) * height() * 0.5 / *(d + 5));
return world;
}
}