First commit of brand new rewrite
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
#include "collapsiblewidget.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QLabel>
|
||||
#include <QCheckBox>
|
||||
#include <QLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QVBoxLayout>
|
||||
#include <QPushButton>
|
||||
|
||||
CollapsibleWidget::CollapsibleWidget(QWidget* parent) : QWidget(parent)
|
||||
{
|
||||
layout = new QVBoxLayout(this);
|
||||
layout->setMargin(0);
|
||||
|
||||
title_bar = new QHBoxLayout();
|
||||
title_bar->setMargin(0);
|
||||
enabled_check = new QCheckBox();
|
||||
enabled_check->setChecked(true);
|
||||
header = new QLabel();
|
||||
collapse_button = new QPushButton("-");
|
||||
collapse_button->setMaximumWidth(25);
|
||||
setText("<untitled>");
|
||||
title_bar->addWidget(collapse_button);
|
||||
title_bar->addWidget(enabled_check);
|
||||
title_bar->addWidget(header);
|
||||
title_bar->addStretch();
|
||||
layout->addLayout(title_bar);
|
||||
|
||||
line = new QFrame();
|
||||
line->setFrameShape(QFrame::HLine);
|
||||
line->setFrameShadow(QFrame::Sunken);
|
||||
layout->addWidget(line);
|
||||
|
||||
contents = NULL;
|
||||
}
|
||||
|
||||
void CollapsibleWidget::setContents(QWidget* c) {
|
||||
bool existing = (contents != NULL);
|
||||
contents = c;
|
||||
if (!existing) {
|
||||
layout->addWidget(contents);
|
||||
connect(enabled_check, SIGNAL(toggled(bool)), this, SLOT(on_enabled_change(bool)));
|
||||
connect(collapse_button, SIGNAL(clicked()), this, SLOT(on_visible_change()));
|
||||
}
|
||||
}
|
||||
|
||||
void CollapsibleWidget::setText(const QString &s) {
|
||||
header->setText(s);
|
||||
}
|
||||
|
||||
void CollapsibleWidget::on_enabled_change(bool b) {
|
||||
contents->setEnabled(b);
|
||||
}
|
||||
|
||||
void CollapsibleWidget::on_visible_change() {
|
||||
contents->setVisible(!contents->isVisible());
|
||||
collapse_button->setText(contents->isVisible() ? "-" : "+");
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifndef COLLAPSIBLEWIDGET_H
|
||||
#define COLLAPSIBLEWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
class QLabel;
|
||||
class QCheckBox;
|
||||
class QHBoxLayout;
|
||||
class QVBoxLayout;
|
||||
class QPushButton;
|
||||
class QFrame;
|
||||
|
||||
class CollapsibleWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CollapsibleWidget(QWidget* parent = 0);
|
||||
void setContents(QWidget* c);
|
||||
void setText(const QString &);
|
||||
private:
|
||||
QLabel* header;
|
||||
QCheckBox* enabled_check;
|
||||
QHBoxLayout* title_bar;
|
||||
QVBoxLayout* layout;
|
||||
QPushButton* collapse_button;
|
||||
QWidget* contents;
|
||||
QFrame* line;
|
||||
|
||||
private slots:
|
||||
void on_enabled_change(bool b);
|
||||
void on_visible_change();
|
||||
};
|
||||
|
||||
#endif // COLLAPSIBLEWIDGET_H
|
||||
@@ -0,0 +1,9 @@
|
||||
#include "playbutton.h"
|
||||
|
||||
PlayButton::PlayButton(QWidget* parent) : QPushButton(parent)
|
||||
{
|
||||
play_text = ">";
|
||||
pause_text = "||";
|
||||
|
||||
setText(play_text);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef PLAYBUTTON_H
|
||||
#define PLAYBUTTON_H
|
||||
|
||||
#include <QPushButton>
|
||||
|
||||
class PlayButton : public QPushButton
|
||||
{
|
||||
public:
|
||||
PlayButton(QWidget* parent = 0);
|
||||
private:
|
||||
QString play_text;
|
||||
QString pause_text;
|
||||
};
|
||||
|
||||
#endif // PLAYBUTTON_H
|
||||
@@ -0,0 +1,30 @@
|
||||
#include "sourcetable.h"
|
||||
#include "panels/project.h"
|
||||
|
||||
#include "io/media.h"
|
||||
#include "panels/timeline.h"
|
||||
#include "panels/viewer.h"
|
||||
#include "panels/panels.h"
|
||||
|
||||
#include <QDragEnterEvent>
|
||||
|
||||
SourceTable::SourceTable(QWidget* parent) : QTreeWidget(parent) {
|
||||
this->sortByColumn(0, Qt::AscendingOrder);
|
||||
}
|
||||
|
||||
void SourceTable::mouseDoubleClickEvent(QMouseEvent* )
|
||||
{
|
||||
if (selectedItems().count() == 0) {
|
||||
panel_project->import_dialog();
|
||||
} else if (selectedItems().count() == 1) {
|
||||
Media* m = reinterpret_cast<Media*>(selectedItems().at(0)->data(0, Qt::UserRole + 1).value<quintptr>());
|
||||
if (m->is_sequence) {
|
||||
panel_timeline->set_sequence(m->sequence);
|
||||
panel_viewer->set_sequence(m->sequence);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void SourceTable::dragEnterEvent(QDragEnterEvent *event) {
|
||||
// event->accept();
|
||||
//}
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef SOURCETABLE_H
|
||||
#define SOURCETABLE_H
|
||||
|
||||
#include <QTreeWidget>
|
||||
|
||||
class Project;
|
||||
|
||||
class SourceTable : public QTreeWidget
|
||||
{
|
||||
public:
|
||||
SourceTable(QWidget* parent = 0);
|
||||
protected:
|
||||
void mouseDoubleClickEvent(QMouseEvent *event) override;
|
||||
// void dragEnterEvent(QDragEnterEvent *event) override;
|
||||
private:
|
||||
};
|
||||
|
||||
#endif // SOURCETABLE_H
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef TIMELINETOOLS_H
|
||||
#define TIMELINETOOLS_H
|
||||
|
||||
enum TimelineTools {
|
||||
TIMELINE_TOOL_POINTER,
|
||||
TIMELINE_TOOL_EDIT,
|
||||
TIMELINE_TOOL_RAZOR,
|
||||
TIMELINE_TOOL_RIPPLE,
|
||||
TIMELINE_TOOL_ROLLING,
|
||||
TIMELINE_TOOL_SLIP,
|
||||
TIMELINE_TOOL_HAND,
|
||||
TIMELINE_TOOL_ZOOM
|
||||
};
|
||||
|
||||
#endif // TIMELINETOOLS_H
|
||||
@@ -0,0 +1,799 @@
|
||||
#include "timelinewidget.h"
|
||||
|
||||
#include "panels/panels.h"
|
||||
#include "io/config.h"
|
||||
#include "project/sequence.h"
|
||||
#include "project/clip.h"
|
||||
#include "panels/project.h"
|
||||
#include "panels/timeline.h"
|
||||
#include "io/media.h"
|
||||
#include "ui/sourcetable.h"
|
||||
#include "panels/effectcontrols.h"
|
||||
|
||||
#include "effects/effects.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QColor>
|
||||
#include <QDebug>
|
||||
#include <QMouseEvent>
|
||||
#include <QObject>
|
||||
#include <QVariant>
|
||||
#include <QPointF>
|
||||
|
||||
TimelineWidget::TimelineWidget(QWidget *parent) : QWidget(parent)
|
||||
{
|
||||
bottom_align = false;
|
||||
setMouseTracking(true);
|
||||
track_height = 40;
|
||||
|
||||
clip_pixmap = NULL;
|
||||
|
||||
setAcceptDrops(true);
|
||||
}
|
||||
|
||||
bool same_sign(int a, int b) {
|
||||
return (a < 0) == (b < 0);
|
||||
}
|
||||
|
||||
void TimelineWidget::dragEnterEvent(QDragEnterEvent *event) {
|
||||
if (static_cast<SourceTable*>(event->source()) == panel_project->source_table) {
|
||||
QPoint pos = event->pos();
|
||||
event->accept();
|
||||
QList<QTreeWidgetItem*> items = panel_project->source_table->selectedItems();
|
||||
long entry_point = getFrameFromScreenPoint(pos.x(), false);
|
||||
panel_timeline->drag_frame_start = entry_point + getFrameFromScreenPoint(50, false);
|
||||
panel_timeline->drag_track_start = (bottom_align) ? -1 : 0;
|
||||
for (int i=0;i<items.size();i++) {
|
||||
bool ignore_infinite_length = false;
|
||||
Media* m = reinterpret_cast<Media*>(items.at(i)->data(0, Qt::UserRole + 1).value<quintptr>());
|
||||
long duration = m->get_length_in_frames(panel_timeline->sequence->frame_rate);
|
||||
Ghost g = {NULL, entry_point, entry_point + duration};
|
||||
g.media = m;
|
||||
g.clip_in = 0;
|
||||
for (int j=0;j<m->audio_tracks.size();j++) {
|
||||
g.track = j;
|
||||
g.media_stream = &m->audio_tracks[j];
|
||||
ignore_infinite_length = true;
|
||||
panel_timeline->ghosts.append(g);
|
||||
}
|
||||
for (int j=0;j<m->video_tracks.size();j++) {
|
||||
g.track = -1-j;
|
||||
g.media_stream = &m->video_tracks[j];
|
||||
if (m->video_tracks[j].infinite_length && !ignore_infinite_length) g.out = g.in + 100;
|
||||
panel_timeline->ghosts.append(g);
|
||||
}
|
||||
entry_point += duration;
|
||||
}
|
||||
init_ghosts();
|
||||
panel_timeline->importing = true;
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::dragMoveEvent(QDragMoveEvent *event) {
|
||||
if (panel_timeline->importing) {
|
||||
QPoint pos = event->pos();
|
||||
update_ghosts(pos);
|
||||
panel_timeline->repaint_timeline();
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::dragLeaveEvent(QDragLeaveEvent *event) {
|
||||
if (panel_timeline->importing) {
|
||||
panel_timeline->ghosts.clear();
|
||||
panel_timeline->importing = false;
|
||||
panel_timeline->repaint_timeline();
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::dropEvent(QDropEvent* event) {
|
||||
if (panel_timeline->importing) {
|
||||
event->accept();
|
||||
|
||||
for (int i=0;i<panel_timeline->ghosts.size();i++) {
|
||||
const Ghost& g = panel_timeline->ghosts.at(i);
|
||||
|
||||
panel_timeline->sequence->delete_area(g.in, g.out, g.track);
|
||||
|
||||
Clip& c = panel_timeline->sequence->new_clip();
|
||||
c.media = g.media;
|
||||
c.media_stream = g.media_stream;
|
||||
c.timeline_in = g.in;
|
||||
c.timeline_out = g.out;
|
||||
c.clip_in = g.clip_in;
|
||||
c.color_r = 128;
|
||||
c.color_g = 128;
|
||||
c.color_b = 192;
|
||||
c.sequence = panel_timeline->sequence;
|
||||
c.track = g.track;
|
||||
c.name = c.media->name;
|
||||
|
||||
if (c.track < 0) {
|
||||
// add default video effects
|
||||
c.effects.append(new TransformEffect(&c));
|
||||
} else {
|
||||
// add default audio effects
|
||||
c.effects.append(new VolumeEffect(&c));
|
||||
c.effects.append(new PanEffect(&c));
|
||||
}
|
||||
}
|
||||
|
||||
panel_timeline->ghosts.clear();
|
||||
panel_timeline->importing = false;
|
||||
|
||||
panel_timeline->redraw_all_clips();
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::mousePressEvent(QMouseEvent *event) {
|
||||
QPoint pos = event->pos();
|
||||
|
||||
panel_timeline->drag_frame_start = getFrameFromScreenPoint(pos.x(), false);
|
||||
panel_timeline->drag_track_start = getTrackFromScreenPoint(pos.y());
|
||||
int clip_index = panel_timeline->trim_target;
|
||||
if (clip_index == -1) clip_index = getClipIndexFromCoords(panel_timeline->drag_frame_start, panel_timeline->drag_track_start);
|
||||
|
||||
switch (panel_timeline->tool) {
|
||||
case TIMELINE_TOOL_POINTER:
|
||||
case TIMELINE_TOOL_RIPPLE:
|
||||
{
|
||||
if (clip_index >= 0) {
|
||||
if (panel_timeline->is_clip_selected(clip_index)) {
|
||||
// TODO if shift is down, deselect it
|
||||
} else {
|
||||
// if "shift" is not down
|
||||
if (!(event->modifiers() & Qt::ShiftModifier)) {
|
||||
panel_timeline->selections.clear();
|
||||
}
|
||||
|
||||
Clip& clip = panel_timeline->sequence->get_clip(clip_index);
|
||||
panel_timeline->selections.append({clip.timeline_in, clip.timeline_out, clip.track});
|
||||
}
|
||||
panel_timeline->moving_init = true;
|
||||
} else {
|
||||
panel_timeline->selections.clear();
|
||||
}
|
||||
panel_timeline->repaint_timeline();
|
||||
}
|
||||
break;
|
||||
case TIMELINE_TOOL_EDIT:
|
||||
panel_timeline->seek(panel_timeline->drag_frame_start);
|
||||
panel_timeline->selecting = true;
|
||||
panel_timeline->repaint_timeline();
|
||||
break;
|
||||
case TIMELINE_TOOL_RAZOR:
|
||||
{
|
||||
if (clip_index >= 0) {
|
||||
panel_timeline->sequence->split_clip(clip_index, panel_timeline->drag_frame_start);
|
||||
}
|
||||
panel_timeline->splitting = true;
|
||||
panel_timeline->redraw_all_clips();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) {
|
||||
if (panel_timeline->moving_proc) {
|
||||
if (event->modifiers() & Qt::AltModifier) { // if holding alt, duplicate rather than move
|
||||
// duplicate clips
|
||||
for (int i=0;i<panel_timeline->ghosts.size();i++) {
|
||||
Ghost& g = panel_timeline->ghosts[i];
|
||||
Clip& c = panel_timeline->sequence->insert_clip(*g.clip);
|
||||
|
||||
c.timeline_in = g.in;
|
||||
c.timeline_out = g.out;
|
||||
c.track = g.track;
|
||||
c.undeletable = true;
|
||||
|
||||
// step 2 - delete anything that exists in area that clip is moving to
|
||||
panel_timeline->sequence->delete_area(g.in, g.out, g.track);
|
||||
|
||||
c.undeletable = false;
|
||||
}
|
||||
} else {
|
||||
// move clips
|
||||
// TODO can we do this better than 3 consecutive for loops?
|
||||
for (int i=0;i<panel_timeline->ghosts.size();i++) {
|
||||
// step 1 - set clips that are moving to "undeletable" (to avoid step 2 deleting any part of them)
|
||||
panel_timeline->ghosts[i].clip->undeletable = true;
|
||||
}
|
||||
for (int i=0;i<panel_timeline->ghosts.size();i++) {
|
||||
Ghost& g = panel_timeline->ghosts[i];
|
||||
|
||||
if (panel_timeline->tool == TIMELINE_TOOL_POINTER) {
|
||||
// step 2 - delete anything that exists in area that clip is moving to
|
||||
// note: ripples are non-destructive so this is pointer-tool exclusive
|
||||
panel_timeline->sequence->delete_area(g.in, g.out, g.track);
|
||||
}
|
||||
|
||||
// step 3 - move clips
|
||||
g.clip->timeline_in = g.in;
|
||||
g.clip->timeline_out = g.out;
|
||||
g.clip->track = g.track;
|
||||
g.clip->clip_in = g.clip_in;
|
||||
}
|
||||
for (int i=0;i<panel_timeline->ghosts.size();i++) {
|
||||
// step 4 - set clips back to deletable
|
||||
panel_timeline->ghosts[i].clip->undeletable = false;
|
||||
}
|
||||
}
|
||||
|
||||
// ripple ops
|
||||
if (panel_timeline->tool == TIMELINE_TOOL_RIPPLE) {
|
||||
long ripple_length, ripple_point;
|
||||
if (panel_timeline->trim_in) {
|
||||
ripple_length = panel_timeline->ghosts.at(0).old_in - panel_timeline->ghosts.at(0).in;
|
||||
ripple_point = (ripple_length < 0) ? panel_timeline->ghosts.at(0).old_in : panel_timeline->ghosts.at(0).in;
|
||||
} else {
|
||||
ripple_length = panel_timeline->ghosts.at(0).old_out - panel_timeline->ghosts.at(0).out;
|
||||
ripple_point = (ripple_length < 0) ? panel_timeline->ghosts.at(0).old_out : panel_timeline->ghosts.at(0).out;
|
||||
}
|
||||
for (int i=0;i<panel_timeline->ghosts.size();i++) {
|
||||
long comp_point;
|
||||
if (panel_timeline->trim_in) {
|
||||
comp_point = (ripple_length < 0) ? panel_timeline->ghosts.at(i).old_in : panel_timeline->ghosts.at(i).in;
|
||||
} else {
|
||||
comp_point = (ripple_length < 0) ? panel_timeline->ghosts.at(i).old_out : panel_timeline->ghosts.at(i).out;
|
||||
}
|
||||
if (ripple_point > comp_point) ripple_point = comp_point;
|
||||
}
|
||||
if (panel_timeline->trim_in) {
|
||||
panel_timeline->ripple(ripple_point, ripple_length);
|
||||
} else {
|
||||
panel_timeline->ripple(ripple_point, -ripple_length);
|
||||
}
|
||||
}
|
||||
|
||||
panel_timeline->redraw_all_clips();
|
||||
}
|
||||
|
||||
// destroy all ghosts
|
||||
panel_timeline->ghosts.clear();
|
||||
|
||||
panel_timeline->selecting = false;
|
||||
panel_timeline->moving_proc = false;
|
||||
panel_timeline->moving_init = false;
|
||||
panel_timeline->splitting = false;
|
||||
pre_clips.clear();
|
||||
post_clips.clear();
|
||||
|
||||
// find out how many clips are selected
|
||||
bool single_select = false;
|
||||
int selected_clip = 0;
|
||||
for (int i=0;i<panel_timeline->sequence->clip_count();i++) {
|
||||
if (panel_timeline->is_clip_selected(i)) {
|
||||
if (!single_select) {
|
||||
// found ONE selected clip
|
||||
selected_clip = i;
|
||||
single_select = true;
|
||||
} else {
|
||||
// more than one clip is selected
|
||||
single_select = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (single_select) {
|
||||
panel_effect_controls->set_clip(&panel_timeline->sequence->get_clip(selected_clip));
|
||||
} else {
|
||||
panel_effect_controls->set_clip(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::init_ghosts() {
|
||||
for (int i=0;i<panel_timeline->ghosts.size();i++) {
|
||||
Ghost& g = panel_timeline->ghosts[i];
|
||||
g.old_in = g.in;
|
||||
g.old_out = g.out;
|
||||
g.old_track = g.track;
|
||||
g.old_clip_in = g.clip_in;
|
||||
|
||||
if (panel_timeline->trim_target > -1) {
|
||||
// used for trim ops
|
||||
g.ghost_length = g.old_out - g.old_in;
|
||||
g.media_length = g.clip->media->get_length_in_frames(panel_timeline->sequence->frame_rate);
|
||||
}
|
||||
}
|
||||
for (int i=0;i<panel_timeline->selections.size();i++) {
|
||||
Selection& s = panel_timeline->selections[i];
|
||||
s.old_in = s.in;
|
||||
s.old_out = s.out;
|
||||
s.old_track = s.track;
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::update_ghosts(QPoint& mouse_pos) {
|
||||
int mouse_track = getTrackFromScreenPoint(mouse_pos.y());
|
||||
long frame_diff = getFrameFromScreenPoint(mouse_pos.x(), false) - panel_timeline->drag_frame_start;
|
||||
int track_diff = mouse_track - panel_timeline->drag_track_start;
|
||||
|
||||
long validator;
|
||||
if (panel_timeline->trim_target > -1) {
|
||||
// trim ops
|
||||
|
||||
// validate ghosts
|
||||
for (int i=0;i<panel_timeline->ghosts.size();i++) {
|
||||
Ghost& g = panel_timeline->ghosts[i];
|
||||
|
||||
if (panel_timeline->trim_in) {
|
||||
// prevent clip length from being less than 1 frame long
|
||||
validator = g.ghost_length - frame_diff;
|
||||
if (validator < 1) frame_diff -= (1 - validator);
|
||||
|
||||
// prevent timeline in from going below 0
|
||||
validator = g.old_in + frame_diff;
|
||||
if (validator < 0) frame_diff -= validator;
|
||||
|
||||
if (!g.clip->media_stream->infinite_length) {
|
||||
// prevent clip_in from going below 0
|
||||
validator = g.old_clip_in + frame_diff;
|
||||
if (validator < 0) frame_diff -= validator;
|
||||
}
|
||||
|
||||
// ripple ops
|
||||
if (panel_timeline->tool == TIMELINE_TOOL_RIPPLE) {
|
||||
for (int j=0;j<post_clips.size();j++) {
|
||||
// prevent any rippled clip from going below 0
|
||||
Clip* post = post_clips.at(j);
|
||||
validator = post->timeline_in - frame_diff;
|
||||
if (validator < 0) frame_diff += validator;
|
||||
|
||||
// prevent any post-clips colliding with pre-clips
|
||||
for (int k=0;k<pre_clips.size();k++) {
|
||||
Clip* pre = pre_clips.at(k);
|
||||
if (pre->track == post->track) {
|
||||
validator = post->timeline_in - frame_diff - pre->timeline_out;
|
||||
if (validator < 0) frame_diff += validator;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// prevent clip length from being less than 1 frame long
|
||||
validator = g.ghost_length + frame_diff;
|
||||
if (validator < 1) frame_diff += (1 - validator);
|
||||
|
||||
if (!g.clip->media_stream->infinite_length) {
|
||||
// prevent clip length exceeding media length
|
||||
validator = g.ghost_length + frame_diff;
|
||||
if (validator > g.media_length) frame_diff -= validator - g.media_length;
|
||||
}
|
||||
|
||||
// ripple ops
|
||||
if (panel_timeline->tool == TIMELINE_TOOL_RIPPLE) {
|
||||
for (int j=0;j<post_clips.size();j++) {
|
||||
Clip* post = post_clips.at(j);
|
||||
|
||||
// prevent any post-clips colliding with pre-clips
|
||||
for (int k=0;k<pre_clips.size();k++) {
|
||||
Clip* pre = pre_clips.at(k);
|
||||
if (pre->track == post->track) {
|
||||
validator = post->timeline_in + frame_diff - pre->timeline_out;
|
||||
if (validator < 0) frame_diff -= validator;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// resize ghosts
|
||||
for (int i=0;i<panel_timeline->ghosts.size();i++) {
|
||||
Ghost& g = panel_timeline->ghosts[i];
|
||||
|
||||
if (panel_timeline->trim_in) {
|
||||
g.in = g.old_in + frame_diff;
|
||||
g.clip_in = g.old_clip_in + frame_diff;
|
||||
} else {
|
||||
g.out = g.old_out + frame_diff;
|
||||
}
|
||||
}
|
||||
|
||||
// resize selections
|
||||
for (int i=0;i<panel_timeline->selections.size();i++) {
|
||||
Selection& s = panel_timeline->selections[i];
|
||||
|
||||
if (panel_timeline->trim_in) {
|
||||
s.in = s.old_in + frame_diff;
|
||||
} else {
|
||||
s.out = s.old_out + frame_diff;
|
||||
}
|
||||
}
|
||||
} else if (panel_timeline->tool == TIMELINE_TOOL_POINTER || panel_timeline->importing) { // only move clips on pointer (not ripple or rolling)
|
||||
// validate ghosts
|
||||
for (int i=0;i<panel_timeline->ghosts.size();i++) {
|
||||
Ghost& g = panel_timeline->ghosts[i];
|
||||
|
||||
// prevent clips from moving below 0 on the timeline
|
||||
validator = g.old_in + frame_diff;
|
||||
if (validator < 0) frame_diff -= validator;
|
||||
|
||||
// prevent clips from crossing tracks
|
||||
if (same_sign(g.old_track, panel_timeline->drag_track_start)) {
|
||||
while (!same_sign(g.old_track, g.old_track + track_diff)) {
|
||||
if (g.old_track < 0) {
|
||||
track_diff--;
|
||||
} else {
|
||||
track_diff++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// move ghosts
|
||||
for (int i=0;i<panel_timeline->ghosts.size();i++) {
|
||||
Ghost& g = panel_timeline->ghosts[i];
|
||||
g.in = g.old_in + frame_diff;
|
||||
g.out = g.old_out + frame_diff;
|
||||
|
||||
g.track = g.old_track;
|
||||
|
||||
if (panel_timeline->importing) {
|
||||
int abs_track_diff = abs(track_diff);
|
||||
if (g.old_track < 0) { // clip is video
|
||||
g.track -= abs_track_diff;
|
||||
} else { // clip is audio
|
||||
g.track += abs_track_diff;
|
||||
}
|
||||
} else {
|
||||
if (same_sign(g.old_track, panel_timeline->drag_track_start)) g.track += track_diff;
|
||||
}
|
||||
}
|
||||
|
||||
// move selections
|
||||
if (!panel_timeline->importing) {
|
||||
for (int i=0;i<panel_timeline->selections.size();i++) {
|
||||
Selection& s = panel_timeline->selections[i];
|
||||
s.in = s.old_in + frame_diff;
|
||||
s.out = s.old_out + frame_diff;
|
||||
s.track = s.old_track;
|
||||
if (panel_timeline->importing) {
|
||||
int abs_track_diff = abs(track_diff);
|
||||
if (s.old_track < 0) {
|
||||
s.track -= abs_track_diff;
|
||||
} else {
|
||||
s.track += abs_track_diff;
|
||||
}
|
||||
} else {
|
||||
if (same_sign(s.track, panel_timeline->drag_track_start)) s.track += track_diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::mouseMoveEvent(QMouseEvent *event) {
|
||||
if (panel_timeline->selecting) {
|
||||
QPoint pos = event->pos();
|
||||
|
||||
int current_selection_track = getTrackFromScreenPoint(pos.y());
|
||||
long current_selection_frame = getFrameFromScreenPoint(pos.x(), false);;
|
||||
int selection_count = 1 + qMax(current_selection_track, panel_timeline->drag_track_start) - qMin(current_selection_track, panel_timeline->drag_track_start);
|
||||
if (panel_timeline->selections.size() != selection_count) {
|
||||
panel_timeline->selections.resize(selection_count);
|
||||
}
|
||||
int minimum_selection_track = qMin(current_selection_track, panel_timeline->drag_track_start);
|
||||
for (int i=0;i<selection_count;i++) {
|
||||
Selection* s = &panel_timeline->selections[i];
|
||||
s->track = minimum_selection_track + i;
|
||||
long in = panel_timeline->drag_frame_start;
|
||||
long out = current_selection_frame;
|
||||
s->in = qMin(in, out);
|
||||
s->out = qMax(in, out);
|
||||
}
|
||||
panel_timeline->playhead = qMin(panel_timeline->drag_frame_start, current_selection_frame);
|
||||
panel_timeline->repaint_timeline();
|
||||
} else if (panel_timeline->moving_init) {
|
||||
QPoint pos = event->pos();
|
||||
|
||||
if (panel_timeline->moving_proc) {
|
||||
update_ghosts(pos);
|
||||
} else {
|
||||
// set up movement
|
||||
// create ghosts
|
||||
for (int i=0;i<panel_timeline->sequence->clip_count();i++) {
|
||||
if (panel_timeline->is_clip_selected(i)) {
|
||||
Clip& c = panel_timeline->sequence->get_clip(i);
|
||||
panel_timeline->ghosts.append({&c, c.timeline_in, c.timeline_out, c.track, c.clip_in});
|
||||
}
|
||||
}
|
||||
|
||||
// ripple edit prep
|
||||
if (panel_timeline->tool == TIMELINE_TOOL_RIPPLE) {
|
||||
for (int i=0;i<panel_timeline->ghosts.size();i++) {
|
||||
// get clips before and after ripple point
|
||||
for (int j=0;j<panel_timeline->sequence->clip_count();j++) {
|
||||
// don't cache any currently selected clips
|
||||
Clip* c = panel_timeline->ghosts.at(i).clip;
|
||||
Clip& cc = panel_timeline->sequence->get_clip(j);
|
||||
bool is_selected = false;
|
||||
for (int k=0;k<panel_timeline->ghosts.size();k++) {
|
||||
if (panel_timeline->ghosts.at(k).clip == &cc) {
|
||||
is_selected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_selected) {
|
||||
if (cc.timeline_in < c->timeline_in) {
|
||||
// add clip to pre-cache UNLESS there is already a clip on that track closer to the ripple point
|
||||
bool found = false;
|
||||
for (int k=0;k<pre_clips.size();k++) {
|
||||
Clip* ccc = pre_clips.at(k);
|
||||
if (ccc->track == cc.track) {
|
||||
if (ccc->timeline_in < cc.timeline_in) {
|
||||
// clip is closer to ripple point than the one in cache, replace it
|
||||
ccc = &cc;
|
||||
}
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
// no clip from that track in the cache, add it
|
||||
pre_clips.append(&cc);
|
||||
}
|
||||
} else {
|
||||
// add clip to post-cache UNLESS there is already a clip on that track closer to the ripple point
|
||||
bool found = false;
|
||||
for (int k=0;k<post_clips.size();k++) {
|
||||
Clip* ccc = post_clips.at(k);
|
||||
if (ccc->track == cc.track) {
|
||||
if (ccc->timeline_in > cc.timeline_in) {
|
||||
// clip is closer to ripple point than the one in cache, replace it
|
||||
ccc = &cc;
|
||||
}
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
// no clip from that track in the cache, add it
|
||||
post_clips.append(&cc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// debug code - print the information we got
|
||||
qDebug() << "found" << pre_clips.size() << "preceding clips and" << post_clips.size() << "following";
|
||||
}
|
||||
|
||||
init_ghosts();
|
||||
|
||||
panel_timeline->moving_proc = true;
|
||||
}
|
||||
panel_timeline->repaint_timeline();
|
||||
} else if (panel_timeline->splitting) {
|
||||
QPoint pos = event->pos();
|
||||
|
||||
int track = getTrackFromScreenPoint(pos.y());
|
||||
bool repaint = false;
|
||||
for (int i=0;i<panel_timeline->sequence->clip_count();i++) {
|
||||
if (panel_timeline->sequence->get_clip(i).track == track) {
|
||||
panel_timeline->sequence->split_clip(i, panel_timeline->drag_frame_start);
|
||||
repaint = true;
|
||||
}
|
||||
}
|
||||
|
||||
// redraw clips since we changed them
|
||||
if (repaint) panel_timeline->redraw_all_clips();
|
||||
} else if (panel_timeline->tool == TIMELINE_TOOL_POINTER || panel_timeline->tool == TIMELINE_TOOL_RIPPLE) {
|
||||
QPoint pos = event->pos();
|
||||
|
||||
int lim = 5;
|
||||
int mouse_track = getTrackFromScreenPoint(pos.y());
|
||||
long mouse_frame_lower = getFrameFromScreenPoint(pos.x()-lim, false)-1;
|
||||
long mouse_frame_upper = getFrameFromScreenPoint(pos.x()+lim, false)+1;
|
||||
bool found = false;
|
||||
for (int i=0;i<panel_timeline->sequence->clip_count();i++) {
|
||||
Clip& c = panel_timeline->sequence->get_clip(i);
|
||||
if (c.track == mouse_track) {
|
||||
if (c.timeline_in > mouse_frame_lower && c.timeline_in < mouse_frame_upper) {
|
||||
panel_timeline->trim_target = i;
|
||||
panel_timeline->trim_in = true;
|
||||
found = true;
|
||||
break;
|
||||
} else if (c.timeline_out > mouse_frame_lower && c.timeline_out < mouse_frame_upper) {
|
||||
panel_timeline->trim_target = i;
|
||||
panel_timeline->trim_in = false;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
setCursor(Qt::SizeHorCursor);
|
||||
} else {
|
||||
unsetCursor();
|
||||
panel_timeline->trim_target = -1;
|
||||
}
|
||||
} else if (panel_timeline->tool == TIMELINE_TOOL_EDIT || panel_timeline->tool == TIMELINE_TOOL_RAZOR) {
|
||||
// redraw because we have a cursor
|
||||
panel_timeline->repaint_timeline();
|
||||
}
|
||||
}
|
||||
|
||||
int color_brightness(int r, int g, int b) {
|
||||
return (0.2126*r + 0.7152*g + 0.0722*b);
|
||||
}
|
||||
|
||||
void TimelineWidget::redraw_clips() {
|
||||
// Draw clips
|
||||
if (clip_pixmap != NULL) delete clip_pixmap;
|
||||
|
||||
int panel_width = getScreenPointFromFrame(panel_timeline->sequence->getEndFrame()) + 100;
|
||||
setMinimumWidth(panel_width);
|
||||
|
||||
clip_pixmap = new QPixmap(panel_width, height());
|
||||
clip_pixmap->fill(Qt::transparent);
|
||||
QPainter clip_painter(clip_pixmap);
|
||||
int video_track_limit = 0;
|
||||
int audio_track_limit = 0;
|
||||
for (int i=0;i<panel_timeline->sequence->clip_count();i++) {
|
||||
Clip& clip = panel_timeline->sequence->get_clip(i);
|
||||
if (is_track_visible(clip.track)) {
|
||||
if (clip.track < 0 && clip.track < video_track_limit) { // video clip
|
||||
video_track_limit = clip.track;
|
||||
} else if (clip.track > audio_track_limit) {
|
||||
audio_track_limit = clip.track;
|
||||
}
|
||||
|
||||
QRect clip_rect(getScreenPointFromFrame(clip.timeline_in), getScreenPointFromTrack(clip.track), clip.getLength() * panel_timeline->zoom, track_height);
|
||||
clip_painter.fillRect(clip_rect, QColor(clip.color_r, clip.color_g, clip.color_b));
|
||||
clip_painter.setPen(Qt::white);
|
||||
clip_painter.drawLine(clip_rect.bottomLeft(), clip_rect.topLeft());
|
||||
clip_painter.drawLine(clip_rect.topLeft(), clip_rect.topRight());
|
||||
clip_painter.setPen(QColor(0, 0, 0, 128));
|
||||
clip_painter.drawLine(clip_rect.bottomLeft(), clip_rect.bottomRight());
|
||||
clip_painter.drawLine(clip_rect.bottomRight(), clip_rect.topRight());
|
||||
|
||||
if (color_brightness(clip.color_r, clip.color_g, clip.color_b) > 160) {
|
||||
clip_painter.setPen(Qt::black);
|
||||
} else {
|
||||
clip_painter.setPen(Qt::white);
|
||||
}
|
||||
QRect text_rect(clip_rect.left() + CLIP_TEXT_PADDING, clip_rect.top() + CLIP_TEXT_PADDING, clip_rect.width() - CLIP_TEXT_PADDING - CLIP_TEXT_PADDING, clip_rect.height() - CLIP_TEXT_PADDING - CLIP_TEXT_PADDING);
|
||||
clip_painter.drawText(text_rect, 0, clip.name, &text_rect);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw track lines
|
||||
if (show_track_lines) {
|
||||
clip_painter.setPen(QColor(0, 0, 0, 96));
|
||||
audio_track_limit++;
|
||||
if (video_track_limit == 0) video_track_limit--;
|
||||
|
||||
if (bottom_align) {
|
||||
// only draw lines for video tracks
|
||||
for (int i=video_track_limit;i<0;i++) {
|
||||
int line_y = getScreenPointFromTrack(i) - 1;
|
||||
clip_painter.drawLine(0, line_y, rect().width(), line_y);
|
||||
}
|
||||
} else {
|
||||
// only draw lines for audio tracks
|
||||
for (int i=0;i<audio_track_limit;i++) {
|
||||
int line_y = getScreenPointFromTrack(i) + track_height;
|
||||
clip_painter.drawLine(0, line_y, rect().width(), line_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void TimelineWidget::paintEvent(QPaintEvent*) {
|
||||
if (panel_timeline->sequence != NULL) {
|
||||
QPainter p(this);
|
||||
|
||||
if (clip_pixmap != NULL) p.drawPixmap(0, 0, minimumWidth(), height(), *clip_pixmap);
|
||||
|
||||
// Draw selections
|
||||
for (int i=0;i<panel_timeline->selections.size();i++) {
|
||||
const Selection& s = panel_timeline->selections.at(i);
|
||||
if (is_track_visible(s.track)) {
|
||||
int selection_y = getScreenPointFromTrack(s.track);
|
||||
int selection_x = getScreenPointFromFrame(s.in);
|
||||
p.fillRect(selection_x, selection_y, getScreenPointFromFrame(s.out) - selection_x, track_height, QColor(0, 0, 0, 64));
|
||||
}
|
||||
}
|
||||
|
||||
// Draw ghosts
|
||||
for (int i=0;i<panel_timeline->ghosts.size();i++) {
|
||||
const Ghost& g = panel_timeline->ghosts.at(i);
|
||||
if (is_track_visible(g.track)) {
|
||||
int ghost_x = getScreenPointFromFrame(g.in);
|
||||
int ghost_y = getScreenPointFromTrack(g.track);
|
||||
int ghost_width = getScreenPointFromFrame(g.out - g.in) - 1;
|
||||
int ghost_height = track_height - 1;
|
||||
p.setPen(QColor(255, 255, 0));
|
||||
for (int j=0;j<GHOST_THICKNESS;j++) {
|
||||
p.drawRect(ghost_x+j, ghost_y+j, ghost_width-j-j, ghost_height-j-j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw edit cursor
|
||||
if (panel_timeline->tool == TIMELINE_TOOL_EDIT || panel_timeline->tool == TIMELINE_TOOL_RAZOR) {
|
||||
QPoint mouse_pos = mapFromGlobal(QCursor::pos());
|
||||
int track = getTrackFromScreenPoint(mouse_pos.y());
|
||||
if (is_track_visible(track)) {
|
||||
int cursor_x = getScreenPointFromFrame(getFrameFromScreenPoint(mouse_pos.x(), false));
|
||||
int cursor_y = getScreenPointFromTrack(track);
|
||||
|
||||
p.setPen(Qt::gray);
|
||||
p.drawLine(cursor_x, cursor_y, cursor_x, cursor_y + track_height);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw playhead
|
||||
p.setPen(Qt::red);
|
||||
int playhead_x = getScreenPointFromFrame(panel_timeline->playhead);
|
||||
p.drawLine(playhead_x, rect().top(), playhead_x, rect().bottom());
|
||||
|
||||
p.setPen(QColor(0, 0, 0, 64));
|
||||
int edge_y = (bottom_align) ? rect().height()-1 : 0;
|
||||
p.drawLine(0, edge_y, rect().width(), edge_y);
|
||||
}
|
||||
}
|
||||
|
||||
bool TimelineWidget::is_track_visible(int track) {
|
||||
return ((bottom_align && track < 0) || (!bottom_align && track >= 0));
|
||||
}
|
||||
|
||||
// **************************************
|
||||
// screen point <-> frame/track functions
|
||||
// **************************************
|
||||
|
||||
long TimelineWidget::getFrameFromScreenPoint(int x, bool f) {
|
||||
float div = (float) x / panel_timeline->zoom;
|
||||
if (div < 0) {
|
||||
return 0;
|
||||
}
|
||||
if (f) {
|
||||
return floor(div);
|
||||
} else {
|
||||
return round(div);
|
||||
}
|
||||
}
|
||||
|
||||
int TimelineWidget::getTrackFromScreenPoint(int y) {
|
||||
if (bottom_align) {
|
||||
y -= rect().bottom();
|
||||
if (show_track_lines) y -= 1;
|
||||
} else {
|
||||
y -= 1;
|
||||
}
|
||||
int temp_track_height = track_height;
|
||||
if (show_track_lines) temp_track_height--;
|
||||
return (int)floor((float) (y)/ (float) temp_track_height);
|
||||
}
|
||||
|
||||
int TimelineWidget::getScreenPointFromFrame(long frame) {
|
||||
return (int) round(frame*panel_timeline->zoom);
|
||||
}
|
||||
|
||||
int TimelineWidget::getScreenPointFromTrack(int track) {
|
||||
int temp_track_height = track_height;
|
||||
if (show_track_lines) temp_track_height++;
|
||||
int y = track * temp_track_height;
|
||||
|
||||
if (bottom_align) { // video track
|
||||
y += rect().bottom();
|
||||
if (show_track_lines) y += 1;
|
||||
} else { // audio track
|
||||
y += 1;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
int TimelineWidget::getClipIndexFromCoords(long frame, int track) {
|
||||
for (int i=0;i<panel_timeline->sequence->clip_count();i++) {
|
||||
Clip& c = panel_timeline->sequence->get_clip(i);
|
||||
if (c.track == track) {
|
||||
if (frame >= c.timeline_in && frame < c.timeline_out) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
#ifndef TIMELINEWIDGET_H
|
||||
#define TIMELINEWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
#include "timeline-tools.h"
|
||||
|
||||
#define GHOST_THICKNESS 2 // thiccccc
|
||||
#define CLIP_TEXT_PADDING 3
|
||||
|
||||
struct Sequence;
|
||||
struct Clip;
|
||||
class Timeline;
|
||||
|
||||
class TimelineWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TimelineWidget(QWidget *parent = nullptr);
|
||||
|
||||
bool bottom_align;
|
||||
|
||||
void redraw_clips();
|
||||
protected:
|
||||
void paintEvent(QPaintEvent*) override;
|
||||
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
|
||||
void dragEnterEvent(QDragEnterEvent *event);
|
||||
void dragLeaveEvent(QDragLeaveEvent *event);
|
||||
void dropEvent(QDropEvent* event);
|
||||
void dragMoveEvent(QDragMoveEvent *event);
|
||||
private:
|
||||
void init_ghosts();
|
||||
void update_ghosts(QPoint& mouse_pos);
|
||||
bool is_track_visible(int track);
|
||||
long getFrameFromScreenPoint(int x, bool floor);
|
||||
int getTrackFromScreenPoint(int y);
|
||||
int getScreenPointFromFrame(long frame);
|
||||
int getScreenPointFromTrack(int track);
|
||||
int getClipIndexFromCoords(long frame, int track);
|
||||
int track_height;
|
||||
|
||||
QList<Clip*> pre_clips;
|
||||
QList<Clip*> post_clips;
|
||||
|
||||
QPixmap* clip_pixmap;
|
||||
// QPixmap selection_pixmap;
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
};
|
||||
|
||||
#endif // TIMELINEWIDGET_H
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "viewercontainer.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QResizeEvent>
|
||||
|
||||
// enforces aspect ratio
|
||||
ViewerContainer::ViewerContainer(QWidget *parent) : QWidget(parent)
|
||||
{
|
||||
child = NULL;
|
||||
aspect_ratio = 1;
|
||||
}
|
||||
|
||||
void ViewerContainer::adjust() {
|
||||
if (child != NULL) {
|
||||
QSize widget_size = size();
|
||||
int widget_x = 0;
|
||||
int widget_y = 0;
|
||||
int widget_width = widget_size.width();
|
||||
int widget_height = widget_size.height();
|
||||
float widget_ar = (float) widget_width /(float) widget_height;
|
||||
|
||||
bool widget_is_larger_than_sequence = widget_ar > aspect_ratio;
|
||||
|
||||
if (widget_is_larger_than_sequence) {
|
||||
widget_width = widget_height * aspect_ratio;
|
||||
widget_x = (widget_size.width() / 2) - (widget_width / 2);
|
||||
} else {
|
||||
widget_height = widget_width / aspect_ratio;
|
||||
widget_y = (widget_size.height() / 2) - (widget_height / 2);
|
||||
}
|
||||
|
||||
child->move(widget_x, widget_y);
|
||||
child->resize(widget_width, widget_height);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerContainer::resizeEvent(QResizeEvent *event) {
|
||||
event->accept();
|
||||
adjust();
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef VIEWERCONTAINER_H
|
||||
#define VIEWERCONTAINER_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class ViewerContainer : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ViewerContainer(QWidget *parent = nullptr);
|
||||
float aspect_ratio;
|
||||
QWidget* child;
|
||||
void adjust();
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
};
|
||||
|
||||
#endif // VIEWERCONTAINER_H
|
||||
@@ -0,0 +1,157 @@
|
||||
#include "viewerwidget.h"
|
||||
|
||||
#include "panels/panels.h"
|
||||
#include "panels/viewer.h"
|
||||
#include "panels/timeline.h"
|
||||
#include "project/sequence.h"
|
||||
#include "project/effect.h"
|
||||
#include "playback/playback.h"
|
||||
#include "playback/audio.h"
|
||||
#include "io/media.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
}
|
||||
|
||||
ViewerWidget::ViewerWidget(QWidget *parent) : QOpenGLWidget(parent)
|
||||
{
|
||||
multithreaded = true;
|
||||
|
||||
QSurfaceFormat format;
|
||||
format.setDepthBufferSize(24);
|
||||
setFormat(format);
|
||||
|
||||
// error handler - retries after 200ms if we couldn't get the entire image
|
||||
retry_timer.setInterval(200);
|
||||
connect(&retry_timer, SIGNAL(timeout()), this, SLOT(retry()));
|
||||
}
|
||||
|
||||
void ViewerWidget::retry() {
|
||||
update();
|
||||
}
|
||||
|
||||
void ViewerWidget::initializeGL() {
|
||||
initializeOpenGLFunctions();
|
||||
|
||||
glClearColor(0, 0, 0, 1);
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
|
||||
//void ViewerWidget::resizeGL(int w, int h)
|
||||
//{
|
||||
//}
|
||||
|
||||
int audio_bytes_written = 0;
|
||||
|
||||
void ViewerWidget::paintGL()
|
||||
{
|
||||
if (multithreaded) retry_timer.stop();
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
long playhead = panel_timeline->playhead;
|
||||
|
||||
handle_media(panel_viewer->sequence, playhead, multithreaded);
|
||||
texture_failed = false;
|
||||
|
||||
bool render_audio = (panel_timeline->playing);
|
||||
|
||||
if (render_audio && switch_audio_cache) {
|
||||
reading_audio_cache_A = !reading_audio_cache_A;
|
||||
audio_bytes_written = 0;
|
||||
clear_cache(!reading_audio_cache_A, reading_audio_cache_A);
|
||||
}
|
||||
|
||||
cc_lock.lock();
|
||||
for (int i=0;i<current_clips.size();i++) {
|
||||
Clip* c = current_clips.at(i);
|
||||
|
||||
if (!c->open) {
|
||||
qDebug() << "[WARNING] Tried to display clip" << i << "but it's closed";
|
||||
texture_failed = true;
|
||||
} else if (is_clip_active(c, playhead)) {
|
||||
if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||
// start preparing cache
|
||||
get_clip_frame(c, playhead);
|
||||
|
||||
if (c->texture == NULL) {
|
||||
qDebug() << "[WARNING] Texture hasn't been created yet";
|
||||
texture_failed = true;
|
||||
} else if (playhead >= c->timeline_in) {
|
||||
glLoadIdentity();
|
||||
int half_width = c->sequence->width/2;
|
||||
int half_height = c->sequence->height/2;
|
||||
glOrtho(-half_width, half_width, half_height,- half_height, -1, 1);
|
||||
int anchor_x = 0;
|
||||
int anchor_y = 0;
|
||||
|
||||
// perform all transform effects
|
||||
for (unsigned int j=0;j<c->effects.size();j++) {
|
||||
c->effects.at(j)->process_gl(&anchor_x, &anchor_y);
|
||||
}
|
||||
|
||||
int anchor_right = c->media_stream->video_width - anchor_x;
|
||||
int anchor_bottom = c->media_stream->video_height - anchor_y;
|
||||
|
||||
c->texture->bind();
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
glTexCoord2f(0.0, 0.0);
|
||||
glVertex2f(-anchor_x, -anchor_y);
|
||||
glTexCoord2f(1.0, 0.0);
|
||||
glVertex2f(anchor_right, -anchor_y);
|
||||
glTexCoord2f(1.0, 1.0);
|
||||
glVertex2f(anchor_right, anchor_bottom);
|
||||
glTexCoord2f(0.0, 1.0);
|
||||
glVertex2f(-anchor_x, anchor_bottom);
|
||||
glEnd();
|
||||
|
||||
c->texture->release();
|
||||
}
|
||||
} else if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
|
||||
playhead >= c->timeline_in &&
|
||||
render_audio &&
|
||||
!c->reached_end &&
|
||||
(switch_audio_cache || c->reset_audio)) {
|
||||
// TODO doesn't new clips appearing in the timeline (they'll ALWAYS end up in the other cache with a 1/8 sec delay)
|
||||
|
||||
// cache audio
|
||||
if (c->lock.tryLock()) {
|
||||
// clip is not caching, start cache
|
||||
c->lock.unlock();
|
||||
cache_clip(c, playhead, !reading_audio_cache_A, reading_audio_cache_A, c->reset_audio);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (render_audio) {
|
||||
switch_audio_cache = false;
|
||||
|
||||
if (audio_cache_A != NULL && audio_bytes_written < audio_cache_size) {
|
||||
// send cached/buffered audio to QIODevice/QAudioOutput
|
||||
uint8_t* cache = reading_audio_cache_A ? audio_cache_A : audio_cache_B;
|
||||
audio_bytes_written += audio_io_device->write((const char*) cache+audio_bytes_written, audio_cache_size-audio_bytes_written);
|
||||
}
|
||||
|
||||
if (audio_bytes_written == audio_cache_size) {
|
||||
// switch_cache
|
||||
switch_audio_cache = true;
|
||||
}
|
||||
}
|
||||
|
||||
cc_lock.unlock();
|
||||
|
||||
if (texture_failed) {
|
||||
if (multithreaded) {
|
||||
retry_timer.start();
|
||||
} else {
|
||||
paintGL();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef VIEWERWIDGET_H
|
||||
#define VIEWERWIDGET_H
|
||||
|
||||
#include <QOpenGLWidget>
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QMatrix4x4>
|
||||
#include <QOpenGLTexture>
|
||||
#include <QTimer>
|
||||
|
||||
class Viewer;
|
||||
|
||||
class ViewerWidget : public QOpenGLWidget, public QOpenGLFunctions
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ViewerWidget(QWidget *parent = 0);
|
||||
void initializeGL();
|
||||
// void resizeGL(int w, int h);
|
||||
void paintGL();
|
||||
|
||||
bool multithreaded;
|
||||
private:
|
||||
QTimer retry_timer;
|
||||
private slots:
|
||||
void retry();
|
||||
};
|
||||
|
||||
#endif // VIEWERWIDGET_H
|
||||
Reference in New Issue
Block a user