Merge pull request #325 from olive-editor/threadedgl2
move renderer to separate thread
This commit is contained in:
@@ -63,7 +63,7 @@ void debug_message_handler(QtMsgType type, const QMessageLogContext &context, co
|
||||
if (debug_file.isOpen()) debug_stream << QString("[FATAL] %1 (%2:%3, %4)\n").arg(localMsg.constData(), context.file, QString::number(context.line), context.function);
|
||||
debug_info.prepend(QString("<font color='red'><b>[FATAL]</b> %1 (%2:%3, %4)</font><br>").arg(localMsg.constData(), context.file, QString::number(context.line), context.function));
|
||||
fflush(stderr);
|
||||
abort();
|
||||
// abort();
|
||||
}
|
||||
if (debug_dialog != nullptr && debug_dialog->isVisible()) {
|
||||
QMetaObject::invokeMethod(debug_dialog, "update_log", Qt::QueuedConnection);
|
||||
|
||||
+60
-62
@@ -57,7 +57,7 @@ enum ExportFormats {
|
||||
ExportDialog::ExportDialog(QWidget *parent) :
|
||||
QDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("Export \"%1\"").arg(sequence->name));
|
||||
setWindowTitle(tr("Export \"%1\"").arg(sequence->name));
|
||||
setup_ui();
|
||||
|
||||
rangeCombobox->setCurrentIndex(0);
|
||||
@@ -320,12 +320,12 @@ void ExportDialog::format_changed(int index)
|
||||
|
||||
void ExportDialog::render_thread_finished() {
|
||||
if (progressBar->value() < 100 && !cancelled) {
|
||||
QMessageBox::critical(
|
||||
this,
|
||||
tr("Export Failed"),
|
||||
tr("Export failed - %1").arg(export_error),
|
||||
QMessageBox::Ok
|
||||
);
|
||||
QMessageBox::critical(
|
||||
this,
|
||||
tr("Export Failed"),
|
||||
tr("Export failed - %1").arg(export_error),
|
||||
QMessageBox::Ok
|
||||
);
|
||||
}
|
||||
prep_ui_for_render(false);
|
||||
panel_sequence_viewer->viewer_widget->makeCurrent();
|
||||
@@ -342,12 +342,12 @@ void ExportDialog::prep_ui_for_render(bool r) {
|
||||
|
||||
void ExportDialog::export_action() {
|
||||
if (widthSpinbox->value()%2 == 1 || heightSpinbox->value()%2 == 1) {
|
||||
QMessageBox::critical(
|
||||
this,
|
||||
tr("Invalid dimensions"),
|
||||
tr("Export width and height must both be even numbers/divisible by 2."),
|
||||
QMessageBox::Ok
|
||||
);
|
||||
QMessageBox::critical(
|
||||
this,
|
||||
tr("Invalid dimensions"),
|
||||
tr("Export width and height must both be even numbers/divisible by 2."),
|
||||
QMessageBox::Ok
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -399,12 +399,12 @@ void ExportDialog::export_action() {
|
||||
break;
|
||||
default:
|
||||
qCritical() << "Invalid codec selection for an image sequence";
|
||||
QMessageBox::critical(
|
||||
this,
|
||||
tr("Invalid codec"),
|
||||
tr("Couldn't determine output parameters for the selected codec. This is a bug, please contact the developers."),
|
||||
QMessageBox::Ok
|
||||
);
|
||||
QMessageBox::critical(
|
||||
this,
|
||||
tr("Invalid codec"),
|
||||
tr("Couldn't determine output parameters for the selected codec. This is a bug, please contact the developers."),
|
||||
QMessageBox::Ok
|
||||
);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@@ -469,20 +469,20 @@ void ExportDialog::export_action() {
|
||||
break;
|
||||
default:
|
||||
qCritical() << "Invalid format - this is a bug, please inform the developers";
|
||||
QMessageBox::critical(
|
||||
this,
|
||||
tr("Invalid format"),
|
||||
tr("Couldn't determine output format. This is a bug, please contact the developers."),
|
||||
QMessageBox::Ok
|
||||
);
|
||||
QMessageBox::critical(
|
||||
this,
|
||||
tr("Invalid format"),
|
||||
tr("Couldn't determine output format. This is a bug, please contact the developers."),
|
||||
QMessageBox::Ok
|
||||
);
|
||||
return;
|
||||
}
|
||||
QString filename = QFileDialog::getSaveFileName(
|
||||
this,
|
||||
tr("Export Media"),
|
||||
"",
|
||||
format_strings[formatCombobox->currentIndex()] + " (*." + ext + ")"
|
||||
);
|
||||
QString filename = QFileDialog::getSaveFileName(
|
||||
this,
|
||||
tr("Export Media"),
|
||||
"",
|
||||
format_strings[formatCombobox->currentIndex()] + " (*." + ext + ")"
|
||||
);
|
||||
if (!filename.isEmpty()) {
|
||||
if (!filename.endsWith("." + ext, Qt::CaseInsensitive)) {
|
||||
filename += "." + ext;
|
||||
@@ -506,11 +506,9 @@ void ExportDialog::export_action() {
|
||||
|
||||
closeActiveClips(sequence);
|
||||
|
||||
mainWindow->autorecover_interval();
|
||||
mainWindow->set_rendering_state(true);
|
||||
|
||||
rendering = true;
|
||||
panel_sequence_viewer->viewer_widget->context()->doneCurrent();
|
||||
panel_sequence_viewer->viewer_widget->context()->moveToThread(et);
|
||||
mainWindow->autorecover_interval();
|
||||
|
||||
prep_ui_for_render(true);
|
||||
|
||||
@@ -556,20 +554,20 @@ void ExportDialog::update_progress_bar(int value, qint64 remaining_ms) {
|
||||
}
|
||||
|
||||
void ExportDialog::cancel_render() {
|
||||
panel_sequence_viewer->viewer_widget->force_quit = true;
|
||||
et->continueEncode = false;
|
||||
cancelled = true;
|
||||
et->wake();
|
||||
}
|
||||
|
||||
void ExportDialog::vcodec_changed(int index) {
|
||||
compressionTypeCombobox->clear();
|
||||
if ((format_vcodecs.size() > 0 && format_vcodecs.at(index) == AV_CODEC_ID_H264)) {
|
||||
compressionTypeCombobox->setEnabled(true);
|
||||
compressionTypeCombobox->addItem(tr("Quality-based (Constant Rate Factor)"), COMPRESSION_TYPE_CFR);
|
||||
compressionTypeCombobox->addItem(tr("Quality-based (Constant Rate Factor)"), COMPRESSION_TYPE_CFR);
|
||||
// compressionTypeCombobox->addItem("File size-based (Two-Pass)", COMPRESSION_TYPE_TARGETSIZE);
|
||||
// compressionTypeCombobox->addItem("Average bitrate (Two-Pass)", COMPRESSION_TYPE_TARGETBR);
|
||||
} else {
|
||||
compressionTypeCombobox->addItem(tr("Constant Bitrate"), COMPRESSION_TYPE_CBR);
|
||||
compressionTypeCombobox->addItem(tr("Constant Bitrate"), COMPRESSION_TYPE_CBR);
|
||||
compressionTypeCombobox->setCurrentIndex(0);
|
||||
compressionTypeCombobox->setEnabled(false);
|
||||
}
|
||||
@@ -582,17 +580,17 @@ void ExportDialog::comp_type_changed(int) {
|
||||
switch (compressionTypeCombobox->currentData().toInt()) {
|
||||
case COMPRESSION_TYPE_CBR:
|
||||
case COMPRESSION_TYPE_TARGETBR:
|
||||
videoBitrateLabel->setText(tr("Bitrate (Mbps):"));
|
||||
videoBitrateLabel->setText(tr("Bitrate (Mbps):"));
|
||||
videobitrateSpinbox->setValue(qMax(0.5, (double) qRound((0.01528 * sequence->height) - 4.5)));
|
||||
break;
|
||||
case COMPRESSION_TYPE_CFR:
|
||||
videoBitrateLabel->setText(tr("Quality (CRF):"));
|
||||
videoBitrateLabel->setText(tr("Quality (CRF):"));
|
||||
videobitrateSpinbox->setValue(36);
|
||||
videobitrateSpinbox->setMaximum(51);
|
||||
videobitrateSpinbox->setToolTip(tr("Quality Factor:\n\n0 = lossless\n17-18 = visually lossless (compressed, but unnoticeable)\n23 = high quality\n51 = lowest quality possible"));
|
||||
videobitrateSpinbox->setToolTip(tr("Quality Factor:\n\n0 = lossless\n17-18 = visually lossless (compressed, but unnoticeable)\n23 = high quality\n51 = lowest quality possible"));
|
||||
break;
|
||||
case COMPRESSION_TYPE_TARGETSIZE:
|
||||
videoBitrateLabel->setText(tr("Target File Size (MB):"));
|
||||
videoBitrateLabel->setText(tr("Target File Size (MB):"));
|
||||
videobitrateSpinbox->setValue(100);
|
||||
break;
|
||||
}
|
||||
@@ -601,57 +599,57 @@ void ExportDialog::comp_type_changed(int) {
|
||||
void ExportDialog::setup_ui() {
|
||||
QVBoxLayout* verticalLayout = new QVBoxLayout(this);
|
||||
|
||||
QHBoxLayout* format_layout = new QHBoxLayout();
|
||||
QHBoxLayout* format_layout = new QHBoxLayout();
|
||||
|
||||
format_layout->addWidget(new QLabel(tr("Format:")));
|
||||
format_layout->addWidget(new QLabel(tr("Format:")));
|
||||
|
||||
formatCombobox = new QComboBox(this);
|
||||
|
||||
format_layout->addWidget(formatCombobox);
|
||||
format_layout->addWidget(formatCombobox);
|
||||
|
||||
verticalLayout->addLayout(format_layout);
|
||||
verticalLayout->addLayout(format_layout);
|
||||
|
||||
QHBoxLayout* range_layout = new QHBoxLayout();
|
||||
QHBoxLayout* range_layout = new QHBoxLayout();
|
||||
|
||||
range_layout->addWidget(new QLabel(tr("Range:")));
|
||||
range_layout->addWidget(new QLabel(tr("Range:")));
|
||||
|
||||
rangeCombobox = new QComboBox(this);
|
||||
rangeCombobox->addItem(tr("Entire Sequence"));
|
||||
rangeCombobox->addItem(tr("In to Out"));
|
||||
rangeCombobox->addItem(tr("Entire Sequence"));
|
||||
rangeCombobox->addItem(tr("In to Out"));
|
||||
|
||||
range_layout->addWidget(rangeCombobox);
|
||||
range_layout->addWidget(rangeCombobox);
|
||||
|
||||
verticalLayout->addLayout(range_layout);
|
||||
verticalLayout->addLayout(range_layout);
|
||||
|
||||
videoGroupbox = new QGroupBox(this);
|
||||
videoGroupbox->setTitle(tr("Video"));
|
||||
videoGroupbox->setTitle(tr("Video"));
|
||||
videoGroupbox->setFlat(false);
|
||||
videoGroupbox->setCheckable(true);
|
||||
|
||||
QGridLayout* videoGridLayout = new QGridLayout(videoGroupbox);
|
||||
|
||||
videoGridLayout->addWidget(new QLabel(tr("Codec:")), 0, 0, 1, 1);
|
||||
videoGridLayout->addWidget(new QLabel(tr("Codec:")), 0, 0, 1, 1);
|
||||
vcodecCombobox = new QComboBox(videoGroupbox);
|
||||
videoGridLayout->addWidget(vcodecCombobox, 0, 1, 1, 1);
|
||||
|
||||
videoGridLayout->addWidget(new QLabel(tr("Width:")), 1, 0, 1, 1);
|
||||
videoGridLayout->addWidget(new QLabel(tr("Width:")), 1, 0, 1, 1);
|
||||
widthSpinbox = new QSpinBox(videoGroupbox);
|
||||
widthSpinbox->setMaximum(16777216);
|
||||
videoGridLayout->addWidget(widthSpinbox, 1, 1, 1, 1);
|
||||
|
||||
videoGridLayout->addWidget(new QLabel(tr("Height:")), 2, 0, 1, 1);
|
||||
videoGridLayout->addWidget(new QLabel(tr("Height:")), 2, 0, 1, 1);
|
||||
heightSpinbox = new QSpinBox(videoGroupbox);
|
||||
heightSpinbox->setMaximum(16777216);
|
||||
videoGridLayout->addWidget(heightSpinbox, 2, 1, 1, 1);
|
||||
|
||||
videoGridLayout->addWidget(new QLabel(tr("Frame Rate:")), 3, 0, 1, 1);
|
||||
videoGridLayout->addWidget(new QLabel(tr("Frame Rate:")), 3, 0, 1, 1);
|
||||
framerateSpinbox = new QDoubleSpinBox(videoGroupbox);
|
||||
framerateSpinbox->setMaximum(60);
|
||||
framerateSpinbox->setValue(0);
|
||||
videoGridLayout->addWidget(framerateSpinbox, 3, 1, 1, 1);
|
||||
|
||||
videoGridLayout->addWidget(new QLabel(tr("Compression Type:")), 4, 0, 1, 1);
|
||||
compressionTypeCombobox = new QComboBox(videoGroupbox);
|
||||
videoGridLayout->addWidget(new QLabel(tr("Compression Type:")), 4, 0, 1, 1);
|
||||
compressionTypeCombobox = new QComboBox(videoGroupbox);
|
||||
videoGridLayout->addWidget(compressionTypeCombobox, 4, 1, 1, 1);
|
||||
|
||||
videoBitrateLabel = new QLabel(videoGroupbox);
|
||||
@@ -669,17 +667,17 @@ void ExportDialog::setup_ui() {
|
||||
|
||||
QGridLayout* audioGridLayout = new QGridLayout(audioGroupbox);
|
||||
|
||||
audioGridLayout->addWidget(new QLabel(tr("Codec:")), 0, 0, 1, 1);
|
||||
audioGridLayout->addWidget(new QLabel(tr("Codec:")), 0, 0, 1, 1);
|
||||
acodecCombobox = new QComboBox(audioGroupbox);
|
||||
audioGridLayout->addWidget(acodecCombobox, 0, 1, 1, 1);
|
||||
|
||||
audioGridLayout->addWidget(new QLabel(tr("Sampling Rate:")), 1, 0, 1, 1);
|
||||
audioGridLayout->addWidget(new QLabel(tr("Sampling Rate:")), 1, 0, 1, 1);
|
||||
samplingRateSpinbox = new QSpinBox(audioGroupbox);
|
||||
samplingRateSpinbox->setMaximum(96000);
|
||||
samplingRateSpinbox->setValue(0);
|
||||
audioGridLayout->addWidget(samplingRateSpinbox, 1, 1, 1, 1);
|
||||
|
||||
audioGridLayout->addWidget(new QLabel(tr("Bitrate (Kbps/CBR):")), 3, 0, 1, 1);
|
||||
audioGridLayout->addWidget(new QLabel(tr("Bitrate (Kbps/CBR):")), 3, 0, 1, 1);
|
||||
audiobitrateSpinbox = new QSpinBox(audioGroupbox);
|
||||
audiobitrateSpinbox->setMaximum(320);
|
||||
audiobitrateSpinbox->setValue(256);
|
||||
|
||||
@@ -29,49 +29,49 @@ TextEffect::TextEffect(Clip *c, const EffectMeta* em) :
|
||||
enable_superimpose = true;
|
||||
//enable_shader = true;
|
||||
|
||||
text_val = add_row(tr("Text"))->add_field(EFFECT_FIELD_STRING, "text", 2);
|
||||
text_val = add_row(tr("Text"))->add_field(EFFECT_FIELD_STRING, "text", 2);
|
||||
QTextEdit* text_widget = static_cast<QTextEdit*>(text_val->ui_element);
|
||||
text_widget->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(text_widget, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(text_edit_menu()));
|
||||
|
||||
set_font_combobox = add_row(tr("Font"))->add_field(EFFECT_FIELD_FONT, "font", 2);
|
||||
set_font_combobox = add_row(tr("Font"))->add_field(EFFECT_FIELD_FONT, "font", 2);
|
||||
|
||||
size_val = add_row(tr("Size"))->add_field(EFFECT_FIELD_DOUBLE, "size", 2);
|
||||
size_val = add_row(tr("Size"))->add_field(EFFECT_FIELD_DOUBLE, "size", 2);
|
||||
size_val->set_double_minimum_value(0);
|
||||
|
||||
set_color_button = add_row(tr("Color"))->add_field(EFFECT_FIELD_COLOR, "color", 2);
|
||||
set_color_button = add_row(tr("Color"))->add_field(EFFECT_FIELD_COLOR, "color", 2);
|
||||
|
||||
EffectRow* alignment_row = add_row(tr("Alignment"));
|
||||
EffectRow* alignment_row = add_row(tr("Alignment"));
|
||||
halign_field = alignment_row->add_field(EFFECT_FIELD_COMBO, "halign");
|
||||
halign_field->add_combo_item(tr("Left"), Qt::AlignLeft);
|
||||
halign_field->add_combo_item(tr("Center"), Qt::AlignHCenter);
|
||||
halign_field->add_combo_item(tr("Right"), Qt::AlignRight);
|
||||
halign_field->add_combo_item(tr("Justify"), Qt::AlignJustify);
|
||||
halign_field->add_combo_item(tr("Left"), Qt::AlignLeft);
|
||||
halign_field->add_combo_item(tr("Center"), Qt::AlignHCenter);
|
||||
halign_field->add_combo_item(tr("Right"), Qt::AlignRight);
|
||||
halign_field->add_combo_item(tr("Justify"), Qt::AlignJustify);
|
||||
|
||||
valign_field = alignment_row->add_field(EFFECT_FIELD_COMBO, "valign");
|
||||
valign_field->add_combo_item(tr("Top"), Qt::AlignTop);
|
||||
valign_field->add_combo_item(tr("Center"), Qt::AlignVCenter);
|
||||
valign_field->add_combo_item(tr("Bottom"), Qt::AlignBottom);
|
||||
valign_field->add_combo_item(tr("Top"), Qt::AlignTop);
|
||||
valign_field->add_combo_item(tr("Center"), Qt::AlignVCenter);
|
||||
valign_field->add_combo_item(tr("Bottom"), Qt::AlignBottom);
|
||||
|
||||
word_wrap_field = add_row(tr("Word Wrap"))->add_field(EFFECT_FIELD_BOOL, "wordwrap", 2);
|
||||
word_wrap_field = add_row(tr("Word Wrap"))->add_field(EFFECT_FIELD_BOOL, "wordwrap", 2);
|
||||
|
||||
outline_bool = add_row(tr("Outline"))->add_field(EFFECT_FIELD_BOOL, "outline", 2);
|
||||
outline_color = add_row(tr("Outline Color"))->add_field(EFFECT_FIELD_COLOR, "outlinecolor", 2);
|
||||
outline_width = add_row(tr("Outline Width"))->add_field(EFFECT_FIELD_DOUBLE, "outlinewidth", 2);
|
||||
outline_bool = add_row(tr("Outline"))->add_field(EFFECT_FIELD_BOOL, "outline", 2);
|
||||
outline_color = add_row(tr("Outline Color"))->add_field(EFFECT_FIELD_COLOR, "outlinecolor", 2);
|
||||
outline_width = add_row(tr("Outline Width"))->add_field(EFFECT_FIELD_DOUBLE, "outlinewidth", 2);
|
||||
outline_width->set_double_minimum_value(0);
|
||||
|
||||
shadow_bool = add_row(tr("Shadow"))->add_field(EFFECT_FIELD_BOOL, "shadow", 2);
|
||||
shadow_color = add_row(tr("Shadow Color"))->add_field(EFFECT_FIELD_COLOR, "shadowcolor", 2);
|
||||
shadow_distance = add_row(tr("Shadow Distance"))->add_field(EFFECT_FIELD_DOUBLE, "shadowdistance", 2);
|
||||
shadow_bool = add_row(tr("Shadow"))->add_field(EFFECT_FIELD_BOOL, "shadow", 2);
|
||||
shadow_color = add_row(tr("Shadow Color"))->add_field(EFFECT_FIELD_COLOR, "shadowcolor", 2);
|
||||
shadow_distance = add_row(tr("Shadow Distance"))->add_field(EFFECT_FIELD_DOUBLE, "shadowdistance", 2);
|
||||
shadow_distance->set_double_minimum_value(0);
|
||||
shadow_softness = add_row(tr("Shadow Softness"))->add_field(EFFECT_FIELD_DOUBLE, "shadowsoftness", 2);
|
||||
shadow_softness = add_row(tr("Shadow Softness"))->add_field(EFFECT_FIELD_DOUBLE, "shadowsoftness", 2);
|
||||
shadow_softness->set_double_minimum_value(0);
|
||||
shadow_opacity = add_row(tr("Shadow Opacity"))->add_field(EFFECT_FIELD_DOUBLE, "shadowopacity", 2);
|
||||
shadow_opacity = add_row(tr("Shadow Opacity"))->add_field(EFFECT_FIELD_DOUBLE, "shadowopacity", 2);
|
||||
shadow_opacity->set_double_minimum_value(0);
|
||||
shadow_opacity->set_double_maximum_value(100);
|
||||
|
||||
size_val->set_double_default_value(48);
|
||||
text_val->set_string_value(tr("Sample Text"));
|
||||
text_val->set_string_value(tr("Sample Text"));
|
||||
halign_field->set_combo_index(1);
|
||||
valign_field->set_combo_index(1);
|
||||
word_wrap_field->set_bool_value(true);
|
||||
@@ -94,7 +94,9 @@ TextEffect::TextEffect(Clip *c, const EffectMeta* em) :
|
||||
}
|
||||
|
||||
void TextEffect::redraw(double timecode) {
|
||||
img.fill(Qt::transparent);
|
||||
QColor bkg = set_color_button->get_color_value(timecode);
|
||||
bkg.setAlpha(0);
|
||||
img.fill(bkg);
|
||||
|
||||
QPainter p(&img);
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
@@ -217,7 +219,7 @@ void TextEffect::shadow_enable(bool e) {
|
||||
void TextEffect::text_edit_menu() {
|
||||
QMenu menu;
|
||||
|
||||
menu.addAction(tr("&Edit Text"), this, SLOT(open_text_edit()));
|
||||
menu.addAction(tr("&Edit Text"), this, SLOT(open_text_edit()));
|
||||
|
||||
menu.exec(QCursor::pos());
|
||||
}
|
||||
|
||||
+35
-28
@@ -6,9 +6,12 @@
|
||||
#include "panels/timeline.h"
|
||||
#include "panels/viewer.h"
|
||||
#include "ui/viewerwidget.h"
|
||||
#include "ui/renderthread.h"
|
||||
#include "ui/renderfunctions.h"
|
||||
#include "playback/playback.h"
|
||||
#include "playback/audio.h"
|
||||
#include "dialogs/exportdialog.h"
|
||||
#include "mainwindow.h"
|
||||
#include "debug.h"
|
||||
|
||||
extern "C" {
|
||||
@@ -111,7 +114,7 @@ bool ExportThread::setupVideo() {
|
||||
vcodec_ctx->sample_aspect_ratio = {1, 1};
|
||||
vcodec_ctx->pix_fmt = vcodec->pix_fmts[0]; // maybe be breakable code
|
||||
vcodec_ctx->framerate = av_d2q(video_frame_rate, INT_MAX);
|
||||
if (video_compression_type == COMPRESSION_TYPE_CBR) vcodec_ctx->bit_rate = video_bitrate * 1000000;
|
||||
if (video_compression_type == COMPRESSION_TYPE_CBR) vcodec_ctx->bit_rate = qRound(video_bitrate * 1000000);
|
||||
vcodec_ctx->time_base = av_inv_q(vcodec_ctx->framerate);
|
||||
video_stream->time_base = vcodec_ctx->time_base;
|
||||
|
||||
@@ -312,12 +315,7 @@ bool ExportThread::setupContainer() {
|
||||
|
||||
void ExportThread::run() {
|
||||
panel_sequence_viewer->pause();
|
||||
|
||||
if (!panel_sequence_viewer->viewer_widget->context()->makeCurrent(&surface)) {
|
||||
qCritical() << "Make current failed";
|
||||
ed->export_error = tr("could not make OpenGL context current");
|
||||
return;
|
||||
}
|
||||
panel_sequence_viewer->seek(start_frame);
|
||||
|
||||
// copy filename
|
||||
QByteArray ba = filename.toUtf8();
|
||||
@@ -339,28 +337,35 @@ void ExportThread::run() {
|
||||
}
|
||||
}
|
||||
|
||||
panel_sequence_viewer->seek(start_frame);
|
||||
panel_sequence_viewer->reset_all_audio();
|
||||
|
||||
QOpenGLFramebufferObject fbo(sequence->width, sequence->height, QOpenGLFramebufferObject::CombinedDepthStencil, GL_TEXTURE_RECTANGLE);
|
||||
fbo.bind();
|
||||
|
||||
panel_sequence_viewer->viewer_widget->default_fbo = &fbo;
|
||||
|
||||
long file_audio_samples = 0;
|
||||
qint64 start_time, frame_time, avg_time, eta, total_time = 0;
|
||||
long remaining_frames, frame_count = 1;
|
||||
|
||||
RenderThread* renderer = panel_sequence_viewer->viewer_widget->get_renderer();
|
||||
disconnect(renderer, SIGNAL(ready()), panel_sequence_viewer->viewer_widget, SLOT(queue_repaint()));
|
||||
connect(renderer, SIGNAL(ready()), this, SLOT(wake()));
|
||||
|
||||
mutex.lock();
|
||||
|
||||
while (sequence->playhead <= end_frame && continueEncode) {
|
||||
start_time = QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
panel_sequence_viewer->viewer_widget->paintGL();
|
||||
if (audio_enabled) {
|
||||
compose_audio(nullptr, sequence, true);
|
||||
}
|
||||
if (video_enabled) {
|
||||
do {
|
||||
// TODO optimize by rendering the next frame while encoding the last
|
||||
renderer->start_render(nullptr, sequence, nullptr, video_frame->data[0]);
|
||||
waitCond.wait(&mutex);
|
||||
if (!continueEncode) break;
|
||||
} while (renderer->did_texture_fail());
|
||||
if (!continueEncode) break;
|
||||
}
|
||||
|
||||
// encode last frame while rendering next frame
|
||||
double timecode_secs = (double) (sequence->playhead-start_frame) / sequence->frame_rate;
|
||||
if (video_enabled) {
|
||||
// get image from opengl
|
||||
glReadPixels(0, 0, video_frame->linesize[0]/4, sequence->height, GL_RGBA, GL_UNSIGNED_BYTE, video_frame->data[0]);
|
||||
|
||||
// change pixel format
|
||||
sws_scale(sws_ctx, video_frame->data, video_frame->linesize, 0, video_frame->height, sws_frame->data, sws_frame->linesize);
|
||||
sws_frame->pts = qRound(timecode_secs/av_q2d(video_stream->time_base));
|
||||
@@ -371,6 +376,7 @@ void ExportThread::run() {
|
||||
if (audio_enabled) {
|
||||
// do we need to encode more audio samples?
|
||||
while (continueEncode && file_audio_samples <= (timecode_secs*audio_sampling_rate)) {
|
||||
// copy samples from audio buffer to AVFrame
|
||||
int adjusted_read = audio_ibuffer_read%audio_ibuffer_size;
|
||||
int copylen = qMin(aframe_bytes, audio_ibuffer_size-adjusted_read);
|
||||
memcpy(audio_frame->data[0], audio_ibuffer+adjusted_read, copylen);
|
||||
@@ -405,20 +411,22 @@ void ExportThread::run() {
|
||||
|
||||
// qInfo() << "Encoded frame" << sequence->playhead << "- took" << frame_time << "ms (avg:" << avg_time << "ms, remaining:" << remaining_frames << ", ETA:" << eta << ")";
|
||||
|
||||
emit progress_changed(qRound(((double) (sequence->playhead-start_frame) / (double) (end_frame-start_frame)) * 100), eta);
|
||||
emit progress_changed(qRound((double(sequence->playhead-start_frame) / double(end_frame-start_frame)) * 100.0), eta);
|
||||
sequence->playhead++;
|
||||
frame_count++;
|
||||
}
|
||||
|
||||
disconnect(renderer, SIGNAL(ready()), this, SLOT(wake()));
|
||||
connect(renderer, SIGNAL(ready()), panel_sequence_viewer->viewer_widget, SLOT(queue_repaint()));
|
||||
|
||||
mutex.unlock();
|
||||
|
||||
if (continueEncode) {
|
||||
if (video_enabled) vpkt_alloc = true;
|
||||
if (audio_enabled) apkt_alloc = true;
|
||||
}
|
||||
|
||||
panel_sequence_viewer->viewer_widget->default_fbo = nullptr;
|
||||
rendering = false;
|
||||
|
||||
fbo.release();
|
||||
mainWindow->set_rendering_state(false);
|
||||
|
||||
if (audio_enabled && continueEncode) {
|
||||
// flush swresample
|
||||
@@ -478,9 +486,8 @@ void ExportThread::run() {
|
||||
}
|
||||
|
||||
delete [] c_filename;
|
||||
}
|
||||
|
||||
panel_sequence_viewer->viewer_widget->context()->doneCurrent();
|
||||
panel_sequence_viewer->viewer_widget->context()->moveToThread(qApp->thread());
|
||||
|
||||
rendering = false;
|
||||
void ExportThread::wake() {
|
||||
waitCond.wakeAll();
|
||||
}
|
||||
|
||||
+12
-5
@@ -3,6 +3,8 @@
|
||||
|
||||
#include <QThread>
|
||||
#include <QOffscreenSurface>
|
||||
#include <QMutex>
|
||||
#include <QWaitCondition>
|
||||
|
||||
class ExportDialog;
|
||||
struct AVFormatContext;
|
||||
@@ -52,19 +54,21 @@ public:
|
||||
bool continueEncode;
|
||||
signals:
|
||||
void progress_changed(int value, qint64 remaining_ms);
|
||||
public slots:
|
||||
void wake();
|
||||
private:
|
||||
bool encode(AVFormatContext* ofmt_ctx, AVCodecContext* codec_ctx, AVFrame* frame, AVPacket* packet, AVStream* stream, bool rescale);
|
||||
bool setupVideo();
|
||||
bool setupAudio();
|
||||
bool setupContainer();
|
||||
|
||||
AVFormatContext* fmt_ctx;
|
||||
AVFormatContext* fmt_ctx;
|
||||
AVStream* video_stream;
|
||||
AVCodec* vcodec;
|
||||
AVCodecContext* vcodec_ctx;
|
||||
AVFrame* video_frame;
|
||||
AVFrame* sws_frame;
|
||||
SwsContext* sws_ctx;
|
||||
SwsContext* sws_ctx;
|
||||
AVStream* audio_stream;
|
||||
AVCodec* acodec;
|
||||
AVFrame* audio_frame;
|
||||
@@ -72,14 +76,17 @@ private:
|
||||
AVCodecContext* acodec_ctx;
|
||||
AVPacket video_pkt;
|
||||
AVPacket audio_pkt;
|
||||
SwrContext* swr_ctx;
|
||||
SwrContext* swr_ctx;
|
||||
|
||||
bool vpkt_alloc;
|
||||
bool apkt_alloc;
|
||||
bool vpkt_alloc;
|
||||
bool apkt_alloc;
|
||||
|
||||
int aframe_bytes;
|
||||
int ret;
|
||||
char* c_filename;
|
||||
|
||||
QMutex mutex;
|
||||
QWaitCondition waitCond;
|
||||
};
|
||||
|
||||
#endif // EXPORTTHREAD_H
|
||||
|
||||
+20
-20
@@ -153,12 +153,12 @@ bool LoadThread::load_worker(QFile& f, QXmlStreamReader& stream, int type) {
|
||||
if (type == LOAD_TYPE_VERSION) {
|
||||
int proj_version = stream.readElementText().toInt();
|
||||
if (proj_version < MIN_SAVE_VERSION && proj_version > SAVE_VERSION) {
|
||||
if (QMessageBox::warning(
|
||||
mainWindow,
|
||||
tr("Version Mismatch"),
|
||||
tr("This project was saved in a different version of Olive and may not be fully compatible with this version. Would you like to attempt loading it anyway?"),
|
||||
QMessageBox::Yes,
|
||||
QMessageBox::No) == QMessageBox::No) {
|
||||
if (QMessageBox::warning(
|
||||
mainWindow,
|
||||
tr("Version Mismatch"),
|
||||
tr("This project was saved in a different version of Olive and may not be fully compatible with this version. Would you like to attempt loading it anyway?"),
|
||||
QMessageBox::Yes,
|
||||
QMessageBox::No) == QMessageBox::No) {
|
||||
show_err = false;
|
||||
return false;
|
||||
}
|
||||
@@ -444,11 +444,11 @@ bool LoadThread::load_worker(QFile& f, QXmlStreamReader& stream, int type) {
|
||||
if (!found) {
|
||||
correct_clip->linked.removeAt(j);
|
||||
j--;
|
||||
if (QMessageBox::warning(mainWindow,
|
||||
tr("Invalid Clip Link"),
|
||||
tr("This project contains an invalid clip link. It may be corrupt. Would you like to continue loading it?"),
|
||||
QMessageBox::Yes,
|
||||
QMessageBox::No) == QMessageBox::No) {
|
||||
if (QMessageBox::warning(mainWindow,
|
||||
tr("Invalid Clip Link"),
|
||||
tr("This project contains an invalid clip link. It may be corrupt. Would you like to continue loading it?"),
|
||||
QMessageBox::Yes,
|
||||
QMessageBox::No) == QMessageBox::No) {
|
||||
delete s;
|
||||
return false;
|
||||
}
|
||||
@@ -601,7 +601,7 @@ void LoadThread::run() {
|
||||
xml_error = false;
|
||||
if (show_err) emit error();
|
||||
} else if (stream.hasError()) {
|
||||
error_str = tr("%1 - Line: %2 Col: %3").arg(stream.errorString(), QString::number(stream.lineNumber()), QString::number(stream.columnNumber()));
|
||||
error_str = tr("%1 - Line: %2 Col: %3").arg(stream.errorString(), QString::number(stream.lineNumber()), QString::number(stream.columnNumber()));
|
||||
xml_error = true;
|
||||
emit error();
|
||||
cont = false;
|
||||
@@ -641,15 +641,15 @@ void LoadThread::cancel() {
|
||||
void LoadThread::error_func() {
|
||||
if (xml_error) {
|
||||
qCritical() << "Error parsing XML." << error_str;
|
||||
QMessageBox::critical(mainWindow,
|
||||
tr("XML Parsing Error"),
|
||||
tr("Couldn't load '%1'. %2").arg(project_url, error_str),
|
||||
QMessageBox::Ok);
|
||||
QMessageBox::critical(mainWindow,
|
||||
tr("XML Parsing Error"),
|
||||
tr("Couldn't load '%1'. %2").arg(project_url, error_str),
|
||||
QMessageBox::Ok);
|
||||
} else {
|
||||
QMessageBox::critical(mainWindow,
|
||||
tr("Project Load Error"),
|
||||
tr("Error loading project: %1").arg(error_str),
|
||||
QMessageBox::Ok);
|
||||
QMessageBox::critical(mainWindow,
|
||||
tr("Project Load Error"),
|
||||
tr("Error loading project: %1").arg(error_str),
|
||||
QMessageBox::Ok);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ int main(int argc, char *argv[]) {
|
||||
bool launch_fullscreen = false;
|
||||
QString load_proj;
|
||||
|
||||
qInstallMessageHandler(debug_message_handler);
|
||||
// qInstallMessageHandler(debug_message_handler);
|
||||
|
||||
if (argc > 1) {
|
||||
for (int i=1;i<argc;i++) {
|
||||
|
||||
+76
-64
@@ -351,6 +351,15 @@ void MainWindow::load_css_from_file(const QString &fn) {
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::set_rendering_state(bool rendering) {
|
||||
audio_rendering = rendering;
|
||||
if (rendering) {
|
||||
autorecovery_timer.stop();
|
||||
} else {
|
||||
autorecovery_timer.start();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::show_about() {
|
||||
AboutDialog a(this);
|
||||
a.exec();
|
||||
@@ -377,10 +386,10 @@ void MainWindow::delete_slot() {
|
||||
}
|
||||
|
||||
void MainWindow::select_all() {
|
||||
QDockWidget* focused_panel = get_focused_panel();
|
||||
if (focused_panel == panel_timeline) {
|
||||
QDockWidget* focused_panel = get_focused_panel();
|
||||
if (focused_panel == panel_timeline) {
|
||||
panel_timeline->select_all();
|
||||
} else if (focused_panel == panel_graph_editor) {
|
||||
} else if (focused_panel == panel_graph_editor) {
|
||||
panel_graph_editor->select_all();
|
||||
}
|
||||
}
|
||||
@@ -452,7 +461,7 @@ void MainWindow::open_speed_dialog() {
|
||||
SpeedDialog s(this);
|
||||
for (int i=0;i<sequence->clips.size();i++) {
|
||||
Clip* c = sequence->clips.at(i);
|
||||
if (c != nullptr && panel_timeline->is_clip_selected(c, true)) {
|
||||
if (c != nullptr && is_clip_selected(c, true)) {
|
||||
s.clips.append(c);
|
||||
}
|
||||
}
|
||||
@@ -502,7 +511,7 @@ void MainWindow::new_project() {
|
||||
}
|
||||
|
||||
void MainWindow::autorecover_interval() {
|
||||
if (!rendering && isWindowModified()) {
|
||||
if (isWindowModified()) {
|
||||
panel_project->save_project(true);
|
||||
qInfo() << "Auto-recovery project saved";
|
||||
}
|
||||
@@ -972,6 +981,9 @@ void MainWindow::closeEvent(QCloseEvent *e) {
|
||||
|
||||
set_sequence(nullptr);
|
||||
|
||||
panel_footage_viewer->viewer_widget->close_window();
|
||||
panel_sequence_viewer->viewer_widget->close_window();
|
||||
|
||||
panel_footage_viewer->set_main_sequence();
|
||||
|
||||
QString data_dir = get_data_path();
|
||||
@@ -1051,78 +1063,78 @@ void MainWindow::reset_layout() {
|
||||
}
|
||||
|
||||
void MainWindow::go_to_in() {
|
||||
QDockWidget* focused_panel = get_focused_panel();
|
||||
if (focused_panel == panel_footage_viewer) {
|
||||
panel_footage_viewer->go_to_in();
|
||||
} else {
|
||||
panel_sequence_viewer->go_to_in();
|
||||
}
|
||||
QDockWidget* focused_panel = get_focused_panel();
|
||||
if (focused_panel == panel_footage_viewer) {
|
||||
panel_footage_viewer->go_to_in();
|
||||
} else {
|
||||
panel_sequence_viewer->go_to_in();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::go_to_out() {
|
||||
QDockWidget* focused_panel = get_focused_panel();
|
||||
if (focused_panel == panel_footage_viewer) {
|
||||
panel_footage_viewer->go_to_out();
|
||||
} else {
|
||||
panel_sequence_viewer->go_to_out();
|
||||
}
|
||||
QDockWidget* focused_panel = get_focused_panel();
|
||||
if (focused_panel == panel_footage_viewer) {
|
||||
panel_footage_viewer->go_to_out();
|
||||
} else {
|
||||
panel_sequence_viewer->go_to_out();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::go_to_start() {
|
||||
QDockWidget* focused_panel = get_focused_panel();
|
||||
if (focused_panel == panel_footage_viewer) {
|
||||
panel_footage_viewer->go_to_start();
|
||||
} else {
|
||||
panel_sequence_viewer->go_to_start();
|
||||
}
|
||||
QDockWidget* focused_panel = get_focused_panel();
|
||||
if (focused_panel == panel_footage_viewer) {
|
||||
panel_footage_viewer->go_to_start();
|
||||
} else {
|
||||
panel_sequence_viewer->go_to_start();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::prev_frame() {
|
||||
QDockWidget* focused_panel = get_focused_panel();
|
||||
if (focused_panel == panel_footage_viewer) {
|
||||
panel_footage_viewer->previous_frame();
|
||||
} else {
|
||||
panel_sequence_viewer->previous_frame();
|
||||
}
|
||||
QDockWidget* focused_panel = get_focused_panel();
|
||||
if (focused_panel == panel_footage_viewer) {
|
||||
panel_footage_viewer->previous_frame();
|
||||
} else {
|
||||
panel_sequence_viewer->previous_frame();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::next_frame() {
|
||||
QDockWidget* focused_panel = get_focused_panel();
|
||||
if (focused_panel == panel_footage_viewer) {
|
||||
panel_footage_viewer->next_frame();
|
||||
} else {
|
||||
panel_sequence_viewer->next_frame();
|
||||
}
|
||||
QDockWidget* focused_panel = get_focused_panel();
|
||||
if (focused_panel == panel_footage_viewer) {
|
||||
panel_footage_viewer->next_frame();
|
||||
} else {
|
||||
panel_sequence_viewer->next_frame();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::go_to_end() {
|
||||
QDockWidget* focused_panel = get_focused_panel();
|
||||
if (focused_panel == panel_footage_viewer) {
|
||||
panel_footage_viewer->go_to_end();
|
||||
} else {
|
||||
panel_sequence_viewer->go_to_end();
|
||||
}
|
||||
QDockWidget* focused_panel = get_focused_panel();
|
||||
if (focused_panel == panel_footage_viewer) {
|
||||
panel_footage_viewer->go_to_end();
|
||||
} else {
|
||||
panel_sequence_viewer->go_to_end();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::playpause() {
|
||||
QDockWidget* focused_panel = get_focused_panel();
|
||||
if (focused_panel == panel_footage_viewer) {
|
||||
panel_footage_viewer->toggle_play();
|
||||
} else {
|
||||
panel_sequence_viewer->toggle_play();
|
||||
}
|
||||
QDockWidget* focused_panel = get_focused_panel();
|
||||
if (focused_panel == panel_footage_viewer) {
|
||||
panel_footage_viewer->toggle_play();
|
||||
} else {
|
||||
panel_sequence_viewer->toggle_play();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::prev_cut() {
|
||||
QDockWidget* focused_panel = get_focused_panel();
|
||||
if (sequence != nullptr && (panel_timeline == focused_panel || panel_sequence_viewer == focused_panel)) {
|
||||
QDockWidget* focused_panel = get_focused_panel();
|
||||
if (sequence != nullptr && (panel_timeline == focused_panel || panel_sequence_viewer == focused_panel)) {
|
||||
panel_timeline->previous_cut();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::next_cut() {
|
||||
QDockWidget* focused_panel = get_focused_panel();
|
||||
if (sequence != nullptr && (panel_timeline == focused_panel || panel_sequence_viewer == focused_panel)) {
|
||||
QDockWidget* focused_panel = get_focused_panel();
|
||||
if (sequence != nullptr && (panel_timeline == focused_panel || panel_sequence_viewer == focused_panel)) {
|
||||
panel_timeline->next_cut();
|
||||
}
|
||||
}
|
||||
@@ -1404,7 +1416,7 @@ void MainWindow::toggle_enable_clips() {
|
||||
bool push_undo = false;
|
||||
for (int i=0;i<sequence->clips.size();i++) {
|
||||
Clip* c = sequence->clips.at(i);
|
||||
if (c != nullptr && panel_timeline->is_clip_selected(c, true)) {
|
||||
if (c != nullptr && is_clip_selected(c, true)) {
|
||||
ca->append(new SetEnableCommand(c, !c->enabled));
|
||||
push_undo = true;
|
||||
}
|
||||
@@ -1419,13 +1431,13 @@ void MainWindow::toggle_enable_clips() {
|
||||
}
|
||||
|
||||
void MainWindow::edit_to_in_point() {
|
||||
QDockWidget* focused_panel = get_focused_panel();
|
||||
if (focused_panel == panel_timeline) panel_timeline->ripple_to_in_point(true, false);
|
||||
QDockWidget* focused_panel = get_focused_panel();
|
||||
if (focused_panel == panel_timeline) panel_timeline->ripple_to_in_point(true, false);
|
||||
}
|
||||
|
||||
void MainWindow::edit_to_out_point() {
|
||||
QDockWidget* focused_panel = get_focused_panel();
|
||||
if (focused_panel == panel_timeline) panel_timeline->ripple_to_in_point(false, false);
|
||||
QDockWidget* focused_panel = get_focused_panel();
|
||||
if (focused_panel == panel_timeline) panel_timeline->ripple_to_in_point(false, false);
|
||||
}
|
||||
|
||||
void MainWindow::nest() {
|
||||
@@ -1436,7 +1448,7 @@ void MainWindow::nest() {
|
||||
// get selected clips
|
||||
for (int i=0;i<sequence->clips.size();i++) {
|
||||
Clip* c = sequence->clips.at(i);
|
||||
if (c != nullptr && panel_timeline->is_clip_selected(c, true)) {
|
||||
if (c != nullptr && is_clip_selected(c, true)) {
|
||||
selected_clips.append(i);
|
||||
earliest_point = qMin(c->timeline_in, earliest_point);
|
||||
}
|
||||
@@ -1488,8 +1500,8 @@ void MainWindow::nest() {
|
||||
}
|
||||
|
||||
void MainWindow::paste_insert() {
|
||||
QDockWidget* focused_panel = get_focused_panel();
|
||||
if (focused_panel == panel_timeline && sequence != nullptr) {
|
||||
QDockWidget* focused_panel = get_focused_panel();
|
||||
if (focused_panel == panel_timeline && sequence != nullptr) {
|
||||
panel_timeline->paste(true);
|
||||
}
|
||||
}
|
||||
@@ -1507,11 +1519,11 @@ void MainWindow::set_autoscroll() {
|
||||
}
|
||||
|
||||
void MainWindow::menu_click_button() {
|
||||
QDockWidget* focused_panel = get_focused_panel();
|
||||
if (focused_panel == panel_timeline
|
||||
|| focused_panel == panel_effect_controls
|
||||
|| focused_panel == panel_footage_viewer
|
||||
|| focused_panel == panel_sequence_viewer)
|
||||
QDockWidget* focused_panel = get_focused_panel();
|
||||
if (focused_panel == panel_timeline
|
||||
|| focused_panel == panel_effect_controls
|
||||
|| focused_panel == panel_footage_viewer
|
||||
|| focused_panel == panel_sequence_viewer)
|
||||
reinterpret_cast<QPushButton*>(static_cast<QAction*>(sender())->data().value<quintptr>())->click();
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -23,7 +23,9 @@ public:
|
||||
void load_shortcuts(const QString &fn, bool first = false);
|
||||
void save_shortcuts(const QString &fn);
|
||||
|
||||
void load_css_from_file(const QString& fn);
|
||||
void load_css_from_file(const QString& fn);
|
||||
|
||||
void set_rendering_state(bool rendering);
|
||||
|
||||
public slots:
|
||||
void undo();
|
||||
|
||||
@@ -124,7 +124,10 @@ SOURCES += \
|
||||
effects/internal/fillleftrighteffect.cpp \
|
||||
effects/internal/voideffect.cpp \
|
||||
dialogs/texteditdialog.cpp \
|
||||
dialogs/debugdialog.cpp
|
||||
dialogs/debugdialog.cpp \
|
||||
ui/renderthread.cpp \
|
||||
ui/renderfunctions.cpp \
|
||||
ui/viewerwindow.cpp
|
||||
|
||||
HEADERS += \
|
||||
mainwindow.h \
|
||||
@@ -216,7 +219,10 @@ HEADERS += \
|
||||
effects/internal/fillleftrighteffect.h \
|
||||
effects/internal/voideffect.h \
|
||||
dialogs/texteditdialog.h \
|
||||
dialogs/debugdialog.h
|
||||
dialogs/debugdialog.h \
|
||||
ui/renderthread.h \
|
||||
ui/renderfunctions.h \
|
||||
ui/viewerwindow.h
|
||||
|
||||
FORMS +=
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ EffectControls::EffectControls(QWidget *parent) :
|
||||
QDockWidget(parent),
|
||||
multiple(false),
|
||||
zoom(1),
|
||||
panel_name(tr("Effects: ")),
|
||||
panel_name(tr("Effects: ")),
|
||||
mode(TA_NO_TRANSITION)
|
||||
{
|
||||
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
@@ -94,7 +94,7 @@ void EffectControls::menu_select(QAction* q) {
|
||||
update_ui(true);
|
||||
} else {
|
||||
reload_clips();
|
||||
panel_sequence_viewer->viewer_widget->update();
|
||||
panel_sequence_viewer->viewer_widget->frame_update();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,7 +303,7 @@ void EffectControls::setup_ui() {
|
||||
|
||||
QPushButton* btnAddVideoEffect = new QPushButton(veHeader);
|
||||
btnAddVideoEffect->setIcon(QIcon(":/icons/add-effect.png"));
|
||||
btnAddVideoEffect->setToolTip(tr("Add Video Effect"));
|
||||
btnAddVideoEffect->setToolTip(tr("Add Video Effect"));
|
||||
veHeaderLayout->addWidget(btnAddVideoEffect);
|
||||
connect(btnAddVideoEffect, SIGNAL(clicked(bool)), this, SLOT(video_effect_click()));
|
||||
|
||||
@@ -314,14 +314,14 @@ void EffectControls::setup_ui() {
|
||||
font.setPointSize(9);
|
||||
lblVideoEffects->setFont(font);
|
||||
lblVideoEffects->setAlignment(Qt::AlignCenter);
|
||||
lblVideoEffects->setText(tr("VIDEO EFFECTS"));
|
||||
lblVideoEffects->setText(tr("VIDEO EFFECTS"));
|
||||
veHeaderLayout->addWidget(lblVideoEffects);
|
||||
|
||||
veHeaderLayout->addStretch();
|
||||
|
||||
QPushButton* btnAddVideoTransition = new QPushButton(veHeader);
|
||||
btnAddVideoTransition->setIcon(QIcon(":/icons/add-transition.png"));
|
||||
btnAddVideoTransition->setToolTip(tr("Add Video Transition"));
|
||||
btnAddVideoTransition->setToolTip(tr("Add Video Transition"));
|
||||
connect(btnAddVideoTransition, SIGNAL(clicked(bool)), this, SLOT(video_transition_click()));
|
||||
|
||||
veHeaderLayout->addWidget(btnAddVideoTransition);
|
||||
@@ -351,7 +351,7 @@ void EffectControls::setup_ui() {
|
||||
|
||||
QPushButton* btnAddAudioEffect = new QPushButton(aeHeader);
|
||||
btnAddAudioEffect->setIcon(QIcon(":/icons/add-effect.png"));
|
||||
btnAddAudioEffect->setToolTip(tr("Add Audio Effect"));
|
||||
btnAddAudioEffect->setToolTip(tr("Add Audio Effect"));
|
||||
connect(btnAddAudioEffect, SIGNAL(clicked(bool)), this, SLOT(audio_effect_click()));
|
||||
aeHeaderLayout->addWidget(btnAddAudioEffect);
|
||||
|
||||
@@ -360,14 +360,14 @@ void EffectControls::setup_ui() {
|
||||
QLabel* lblAudioEffects = new QLabel(aeHeader);
|
||||
lblAudioEffects->setFont(font);
|
||||
lblAudioEffects->setAlignment(Qt::AlignCenter);
|
||||
lblAudioEffects->setText(tr("AUDIO EFFECTS"));
|
||||
lblAudioEffects->setText(tr("AUDIO EFFECTS"));
|
||||
aeHeaderLayout->addWidget(lblAudioEffects);
|
||||
|
||||
aeHeaderLayout->addStretch();
|
||||
|
||||
QPushButton* btnAddAudioTransition = new QPushButton(aeHeader);
|
||||
btnAddAudioTransition->setIcon(QIcon(":/icons/add-transition.png"));
|
||||
btnAddAudioTransition->setToolTip(tr("Add Audio Transition"));
|
||||
btnAddAudioTransition->setToolTip(tr("Add Audio Transition"));
|
||||
connect(btnAddAudioTransition, SIGNAL(clicked(bool)), this, SLOT(audio_transition_click()));
|
||||
aeHeaderLayout->addWidget(btnAddAudioTransition);
|
||||
|
||||
@@ -384,7 +384,7 @@ void EffectControls::setup_ui() {
|
||||
|
||||
lblMultipleClipsSelected = new QLabel(effects_area);
|
||||
lblMultipleClipsSelected->setAlignment(Qt::AlignCenter);
|
||||
lblMultipleClipsSelected->setText(tr("(Multiple clips selected)"));
|
||||
lblMultipleClipsSelected->setText(tr("(Multiple clips selected)"));
|
||||
effects_area_layout->addWidget(lblMultipleClipsSelected);
|
||||
|
||||
effects_area_layout->addStretch();
|
||||
|
||||
+1
-1
@@ -913,7 +913,7 @@ void Project::save_folder(QXmlStreamWriter& stream, int type, bool set_ids_only,
|
||||
}
|
||||
// save_folder(stream, item, type, set_ids_only);
|
||||
} else {
|
||||
int folder = (m->parentItem() != nullptr) ? m->parentItem()->temp_id : 0;
|
||||
int folder = m->parentItem()->temp_id;
|
||||
if (type == MEDIA_TYPE_FOOTAGE) {
|
||||
Footage* f = m->to_footage();
|
||||
f->save_id = media_id;
|
||||
|
||||
+37
-41
@@ -39,10 +39,6 @@
|
||||
#include <QSplitter>
|
||||
#include <QStatusBar>
|
||||
|
||||
long refactor_frame_number(long framenumber, double source_frame_rate, double target_frame_rate) {
|
||||
return qRound((double(framenumber)/source_frame_rate)*target_frame_rate);
|
||||
}
|
||||
|
||||
Timeline::Timeline(QWidget *parent) :
|
||||
QDockWidget(parent),
|
||||
cursor_frame(0),
|
||||
@@ -397,11 +393,11 @@ void Timeline::update_sequence() {
|
||||
addButton->setEnabled(!null_sequence);
|
||||
headers->setEnabled(!null_sequence);
|
||||
|
||||
QString title = tr("Timeline: ");
|
||||
QString title = tr("Timeline: ");
|
||||
if (null_sequence) {
|
||||
setWindowTitle(title + tr("<none>"));
|
||||
setWindowTitle(title + tr("<none>"));
|
||||
} else {
|
||||
setWindowTitle(title + sequence->name);
|
||||
setWindowTitle(title + sequence->name);
|
||||
update_ui(false);
|
||||
}
|
||||
}
|
||||
@@ -623,7 +619,7 @@ void Timeline::zoom_out() {
|
||||
set_zoom(false);
|
||||
}
|
||||
|
||||
bool Timeline::is_clip_selected(Clip* clip, bool containing) {
|
||||
bool is_clip_selected(Clip* clip, bool containing) {
|
||||
for (int i=0;i<clip->sequence->selections.size();i++) {
|
||||
const Selection& s = clip->sequence->selections.at(i);
|
||||
if (clip->track == s.track && ((clip->timeline_in >= s.in && clip->timeline_out <= s.out && containing) ||
|
||||
@@ -1008,15 +1004,15 @@ void Timeline::paste(bool insert) {
|
||||
}
|
||||
if (found >= 0 && ask_conflict) {
|
||||
QMessageBox box(this);
|
||||
box.setWindowTitle(tr("Effect already exists"));
|
||||
box.setText(tr("Clip '%1' already contains a '%2' effect. Would you like to replace it with the pasted one or add it as a separate effect?").arg(c->name, e->meta->name));
|
||||
box.setWindowTitle(tr("Effect already exists"));
|
||||
box.setText(tr("Clip '%1' already contains a '%2' effect. Would you like to replace it with the pasted one or add it as a separate effect?").arg(c->name, e->meta->name));
|
||||
box.setIcon(QMessageBox::Icon::Question);
|
||||
|
||||
box.addButton(tr("Add"), QMessageBox::YesRole);
|
||||
QPushButton* replace_button = box.addButton(tr("Replace"), QMessageBox::NoRole);
|
||||
QPushButton* skip_button = box.addButton(tr("Skip"), QMessageBox::RejectRole);
|
||||
box.addButton(tr("Add"), QMessageBox::YesRole);
|
||||
QPushButton* replace_button = box.addButton(tr("Replace"), QMessageBox::NoRole);
|
||||
QPushButton* skip_button = box.addButton(tr("Skip"), QMessageBox::RejectRole);
|
||||
|
||||
QCheckBox* future_box = new QCheckBox(tr("Do this for all conflicts found"));
|
||||
QCheckBox* future_box = new QCheckBox(tr("Do this for all conflicts found"));
|
||||
box.setCheckBox(future_box);
|
||||
|
||||
box.exec();
|
||||
@@ -1390,8 +1386,8 @@ void Timeline::set_marker() {
|
||||
|
||||
if (!add_marker) {
|
||||
QInputDialog d(this);
|
||||
d.setWindowTitle(tr("Set Marker"));
|
||||
d.setLabelText(tr("Set marker name:"));
|
||||
d.setWindowTitle(tr("Set Marker"));
|
||||
d.setLabelText(tr("Set marker name:"));
|
||||
d.setInputMode(QInputDialog::TextInput);
|
||||
add_marker = (d.exec() == QDialog::Accepted);
|
||||
marker_name = d.textValue();
|
||||
@@ -1479,29 +1475,29 @@ void Timeline::add_btn_click() {
|
||||
QMenu add_menu(this);
|
||||
|
||||
QAction* titleMenuItem = new QAction(&add_menu);
|
||||
titleMenuItem->setText(tr("Title..."));
|
||||
titleMenuItem->setText(tr("Title..."));
|
||||
titleMenuItem->setData(ADD_OBJ_TITLE);
|
||||
add_menu.addAction(titleMenuItem);
|
||||
|
||||
QAction* solidMenuItem = new QAction(&add_menu);
|
||||
solidMenuItem->setText(tr("Solid Color..."));
|
||||
solidMenuItem->setText(tr("Solid Color..."));
|
||||
solidMenuItem->setData(ADD_OBJ_SOLID);
|
||||
add_menu.addAction(solidMenuItem);
|
||||
|
||||
QAction* barsMenuItem = new QAction(&add_menu);
|
||||
barsMenuItem->setText(tr("Bars..."));
|
||||
barsMenuItem->setText(tr("Bars..."));
|
||||
barsMenuItem->setData(ADD_OBJ_BARS);
|
||||
add_menu.addAction(barsMenuItem);
|
||||
|
||||
add_menu.addSeparator();
|
||||
|
||||
QAction* toneMenuItem = new QAction(&add_menu);
|
||||
toneMenuItem->setText(tr("Tone..."));
|
||||
toneMenuItem->setText(tr("Tone..."));
|
||||
toneMenuItem->setData(ADD_OBJ_TONE);
|
||||
add_menu.addAction(toneMenuItem);
|
||||
|
||||
QAction* noiseMenuItem = new QAction(&add_menu);
|
||||
noiseMenuItem->setText(tr("Noise..."));
|
||||
noiseMenuItem->setText(tr("Noise..."));
|
||||
noiseMenuItem->setData(ADD_OBJ_NOISE);
|
||||
add_menu.addAction(noiseMenuItem);
|
||||
|
||||
@@ -1523,16 +1519,16 @@ void Timeline::setScroll(int s) {
|
||||
|
||||
void Timeline::record_btn_click() {
|
||||
if (project_url.isEmpty()) {
|
||||
QMessageBox::critical(this,
|
||||
tr("Unsaved Project"),
|
||||
tr("You must save this project before you can record audio in it."),
|
||||
QMessageBox::Ok);
|
||||
QMessageBox::critical(this,
|
||||
tr("Unsaved Project"),
|
||||
tr("You must save this project before you can record audio in it."),
|
||||
QMessageBox::Ok);
|
||||
} else {
|
||||
creating = true;
|
||||
creating_object = ADD_OBJ_AUDIO;
|
||||
mainWindow->statusBar()->showMessage(
|
||||
tr("Click on the timeline where you want to start recording (drag to limit the recording to a certain timeframe)"),
|
||||
10000);
|
||||
mainWindow->statusBar()->showMessage(
|
||||
tr("Click on the timeline where you want to start recording (drag to limit the recording to a certain timeframe)"),
|
||||
10000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1611,7 +1607,7 @@ void Timeline::setup_ui() {
|
||||
arrow_icon.addFile(QStringLiteral(":/icons/arrow-disabled.png"), QSize(), QIcon::Disabled, QIcon::Off);
|
||||
toolArrowButton->setIcon(arrow_icon);
|
||||
toolArrowButton->setCheckable(true);
|
||||
toolArrowButton->setToolTip(tr("Pointer Tool") + " (V)");
|
||||
toolArrowButton->setToolTip(tr("Pointer Tool") + " (V)");
|
||||
toolArrowButton->setProperty("tool", TIMELINE_TOOL_POINTER);
|
||||
connect(toolArrowButton, SIGNAL(clicked(bool)), this, SLOT(set_tool()));
|
||||
tool_buttons_layout->addWidget(toolArrowButton);
|
||||
@@ -1622,7 +1618,7 @@ void Timeline::setup_ui() {
|
||||
icon1.addFile(QStringLiteral(":/icons/beam-disabled.png"), QSize(), QIcon::Disabled, QIcon::Off);
|
||||
toolEditButton->setIcon(icon1);
|
||||
toolEditButton->setCheckable(true);
|
||||
toolEditButton->setToolTip(tr("Edit Tool") + " (X)");
|
||||
toolEditButton->setToolTip(tr("Edit Tool") + " (X)");
|
||||
toolEditButton->setProperty("tool", TIMELINE_TOOL_EDIT);
|
||||
connect(toolEditButton, SIGNAL(clicked(bool)), this, SLOT(set_tool()));
|
||||
tool_buttons_layout->addWidget(toolEditButton);
|
||||
@@ -1633,7 +1629,7 @@ void Timeline::setup_ui() {
|
||||
icon2.addFile(QStringLiteral(":/icons/ripple-disabled.png"), QSize(), QIcon::Disabled, QIcon::Off);
|
||||
toolRippleButton->setIcon(icon2);
|
||||
toolRippleButton->setCheckable(true);
|
||||
toolRippleButton->setToolTip(tr("Ripple Tool") + " (B)");
|
||||
toolRippleButton->setToolTip(tr("Ripple Tool") + " (B)");
|
||||
toolRippleButton->setProperty("tool", TIMELINE_TOOL_RIPPLE);
|
||||
connect(toolRippleButton, SIGNAL(clicked(bool)), this, SLOT(set_tool()));
|
||||
tool_buttons_layout->addWidget(toolRippleButton);
|
||||
@@ -1644,7 +1640,7 @@ void Timeline::setup_ui() {
|
||||
icon4.addFile(QStringLiteral(":/icons/razor-disabled.png"), QSize(), QIcon::Disabled, QIcon::Off);
|
||||
toolRazorButton->setIcon(icon4);
|
||||
toolRazorButton->setCheckable(true);
|
||||
toolRazorButton->setToolTip(tr("Razor Tool") + " (C)");
|
||||
toolRazorButton->setToolTip(tr("Razor Tool") + " (C)");
|
||||
toolRazorButton->setProperty("tool", TIMELINE_TOOL_RAZOR);
|
||||
connect(toolRazorButton, SIGNAL(clicked(bool)), this, SLOT(set_tool()));
|
||||
tool_buttons_layout->addWidget(toolRazorButton);
|
||||
@@ -1655,7 +1651,7 @@ void Timeline::setup_ui() {
|
||||
icon5.addFile(QStringLiteral(":/icons/slip-disabled.png"), QSize(), QIcon::Disabled, QIcon::On);
|
||||
toolSlipButton->setIcon(icon5);
|
||||
toolSlipButton->setCheckable(true);
|
||||
toolSlipButton->setToolTip(tr("Slip Tool") + " (Y)");
|
||||
toolSlipButton->setToolTip(tr("Slip Tool") + " (Y)");
|
||||
toolSlipButton->setProperty("tool", TIMELINE_TOOL_SLIP);
|
||||
connect(toolSlipButton, SIGNAL(clicked(bool)), this, SLOT(set_tool()));
|
||||
tool_buttons_layout->addWidget(toolSlipButton);
|
||||
@@ -1666,7 +1662,7 @@ void Timeline::setup_ui() {
|
||||
icon6.addFile(QStringLiteral(":/icons/slide-disabled.png"), QSize(), QIcon::Disabled, QIcon::On);
|
||||
toolSlideButton->setIcon(icon6);
|
||||
toolSlideButton->setCheckable(true);
|
||||
toolSlideButton->setToolTip(tr("Slide Tool") + " (U)");
|
||||
toolSlideButton->setToolTip(tr("Slide Tool") + " (U)");
|
||||
toolSlideButton->setProperty("tool", TIMELINE_TOOL_SLIDE);
|
||||
connect(toolSlideButton, SIGNAL(clicked(bool)), this, SLOT(set_tool()));
|
||||
tool_buttons_layout->addWidget(toolSlideButton);
|
||||
@@ -1677,7 +1673,7 @@ void Timeline::setup_ui() {
|
||||
icon7.addFile(QStringLiteral(":/icons/hand-disabled.png"), QSize(), QIcon::Disabled, QIcon::On);
|
||||
toolHandButton->setIcon(icon7);
|
||||
toolHandButton->setCheckable(true);
|
||||
toolHandButton->setToolTip(tr("Hand Tool") + " (H)");
|
||||
toolHandButton->setToolTip(tr("Hand Tool") + " (H)");
|
||||
toolHandButton->setProperty("tool", TIMELINE_TOOL_HAND);
|
||||
connect(toolHandButton, SIGNAL(clicked(bool)), this, SLOT(set_tool()));
|
||||
tool_buttons_layout->addWidget(toolHandButton);
|
||||
@@ -1688,7 +1684,7 @@ void Timeline::setup_ui() {
|
||||
icon8.addFile(QStringLiteral(":/icons/transition-tool-disabled.png"), QSize(), QIcon::Disabled, QIcon::On);
|
||||
toolTransitionButton->setIcon(icon8);
|
||||
toolTransitionButton->setCheckable(true);
|
||||
toolTransitionButton->setToolTip(tr("Transition Tool") + " (T)");
|
||||
toolTransitionButton->setToolTip(tr("Transition Tool") + " (T)");
|
||||
connect(toolTransitionButton, SIGNAL(clicked(bool)), this, SLOT(transition_tool_click()));
|
||||
tool_buttons_layout->addWidget(toolTransitionButton);
|
||||
|
||||
@@ -1699,7 +1695,7 @@ void Timeline::setup_ui() {
|
||||
snappingButton->setIcon(icon9);
|
||||
snappingButton->setCheckable(true);
|
||||
snappingButton->setChecked(true);
|
||||
snappingButton->setToolTip(tr("Snapping") + " (S)");
|
||||
snappingButton->setToolTip(tr("Snapping") + " (S)");
|
||||
connect(snappingButton, SIGNAL(toggled(bool)), this, SLOT(snapping_clicked(bool)));
|
||||
tool_buttons_layout->addWidget(snappingButton);
|
||||
|
||||
@@ -1708,7 +1704,7 @@ void Timeline::setup_ui() {
|
||||
icon10.addFile(QStringLiteral(":/icons/zoomin.png"), QSize(), QIcon::Normal, QIcon::On);
|
||||
icon10.addFile(QStringLiteral(":/icons/zoomin-disabled.png"), QSize(), QIcon::Disabled, QIcon::On);
|
||||
zoomInButton->setIcon(icon10);
|
||||
zoomInButton->setToolTip(tr("Zoom In") + " (=)");
|
||||
zoomInButton->setToolTip(tr("Zoom In") + " (=)");
|
||||
connect(zoomInButton, SIGNAL(clicked(bool)), this, SLOT(zoom_in()));
|
||||
tool_buttons_layout->addWidget(zoomInButton);
|
||||
|
||||
@@ -1717,7 +1713,7 @@ void Timeline::setup_ui() {
|
||||
icon11.addFile(QStringLiteral(":/icons/zoomout.png"), QSize(), QIcon::Normal, QIcon::On);
|
||||
icon11.addFile(QStringLiteral(":/icons/zoomout-disabled.png"), QSize(), QIcon::Disabled, QIcon::On);
|
||||
zoomOutButton->setIcon(icon11);
|
||||
zoomOutButton->setToolTip(tr("Zoom Out") + " (-)");
|
||||
zoomOutButton->setToolTip(tr("Zoom Out") + " (-)");
|
||||
connect(zoomOutButton, SIGNAL(clicked(bool)), this, SLOT(zoom_out()));
|
||||
tool_buttons_layout->addWidget(zoomOutButton);
|
||||
|
||||
@@ -1726,7 +1722,7 @@ void Timeline::setup_ui() {
|
||||
icon12.addFile(QStringLiteral(":/icons/record.png"), QSize(), QIcon::Normal, QIcon::On);
|
||||
icon12.addFile(QStringLiteral(":/icons/record-disabled.png"), QSize(), QIcon::Disabled, QIcon::On);
|
||||
recordButton->setIcon(icon12);
|
||||
recordButton->setToolTip(tr("Record audio"));
|
||||
recordButton->setToolTip(tr("Record audio"));
|
||||
connect(recordButton, SIGNAL(clicked(bool)), this, SLOT(record_btn_click()));
|
||||
|
||||
tool_buttons_layout->addWidget(recordButton);
|
||||
@@ -1736,7 +1732,7 @@ void Timeline::setup_ui() {
|
||||
icon13.addFile(QStringLiteral(":/icons/add-button.png"), QSize(), QIcon::Normal, QIcon::On);
|
||||
icon13.addFile(QStringLiteral(":/icons/add-button-disabled.png"), QSize(), QIcon::Disabled, QIcon::On);
|
||||
addButton->setIcon(icon13);
|
||||
addButton->setToolTip(tr("Add title, solid, bars, etc."));
|
||||
addButton->setToolTip(tr("Add title, solid, bars, etc."));
|
||||
connect(addButton, SIGNAL(clicked()), this, SLOT(add_btn_click()));
|
||||
tool_buttons_layout->addWidget(addButton);
|
||||
|
||||
|
||||
+2
-3
@@ -35,7 +35,7 @@ struct Clip;
|
||||
struct Footage;
|
||||
struct FootageStream;
|
||||
|
||||
long refactor_frame_number(long framenumber, double source_frame_rate, double target_frame_rate);
|
||||
bool is_clip_selected(Clip* clip, bool containing);
|
||||
int getScreenPointFromFrame(double zoom, long frame);
|
||||
long getFrameFromScreenPoint(double zoom, int x);
|
||||
bool selection_contains_transition(const Selection& s, Clip* c, int type);
|
||||
@@ -137,7 +137,6 @@ public:
|
||||
// selecting functions
|
||||
bool selecting;
|
||||
int selection_offset;
|
||||
bool is_clip_selected(Clip* clip, bool containing);
|
||||
void delete_selection(QVector<Selection> &selections, bool ripple);
|
||||
void select_all();
|
||||
bool rect_select_init;
|
||||
@@ -204,7 +203,7 @@ public:
|
||||
QPushButton* snappingButton;
|
||||
|
||||
void scroll_to_frame(long frame);
|
||||
void select_from_playhead();
|
||||
void select_from_playhead();
|
||||
|
||||
void resizeEvent(QResizeEvent *event);
|
||||
public slots:
|
||||
|
||||
+9
-7
@@ -44,7 +44,7 @@ Viewer::Viewer(QWidget *parent) :
|
||||
seq(nullptr),
|
||||
created_sequence(false),
|
||||
cue_recording_internal(false),
|
||||
panel_name(tr("Viewer: ")),
|
||||
panel_name(tr("Viewer: ")),
|
||||
minimum_zoom(1.0)
|
||||
{
|
||||
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
@@ -107,7 +107,9 @@ void Viewer::reset_all_audio() {
|
||||
|
||||
for (int i=0;i<seq->clips.size();i++) {
|
||||
Clip* c = seq->clips.at(i);
|
||||
if (c != nullptr) c->reset_audio();
|
||||
if (c != nullptr) {
|
||||
c->reset_audio();
|
||||
}
|
||||
}
|
||||
}
|
||||
clear_audio_ibuffer();
|
||||
@@ -251,9 +253,9 @@ void Viewer::seek(long p) {
|
||||
update_fx = true;
|
||||
}
|
||||
}
|
||||
update_parents(update_fx);
|
||||
reset_all_audio();
|
||||
audio_scrub = true;
|
||||
update_parents(update_fx);
|
||||
}
|
||||
|
||||
void Viewer::go_to_start() {
|
||||
@@ -439,7 +441,7 @@ void Viewer::resizeEvent(QResizeEvent *) {
|
||||
|
||||
void Viewer::update_viewer() {
|
||||
update_header_zoom();
|
||||
viewer_widget->update();
|
||||
viewer_widget->frame_update();
|
||||
if (seq != nullptr) update_playhead_timecode(seq->playhead);
|
||||
update_end_timecode();
|
||||
}
|
||||
@@ -675,7 +677,7 @@ void Viewer::set_media(Media* m) {
|
||||
viewer_widget->waveform = true;
|
||||
viewer_widget->waveform_clip = c;
|
||||
viewer_widget->waveform_ms = &audio_stream;
|
||||
viewer_widget->update();
|
||||
viewer_widget->frame_update();
|
||||
}
|
||||
} else {
|
||||
seq->audio_frequency = 48000;
|
||||
@@ -785,12 +787,12 @@ void Viewer::set_sequence(bool main, Sequence *s) {
|
||||
update_playhead_timecode(0);
|
||||
update_end_timecode();
|
||||
|
||||
setWindowTitle(panel_name + tr("(none)"));
|
||||
setWindowTitle(panel_name + tr("(none)"));
|
||||
}
|
||||
|
||||
update_header_zoom();
|
||||
|
||||
viewer_widget->update();
|
||||
viewer_widget->frame_update();
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
+6
-3
@@ -29,6 +29,7 @@ bool audio_scrub = false;
|
||||
QMutex audio_write_lock;
|
||||
QAudioInput* audio_input = nullptr;
|
||||
QFile output_recording;
|
||||
bool audio_rendering = false;
|
||||
bool recording = false;
|
||||
|
||||
qint8 audio_ibuffer[audio_ibuffer_size];
|
||||
@@ -102,13 +103,15 @@ void stop_audio() {
|
||||
|
||||
void clear_audio_ibuffer() {
|
||||
if (audio_thread != nullptr) audio_thread->lock.lock();
|
||||
audio_write_lock.lock();
|
||||
memset(audio_ibuffer, 0, audio_ibuffer_size);
|
||||
audio_ibuffer_read = 0;
|
||||
audio_write_lock.unlock();
|
||||
if (audio_thread != nullptr) audio_thread->lock.unlock();
|
||||
}
|
||||
|
||||
int current_audio_freq() {
|
||||
return rendering ? sequence->audio_frequency : audio_output->format().sampleRate();
|
||||
return audio_rendering ? sequence->audio_frequency : audio_output->format().sampleRate();
|
||||
}
|
||||
|
||||
int get_buffer_offset_from_frame(double framerate, long frame) {
|
||||
@@ -292,7 +295,7 @@ bool start_recording() {
|
||||
return false;
|
||||
}
|
||||
|
||||
QString audio_path = project_url + " " + QCoreApplication::translate("Audio", "Audio");
|
||||
QString audio_path = project_url + " " + QCoreApplication::translate("Audio", "Audio");
|
||||
QDir audio_dir(audio_path);
|
||||
if (!audio_dir.exists() && !audio_dir.mkpath(".")) {
|
||||
qCritical() << "Failed to create audio directory";
|
||||
@@ -303,7 +306,7 @@ bool start_recording() {
|
||||
int file_number = 0;
|
||||
do {
|
||||
file_number++;
|
||||
audio_filename = audio_path + "/" + QCoreApplication::translate("Audio", "Recording") + " " + QString::number(file_number) + ".wav";
|
||||
audio_filename = audio_path + "/" + QCoreApplication::translate("Audio", "Recording") + " " + QString::number(file_number) + ".wav";
|
||||
} while (QFile(audio_filename).exists());
|
||||
|
||||
output_recording.setFileName(audio_filename);
|
||||
|
||||
+2
-1
@@ -41,6 +41,8 @@ extern int audio_ibuffer_read;
|
||||
extern long audio_ibuffer_frame;
|
||||
extern double audio_ibuffer_timecode;
|
||||
extern bool audio_scrub;
|
||||
extern bool recording;
|
||||
extern bool audio_rendering;
|
||||
void clear_audio_ibuffer();
|
||||
|
||||
int current_audio_freq();
|
||||
@@ -54,6 +56,5 @@ int get_buffer_offset_from_frame(double framerate, long frame);
|
||||
bool start_recording();
|
||||
void stop_recording();
|
||||
QString get_recorded_audio_filename();
|
||||
extern bool recording;
|
||||
|
||||
#endif // AUDIO_H
|
||||
|
||||
+8
-2
@@ -376,11 +376,13 @@ void cache_audio_worker(Clip* c, bool scrubbing, QVector<Clip*>& nests) {
|
||||
qint16 new_sample = static_cast<qint16>((frame->data[0][c->frame_sample_index+1] & 0xFF) << 8 | (frame->data[0][c->frame_sample_index] & 0xFF));
|
||||
qint16 mixed_sample = mix_audio_sample(old_sample, new_sample);
|
||||
|
||||
audio_ibuffer[upper_byte_index] = static_cast<quint8>((mixed_sample >> 8) & 0xFF);
|
||||
audio_ibuffer[lower_byte_index] = static_cast<quint8>(mixed_sample & 0xFF);
|
||||
audio_ibuffer[upper_byte_index] = quint8((mixed_sample >> 8) & 0xFF);
|
||||
audio_ibuffer[lower_byte_index] = quint8(mixed_sample & 0xFF);
|
||||
|
||||
c->audio_buffer_write+=2;
|
||||
c->frame_sample_index+=2;
|
||||
|
||||
if (c->audio_reset) break;
|
||||
}
|
||||
|
||||
#ifdef AUDIOWARNINGS
|
||||
@@ -389,6 +391,8 @@ void cache_audio_worker(Clip* c, bool scrubbing, QVector<Clip*>& nests) {
|
||||
|
||||
audio_write_lock.unlock();
|
||||
|
||||
if (c->audio_reset) return;
|
||||
|
||||
if (scrubbing) {
|
||||
if (audio_thread != nullptr) audio_thread->notifyReceiver();
|
||||
}
|
||||
@@ -558,7 +562,9 @@ void reset_cache(Clip* c, long target_frame) {
|
||||
} else {
|
||||
if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||
// clear current queue
|
||||
c->queue_lock.lock();
|
||||
c->queue_clear();
|
||||
c->queue_lock.unlock();
|
||||
|
||||
// seeks to nearest keyframe (target_frame represents internal clip frame)
|
||||
int64_t target_ts = seconds_to_timestamp(c, playhead_to_clip_seconds(c, target_frame));
|
||||
|
||||
@@ -33,9 +33,12 @@ extern "C" {
|
||||
//#define GCF_DEBUG
|
||||
#endif
|
||||
|
||||
bool texture_failed = false;
|
||||
bool rendering = false;
|
||||
|
||||
long refactor_frame_number(long framenumber, double source_frame_rate, double target_frame_rate) {
|
||||
return qRound((double(framenumber)/source_frame_rate)*target_frame_rate);
|
||||
}
|
||||
|
||||
bool clip_uses_cacher(Clip* clip) {
|
||||
return (clip->media == nullptr && clip->track >= 0) || (clip->media != nullptr && clip->media->get_type() == MEDIA_TYPE_FOOTAGE);
|
||||
}
|
||||
@@ -118,7 +121,7 @@ double get_timecode(Clip* c, long playhead) {
|
||||
return ((double)(playhead-c->get_timeline_in_with_transition()+c->get_clip_in_with_transition())/(double)c->sequence->frame_rate);
|
||||
}
|
||||
|
||||
void get_clip_frame(Clip* c, long playhead) {
|
||||
void get_clip_frame(Clip* c, long playhead, bool& texture_failed) {
|
||||
if (c->finished_opening) {
|
||||
const FootageStream* ms = c->media->to_footage()->get_stream_from_file_index(c->track < 0, c->media_stream);
|
||||
|
||||
@@ -154,8 +157,10 @@ void get_clip_frame(Clip* c, long playhead) {
|
||||
#endif
|
||||
closest_frame = i;
|
||||
break;
|
||||
} else if (c->queue.at(i)->pts > c->queue.at(closest_frame)->pts && c->queue.at(i)->pts < target_pts) {
|
||||
closest_frame = i;
|
||||
} else {
|
||||
if (c->queue.at(i)->pts > c->queue.at(closest_frame)->pts && c->queue.at(i)->pts < target_pts) {
|
||||
closest_frame = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-4
@@ -9,9 +9,7 @@ struct ClipCache;
|
||||
struct Sequence;
|
||||
struct AVFrame;
|
||||
|
||||
extern bool texture_failed;
|
||||
extern bool rendering;
|
||||
|
||||
long refactor_frame_number(long framenumber, double source_frame_rate, double target_frame_rate);
|
||||
bool clip_uses_cacher(Clip* clip);
|
||||
void open_clip(Clip* clip, bool multithreaded);
|
||||
void cache_clip(Clip* clip, long playhead, bool reset, bool scrubbing, QVector<Clip *> &nests);
|
||||
@@ -20,7 +18,7 @@ void cache_audio_worker(Clip* c, bool write_A);
|
||||
void cache_video_worker(Clip* c, long playhead);
|
||||
void handle_media(Sequence* sequence, long playhead, bool multithreaded);
|
||||
void reset_cache(Clip* c, long target_frame);
|
||||
void get_clip_frame(Clip* c, long playhead);
|
||||
void get_clip_frame(Clip* c, long playhead, bool &texture_failed);
|
||||
double get_timecode(Clip* c, long playhead);
|
||||
|
||||
long playhead_to_clip_frame(Clip* c, long playhead);
|
||||
|
||||
+1
-1
@@ -537,7 +537,7 @@ int Effect::gizmo_count(){
|
||||
void Effect::refresh() {}
|
||||
|
||||
void Effect::field_changed() {
|
||||
panel_sequence_viewer->viewer_widget->update();
|
||||
panel_sequence_viewer->viewer_widget->frame_update();
|
||||
panel_graph_editor->update_panel();
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -134,7 +134,7 @@ public:
|
||||
Effect* copy(Clip* c);
|
||||
void copy_field_keyframes(Effect *e);
|
||||
|
||||
virtual void load(QXmlStreamReader& stream);
|
||||
virtual void load(QXmlStreamReader& stream);
|
||||
virtual void custom_load(QXmlStreamReader& stream);
|
||||
virtual void save(QXmlStreamWriter& stream);
|
||||
|
||||
|
||||
+11
-8
@@ -31,9 +31,10 @@ EffectRow::EffectRow(Effect *parent, bool save, QGridLayout *uilayout, const QSt
|
||||
|
||||
column_count = 1;
|
||||
|
||||
if (parent_effect->meta != nullptr
|
||||
&& parent_effect->meta->type != EFFECT_TYPE_TRANSITION
|
||||
&& keyframable) {
|
||||
keyframe_nav = nullptr;
|
||||
if (parent_effect->meta != nullptr
|
||||
&& parent_effect->meta->type != EFFECT_TYPE_TRANSITION
|
||||
&& keyframable) {
|
||||
connect(label, SIGNAL(clicked()), this, SLOT(focus_row()));
|
||||
|
||||
keyframe_nav = new KeyframeNavigator();
|
||||
@@ -53,7 +54,9 @@ bool EffectRow::isKeyframing() {
|
||||
void EffectRow::setKeyframing(bool b) {
|
||||
if (parent_effect->meta->type != EFFECT_TYPE_TRANSITION) {
|
||||
keyframing = b;
|
||||
keyframe_nav->enable_keyframes(b);
|
||||
if (keyframe_nav != nullptr) {
|
||||
keyframe_nav->enable_keyframes(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,10 +67,10 @@ void EffectRow::set_keyframe_enabled(bool enabled) {
|
||||
set_keyframe_now(ca);
|
||||
undo_stack.push(ca);
|
||||
} else {
|
||||
if (QMessageBox::question(panel_effect_controls,
|
||||
tr("Disable Keyframes"),
|
||||
tr("Disabling keyframes will delete all current keyframes. Are you sure you want to do this?"),
|
||||
QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) {
|
||||
if (QMessageBox::question(panel_effect_controls,
|
||||
tr("Disable Keyframes"),
|
||||
tr("Disabling keyframes will delete all current keyframes. Are you sure you want to do this?"),
|
||||
QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) {
|
||||
// clear
|
||||
ComboAction* ca = new ComboAction();
|
||||
for (int i=0;i<fieldCount();i++) {
|
||||
|
||||
@@ -7,14 +7,19 @@
|
||||
#include "debug.h"
|
||||
|
||||
ProjectModel::ProjectModel(QObject *parent) : QAbstractItemModel(parent), root_item(nullptr) {
|
||||
root_item = new Media(0);
|
||||
root_item->root = true;
|
||||
make_root();
|
||||
}
|
||||
|
||||
ProjectModel::~ProjectModel() {
|
||||
destroy_root();
|
||||
}
|
||||
|
||||
void ProjectModel::make_root() {
|
||||
root_item = new Media(nullptr);
|
||||
root_item->temp_id = 0;
|
||||
root_item->root = true;
|
||||
}
|
||||
|
||||
void ProjectModel::destroy_root() {
|
||||
if (panel_sequence_viewer != nullptr) panel_sequence_viewer->viewer_widget->delete_function();
|
||||
if (panel_footage_viewer != nullptr) panel_footage_viewer->viewer_widget->delete_function();
|
||||
@@ -27,8 +32,7 @@ void ProjectModel::destroy_root() {
|
||||
void ProjectModel::clear() {
|
||||
beginResetModel();
|
||||
destroy_root();
|
||||
root_item = new Media(0);
|
||||
root_item->root = true;
|
||||
make_root();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
|
||||
@@ -9,9 +9,10 @@ class ProjectModel : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ProjectModel(QObject* parent = 0);
|
||||
ProjectModel(QObject* parent = nullptr);
|
||||
~ProjectModel() override;
|
||||
|
||||
void make_root();
|
||||
void destroy_root();
|
||||
void clear();
|
||||
Media* get_root();
|
||||
|
||||
+43
-35
@@ -14,50 +14,58 @@ KeyframeNavigator::KeyframeNavigator(QWidget *parent) : QWidget(parent) {
|
||||
QSize clock_size(clock_size_val, clock_size_val);
|
||||
QSize button_size(button_size_val, button_size_val);
|
||||
|
||||
key_controls = new QHBoxLayout();
|
||||
key_controls->setSpacing(0);
|
||||
key_controls->setMargin(0);
|
||||
key_controls = new QHBoxLayout();
|
||||
key_controls->setSpacing(0);
|
||||
key_controls->setMargin(0);
|
||||
//key_controls->addStretch();
|
||||
|
||||
setLayout(key_controls);
|
||||
setLayout(key_controls);
|
||||
|
||||
left_key_nav = new QPushButton();
|
||||
left_key_nav->setIcon(QIcon(":/icons/tri-left.png"));
|
||||
left_key_nav->setMaximumSize(button_size);
|
||||
left_key_nav->setIconSize(icon_size);
|
||||
left_key_nav->setVisible(false);
|
||||
key_controls->addWidget(left_key_nav);
|
||||
connect(left_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(goto_previous_key()));
|
||||
left_key_nav = new QPushButton();
|
||||
left_key_nav->setIcon(QIcon(":/icons/tri-left.png"));
|
||||
left_key_nav->setMaximumSize(button_size);
|
||||
left_key_nav->setIconSize(icon_size);
|
||||
left_key_nav->setVisible(false);
|
||||
key_controls->addWidget(left_key_nav);
|
||||
connect(left_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(goto_previous_key()));
|
||||
connect(left_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(clicked()));
|
||||
|
||||
key_addremove = new QPushButton();
|
||||
key_addremove->setIcon(QIcon(":/icons/diamond.png"));
|
||||
key_addremove->setMaximumSize(button_size);
|
||||
key_addremove->setIconSize(QSize(8, 8));
|
||||
key_addremove->setVisible(false);
|
||||
key_controls->addWidget(key_addremove);
|
||||
connect(key_addremove, SIGNAL(clicked(bool)), this, SIGNAL(toggle_key()));
|
||||
key_addremove = new QPushButton();
|
||||
key_addremove->setIcon(QIcon(":/icons/diamond.png"));
|
||||
key_addremove->setMaximumSize(button_size);
|
||||
key_addremove->setIconSize(QSize(8, 8));
|
||||
key_addremove->setVisible(false);
|
||||
key_controls->addWidget(key_addremove);
|
||||
connect(key_addremove, SIGNAL(clicked(bool)), this, SIGNAL(toggle_key()));
|
||||
connect(key_addremove, SIGNAL(clicked(bool)), this, SIGNAL(clicked()));
|
||||
|
||||
right_key_nav = new QPushButton();
|
||||
right_key_nav->setIcon(QIcon(":/icons/tri-right.png"));
|
||||
right_key_nav->setMaximumSize(button_size);
|
||||
right_key_nav->setIconSize(icon_size);
|
||||
right_key_nav->setVisible(false);
|
||||
key_controls->addWidget(right_key_nav);
|
||||
connect(right_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(goto_next_key()));
|
||||
right_key_nav = new QPushButton();
|
||||
right_key_nav->setIcon(QIcon(":/icons/tri-right.png"));
|
||||
right_key_nav->setMaximumSize(button_size);
|
||||
right_key_nav->setIconSize(icon_size);
|
||||
right_key_nav->setVisible(false);
|
||||
key_controls->addWidget(right_key_nav);
|
||||
connect(right_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(goto_next_key()));
|
||||
connect(right_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(clicked()));
|
||||
|
||||
keyframe_enable = new QPushButton(QIcon(":/icons/clock.png"), "");
|
||||
keyframe_enable->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
|
||||
keyframe_enable->setMaximumSize(button_size);
|
||||
keyframe_enable = new QPushButton(QIcon(":/icons/clock.png"), "");
|
||||
keyframe_enable->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
|
||||
keyframe_enable->setMaximumSize(button_size);
|
||||
keyframe_enable->setIconSize(clock_size);
|
||||
keyframe_enable->setCheckable(true);
|
||||
keyframe_enable->setToolTip(tr("Enable Keyframes"));
|
||||
keyframe_enable->setCheckable(true);
|
||||
keyframe_enable->setToolTip(tr("Enable Keyframes"));
|
||||
connect(keyframe_enable, SIGNAL(clicked(bool)), this, SIGNAL(keyframe_enabled_changed(bool)));
|
||||
connect(keyframe_enable, SIGNAL(toggled(bool)), this, SLOT(keyframe_ui_enabled(bool)));
|
||||
connect(keyframe_enable, SIGNAL(toggled(bool)), this, SLOT(keyframe_ui_enabled(bool)));
|
||||
connect(keyframe_enable, SIGNAL(clicked(bool)), this, SIGNAL(clicked()));
|
||||
key_controls->addWidget(keyframe_enable);
|
||||
key_controls->addWidget(keyframe_enable);
|
||||
}
|
||||
|
||||
KeyframeNavigator::~KeyframeNavigator() {
|
||||
delete keyframe_enable;
|
||||
delete right_key_nav;
|
||||
delete key_addremove;
|
||||
delete left_key_nav;
|
||||
delete key_controls;
|
||||
}
|
||||
|
||||
void KeyframeNavigator::enable_keyframes(bool b) {
|
||||
@@ -69,7 +77,7 @@ void KeyframeNavigator::enable_keyframe_toggle(bool b) {
|
||||
}
|
||||
|
||||
void KeyframeNavigator::keyframe_ui_enabled(bool enabled) {
|
||||
left_key_nav->setVisible(enabled);
|
||||
key_addremove->setVisible(enabled);
|
||||
right_key_nav->setVisible(enabled);
|
||||
left_key_nav->setVisible(enabled);
|
||||
key_addremove->setVisible(enabled);
|
||||
right_key_nav->setVisible(enabled);
|
||||
}
|
||||
|
||||
+13
-12
@@ -8,25 +8,26 @@ class QPushButton;
|
||||
|
||||
class KeyframeNavigator : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
KeyframeNavigator(QWidget* parent = 0);
|
||||
void enable_keyframes(bool);
|
||||
KeyframeNavigator(QWidget* parent = 0);
|
||||
~KeyframeNavigator();
|
||||
void enable_keyframes(bool);
|
||||
void enable_keyframe_toggle(bool);
|
||||
signals:
|
||||
void goto_previous_key();
|
||||
void toggle_key();
|
||||
void goto_next_key();
|
||||
void goto_previous_key();
|
||||
void toggle_key();
|
||||
void goto_next_key();
|
||||
void keyframe_enabled_changed(bool);
|
||||
void clicked();
|
||||
private slots:
|
||||
void keyframe_ui_enabled(bool);
|
||||
void keyframe_ui_enabled(bool);
|
||||
private:
|
||||
QHBoxLayout* key_controls;
|
||||
QPushButton* left_key_nav;
|
||||
QPushButton* key_addremove;
|
||||
QPushButton* right_key_nav;
|
||||
QPushButton* keyframe_enable;
|
||||
QHBoxLayout* key_controls;
|
||||
QPushButton* left_key_nav;
|
||||
QPushButton* key_addremove;
|
||||
QPushButton* right_key_nav;
|
||||
QPushButton* keyframe_enable;
|
||||
};
|
||||
|
||||
#endif // KEYFRAMENAVIGATOR_H
|
||||
|
||||
@@ -0,0 +1,503 @@
|
||||
#include "renderfunctions.h"
|
||||
|
||||
#include <QOpenGLFramebufferObject>
|
||||
#include <QApplication>
|
||||
#include <QDesktopWidget>
|
||||
#include <QDebug>
|
||||
|
||||
#include "project/clip.h"
|
||||
#include "project/sequence.h"
|
||||
#include "project/media.h"
|
||||
#include "project/effect.h"
|
||||
#include "project/footage.h"
|
||||
#include "project/transition.h"
|
||||
|
||||
#include "ui/collapsiblewidget.h"
|
||||
|
||||
#include "playback/audio.h"
|
||||
#include "playback/playback.h"
|
||||
|
||||
#include "io/math.h"
|
||||
#include "io/config.h"
|
||||
#include "io/avtogl.h"
|
||||
|
||||
#include "panels/timeline.h"
|
||||
#include "panels/viewer.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
}
|
||||
|
||||
//#define GL_DEFAULT_BLEND glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE)
|
||||
#define GL_DEFAULT_BLEND glBlendFuncSeparate(GL_ONE, GL_ONE, GL_ONE, GL_ONE)
|
||||
|
||||
GLuint draw_clip(QOpenGLContext* ctx, QOpenGLFramebufferObject* fbo, GLuint texture, bool clear) {
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glOrtho(0, 1, 0, 1, -1, 1);
|
||||
|
||||
GLint current_fbo = 0;
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_fbo);
|
||||
|
||||
fbo->bind();
|
||||
|
||||
if (clear) glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
// get current blend mode
|
||||
GLint src_rgb, src_alpha, dst_rgb, dst_alpha;
|
||||
glGetIntegerv(GL_BLEND_SRC_RGB, &src_rgb);
|
||||
glGetIntegerv(GL_BLEND_SRC_ALPHA, &src_alpha);
|
||||
glGetIntegerv(GL_BLEND_DST_RGB, &dst_rgb);
|
||||
glGetIntegerv(GL_BLEND_DST_ALPHA, &dst_alpha);
|
||||
|
||||
ctx->functions()->GL_DEFAULT_BLEND;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glBegin(GL_QUADS);
|
||||
glTexCoord2f(0, 0); // top left
|
||||
glVertex2f(0, 0); // top left
|
||||
glTexCoord2f(1, 0); // top right
|
||||
glVertex2f(1, 0); // top right
|
||||
glTexCoord2f(1, 1); // bottom right
|
||||
glVertex2f(1, 1); // bottom right
|
||||
glTexCoord2f(0, 1); // bottom left
|
||||
glVertex2f(0, 1); // bottom left
|
||||
glEnd();
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
// fbo->release();
|
||||
ctx->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo);
|
||||
|
||||
// restore previous blendFunc
|
||||
ctx->functions()->glBlendFuncSeparate(src_rgb, dst_rgb, src_alpha, dst_alpha);
|
||||
|
||||
//if (default_fbo != nullptr) default_fbo->bind();
|
||||
|
||||
glPopMatrix();
|
||||
return fbo->texture();
|
||||
}
|
||||
|
||||
void process_effect(QOpenGLContext* ctx,
|
||||
Clip* c,
|
||||
Effect* e,
|
||||
double timecode,
|
||||
GLTextureCoords& coords,
|
||||
GLuint& composite_texture,
|
||||
bool& fbo_switcher,
|
||||
bool& texture_failed,
|
||||
int data) {
|
||||
if (e->is_enabled()) {
|
||||
if (e->enable_coords) {
|
||||
e->process_coords(timecode, coords, data);
|
||||
}
|
||||
if ((e->enable_shader && shaders_are_enabled) || e->enable_superimpose) {
|
||||
e->startEffect();
|
||||
if ((e->enable_shader && shaders_are_enabled) && e->is_glsl_linked()) {
|
||||
e->process_shader(timecode, coords);
|
||||
composite_texture = draw_clip(ctx, c->fbo[fbo_switcher], composite_texture, true);
|
||||
fbo_switcher = !fbo_switcher;
|
||||
}
|
||||
if (e->enable_superimpose) {
|
||||
GLuint superimpose_texture = e->process_superimpose(timecode);
|
||||
if (superimpose_texture == 0) {
|
||||
qWarning() << "Superimpose texture was nullptr, retrying...";
|
||||
texture_failed = true;
|
||||
} else {
|
||||
composite_texture = draw_clip(ctx, c->fbo[!fbo_switcher], superimpose_texture, false);
|
||||
}
|
||||
}
|
||||
e->endEffect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GLuint compose_sequence(Viewer* viewer,
|
||||
QOpenGLContext* ctx,
|
||||
Sequence* seq,
|
||||
QVector<Clip*>& nests,
|
||||
bool video,
|
||||
bool render_audio,
|
||||
Effect** gizmos,
|
||||
bool& texture_failed,
|
||||
bool rendering) {
|
||||
GLint current_fbo = 0;
|
||||
if (video) {
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_fbo);
|
||||
}
|
||||
|
||||
Sequence* s = seq;
|
||||
long playhead = s->playhead;
|
||||
|
||||
if (!nests.isEmpty()) {
|
||||
for (int i=0;i<nests.size();i++) {
|
||||
s = nests.at(i)->media->to_sequence();
|
||||
playhead += nests.at(i)->clip_in - nests.at(i)->get_timeline_in_with_transition();
|
||||
playhead = refactor_frame_number(playhead, nests.at(i)->sequence->frame_rate, s->frame_rate);
|
||||
}
|
||||
|
||||
if (video && nests.last()->fbo != nullptr) {
|
||||
nests.last()->fbo[0]->bind();
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
// nests.last()->fbo[0]->release();
|
||||
ctx->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo);
|
||||
}
|
||||
}
|
||||
|
||||
int audio_track_count = 0;
|
||||
|
||||
QVector<Clip*> current_clips;
|
||||
|
||||
for (int i=0;i<s->clips.size();i++) {
|
||||
Clip* c = s->clips.at(i);
|
||||
|
||||
// if clip starts within one second and/or hasn't finished yet
|
||||
if (c != nullptr) {
|
||||
// if (!(!nests.isEmpty() && !same_sign(c->track, nests.last()->track))) {
|
||||
if ((c->track < 0) == video) {
|
||||
bool clip_is_active = false;
|
||||
|
||||
if (c->media != nullptr && c->media->get_type() == MEDIA_TYPE_FOOTAGE) {
|
||||
Footage* m = c->media->to_footage();
|
||||
if (!m->invalid && !(c->track >= 0 && !is_audio_device_set())) {
|
||||
if (m->ready) {
|
||||
const FootageStream* ms = m->get_stream_from_file_index(c->track < 0, c->media_stream);
|
||||
if (ms != nullptr && is_clip_active(c, playhead)) {
|
||||
// if thread is already working, we don't want to touch this,
|
||||
// but we also don't want to hang the UI thread
|
||||
if (!c->open) {
|
||||
open_clip(c, !rendering);
|
||||
}
|
||||
clip_is_active = true;
|
||||
if (c->track >= 0) audio_track_count++;
|
||||
} else if (c->open) {
|
||||
close_clip(c, false);
|
||||
}
|
||||
} else {
|
||||
//qWarning() << "Media '" + m->name + "' was not ready, retrying...";
|
||||
texture_failed = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (is_clip_active(c, playhead)) {
|
||||
if (!c->open) open_clip(c, !rendering);
|
||||
clip_is_active = true;
|
||||
} else if (c->open) {
|
||||
close_clip(c, false);
|
||||
}
|
||||
}
|
||||
if (clip_is_active) {
|
||||
bool added = false;
|
||||
for (int j=0;j<current_clips.size();j++) {
|
||||
if (current_clips.at(j)->track < c->track) {
|
||||
current_clips.insert(j, c);
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!added) {
|
||||
current_clips.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int half_width = s->width/2;
|
||||
int half_height = s->height/2;
|
||||
|
||||
if (video) {
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glOrtho(-half_width, half_width, -half_height, half_height, -1, 10);
|
||||
}
|
||||
|
||||
for (int i=0;i<current_clips.size();i++) {
|
||||
Clip* c = current_clips.at(i);
|
||||
|
||||
if (c->media != nullptr && c->media->get_type() == MEDIA_TYPE_FOOTAGE && !c->finished_opening) {
|
||||
qWarning() << "Tried to display clip" << i << "but it's closed";
|
||||
texture_failed = true;
|
||||
} else {
|
||||
if (c->track < 0) {
|
||||
ctx->functions()->GL_DEFAULT_BLEND;
|
||||
glColor4f(1.0, 1.0, 1.0, 1.0);
|
||||
|
||||
GLuint textureID = 0;
|
||||
int video_width = c->getWidth();
|
||||
int video_height = c->getHeight();
|
||||
|
||||
if (c->media != nullptr) {
|
||||
switch (c->media->get_type()) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
// set up opengl texture
|
||||
if (c->texture == nullptr) {
|
||||
c->texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
|
||||
c->texture->setSize(c->stream->codecpar->width, c->stream->codecpar->height);
|
||||
c->texture->setFormat(get_gl_tex_fmt_from_av(c->pix_fmt));
|
||||
c->texture->setMipLevels(c->texture->maximumMipLevels());
|
||||
c->texture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear);
|
||||
c->texture->allocateStorage(get_gl_pix_fmt_from_av(c->pix_fmt), QOpenGLTexture::UInt8);
|
||||
}
|
||||
get_clip_frame(c, playhead, texture_failed);
|
||||
textureID = c->texture->textureId();
|
||||
break;
|
||||
case MEDIA_TYPE_SEQUENCE:
|
||||
textureID = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (textureID == 0 && c->media != nullptr) {
|
||||
qWarning() << "Texture hasn't been created yet";
|
||||
texture_failed = true;
|
||||
} else if (playhead >= c->get_timeline_in_with_transition()) {
|
||||
glPushMatrix();
|
||||
|
||||
// start preparing cache
|
||||
if (c->fbo == nullptr) {
|
||||
c->fbo = new QOpenGLFramebufferObject* [2];
|
||||
c->fbo[0] = new QOpenGLFramebufferObject(video_width, video_height);
|
||||
c->fbo[1] = new QOpenGLFramebufferObject(video_width, video_height);
|
||||
}
|
||||
|
||||
// clear fbos
|
||||
/*c->fbo[0]->bind();
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
c->fbo[0]->release();
|
||||
c->fbo[1]->bind();
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
c->fbo[1]->release();*/
|
||||
|
||||
bool fbo_switcher = false;
|
||||
|
||||
glViewport(0, 0, video_width, video_height);
|
||||
|
||||
GLuint composite_texture;
|
||||
|
||||
if (c->media == nullptr) {
|
||||
c->fbo[fbo_switcher]->bind();
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
// c->fbo[fbo_switcher]->release();
|
||||
ctx->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo);
|
||||
composite_texture = c->fbo[fbo_switcher]->texture();
|
||||
} else {
|
||||
// for nested sequences
|
||||
if (c->media->get_type()== MEDIA_TYPE_SEQUENCE) {
|
||||
nests.append(c);
|
||||
textureID = compose_sequence(viewer, ctx, seq, nests, video, render_audio, gizmos, texture_failed, rendering);
|
||||
nests.removeLast();
|
||||
fbo_switcher = true;
|
||||
}
|
||||
|
||||
composite_texture = draw_clip(ctx, c->fbo[fbo_switcher], textureID, true);
|
||||
}
|
||||
|
||||
fbo_switcher = !fbo_switcher;
|
||||
|
||||
// set up default coords
|
||||
GLTextureCoords coords;
|
||||
coords.grid_size = 1;
|
||||
coords.vertexTopLeftX = coords.vertexBottomLeftX = -video_width/2;
|
||||
coords.vertexTopLeftY = coords.vertexTopRightY = -video_height/2;
|
||||
coords.vertexTopRightX = coords.vertexBottomRightX = video_width/2;
|
||||
coords.vertexBottomLeftY = coords.vertexBottomRightY = video_height/2;
|
||||
coords.vertexBottomLeftZ = coords.vertexBottomRightZ = coords.vertexTopLeftZ = coords.vertexTopRightZ = 1;
|
||||
coords.textureTopLeftY = coords.textureTopRightY = coords.textureTopLeftX = coords.textureBottomLeftX = 0.0;
|
||||
coords.textureBottomLeftY = coords.textureBottomRightY = coords.textureTopRightX = coords.textureBottomRightX = 1.0;
|
||||
coords.textureTopLeftQ = coords.textureTopRightQ = coords.textureTopLeftQ = coords.textureBottomLeftQ = 1;
|
||||
|
||||
// set up autoscale
|
||||
if (c->autoscale && (video_width != s->width && video_height != s->height)) {
|
||||
float width_multiplier = float(s->width) / float(video_width);
|
||||
float height_multiplier = float(s->height) / float(video_height);
|
||||
float scale_multiplier = qMin(width_multiplier, height_multiplier);
|
||||
glScalef(scale_multiplier, scale_multiplier, 1);
|
||||
}
|
||||
|
||||
// EFFECT CODE START
|
||||
double timecode = get_timecode(c, playhead);
|
||||
|
||||
Effect* first_gizmo_effect = nullptr;
|
||||
Effect* selected_effect = nullptr;
|
||||
|
||||
for (int j=0;j<c->effects.size();j++) {
|
||||
Effect* e = c->effects.at(j);
|
||||
process_effect(ctx, c, e, timecode, coords, composite_texture, fbo_switcher, texture_failed, TA_NO_TRANSITION);
|
||||
|
||||
if (e->are_gizmos_enabled()) {
|
||||
if (first_gizmo_effect == nullptr) first_gizmo_effect = e;
|
||||
if (e->container->selected) selected_effect = e;
|
||||
}
|
||||
}
|
||||
|
||||
if (selected_effect != nullptr) {
|
||||
(*gizmos) = selected_effect;
|
||||
} else if (is_clip_selected(c, true)) {
|
||||
(*gizmos) = first_gizmo_effect;
|
||||
}
|
||||
|
||||
if (c->get_opening_transition() != nullptr) {
|
||||
int transition_progress = playhead - c->get_timeline_in_with_transition();
|
||||
if (transition_progress < c->get_opening_transition()->get_length()) {
|
||||
process_effect(ctx, c, c->get_opening_transition(), (double)transition_progress/(double)c->get_opening_transition()->get_length(), coords, composite_texture, fbo_switcher, texture_failed, TA_OPENING_TRANSITION);
|
||||
}
|
||||
}
|
||||
|
||||
if (c->get_closing_transition() != nullptr) {
|
||||
int transition_progress = playhead - (c->get_timeline_out_with_transition() - c->get_closing_transition()->get_length());
|
||||
if (transition_progress >= 0 && transition_progress < c->get_closing_transition()->get_length()) {
|
||||
process_effect(ctx, c, c->get_closing_transition(), (double)transition_progress/(double)c->get_closing_transition()->get_length(), coords, composite_texture, fbo_switcher, texture_failed, TA_CLOSING_TRANSITION);
|
||||
}
|
||||
}
|
||||
// EFFECT CODE END
|
||||
|
||||
/*if (!nests.isEmpty()) {
|
||||
nests.last()->fbo[0]->bind();
|
||||
glViewport(0, 0, s->width, s->height);
|
||||
} else if (rendering) {
|
||||
glViewport(0, 0, s->width, s->height);
|
||||
} else {
|
||||
int widget_width = width();
|
||||
int widget_height = height();
|
||||
|
||||
widget_width *= QApplication::desktop()->devicePixelRatio();
|
||||
widget_height *= QApplication::desktop()->devicePixelRatio();
|
||||
|
||||
glViewport(0, 0, widget_width, widget_height);
|
||||
}*/
|
||||
if (!nests.isEmpty()) {
|
||||
nests.last()->fbo[0]->bind();
|
||||
}
|
||||
glViewport(0, 0, s->width, s->height);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, composite_texture);
|
||||
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
|
||||
if (coords.grid_size <= 1) {
|
||||
float z = 0.0f;
|
||||
|
||||
glTexCoord2f(coords.textureTopLeftX, coords.textureTopLeftY); // top left
|
||||
glVertex3f(coords.vertexTopLeftX, coords.vertexTopLeftY, z); // top left
|
||||
glTexCoord2f(coords.textureTopRightX, coords.textureTopRightY); // top right
|
||||
glVertex3f(coords.vertexTopRightX, coords.vertexTopRightY, z); // top right
|
||||
glTexCoord2f(coords.textureBottomRightX, coords.textureBottomRightY); // bottom right
|
||||
glVertex3f(coords.vertexBottomRightX, coords.vertexBottomRightY, z); // bottom right
|
||||
glTexCoord2f(coords.textureBottomLeftX, coords.textureBottomLeftY); // bottom left
|
||||
glVertex3f(coords.vertexBottomLeftX, coords.vertexBottomLeftY, z); // bottom left
|
||||
} else {
|
||||
float rows = coords.grid_size;
|
||||
float cols = coords.grid_size;
|
||||
|
||||
for (float k=0;k<rows;k++) {
|
||||
float row_prog = k/rows;
|
||||
float next_row_prog = (k+1)/rows;
|
||||
for (float j=0;j<cols;j++) {
|
||||
float col_prog = j/cols;
|
||||
float next_col_prog = (j+1)/cols;
|
||||
|
||||
float vertexTLX = float_lerp(coords.vertexTopLeftX, coords.vertexBottomLeftX, row_prog);
|
||||
float vertexTRX = float_lerp(coords.vertexTopRightX, coords.vertexBottomRightX, row_prog);
|
||||
float vertexBLX = float_lerp(coords.vertexTopLeftX, coords.vertexBottomLeftX, next_row_prog);
|
||||
float vertexBRX = float_lerp(coords.vertexTopRightX, coords.vertexBottomRightX, next_row_prog);
|
||||
|
||||
float vertexTLY = float_lerp(coords.vertexTopLeftY, coords.vertexTopRightY, col_prog);
|
||||
float vertexTRY = float_lerp(coords.vertexTopLeftY, coords.vertexTopRightY, next_col_prog);
|
||||
float vertexBLY = float_lerp(coords.vertexBottomLeftY, coords.vertexBottomRightY, col_prog);
|
||||
float vertexBRY = float_lerp(coords.vertexBottomLeftY, coords.vertexBottomRightY, next_col_prog);
|
||||
|
||||
glTexCoord2f(float_lerp(coords.textureTopLeftX, coords.textureTopRightX, col_prog), float_lerp(coords.textureTopLeftY, coords.textureBottomLeftY, row_prog)); // top left
|
||||
glVertex2f(float_lerp(vertexTLX, vertexTRX, col_prog), float_lerp(vertexTLY, vertexBLY, row_prog)); // top left
|
||||
glTexCoord2f(float_lerp(coords.textureTopLeftX, coords.textureTopRightX, next_col_prog), float_lerp(coords.textureTopRightY, coords.textureBottomRightY, row_prog)); // top right
|
||||
glVertex2f(float_lerp(vertexTLX, vertexTRX, next_col_prog), float_lerp(vertexTRY, vertexBRY, row_prog)); // top right
|
||||
glTexCoord2f(float_lerp(coords.textureBottomLeftX, coords.textureBottomRightX, next_col_prog), float_lerp(coords.textureTopRightY, coords.textureBottomRightY, next_row_prog)); // bottom right
|
||||
glVertex2f(float_lerp(vertexBLX, vertexBRX, next_col_prog), float_lerp(vertexTRY, vertexBRY, next_row_prog)); // bottom right
|
||||
glTexCoord2f(float_lerp(coords.textureBottomLeftX, coords.textureBottomRightX, col_prog), float_lerp(coords.textureTopLeftY, coords.textureBottomLeftY, next_row_prog)); // bottom left
|
||||
glVertex2f(float_lerp(vertexBLX, vertexBRX, col_prog), float_lerp(vertexTLY, vertexBLY, next_row_prog)); // bottom left
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glEnd();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0); // unbind texture
|
||||
|
||||
// prepare gizmos
|
||||
if ((*gizmos) != nullptr
|
||||
&& nests.isEmpty()
|
||||
&& ((*gizmos) == first_gizmo_effect
|
||||
|| (*gizmos) == selected_effect)) {
|
||||
(*gizmos)->gizmo_draw(timecode, coords); // set correct gizmo coords
|
||||
(*gizmos)->gizmo_world_to_screen(); // convert gizmo coords to screen coords
|
||||
}
|
||||
|
||||
if (!nests.isEmpty()) {
|
||||
ctx->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo);
|
||||
}
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
/*GLfloat motion_blur_frac = (GLfloat) motion_blur_prog / (GLfloat) motion_blur_lim;
|
||||
if (motion_blur_prog == 0) {
|
||||
glAccum(GL_LOAD, motion_blur_frac);
|
||||
} else {
|
||||
glAccum(GL_ACCUM, motion_blur_frac);
|
||||
}
|
||||
motion_blur_prog++;*/
|
||||
}
|
||||
} else {
|
||||
if (render_audio || (config.enable_audio_scrubbing && audio_scrub)) {
|
||||
if (c->media != nullptr && c->media->get_type() == MEDIA_TYPE_SEQUENCE) {
|
||||
nests.append(c);
|
||||
compose_sequence(viewer, ctx, seq, nests, video, render_audio, gizmos, texture_failed, rendering);
|
||||
nests.removeLast();
|
||||
} else {
|
||||
if (c->lock.tryLock()) {
|
||||
// clip is not caching, start caching audio
|
||||
cache_clip(c, playhead, c->audio_reset, !render_audio, nests);
|
||||
c->lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// visually update all the keyframe values
|
||||
if (c->sequence == seq) { // only if you can currently see them
|
||||
double ts = (playhead - c->get_timeline_in_with_transition() + c->get_clip_in_with_transition())/s->frame_rate;
|
||||
for (int i=0;i<c->effects.size();i++) {
|
||||
Effect* e = c->effects.at(i);
|
||||
for (int j=0;j<e->row_count();j++) {
|
||||
EffectRow* r = e->row(j);
|
||||
for (int k=0;k<r->fieldCount();k++) {
|
||||
r->field(k)->validate_keyframe_data(ts);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (audio_track_count == 0 && viewer != nullptr) {
|
||||
viewer->play_wake();
|
||||
}
|
||||
|
||||
if (video) {
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
if (!nests.isEmpty() && nests.last()->fbo != nullptr) {
|
||||
// returns nested clip's texture
|
||||
return nests.last()->fbo[0]->texture();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void compose_audio(Viewer* viewer, Sequence* seq, bool render_audio) {
|
||||
QVector<Clip*> nests;
|
||||
bool texture_failed;
|
||||
compose_sequence(viewer, nullptr, seq, nests, false, render_audio, nullptr, texture_failed, audio_rendering);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef RENDERFUNCTIONS_H
|
||||
#define RENDERFUNCTIONS_H
|
||||
|
||||
#include <QOpenGLContext>
|
||||
#include <QVector>
|
||||
|
||||
class Effect;
|
||||
class Viewer;
|
||||
struct Sequence;
|
||||
struct Clip;
|
||||
|
||||
GLuint compose_sequence(Viewer* viewer,
|
||||
QOpenGLContext* ctx,
|
||||
Sequence* seq,
|
||||
QVector<Clip*>& nests,
|
||||
bool video,
|
||||
bool render_audio,
|
||||
Effect **gizmos,
|
||||
bool &texture_failed,
|
||||
bool rendering);
|
||||
|
||||
void compose_audio(Viewer* viewer, Sequence* seq, bool render_audio);
|
||||
|
||||
void viewport_render();
|
||||
|
||||
#endif // RENDERFUNCTIONS_H
|
||||
@@ -0,0 +1,199 @@
|
||||
#include "renderthread.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QImage>
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QDebug>
|
||||
|
||||
#include "ui/renderfunctions.h"
|
||||
#include "playback/playback.h"
|
||||
#include "project/sequence.h"
|
||||
|
||||
RenderThread::RenderThread() :
|
||||
frameBuffer(0),
|
||||
texColorBuffer(0),
|
||||
gizmos(nullptr),
|
||||
share_ctx(nullptr),
|
||||
ctx(nullptr),
|
||||
seq(nullptr),
|
||||
tex_width(-1),
|
||||
tex_height(-1),
|
||||
queued(false),
|
||||
texture_failed(false),
|
||||
running(true)
|
||||
{
|
||||
surface.create();
|
||||
}
|
||||
|
||||
RenderThread::~RenderThread() {
|
||||
surface.destroy();
|
||||
}
|
||||
|
||||
void RenderThread::run() {
|
||||
mutex.lock();
|
||||
|
||||
while (running) {
|
||||
if (!queued) {
|
||||
waitCond.wait(&mutex);
|
||||
}
|
||||
if (!running) {
|
||||
break;
|
||||
}
|
||||
queued = false;
|
||||
|
||||
|
||||
if (share_ctx != nullptr) {
|
||||
if (ctx != nullptr) {
|
||||
ctx->makeCurrent(&surface);
|
||||
|
||||
// gen fbo
|
||||
if (frameBuffer == 0) {
|
||||
delete_fbo();
|
||||
ctx->functions()->glGenFramebuffers(1, &frameBuffer);
|
||||
}
|
||||
|
||||
// bind
|
||||
ctx->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBuffer);
|
||||
|
||||
// gen texture
|
||||
if (texColorBuffer == 0 || tex_width != seq->width || tex_height != seq->height) {
|
||||
delete_texture();
|
||||
glGenTextures(1, &texColorBuffer);
|
||||
glBindTexture(GL_TEXTURE_2D, texColorBuffer);
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_2D, 0, GL_RGB, seq->width, seq->height, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr
|
||||
);
|
||||
tex_width = seq->width;
|
||||
tex_height = seq->height;
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
ctx->functions()->glFramebufferTexture2D(
|
||||
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texColorBuffer, 0
|
||||
);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
// draw
|
||||
paint();
|
||||
|
||||
// flush changes
|
||||
// glFlush();
|
||||
glFinish();
|
||||
|
||||
// release
|
||||
ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
emit ready();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete_ctx();
|
||||
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
void RenderThread::paint() {
|
||||
glLoadIdentity();
|
||||
|
||||
texture_failed = false;
|
||||
|
||||
glClearColor(0, 0, 0, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
glClearColor(0, 0, 0, 0);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glEnable(GL_BLEND);
|
||||
glEnable(GL_DEPTH);
|
||||
|
||||
gizmos = nullptr;
|
||||
QVector<Clip*> nests;
|
||||
compose_sequence(nullptr, ctx, seq, nests, true, false, &gizmos, texture_failed, false);
|
||||
|
||||
if (!save_fn.isEmpty()) {
|
||||
if (texture_failed) {
|
||||
// texture failed, try again
|
||||
queued = true;
|
||||
} else {
|
||||
ctx->functions()->glBindFramebuffer(GL_READ_FRAMEBUFFER, frameBuffer);
|
||||
QImage img(tex_width, tex_height, QImage::Format_RGBA8888);
|
||||
glReadPixels(0, 0, tex_width, tex_height, GL_RGBA, GL_UNSIGNED_BYTE, img.bits());
|
||||
img.save(save_fn);
|
||||
ctx->functions()->glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
save_fn = "";
|
||||
}
|
||||
}
|
||||
|
||||
if (pixel_buffer != nullptr) {
|
||||
ctx->functions()->glBindFramebuffer(GL_READ_FRAMEBUFFER, frameBuffer);
|
||||
glReadPixels(0, 0, tex_width, tex_height, GL_RGBA, GL_UNSIGNED_BYTE, pixel_buffer);
|
||||
ctx->functions()->glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
pixel_buffer = nullptr;
|
||||
}
|
||||
|
||||
glDisable(GL_DEPTH);
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
void RenderThread::start_render(QOpenGLContext *share, Sequence *s, const QString& save, GLvoid* pixels, int idivider) {
|
||||
seq = s;
|
||||
|
||||
// stall any dependent actions
|
||||
texture_failed = true;
|
||||
|
||||
if (share != nullptr && (ctx == nullptr || ctx->shareContext() != share_ctx)) {
|
||||
share_ctx = share;
|
||||
delete_ctx();
|
||||
ctx = new QOpenGLContext();
|
||||
ctx->setFormat(share_ctx->format());
|
||||
ctx->setShareContext(share_ctx);
|
||||
ctx->create();
|
||||
ctx->moveToThread(this);
|
||||
}
|
||||
|
||||
save_fn = save;
|
||||
pixel_buffer = pixels;
|
||||
|
||||
queued = true;
|
||||
waitCond.wakeAll();
|
||||
}
|
||||
|
||||
bool RenderThread::did_texture_fail() {
|
||||
return texture_failed;
|
||||
}
|
||||
|
||||
void RenderThread::cancel() {
|
||||
running = false;
|
||||
waitCond.wakeAll();
|
||||
wait();
|
||||
}
|
||||
|
||||
void RenderThread::delete_texture() {
|
||||
if (texColorBuffer > 0) {
|
||||
ctx->functions()->glFramebufferTexture2D(
|
||||
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0
|
||||
);
|
||||
glDeleteTextures(1, &texColorBuffer);
|
||||
}
|
||||
texColorBuffer = 0;
|
||||
}
|
||||
|
||||
void RenderThread::delete_fbo() {
|
||||
if (frameBuffer > 0) {
|
||||
ctx->functions()->glDeleteFramebuffers(1, &frameBuffer);
|
||||
}
|
||||
frameBuffer = 0;
|
||||
}
|
||||
|
||||
void RenderThread::delete_ctx() {
|
||||
if (ctx != nullptr) {
|
||||
delete_texture();
|
||||
delete_fbo();
|
||||
ctx->doneCurrent();
|
||||
delete ctx;
|
||||
}
|
||||
ctx = nullptr;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#ifndef RENDERTHREAD_H
|
||||
#define RENDERTHREAD_H
|
||||
|
||||
#include <QThread>
|
||||
#include <QMutex>
|
||||
#include <QWaitCondition>
|
||||
#include <QOffscreenSurface>
|
||||
#include <QOpenGLContext>
|
||||
#include <QOpenGLFramebufferObject>
|
||||
|
||||
struct Sequence;
|
||||
class Effect;
|
||||
|
||||
class RenderThread : public QThread {
|
||||
Q_OBJECT
|
||||
public:
|
||||
RenderThread();
|
||||
~RenderThread();
|
||||
void run();
|
||||
QMutex mutex;
|
||||
GLuint frameBuffer;
|
||||
GLuint texColorBuffer;
|
||||
Effect* gizmos;
|
||||
void paint();
|
||||
void start_render(QOpenGLContext* share, Sequence* s, const QString &save = nullptr, GLvoid *pixels = nullptr, int idivider = 0);
|
||||
bool did_texture_fail();
|
||||
void cancel();
|
||||
|
||||
public slots:
|
||||
// cleanup functions
|
||||
void delete_ctx();
|
||||
signals:
|
||||
void ready();
|
||||
private:
|
||||
// cleanup functions
|
||||
void delete_texture();
|
||||
void delete_fbo();
|
||||
|
||||
QWaitCondition waitCond;
|
||||
QOffscreenSurface surface;
|
||||
QOpenGLContext* share_ctx;
|
||||
QOpenGLContext* ctx;
|
||||
Sequence* seq;
|
||||
int divider;
|
||||
int tex_width;
|
||||
int tex_height;
|
||||
bool queued;
|
||||
bool texture_failed;
|
||||
bool running;
|
||||
QString save_fn;
|
||||
GLvoid *pixel_buffer;
|
||||
};
|
||||
|
||||
#endif // RENDERTHREAD_H
|
||||
@@ -98,7 +98,7 @@ void TimelineWidget::show_context_menu(const QPoint& pos) {
|
||||
QVector<Clip*> selected_clips;
|
||||
for (int i=0;i<sequence->clips.size();i++) {
|
||||
Clip* c = sequence->clips.at(i);
|
||||
if (c != nullptr && panel_timeline->is_clip_selected(c, true)) {
|
||||
if (c != nullptr && is_clip_selected(c, true)) {
|
||||
selected_clips.append(c);
|
||||
}
|
||||
}
|
||||
@@ -204,7 +204,7 @@ void TimelineWidget::toggle_autoscale() {
|
||||
SetAutoscaleAction* action = new SetAutoscaleAction();
|
||||
for (int i=0;i<sequence->clips.size();i++) {
|
||||
Clip* c = sequence->clips.at(i);
|
||||
if (c != nullptr && panel_timeline->is_clip_selected(c, true)) {
|
||||
if (c != nullptr && is_clip_selected(c, true)) {
|
||||
action->clips.append(c);
|
||||
}
|
||||
}
|
||||
@@ -237,7 +237,7 @@ void TimelineWidget::rename_clip() {
|
||||
QVector<Clip*> selected_clips;
|
||||
for (int i=0;i<sequence->clips.size();i++) {
|
||||
Clip* c = sequence->clips.at(i);
|
||||
if (c != nullptr && panel_timeline->is_clip_selected(c, true)) {
|
||||
if (c != nullptr && is_clip_selected(c, true)) {
|
||||
selected_clips.append(c);
|
||||
}
|
||||
}
|
||||
@@ -629,7 +629,7 @@ void TimelineWidget::mousePressEvent(QMouseEvent *event) {
|
||||
if (clip_index >= 0) {
|
||||
Clip* clip = sequence->clips.at(clip_index);
|
||||
if (clip != nullptr) {
|
||||
if (panel_timeline->is_clip_selected(clip, true)) {
|
||||
if (is_clip_selected(clip, true)) {
|
||||
if (shift) {
|
||||
panel_timeline->deselect_area(clip->timeline_in, clip->timeline_out, clip->track);
|
||||
|
||||
@@ -695,7 +695,7 @@ void TimelineWidget::mousePressEvent(QMouseEvent *event) {
|
||||
if (!alt && panel_timeline->transition_select == TA_NO_TRANSITION) {
|
||||
for (int i=0;i<clip->linked.size();i++) {
|
||||
Clip* link = sequence->clips.at(clip->linked.at(i));
|
||||
if (!panel_timeline->is_clip_selected(link, true)) {
|
||||
if (!is_clip_selected(link, true)) {
|
||||
Selection ss;
|
||||
ss.in = link->timeline_in;
|
||||
ss.out = link->timeline_out;
|
||||
@@ -1696,7 +1696,7 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) {
|
||||
Ghost g;
|
||||
g.transition = nullptr;
|
||||
|
||||
bool add = panel_timeline->is_clip_selected(c, true);
|
||||
bool add = is_clip_selected(c, true);
|
||||
|
||||
// if a whole clip is not selected, maybe just a transition is
|
||||
if (panel_timeline->tool == TIMELINE_TOOL_POINTER && (c->get_opening_transition() != nullptr || c->get_closing_transition() != nullptr)) {
|
||||
@@ -1838,7 +1838,7 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) {
|
||||
|
||||
for (int i=0;i<sequence->clips.size();i++) {
|
||||
Clip* c = sequence->clips.at(i);
|
||||
if (c != nullptr && !panel_timeline->is_clip_selected(c, true)) {
|
||||
if (c != nullptr && !is_clip_selected(c, true)) {
|
||||
bool clip_is_post = (c->timeline_in >= axis);
|
||||
|
||||
// see if this a clip on this track is already in the list, and if it's closer
|
||||
|
||||
+55
-54
@@ -12,85 +12,86 @@
|
||||
|
||||
// enforces aspect ratio
|
||||
ViewerContainer::ViewerContainer(QWidget *parent) :
|
||||
QScrollArea(parent),
|
||||
fit(true),
|
||||
child(nullptr)
|
||||
QScrollArea(parent),
|
||||
fit(true),
|
||||
child(nullptr)
|
||||
{
|
||||
setFrameShadow(QFrame::Plain);
|
||||
setFrameShape(QFrame::NoFrame);
|
||||
setFrameShadow(QFrame::Plain);
|
||||
setFrameShape(QFrame::NoFrame);
|
||||
|
||||
area = new QWidget(this);
|
||||
area->move(0, 0);
|
||||
setWidget(area);
|
||||
area = new QWidget(this);
|
||||
area->move(0, 0);
|
||||
setWidget(area);
|
||||
|
||||
child = new ViewerWidget(area);
|
||||
child->container = this;
|
||||
child = new ViewerWidget(area);
|
||||
child->container = this;
|
||||
}
|
||||
|
||||
ViewerContainer::~ViewerContainer() {
|
||||
delete area;
|
||||
delete child;
|
||||
delete area;
|
||||
}
|
||||
|
||||
void ViewerContainer::dragScrollPress(const QPoint &p) {
|
||||
drag_start_x = p.x();
|
||||
drag_start_y = p.y();
|
||||
horiz_start = horizontalScrollBar()->value();
|
||||
vert_start = verticalScrollBar()->value();
|
||||
drag_start_x = p.x();
|
||||
drag_start_y = p.y();
|
||||
horiz_start = horizontalScrollBar()->value();
|
||||
vert_start = verticalScrollBar()->value();
|
||||
}
|
||||
|
||||
void ViewerContainer::dragScrollMove(const QPoint &p) {
|
||||
int true_x = p.x() + (horiz_start - horizontalScrollBar()->value());
|
||||
int true_y = p.y() + (vert_start - verticalScrollBar()->value());
|
||||
int true_x = p.x() + (horiz_start - horizontalScrollBar()->value());
|
||||
int true_y = p.y() + (vert_start - verticalScrollBar()->value());
|
||||
|
||||
horizontalScrollBar()->setValue(horizontalScrollBar()->value() + (drag_start_x - true_x));
|
||||
verticalScrollBar()->setValue(verticalScrollBar()->value() + (drag_start_y - true_y));
|
||||
horizontalScrollBar()->setValue(horizontalScrollBar()->value() + (drag_start_x - true_x));
|
||||
verticalScrollBar()->setValue(verticalScrollBar()->value() + (drag_start_y - true_y));
|
||||
|
||||
drag_start_x = true_x;
|
||||
drag_start_y = true_y;
|
||||
drag_start_x = true_x;
|
||||
drag_start_y = true_y;
|
||||
}
|
||||
|
||||
void ViewerContainer::adjust() {
|
||||
if (viewer->seq != nullptr) {
|
||||
if (child->waveform) {
|
||||
child->move(0, 0);
|
||||
child->resize(size());
|
||||
} else if (fit) {
|
||||
double aspect_ratio = double(viewer->seq->width)/double(viewer->seq->height);
|
||||
if (viewer->seq != nullptr) {
|
||||
if (child->waveform) {
|
||||
child->move(0, 0);
|
||||
child->resize(size());
|
||||
} else if (fit) {
|
||||
double aspect_ratio = double(viewer->seq->width)/double(viewer->seq->height);
|
||||
|
||||
int widget_x = 0;
|
||||
int widget_y = 0;
|
||||
int widget_width = width();
|
||||
int widget_height = height();
|
||||
float widget_ar = (float) widget_width /(float) widget_height;
|
||||
int widget_x = 0;
|
||||
int widget_y = 0;
|
||||
int widget_width = width();
|
||||
int widget_height = height();
|
||||
double widget_ar = double(widget_width) / double(widget_height);
|
||||
|
||||
bool widget_is_wider_than_sequence = widget_ar > aspect_ratio;
|
||||
bool widget_is_wider_than_sequence = widget_ar > aspect_ratio;
|
||||
|
||||
if (widget_is_wider_than_sequence) {
|
||||
widget_width = widget_height * aspect_ratio;
|
||||
widget_x = (width() / 2) - (widget_width / 2);
|
||||
} else {
|
||||
widget_height = widget_width / aspect_ratio;
|
||||
widget_y = (height() / 2) - (widget_height / 2);
|
||||
}
|
||||
if (widget_is_wider_than_sequence) {
|
||||
widget_width = widget_height * aspect_ratio;
|
||||
widget_x = (width() / 2) - (widget_width / 2);
|
||||
} else {
|
||||
widget_height = widget_width / aspect_ratio;
|
||||
widget_y = (height() / 2) - (widget_height / 2);
|
||||
}
|
||||
|
||||
child->move(widget_x, widget_y);
|
||||
child->resize(widget_width, widget_height);
|
||||
child->move(widget_x, widget_y);
|
||||
child->resize(widget_width, widget_height);
|
||||
|
||||
zoom = double(widget_width) / double(viewer->seq->width);
|
||||
} else {
|
||||
int zoomed_width = double(viewer->seq->width)*zoom;
|
||||
int zoomed_height = double(viewer->seq->height)*zoom;
|
||||
int zoomed_x = 0;
|
||||
int zoomed_y = 0;
|
||||
zoom = double(widget_width) / double(viewer->seq->width);
|
||||
} else {
|
||||
int zoomed_width = double(viewer->seq->width)*zoom;
|
||||
int zoomed_height = double(viewer->seq->height)*zoom;
|
||||
int zoomed_x = 0;
|
||||
int zoomed_y = 0;
|
||||
|
||||
if (zoomed_width < width()) zoomed_x = (width()>>1)-(zoomed_width>>1);
|
||||
if (zoomed_height < height()) zoomed_y = (height()>>1)-(zoomed_height>>1);
|
||||
if (zoomed_width < width()) zoomed_x = (width()>>1)-(zoomed_width>>1);
|
||||
if (zoomed_height < height()) zoomed_y = (height()>>1)-(zoomed_height>>1);
|
||||
|
||||
child->move(zoomed_x, zoomed_y);
|
||||
child->resize(zoomed_width, zoomed_height);
|
||||
}
|
||||
child->move(zoomed_x, zoomed_y);
|
||||
child->resize(zoomed_width, zoomed_height);
|
||||
}
|
||||
}
|
||||
area->resize(qMax(width(), child->width()), qMax(height(), child->height()));
|
||||
area->resize(qMax(width(), child->width()), qMax(height(), child->height()));
|
||||
}
|
||||
|
||||
void ViewerContainer::resizeEvent(QResizeEvent *event) {
|
||||
|
||||
+224
-581
@@ -21,6 +21,10 @@
|
||||
#include "ui/viewercontainer.h"
|
||||
#include "io/avtogl.h"
|
||||
#include "ui/timelinewidget.h"
|
||||
#include "ui/renderfunctions.h"
|
||||
#include "ui/renderthread.h"
|
||||
#include "ui/viewerwindow.h"
|
||||
#include "mainwindow.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QAudioOutput>
|
||||
@@ -37,21 +41,22 @@
|
||||
#include <QDesktopWidget>
|
||||
#include <QInputDialog>
|
||||
#include <QApplication>
|
||||
#include <QScreen>
|
||||
#include <QMessageBox>
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
}
|
||||
|
||||
#define GL_DEFAULT_BLEND glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
|
||||
|
||||
ViewerWidget::ViewerWidget(QWidget *parent) :
|
||||
QOpenGLWidget(parent),
|
||||
default_fbo(nullptr),
|
||||
waveform(false),
|
||||
waveform_zoom(1.0),
|
||||
waveform_scroll(0),
|
||||
dragging(false),
|
||||
selected_gizmo(nullptr)
|
||||
gizmos(nullptr),
|
||||
selected_gizmo(nullptr),
|
||||
window(nullptr)
|
||||
{
|
||||
setMouseTracking(true);
|
||||
setFocusPolicy(Qt::ClickFocus);
|
||||
@@ -60,21 +65,26 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
|
||||
format.setDepthBufferSize(24);
|
||||
setFormat(format);
|
||||
|
||||
// error handler - retries after 50ms if we couldn't get the entire image
|
||||
retry_timer.setInterval(50);
|
||||
connect(&retry_timer, SIGNAL(timeout()), this, SLOT(retry()));
|
||||
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(show_context_menu()));
|
||||
|
||||
renderer = new RenderThread();
|
||||
renderer->start(QThread::HighPriority);
|
||||
connect(renderer, SIGNAL(ready()), this, SLOT(queue_repaint()));
|
||||
connect(renderer, SIGNAL(finished()), renderer, SLOT(deleteLater()));
|
||||
}
|
||||
|
||||
ViewerWidget::~ViewerWidget() {
|
||||
if (window != nullptr) {
|
||||
window->close();
|
||||
delete window;
|
||||
}
|
||||
renderer->cancel();
|
||||
delete renderer;
|
||||
}
|
||||
|
||||
void ViewerWidget::delete_function() {
|
||||
// destroy all textures as well
|
||||
if (viewer->seq != nullptr) {
|
||||
makeCurrent();
|
||||
closeActiveClips(viewer->seq);
|
||||
doneCurrent();
|
||||
}
|
||||
closeActiveClips(viewer->seq);
|
||||
}
|
||||
|
||||
void ViewerWidget::set_waveform_scroll(int s) {
|
||||
@@ -90,8 +100,21 @@ void ViewerWidget::show_context_menu() {
|
||||
QAction* save_frame_as_image = menu.addAction(tr("Save Frame as Image..."));
|
||||
connect(save_frame_as_image, SIGNAL(triggered(bool)), this, SLOT(save_frame()));
|
||||
|
||||
QAction* show_fullscreen_action = menu.addAction(tr("Show Fullscreen"));
|
||||
connect(show_fullscreen_action, SIGNAL(triggered()), this, SLOT(show_fullscreen()));
|
||||
/*QAction* show_fullscreen_action = menu.addAction(tr("Show Fullscreen"));
|
||||
connect(show_fullscreen_action, SIGNAL(triggered()), this, SLOT(show_fullscreen()));*/
|
||||
QMenu* fullscreen_menu = menu.addMenu(tr("Show Fullscreen"));
|
||||
QList<QScreen*> screens = QGuiApplication::screens();
|
||||
if (window != nullptr && window->isVisible()) {
|
||||
fullscreen_menu->addAction(tr("Disable"));
|
||||
}
|
||||
for (int i=0;i<screens.size();i++) {
|
||||
QAction* screen_action = fullscreen_menu->addAction(tr("Screen %1: %2x%3").arg(
|
||||
QString::number(i),
|
||||
QString::number(screens.at(i)->size().width()),
|
||||
QString::number(screens.at(i)->size().height())));
|
||||
screen_action->setData(i);
|
||||
}
|
||||
connect(fullscreen_menu, SIGNAL(triggered(QAction*)), this, SLOT(fullscreen_menu_action(QAction*)));
|
||||
|
||||
QMenu zoom_menu(tr("Zoom"));
|
||||
QAction* fit_zoom = zoom_menu.addAction(tr("Fit"));
|
||||
@@ -129,27 +152,30 @@ void ViewerWidget::save_frame() {
|
||||
if (!fn.endsWith(selected_ext, Qt::CaseInsensitive)) {
|
||||
fn += selected_ext;
|
||||
}
|
||||
QOpenGLFramebufferObject fbo(viewer->seq->width, viewer->seq->height, QOpenGLFramebufferObject::CombinedDepthStencil, GL_TEXTURE_RECTANGLE);
|
||||
|
||||
rendering = true;
|
||||
fbo.bind();
|
||||
|
||||
default_fbo = &fbo;
|
||||
|
||||
paintGL();
|
||||
|
||||
QImage img(viewer->seq->width, viewer->seq->height, QImage::Format_RGBA8888);
|
||||
glReadPixels(0, 0, img.width(), img.height(), GL_RGBA, GL_UNSIGNED_BYTE, img.bits());
|
||||
img.save(fn);
|
||||
|
||||
fbo.release();
|
||||
default_fbo = nullptr;
|
||||
rendering = false;
|
||||
renderer->start_render(context(), viewer->seq, fn);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerWidget::show_fullscreen() {
|
||||
showFullScreen();
|
||||
void ViewerWidget::queue_repaint() {
|
||||
update();
|
||||
}
|
||||
|
||||
void ViewerWidget::fullscreen_menu_action(QAction *action) {
|
||||
if (window == nullptr) {
|
||||
QMessageBox::critical(this, "Error", "Failed to create viewer window");
|
||||
} else {
|
||||
if (action->data().isNull()) {
|
||||
window->hide();
|
||||
} else {
|
||||
QScreen* selected_screen = QGuiApplication::screens().at(action->data().toInt());
|
||||
window->showFullScreen();
|
||||
window->setGeometry(selected_screen->geometry());
|
||||
|
||||
// HACK: window seems to show with distorted texture on first showing, so we queue an update after it's shown
|
||||
QTimer::singleShot(100, window, SLOT(update()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerWidget::set_fit_zoom() {
|
||||
@@ -186,30 +212,63 @@ void ViewerWidget::retry() {
|
||||
void ViewerWidget::initializeGL() {
|
||||
initializeOpenGLFunctions();
|
||||
|
||||
connect(context(), SIGNAL(aboutToBeDestroyed()), this, SLOT(delete_function()), Qt::DirectConnection);
|
||||
connect(context(), SIGNAL(aboutToBeDestroyed()), this, SLOT(context_destroy()), Qt::DirectConnection);
|
||||
|
||||
retry_timer.start();
|
||||
window = new ViewerWindow(context());
|
||||
}
|
||||
|
||||
void ViewerWidget::frame_update() {
|
||||
if (viewer->seq != nullptr) {
|
||||
bool render_audio = (viewer->playing || audio_rendering);
|
||||
|
||||
// send context to other thread for drawing
|
||||
if (waveform) {
|
||||
update();
|
||||
} else {
|
||||
doneCurrent();
|
||||
renderer->start_render(context(), viewer->seq);
|
||||
}
|
||||
|
||||
// render the audio
|
||||
compose_audio(viewer, viewer->seq, render_audio);
|
||||
}
|
||||
}
|
||||
|
||||
RenderThread *ViewerWidget::get_renderer() {
|
||||
return renderer;
|
||||
}
|
||||
|
||||
//void ViewerWidget::resizeGL(int w, int h)
|
||||
//{
|
||||
//}
|
||||
|
||||
void ViewerWidget::paintEvent(QPaintEvent *e) {
|
||||
if (!rendering && context()->thread() == this->thread()) {
|
||||
makeCurrent();
|
||||
/*void ViewerWidget::paintEvent(QPaintEvent *e) {
|
||||
if (!rendering) {
|
||||
QOpenGLWidget::paintEvent(e);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
void ViewerWidget::seek_from_click(int x) {
|
||||
viewer->seek(getFrameFromScreenPoint(waveform_zoom, x+waveform_scroll));
|
||||
}
|
||||
|
||||
void ViewerWidget::context_destroy() {
|
||||
makeCurrent();
|
||||
if (viewer->seq != nullptr) {
|
||||
closeActiveClips(viewer->seq);
|
||||
}
|
||||
if (window != nullptr) {
|
||||
delete window;
|
||||
}
|
||||
//QMetaObject::invokeMethod(renderer, "delete_ctx", Qt::QueuedConnection);
|
||||
renderer->delete_ctx();
|
||||
doneCurrent();
|
||||
}
|
||||
|
||||
EffectGizmo* ViewerWidget::get_gizmo_from_mouse(int x, int y) {
|
||||
if (gizmos != nullptr) {
|
||||
double multiplier = double(viewer->seq->width) / double(width());
|
||||
QPoint mouse_pos(qRound(x*multiplier), qRound(y*multiplier));
|
||||
QPoint mouse_pos(qRound(x*multiplier), qRound((height()-y)*multiplier));
|
||||
int dot_size = 2 * qRound(GIZMO_DOT_SIZE * multiplier);
|
||||
int target_size = 2 * qRound(GIZMO_TARGET_SIZE * multiplier);
|
||||
for (int i=0;i<gizmos->gizmo_count();i++) {
|
||||
@@ -238,7 +297,6 @@ EffectGizmo* ViewerWidget::get_gizmo_from_mouse(int x, int y) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
@@ -248,8 +306,8 @@ void ViewerWidget::move_gizmos(QMouseEvent *event, bool done) {
|
||||
if (selected_gizmo != nullptr) {
|
||||
double multiplier = double(viewer->seq->width) / double(width());
|
||||
|
||||
int x_movement = (event->pos().x() - drag_start_x)*multiplier;
|
||||
int y_movement = (event->pos().y() - drag_start_y)*multiplier;
|
||||
int x_movement = qRound((event->pos().x() - drag_start_x)*multiplier);
|
||||
int y_movement = qRound((event->pos().y() - drag_start_y)*multiplier);
|
||||
|
||||
gizmos->gizmo_move(selected_gizmo, x_movement, y_movement, get_timecode(gizmos->parent_clip, gizmos->parent_clip->sequence->playhead), done);
|
||||
|
||||
@@ -315,11 +373,38 @@ void ViewerWidget::mouseMoveEvent(QMouseEvent* event) {
|
||||
}
|
||||
|
||||
void ViewerWidget::mouseReleaseEvent(QMouseEvent *event) {
|
||||
if (dragging) move_gizmos(event, true);
|
||||
if (dragging && gizmos != nullptr && !(event->button() == Qt::MiddleButton || panel_timeline->tool == TIMELINE_TOOL_HAND)) {
|
||||
move_gizmos(event, true);
|
||||
}
|
||||
dragging = false;
|
||||
}
|
||||
|
||||
void ViewerWidget::drawTitleSafeArea() {
|
||||
void ViewerWidget::close_window() {
|
||||
if (window != nullptr) window->hide();
|
||||
}
|
||||
|
||||
void ViewerWidget::draw_waveform_func() {
|
||||
QPainter p(this);
|
||||
if (viewer->seq->using_workarea) {
|
||||
int in_x = getScreenPointFromFrame(waveform_zoom, viewer->seq->workarea_in) - waveform_scroll;
|
||||
int out_x = getScreenPointFromFrame(waveform_zoom, viewer->seq->workarea_out) - waveform_scroll;
|
||||
|
||||
p.fillRect(QRect(in_x, 0, out_x - in_x, height()), QColor(255, 255, 255, 64));
|
||||
p.setPen(Qt::white);
|
||||
p.drawLine(in_x, 0, in_x, height());
|
||||
p.drawLine(out_x, 0, out_x, height());
|
||||
}
|
||||
QRect wr = rect();
|
||||
wr.setX(wr.x() - waveform_scroll);
|
||||
|
||||
p.setPen(Qt::green);
|
||||
draw_waveform(waveform_clip, waveform_ms, waveform_clip->timeline_out, &p, wr, waveform_scroll, width()+waveform_scroll, waveform_zoom);
|
||||
p.setPen(Qt::red);
|
||||
int playhead_x = getScreenPointFromFrame(waveform_zoom, viewer->seq->playhead) - waveform_scroll;
|
||||
p.drawLine(playhead_x, 0, playhead_x, height());
|
||||
}
|
||||
|
||||
void ViewerWidget::draw_title_safe_area() {
|
||||
double halfWidth = 0.5;
|
||||
double halfHeight = 0.5;
|
||||
double viewportAr = (double) width() / (double) height();
|
||||
@@ -387,571 +472,129 @@ void ViewerWidget::drawTitleSafeArea() {
|
||||
glEnd();
|
||||
}
|
||||
|
||||
GLuint ViewerWidget::draw_clip(QOpenGLFramebufferObject* fbo, GLuint texture, bool clear) {
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glOrtho(0, 1, 0, 1, -1, 1);
|
||||
void ViewerWidget::draw_gizmos() {
|
||||
float color[4];
|
||||
glGetFloatv(GL_CURRENT_COLOR, color);
|
||||
|
||||
fbo->bind();
|
||||
|
||||
if (clear) glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
// get current blend mode
|
||||
GLint src_rgb, src_alpha, dst_rgb, dst_alpha;
|
||||
glGetIntegerv(GL_BLEND_SRC_RGB, &src_rgb);
|
||||
glGetIntegerv(GL_BLEND_SRC_ALPHA, &src_alpha);
|
||||
glGetIntegerv(GL_BLEND_DST_RGB, &dst_rgb);
|
||||
glGetIntegerv(GL_BLEND_DST_ALPHA, &dst_alpha);
|
||||
|
||||
GL_DEFAULT_BLEND
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glBegin(GL_QUADS);
|
||||
glTexCoord2f(0, 0); // top left
|
||||
glVertex2f(0, 0); // top left
|
||||
glTexCoord2f(1, 0); // top right
|
||||
glVertex2f(1, 0); // top right
|
||||
glTexCoord2f(1, 1); // bottom right
|
||||
glVertex2f(1, 1); // bottom right
|
||||
glTexCoord2f(0, 1); // bottom left
|
||||
glVertex2f(0, 1); // bottom left
|
||||
glEnd();
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
fbo->release();
|
||||
|
||||
// restore previous blendFunc
|
||||
glBlendFuncSeparate(src_rgb, dst_rgb, src_alpha, dst_alpha);
|
||||
|
||||
if (default_fbo != nullptr) default_fbo->bind();
|
||||
|
||||
glPopMatrix();
|
||||
return fbo->texture();
|
||||
}
|
||||
|
||||
void ViewerWidget::process_effect(Clip* c, Effect* e, double timecode, GLTextureCoords& coords, GLuint& composite_texture, bool& fbo_switcher, int data) {
|
||||
if (e->is_enabled()) {
|
||||
if (e->enable_coords) {
|
||||
e->process_coords(timecode, coords, data);
|
||||
}
|
||||
if ((e->enable_shader && shaders_are_enabled) || e->enable_superimpose) {
|
||||
e->startEffect();
|
||||
if ((e->enable_shader && shaders_are_enabled) && e->is_glsl_linked()) {
|
||||
e->process_shader(timecode, coords);
|
||||
composite_texture = draw_clip(c->fbo[fbo_switcher], composite_texture, true);
|
||||
fbo_switcher = !fbo_switcher;
|
||||
}
|
||||
if (e->enable_superimpose) {
|
||||
GLuint superimpose_texture = e->process_superimpose(timecode);
|
||||
if (superimpose_texture == 0) {
|
||||
qWarning() << "Superimpose texture was nullptr, retrying...";
|
||||
texture_failed = true;
|
||||
} else {
|
||||
composite_texture = draw_clip(c->fbo[!fbo_switcher], superimpose_texture, false);
|
||||
}
|
||||
}
|
||||
e->endEffect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int motion_blur_prog = 0;
|
||||
int motion_blur_lim = 4;
|
||||
|
||||
GLuint ViewerWidget::compose_sequence(QVector<Clip*>& nests, bool render_audio) {
|
||||
Sequence* s = viewer->seq;
|
||||
long playhead = s->playhead;
|
||||
|
||||
if (!nests.isEmpty()) {
|
||||
for (int i=0;i<nests.size();i++) {
|
||||
s = nests.at(i)->media->to_sequence();
|
||||
playhead += nests.at(i)->clip_in - nests.at(i)->get_timeline_in_with_transition();
|
||||
playhead = refactor_frame_number(playhead, nests.at(i)->sequence->frame_rate, s->frame_rate);
|
||||
}
|
||||
|
||||
if (nests.last()->fbo != nullptr) {
|
||||
nests.last()->fbo[0]->bind();
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
nests.last()->fbo[0]->release();
|
||||
}
|
||||
}
|
||||
|
||||
int audio_track_count = 0;
|
||||
|
||||
QVector<Clip*> current_clips;
|
||||
|
||||
for (int i=0;i<s->clips.size();i++) {
|
||||
Clip* c = s->clips.at(i);
|
||||
|
||||
// if clip starts within one second and/or hasn't finished yet
|
||||
if (c != nullptr) {
|
||||
if (!(!nests.isEmpty() && !same_sign(c->track, nests.last()->track))) {
|
||||
bool clip_is_active = false;
|
||||
|
||||
if (c->media != nullptr && c->media->get_type() == MEDIA_TYPE_FOOTAGE) {
|
||||
Footage* m = c->media->to_footage();
|
||||
if (!m->invalid && !(c->track >= 0 && !is_audio_device_set())) {
|
||||
if (m->ready) {
|
||||
const FootageStream* ms = m->get_stream_from_file_index(c->track < 0, c->media_stream);
|
||||
if (ms != nullptr && is_clip_active(c, playhead)) {
|
||||
// if thread is already working, we don't want to touch this,
|
||||
// but we also don't want to hang the UI thread
|
||||
if (!c->open) {
|
||||
open_clip(c, !rendering);
|
||||
}
|
||||
clip_is_active = true;
|
||||
if (c->track >= 0) audio_track_count++;
|
||||
} else if (c->open) {
|
||||
close_clip(c, false);
|
||||
}
|
||||
} else {
|
||||
//qWarning() << "Media '" + m->name + "' was not ready, retrying...";
|
||||
texture_failed = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (is_clip_active(c, playhead)) {
|
||||
if (!c->open) open_clip(c, !rendering);
|
||||
clip_is_active = true;
|
||||
} else if (c->open) {
|
||||
close_clip(c, false);
|
||||
}
|
||||
}
|
||||
if (clip_is_active) {
|
||||
bool added = false;
|
||||
for (int j=0;j<current_clips.size();j++) {
|
||||
if (current_clips.at(j)->track < c->track) {
|
||||
current_clips.insert(j, c);
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!added) {
|
||||
current_clips.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int half_width = s->width/2;
|
||||
int half_height = s->height/2;
|
||||
if (rendering || !nests.isEmpty()) half_height = -half_height; // invert vertical
|
||||
float dot_size = GIZMO_DOT_SIZE / width() * viewer->seq->width;
|
||||
float target_size = GIZMO_TARGET_SIZE / width() * viewer->seq->width;
|
||||
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glOrtho(-half_width, half_width, half_height, -half_height, -1, 10);
|
||||
|
||||
for (int i=0;i<current_clips.size();i++) {
|
||||
GL_DEFAULT_BLEND
|
||||
glColor4f(1.0, 1.0, 1.0, 1.0);
|
||||
|
||||
Clip* c = current_clips.at(i);
|
||||
|
||||
if (c->media != nullptr && c->media->get_type() == MEDIA_TYPE_FOOTAGE && !c->finished_opening) {
|
||||
qWarning() << "Tried to display clip" << i << "but it's closed";
|
||||
texture_failed = true;
|
||||
} else {
|
||||
if (c->track < 0) {
|
||||
GLuint textureID = 0;
|
||||
int video_width = c->getWidth();
|
||||
int video_height = c->getHeight();
|
||||
|
||||
if (c->media != nullptr) {
|
||||
switch (c->media->get_type()) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
// set up opengl texture
|
||||
if (c->texture == nullptr) {
|
||||
c->texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
|
||||
c->texture->setSize(c->stream->codecpar->width, c->stream->codecpar->height);
|
||||
c->texture->setFormat(get_gl_tex_fmt_from_av(c->pix_fmt));
|
||||
c->texture->setMipLevels(c->texture->maximumMipLevels());
|
||||
c->texture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear);
|
||||
c->texture->allocateStorage(get_gl_pix_fmt_from_av(c->pix_fmt), QOpenGLTexture::UInt8);
|
||||
}
|
||||
get_clip_frame(c, playhead);
|
||||
textureID = c->texture->textureId();
|
||||
break;
|
||||
case MEDIA_TYPE_SEQUENCE:
|
||||
textureID = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (textureID == 0 && c->media != nullptr) {
|
||||
qWarning() << "Texture hasn't been created yet";
|
||||
texture_failed = true;
|
||||
} else if (playhead >= c->get_timeline_in_with_transition()) {
|
||||
glPushMatrix();
|
||||
|
||||
// start preparing cache
|
||||
if (c->fbo == nullptr) {
|
||||
c->fbo = new QOpenGLFramebufferObject* [2];
|
||||
c->fbo[0] = new QOpenGLFramebufferObject(video_width, video_height);
|
||||
c->fbo[1] = new QOpenGLFramebufferObject(video_width, video_height);
|
||||
}
|
||||
|
||||
// clear fbos
|
||||
/*c->fbo[0]->bind();
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
c->fbo[0]->release();
|
||||
c->fbo[1]->bind();
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
c->fbo[1]->release();*/
|
||||
|
||||
bool fbo_switcher = false;
|
||||
|
||||
glViewport(0, 0, video_width, video_height);
|
||||
|
||||
GLuint composite_texture;
|
||||
|
||||
if (c->media == nullptr) {
|
||||
c->fbo[fbo_switcher]->bind();
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
c->fbo[fbo_switcher]->release();
|
||||
composite_texture = c->fbo[fbo_switcher]->texture();
|
||||
} else {
|
||||
// for nested sequences
|
||||
if (c->media->get_type()== MEDIA_TYPE_SEQUENCE) {
|
||||
nests.append(c);
|
||||
textureID = compose_sequence(nests, render_audio);
|
||||
nests.removeLast();
|
||||
fbo_switcher = true;
|
||||
}
|
||||
|
||||
composite_texture = draw_clip(c->fbo[fbo_switcher], textureID, true);
|
||||
}
|
||||
|
||||
fbo_switcher = !fbo_switcher;
|
||||
|
||||
// set up default coords
|
||||
GLTextureCoords coords;
|
||||
coords.grid_size = 1;
|
||||
coords.vertexTopLeftX = coords.vertexBottomLeftX = -video_width/2;
|
||||
coords.vertexTopLeftY = coords.vertexTopRightY = -video_height/2;
|
||||
coords.vertexTopRightX = coords.vertexBottomRightX = video_width/2;
|
||||
coords.vertexBottomLeftY = coords.vertexBottomRightY = video_height/2;
|
||||
coords.vertexBottomLeftZ = coords.vertexBottomRightZ = coords.vertexTopLeftZ = coords.vertexTopRightZ = 1;
|
||||
coords.textureTopLeftY = coords.textureTopRightY = coords.textureTopLeftX = coords.textureBottomLeftX = 0.0;
|
||||
coords.textureBottomLeftY = coords.textureBottomRightY = coords.textureTopRightX = coords.textureBottomRightX = 1.0;
|
||||
coords.textureTopLeftQ = coords.textureTopRightQ = coords.textureTopLeftQ = coords.textureBottomLeftQ = 1;
|
||||
|
||||
// set up autoscale
|
||||
if (c->autoscale && (video_width != s->width && video_height != s->height)) {
|
||||
float width_multiplier = float(s->width) / float(video_width);
|
||||
float height_multiplier = float(s->height) / float(video_height);
|
||||
float scale_multiplier = qMin(width_multiplier, height_multiplier);
|
||||
glScalef(scale_multiplier, scale_multiplier, 1);
|
||||
}
|
||||
|
||||
// EFFECT CODE START
|
||||
double timecode = get_timecode(c, playhead);
|
||||
|
||||
Effect* first_gizmo_effect = nullptr;
|
||||
Effect* selected_effect = nullptr;
|
||||
|
||||
for (int j=0;j<c->effects.size();j++) {
|
||||
Effect* e = c->effects.at(j);
|
||||
process_effect(c, e, timecode, coords, composite_texture, fbo_switcher, TA_NO_TRANSITION);
|
||||
|
||||
if (e->are_gizmos_enabled()) {
|
||||
if (first_gizmo_effect == nullptr) first_gizmo_effect = e;
|
||||
if (e->container->selected) selected_effect = e;
|
||||
}
|
||||
}
|
||||
|
||||
if (!rendering) {
|
||||
if (selected_effect != nullptr) {
|
||||
gizmos = selected_effect;
|
||||
} else if (panel_timeline->is_clip_selected(c, true)) {
|
||||
gizmos = first_gizmo_effect;
|
||||
}
|
||||
}
|
||||
|
||||
if (c->get_opening_transition() != nullptr) {
|
||||
int transition_progress = playhead - c->get_timeline_in_with_transition();
|
||||
if (transition_progress < c->get_opening_transition()->get_length()) {
|
||||
process_effect(c, c->get_opening_transition(), (double)transition_progress/(double)c->get_opening_transition()->get_length(), coords, composite_texture, fbo_switcher, TA_OPENING_TRANSITION);
|
||||
}
|
||||
}
|
||||
|
||||
if (c->get_closing_transition() != nullptr) {
|
||||
int transition_progress = playhead - (c->get_timeline_out_with_transition() - c->get_closing_transition()->get_length());
|
||||
if (transition_progress >= 0 && transition_progress < c->get_closing_transition()->get_length()) {
|
||||
process_effect(c, c->get_closing_transition(), (double)transition_progress/(double)c->get_closing_transition()->get_length(), coords, composite_texture, fbo_switcher, TA_CLOSING_TRANSITION);
|
||||
}
|
||||
}
|
||||
// EFFECT CODE END
|
||||
|
||||
if (!nests.isEmpty()) {
|
||||
nests.last()->fbo[0]->bind();
|
||||
glViewport(0, 0, s->width, s->height);
|
||||
} else if (rendering) {
|
||||
glViewport(0, 0, s->width, s->height);
|
||||
} else {
|
||||
int widget_width = width();
|
||||
int widget_height = height();
|
||||
|
||||
widget_width *= QApplication::desktop()->devicePixelRatio();
|
||||
widget_height *= QApplication::desktop()->devicePixelRatio();
|
||||
|
||||
glViewport(0, 0, widget_width, widget_height);
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, composite_texture);
|
||||
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
|
||||
if (coords.grid_size <= 1) {
|
||||
float z = 0.0f;
|
||||
|
||||
glTexCoord2f(coords.textureTopLeftX, coords.textureTopLeftY); // top left
|
||||
glVertex3f(coords.vertexTopLeftX, coords.vertexTopLeftY, z); // top left
|
||||
glTexCoord2f(coords.textureTopRightX, coords.textureTopRightY); // top right
|
||||
glVertex3f(coords.vertexTopRightX, coords.vertexTopRightY, z); // top right
|
||||
glTexCoord2f(coords.textureBottomRightX, coords.textureBottomRightY); // bottom right
|
||||
glVertex3f(coords.vertexBottomRightX, coords.vertexBottomRightY, z); // bottom right
|
||||
glTexCoord2f(coords.textureBottomLeftX, coords.textureBottomLeftY); // bottom left
|
||||
glVertex3f(coords.vertexBottomLeftX, coords.vertexBottomLeftY, z); // bottom left
|
||||
} else {
|
||||
float rows = coords.grid_size;
|
||||
float cols = coords.grid_size;
|
||||
|
||||
for (float k=0;k<rows;k++) {
|
||||
float row_prog = k/rows;
|
||||
float next_row_prog = (k+1)/rows;
|
||||
for (float j=0;j<cols;j++) {
|
||||
float col_prog = j/cols;
|
||||
float next_col_prog = (j+1)/cols;
|
||||
|
||||
float vertexTLX = float_lerp(coords.vertexTopLeftX, coords.vertexBottomLeftX, row_prog);
|
||||
float vertexTRX = float_lerp(coords.vertexTopRightX, coords.vertexBottomRightX, row_prog);
|
||||
float vertexBLX = float_lerp(coords.vertexTopLeftX, coords.vertexBottomLeftX, next_row_prog);
|
||||
float vertexBRX = float_lerp(coords.vertexTopRightX, coords.vertexBottomRightX, next_row_prog);
|
||||
|
||||
float vertexTLY = float_lerp(coords.vertexTopLeftY, coords.vertexTopRightY, col_prog);
|
||||
float vertexTRY = float_lerp(coords.vertexTopLeftY, coords.vertexTopRightY, next_col_prog);
|
||||
float vertexBLY = float_lerp(coords.vertexBottomLeftY, coords.vertexBottomRightY, col_prog);
|
||||
float vertexBRY = float_lerp(coords.vertexBottomLeftY, coords.vertexBottomRightY, next_col_prog);
|
||||
|
||||
glTexCoord2f(float_lerp(coords.textureTopLeftX, coords.textureTopRightX, col_prog), float_lerp(coords.textureTopLeftY, coords.textureBottomLeftY, row_prog)); // top left
|
||||
glVertex2f(float_lerp(vertexTLX, vertexTRX, col_prog), float_lerp(vertexTLY, vertexBLY, row_prog)); // top left
|
||||
glTexCoord2f(float_lerp(coords.textureTopLeftX, coords.textureTopRightX, next_col_prog), float_lerp(coords.textureTopRightY, coords.textureBottomRightY, row_prog)); // top right
|
||||
glVertex2f(float_lerp(vertexTLX, vertexTRX, next_col_prog), float_lerp(vertexTRY, vertexBRY, row_prog)); // top right
|
||||
glTexCoord2f(float_lerp(coords.textureBottomLeftX, coords.textureBottomRightX, next_col_prog), float_lerp(coords.textureTopRightY, coords.textureBottomRightY, next_row_prog)); // bottom right
|
||||
glVertex2f(float_lerp(vertexBLX, vertexBRX, next_col_prog), float_lerp(vertexTRY, vertexBRY, next_row_prog)); // bottom right
|
||||
glTexCoord2f(float_lerp(coords.textureBottomLeftX, coords.textureBottomRightX, col_prog), float_lerp(coords.textureTopLeftY, coords.textureBottomLeftY, next_row_prog)); // bottom left
|
||||
glVertex2f(float_lerp(vertexBLX, vertexBRX, col_prog), float_lerp(vertexTLY, vertexBLY, next_row_prog)); // bottom left
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glEnd();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0); // unbind texture
|
||||
|
||||
if (gizmos != nullptr && !drawn_gizmos) {
|
||||
gizmos->gizmo_draw(timecode, coords); // set correct gizmo coords
|
||||
gizmos->gizmo_world_to_screen();
|
||||
|
||||
drawn_gizmos = true;
|
||||
}
|
||||
|
||||
if (!nests.isEmpty()) {
|
||||
nests.last()->fbo[0]->release();
|
||||
if (default_fbo != nullptr) default_fbo->bind();
|
||||
}
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
/*GLfloat motion_blur_frac = (GLfloat) motion_blur_prog / (GLfloat) motion_blur_lim;
|
||||
if (motion_blur_prog == 0) {
|
||||
glAccum(GL_LOAD, motion_blur_frac);
|
||||
} else {
|
||||
glAccum(GL_ACCUM, motion_blur_frac);
|
||||
}
|
||||
motion_blur_prog++;*/
|
||||
}
|
||||
} else {
|
||||
if (render_audio || (config.enable_audio_scrubbing && audio_scrub)) {
|
||||
if (c->media != nullptr && c->media->get_type() == MEDIA_TYPE_SEQUENCE) {
|
||||
nests.append(c);
|
||||
compose_sequence(nests, render_audio);
|
||||
nests.removeLast();
|
||||
} else {
|
||||
if (c->lock.tryLock()) {
|
||||
// clip is not caching, start caching audio
|
||||
cache_clip(c, playhead, c->audio_reset, !render_audio, nests);
|
||||
c->lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// visually update all the keyframe values
|
||||
if (c->sequence == viewer->seq) { // only if you can currently see them
|
||||
double ts = (playhead - c->get_timeline_in_with_transition() + c->get_clip_in_with_transition())/s->frame_rate;
|
||||
for (int i=0;i<c->effects.size();i++) {
|
||||
Effect* e = c->effects.at(i);
|
||||
for (int j=0;j<e->row_count();j++) {
|
||||
EffectRow* r = e->row(j);
|
||||
for (int k=0;k<r->fieldCount();k++) {
|
||||
r->field(k)->validate_keyframe_data(ts);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
glOrtho(0, viewer->seq->width, 0, viewer->seq->height, -1, 10);
|
||||
float gizmo_z = 0.0f;
|
||||
for (int j=0;j<gizmos->gizmo_count();j++) {
|
||||
EffectGizmo* g = gizmos->gizmo(j);
|
||||
glColor4f(g->color.redF(), g->color.greenF(), g->color.blueF(), 1.0);
|
||||
switch (g->get_type()) {
|
||||
case GIZMO_TYPE_DOT: // draw dot
|
||||
glBegin(GL_QUADS);
|
||||
glVertex3f(g->screen_pos[0].x()-dot_size, g->screen_pos[0].y()-dot_size, gizmo_z);
|
||||
glVertex3f(g->screen_pos[0].x()+dot_size, g->screen_pos[0].y()-dot_size, gizmo_z);
|
||||
glVertex3f(g->screen_pos[0].x()+dot_size, g->screen_pos[0].y()+dot_size, gizmo_z);
|
||||
glVertex3f(g->screen_pos[0].x()-dot_size, g->screen_pos[0].y()+dot_size, gizmo_z);
|
||||
glEnd();
|
||||
break;
|
||||
case GIZMO_TYPE_POLY: // draw lines
|
||||
glBegin(GL_LINES);
|
||||
for (int k=1;k<g->get_point_count();k++) {
|
||||
glVertex3f(g->screen_pos[k-1].x(), g->screen_pos[k-1].y(), gizmo_z);
|
||||
glVertex3f(g->screen_pos[k].x(), g->screen_pos[k].y(), gizmo_z);
|
||||
}
|
||||
glVertex3f(g->screen_pos[g->get_point_count()-1].x(), g->screen_pos[g->get_point_count()-1].y(), gizmo_z);
|
||||
glVertex3f(g->screen_pos[0].x(), g->screen_pos[0].y(), gizmo_z);
|
||||
glEnd();
|
||||
break;
|
||||
case GIZMO_TYPE_TARGET: // draw target
|
||||
glBegin(GL_LINES);
|
||||
glVertex3f(g->screen_pos[0].x()-target_size, g->screen_pos[0].y()-target_size, gizmo_z);
|
||||
glVertex3f(g->screen_pos[0].x()+target_size, g->screen_pos[0].y()-target_size, gizmo_z);
|
||||
|
||||
glVertex3f(g->screen_pos[0].x()+target_size, g->screen_pos[0].y()-target_size, gizmo_z);
|
||||
glVertex3f(g->screen_pos[0].x()+target_size, g->screen_pos[0].y()+target_size, gizmo_z);
|
||||
|
||||
glVertex3f(g->screen_pos[0].x()+target_size, g->screen_pos[0].y()+target_size, gizmo_z);
|
||||
glVertex3f(g->screen_pos[0].x()-target_size, g->screen_pos[0].y()+target_size, gizmo_z);
|
||||
|
||||
glVertex3f(g->screen_pos[0].x()-target_size, g->screen_pos[0].y()+target_size, gizmo_z);
|
||||
glVertex3f(g->screen_pos[0].x()-target_size, g->screen_pos[0].y()-target_size, gizmo_z);
|
||||
|
||||
glVertex3f(g->screen_pos[0].x()-target_size, g->screen_pos[0].y(), gizmo_z);
|
||||
glVertex3f(g->screen_pos[0].x()+target_size, g->screen_pos[0].y(), gizmo_z);
|
||||
|
||||
glVertex3f(g->screen_pos[0].x(), g->screen_pos[0].y()-target_size, gizmo_z);
|
||||
glVertex3f(g->screen_pos[0].x(), g->screen_pos[0].y()+target_size, gizmo_z);
|
||||
glEnd();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (audio_track_count == 0) {
|
||||
viewer->play_wake();
|
||||
}
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
if (!nests.isEmpty() && nests.last()->fbo != nullptr) {
|
||||
// returns nested clip's texture
|
||||
return nests.last()->fbo[0]->texture();
|
||||
}
|
||||
|
||||
return 0;
|
||||
glColor4f(color[0], color[1], color[2], color[3]);
|
||||
}
|
||||
|
||||
void ViewerWidget::paintGL() {
|
||||
drawn_gizmos = false;
|
||||
force_quit = false;
|
||||
if (viewer->seq != nullptr) {
|
||||
gizmos = nullptr;
|
||||
if (waveform) {
|
||||
draw_waveform_func();
|
||||
} else {
|
||||
renderer->mutex.lock();
|
||||
|
||||
bool render_audio = (viewer->playing || rendering);
|
||||
bool loop = false;
|
||||
do {
|
||||
loop = false;
|
||||
makeCurrent();
|
||||
|
||||
glClearColor(0, 0, 0, 1);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glEnable(GL_BLEND);
|
||||
glEnable(GL_DEPTH);
|
||||
// clear to solid black
|
||||
|
||||
texture_failed = false;
|
||||
glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
if (!rendering) retry_timer.stop();
|
||||
// set color multipler to straight white
|
||||
glColor4f(1.0, 1.0, 1.0, 1.0);
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
// compose video preview
|
||||
glClearColor(0, 0, 0, 0);
|
||||
// set screen coords to widget size
|
||||
glLoadIdentity();
|
||||
glOrtho(0, 1, 0, 1, -1, 1);
|
||||
|
||||
QVector<Clip*> nests;
|
||||
// draw texture from render thread
|
||||
|
||||
compose_sequence(nests, render_audio);
|
||||
glBindTexture(GL_TEXTURE_2D, renderer->texColorBuffer);
|
||||
|
||||
if (waveform) {
|
||||
QPainter p(this);
|
||||
if (viewer->seq->using_workarea) {
|
||||
int in_x = getScreenPointFromFrame(waveform_zoom, viewer->seq->workarea_in) - waveform_scroll;
|
||||
int out_x = getScreenPointFromFrame(waveform_zoom, viewer->seq->workarea_out) - waveform_scroll;
|
||||
glBegin(GL_QUADS);
|
||||
|
||||
p.fillRect(QRect(in_x, 0, out_x - in_x, height()), QColor(255, 255, 255, 64));
|
||||
p.setPen(Qt::white);
|
||||
p.drawLine(in_x, 0, in_x, height());
|
||||
p.drawLine(out_x, 0, out_x, height());
|
||||
}
|
||||
QRect wr = rect();
|
||||
wr.setX(wr.x() - waveform_scroll);
|
||||
glVertex2f(0, 0);
|
||||
glTexCoord2f(0, 0);
|
||||
glVertex2f(0, 1);
|
||||
glTexCoord2f(1, 0);
|
||||
glVertex2f(1, 1);
|
||||
glTexCoord2f(1, 1);
|
||||
glVertex2f(1, 0);
|
||||
glTexCoord2f(0, 1);
|
||||
|
||||
p.setPen(Qt::green);
|
||||
draw_waveform(waveform_clip, waveform_ms, waveform_clip->timeline_out, &p, wr, waveform_scroll, width()+waveform_scroll, waveform_zoom);
|
||||
p.setPen(Qt::red);
|
||||
int playhead_x = getScreenPointFromFrame(waveform_zoom, viewer->seq->playhead) - waveform_scroll;
|
||||
p.drawLine(playhead_x, 0, playhead_x, height());
|
||||
}
|
||||
glEnd();
|
||||
|
||||
if (force_quit) break;
|
||||
if (texture_failed) {
|
||||
if (rendering) {
|
||||
qInfo() << "Texture failed - looping";
|
||||
loop = true;
|
||||
} else if (!viewer->playing) {
|
||||
retry_timer.start();
|
||||
}
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
if (config.show_title_safe_area && !rendering) {
|
||||
drawTitleSafeArea();
|
||||
}
|
||||
// draw title/action safe area
|
||||
if (config.show_title_safe_area) {
|
||||
draw_title_safe_area();
|
||||
}
|
||||
|
||||
if (gizmos != nullptr && drawn_gizmos) {
|
||||
float color[4];
|
||||
glGetFloatv(GL_CURRENT_COLOR, color);
|
||||
gizmos = renderer->gizmos;
|
||||
if (gizmos != nullptr) {
|
||||
draw_gizmos();
|
||||
}
|
||||
|
||||
float dot_size = GIZMO_DOT_SIZE / width() * viewer->seq->width;
|
||||
float target_size = GIZMO_TARGET_SIZE / width() * viewer->seq->width;
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glOrtho(0, viewer->seq->width, viewer->seq->height, 0, -1, 10);
|
||||
float gizmo_z = 0.0f;
|
||||
for (int j=0;j<gizmos->gizmo_count();j++) {
|
||||
EffectGizmo* g = gizmos->gizmo(j);
|
||||
glColor4f(g->color.redF(), g->color.greenF(), g->color.blueF(), 1.0);
|
||||
switch (g->get_type()) {
|
||||
case GIZMO_TYPE_DOT: // draw dot
|
||||
glBegin(GL_QUADS);
|
||||
glVertex3f(g->screen_pos[0].x()-dot_size, g->screen_pos[0].y()-dot_size, gizmo_z);
|
||||
glVertex3f(g->screen_pos[0].x()+dot_size, g->screen_pos[0].y()-dot_size, gizmo_z);
|
||||
glVertex3f(g->screen_pos[0].x()+dot_size, g->screen_pos[0].y()+dot_size, gizmo_z);
|
||||
glVertex3f(g->screen_pos[0].x()-dot_size, g->screen_pos[0].y()+dot_size, gizmo_z);
|
||||
glEnd();
|
||||
break;
|
||||
case GIZMO_TYPE_POLY: // draw lines
|
||||
glBegin(GL_LINES);
|
||||
for (int k=1;k<g->get_point_count();k++) {
|
||||
glVertex3f(g->screen_pos[k-1].x(), g->screen_pos[k-1].y(), gizmo_z);
|
||||
glVertex3f(g->screen_pos[k].x(), g->screen_pos[k].y(), gizmo_z);
|
||||
}
|
||||
glVertex3f(g->screen_pos[g->get_point_count()-1].x(), g->screen_pos[g->get_point_count()-1].y(), gizmo_z);
|
||||
glVertex3f(g->screen_pos[0].x(), g->screen_pos[0].y(), gizmo_z);
|
||||
glEnd();
|
||||
break;
|
||||
case GIZMO_TYPE_TARGET: // draw target
|
||||
glBegin(GL_LINES);
|
||||
glVertex3f(g->screen_pos[0].x()-target_size, g->screen_pos[0].y()-target_size, gizmo_z);
|
||||
glVertex3f(g->screen_pos[0].x()+target_size, g->screen_pos[0].y()-target_size, gizmo_z);
|
||||
if (window != nullptr && window->isVisible()) {
|
||||
window->set_texture(renderer->texColorBuffer, double(viewer->seq->width)/double(viewer->seq->height), &renderer->mutex);
|
||||
}
|
||||
|
||||
glVertex3f(g->screen_pos[0].x()+target_size, g->screen_pos[0].y()-target_size, gizmo_z);
|
||||
glVertex3f(g->screen_pos[0].x()+target_size, g->screen_pos[0].y()+target_size, gizmo_z);
|
||||
renderer->mutex.unlock();
|
||||
|
||||
glVertex3f(g->screen_pos[0].x()+target_size, g->screen_pos[0].y()+target_size, gizmo_z);
|
||||
glVertex3f(g->screen_pos[0].x()-target_size, g->screen_pos[0].y()+target_size, gizmo_z);
|
||||
|
||||
glVertex3f(g->screen_pos[0].x()-target_size, g->screen_pos[0].y()+target_size, gizmo_z);
|
||||
glVertex3f(g->screen_pos[0].x()-target_size, g->screen_pos[0].y()-target_size, gizmo_z);
|
||||
|
||||
glVertex3f(g->screen_pos[0].x()-target_size, g->screen_pos[0].y(), gizmo_z);
|
||||
glVertex3f(g->screen_pos[0].x()+target_size, g->screen_pos[0].y(), gizmo_z);
|
||||
|
||||
glVertex3f(g->screen_pos[0].x(), g->screen_pos[0].y()-target_size, gizmo_z);
|
||||
glVertex3f(g->screen_pos[0].x(), g->screen_pos[0].y()+target_size, gizmo_z);
|
||||
glEnd();
|
||||
break;
|
||||
}
|
||||
}
|
||||
glPopMatrix();
|
||||
|
||||
glColor4f(color[0], color[1], color[2], color[3]);
|
||||
|
||||
drawn_gizmos = true;
|
||||
}
|
||||
|
||||
glDisable(GL_DEPTH);
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
} while (loop);
|
||||
if (renderer->did_texture_fail()) {
|
||||
doneCurrent();
|
||||
renderer->start_render(context(), viewer->seq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+35
-33
@@ -18,62 +18,64 @@ class Effect;
|
||||
class EffectGizmo;
|
||||
class ViewerContainer;
|
||||
struct GLTextureCoords;
|
||||
class RenderThread;
|
||||
class ViewerWindow;
|
||||
|
||||
class ViewerWidget : public QOpenGLWidget, QOpenGLFunctions
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ViewerWidget(QWidget *parent = 0);
|
||||
ViewerWidget(QWidget *parent = 0);
|
||||
~ViewerWidget();
|
||||
|
||||
void paintGL();
|
||||
void initializeGL();
|
||||
void delete_function();
|
||||
void close_window();
|
||||
|
||||
void paintGL();
|
||||
void initializeGL();
|
||||
Viewer* viewer;
|
||||
ViewerContainer* container;
|
||||
|
||||
QOpenGLFramebufferObject* default_fbo;
|
||||
ViewerContainer* container;
|
||||
|
||||
bool waveform;
|
||||
Clip* waveform_clip;
|
||||
const FootageStream* waveform_ms;
|
||||
double waveform_zoom;
|
||||
int waveform_scroll;
|
||||
const FootageStream* waveform_ms;
|
||||
double waveform_zoom;
|
||||
int waveform_scroll;
|
||||
|
||||
bool force_quit;
|
||||
void frame_update();
|
||||
RenderThread* get_renderer();
|
||||
public slots:
|
||||
void delete_function();
|
||||
void set_waveform_scroll(int s);
|
||||
void set_waveform_scroll(int s);
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e);
|
||||
// void resizeGL(int w, int h);
|
||||
void mousePressEvent(QMouseEvent *event);
|
||||
void mouseMoveEvent(QMouseEvent *event);
|
||||
void mouseReleaseEvent(QMouseEvent *event);
|
||||
private:
|
||||
QTimer retry_timer;
|
||||
void drawTitleSafeArea();
|
||||
void draw_waveform_func();
|
||||
void draw_title_safe_area();
|
||||
void draw_gizmos();
|
||||
EffectGizmo* get_gizmo_from_mouse(int x, int y);
|
||||
void move_gizmos(QMouseEvent *event, bool done);
|
||||
bool dragging;
|
||||
void seek_from_click(int x);
|
||||
GLuint compose_sequence(QVector<Clip *> &nests, bool render_audio);
|
||||
GLuint draw_clip(QOpenGLFramebufferObject *clip, GLuint texture, bool clear);
|
||||
void process_effect(Clip* c, Effect* e, double timecode, GLTextureCoords& coords, GLuint& composite_texture, bool& fbo_switcher, int data);
|
||||
Effect* gizmos;
|
||||
int drag_start_x;
|
||||
int drag_start_y;
|
||||
int gizmo_x_mvmt;
|
||||
int gizmo_y_mvmt;
|
||||
EffectGizmo* selected_gizmo;
|
||||
EffectGizmo* get_gizmo_from_mouse(int x, int y);
|
||||
bool drawn_gizmos;
|
||||
void move_gizmos(QMouseEvent *event, bool done);
|
||||
Effect* gizmos;
|
||||
int drag_start_x;
|
||||
int drag_start_y;
|
||||
int gizmo_x_mvmt;
|
||||
int gizmo_y_mvmt;
|
||||
EffectGizmo* selected_gizmo;
|
||||
RenderThread* renderer;
|
||||
ViewerWindow* window;
|
||||
private slots:
|
||||
void context_destroy();
|
||||
void retry();
|
||||
void show_context_menu();
|
||||
void save_frame();
|
||||
void show_fullscreen();
|
||||
|
||||
void set_fit_zoom();
|
||||
void set_custom_zoom();
|
||||
void set_menu_zoom(QAction *action);
|
||||
void queue_repaint();
|
||||
void fullscreen_menu_action(QAction* action);
|
||||
void set_fit_zoom();
|
||||
void set_custom_zoom();
|
||||
void set_menu_zoom(QAction *action);
|
||||
};
|
||||
|
||||
#endif // VIEWERWIDGET_H
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
#include "viewerwindow.h"
|
||||
|
||||
#include <QMutex>
|
||||
|
||||
ViewerWindow::ViewerWindow(QOpenGLContext *share) :
|
||||
QOpenGLWindow(share),
|
||||
texture(0),
|
||||
mutex(nullptr)
|
||||
{}
|
||||
|
||||
void ViewerWindow::set_texture(GLuint t, double iar, QMutex* imutex) {
|
||||
texture = t;
|
||||
ar = iar;
|
||||
mutex = imutex;
|
||||
update();
|
||||
}
|
||||
|
||||
void ViewerWindow::paintGL() {
|
||||
if (texture > 0) {
|
||||
if (mutex != nullptr) mutex->lock();
|
||||
|
||||
glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
|
||||
glLoadIdentity();
|
||||
glOrtho(0, 1, 0, 1, -1, 1);
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
|
||||
double top = 0;
|
||||
double left = 0;
|
||||
double right = 1;
|
||||
double bottom = 1;
|
||||
|
||||
double widget_ar = double(width()) / double(height());
|
||||
|
||||
if (widget_ar > ar) {
|
||||
double width = 1.0 * ar / widget_ar;
|
||||
left = (1.0 - width)*0.5;
|
||||
right = left + width;
|
||||
} else {
|
||||
double height = 1.0 / ar * widget_ar;
|
||||
top = (1.0 - height)*0.5;
|
||||
bottom = top + height;
|
||||
}
|
||||
|
||||
glVertex2d(left, top);
|
||||
glTexCoord2d(0, 0);
|
||||
glVertex2d(left, bottom);
|
||||
glTexCoord2d(1, 0);
|
||||
glVertex2d(right, bottom);
|
||||
glTexCoord2d(1, 1);
|
||||
glVertex2d(right, top);
|
||||
glTexCoord2d(0, 1);
|
||||
|
||||
glEnd();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
|
||||
if (mutex != nullptr) mutex->unlock();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef VIEWERWINDOW_H
|
||||
#define VIEWERWINDOW_H
|
||||
|
||||
#include <QOpenGLWindow>
|
||||
|
||||
class QMutex;
|
||||
|
||||
class ViewerWindow : public QOpenGLWindow {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ViewerWindow(QOpenGLContext* share);
|
||||
void set_texture(GLuint t, double iar, QMutex *imutex);
|
||||
private:
|
||||
void paintGL();
|
||||
GLuint texture;
|
||||
double ar;
|
||||
QMutex* mutex;
|
||||
};
|
||||
|
||||
#endif // VIEWERWINDOW_H
|
||||
Reference in New Issue
Block a user