Merge branch 'master' into pr/1875
This commit is contained in:
@@ -50,7 +50,7 @@ jobs:
|
||||
${{ matrix.cmake-gen }}>
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: olivevideoeditor/ci-olive:2021.4
|
||||
image: olivevideoeditor/ci-olive:2022.1
|
||||
|
||||
steps:
|
||||
- name: Checkout Source Code
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ find_package(OpenGL REQUIRED)
|
||||
list(APPEND OLIVE_LIBRARIES OpenGL::GL)
|
||||
|
||||
# Link OpenColorIO
|
||||
find_package(OpenColorIO 2.0.0 REQUIRED)
|
||||
find_package(OpenColorIO 2.1.1 REQUIRED)
|
||||
list(APPEND OLIVE_LIBRARIES ${OCIO_LIBRARIES})
|
||||
list(APPEND OLIVE_INCLUDE_DIRS ${OCIO_INCLUDE_DIRS})
|
||||
|
||||
|
||||
@@ -323,7 +323,7 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, const QAtomicIn
|
||||
int64_t new_dur;
|
||||
|
||||
do {
|
||||
new_dur = frame->pts;
|
||||
new_dur = frame->best_effort_timestamp;
|
||||
} while (instance.GetFrame(pkt, frame) >= 0);
|
||||
|
||||
avstream->duration = new_dur;
|
||||
@@ -388,7 +388,7 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, const QAtomicIn
|
||||
int64_t new_dur;
|
||||
|
||||
do {
|
||||
new_dur = frame->pts;
|
||||
new_dur = frame->best_effort_timestamp;
|
||||
} while (instance.GetFrame(pkt, frame) >= 0);
|
||||
|
||||
avstream->duration = new_dur;
|
||||
@@ -540,7 +540,7 @@ bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames, cons
|
||||
break;
|
||||
}
|
||||
|
||||
SignalProcessingProgress(frame->pts, duration);
|
||||
SignalProcessingProgress(frame->best_effort_timestamp, duration);
|
||||
}
|
||||
|
||||
wave_out.close();
|
||||
@@ -733,7 +733,7 @@ FFmpegFramePool::ElementPtr FFmpegDecoder::RetrieveFrame(const rational& time, c
|
||||
if (still_seeking) {
|
||||
// Handle a failure to seek (occurs on some media)
|
||||
// We'll only be here if the frame cache was emptied earlier
|
||||
if (!cache_at_zero_ && (ret == AVERROR_EOF || working_frame->pts > target_ts)) {
|
||||
if (!cache_at_zero_ && (ret == AVERROR_EOF || working_frame->best_effort_timestamp > target_ts)) {
|
||||
|
||||
seek_ts = qMax(min_seek, seek_ts - second_ts_);
|
||||
instance_.Seek(seek_ts);
|
||||
@@ -784,7 +784,7 @@ FFmpegFramePool::ElementPtr FFmpegDecoder::RetrieveFrame(const rational& time, c
|
||||
av_image_copy(&destination_data, &destination_linesize, const_cast<const uint8_t**>(working_frame->data), working_frame->linesize, static_cast<AVPixelFormat>(working_frame->format), working_frame->width, working_frame->height);
|
||||
|
||||
// Set timestamp so this frame can be identified later
|
||||
cached->set_timestamp(working_frame->pts);
|
||||
cached->set_timestamp(working_frame->best_effort_timestamp);
|
||||
|
||||
// Store frame before just in case
|
||||
FFmpegFramePool::ElementPtr previous;
|
||||
|
||||
@@ -37,6 +37,8 @@ set(OLIVE_SOURCES
|
||||
common/flipmodifiers.cpp
|
||||
common/flipmodifiers.h
|
||||
common/functiontimer.h
|
||||
common/html.cpp
|
||||
common/html.h
|
||||
common/jobtime.cpp
|
||||
common/jobtime.h
|
||||
common/lerp.h
|
||||
@@ -61,6 +63,7 @@ set(OLIVE_SOURCES
|
||||
common/timerange.cpp
|
||||
common/timerange.h
|
||||
common/tohex.h
|
||||
common/util.h
|
||||
common/xmlutils.cpp
|
||||
common/xmlutils.h
|
||||
PARENT_SCOPE
|
||||
|
||||
@@ -139,7 +139,10 @@ void FileFunctions::CopyDirectory(const QString &source, const QString &dest, bo
|
||||
} else {
|
||||
// Copy file
|
||||
if (overwrite && QFile::exists(dest_file_path)) {
|
||||
QFile::remove(dest_file_path);
|
||||
QFile file(dest_file_path);
|
||||
file.setPermissions(file.permissions() | QFileDevice::WriteOwner | QFileDevice::WriteUser |
|
||||
QFileDevice::WriteGroup | QFileDevice::WriteOther);
|
||||
file.remove();
|
||||
}
|
||||
|
||||
QFile::copy(info.absoluteFilePath(), dest_file_path);
|
||||
@@ -147,27 +150,10 @@ void FileFunctions::CopyDirectory(const QString &source, const QString &dest, bo
|
||||
}
|
||||
}
|
||||
|
||||
bool FileFunctions::DirectoryIsValid(const QString &dir, bool try_to_create)
|
||||
bool FileFunctions::DirectoryIsValid(const QDir &d, bool try_to_create_if_not_exists)
|
||||
{
|
||||
// Empty string is invalid
|
||||
if (dir.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QDir d(dir);
|
||||
|
||||
// If directory already exists, this is valid
|
||||
if (d.exists()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we can create and creation is successful, this is valid
|
||||
if (try_to_create && d.mkpath(".")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, invalid
|
||||
return false;
|
||||
// Return whether the directory exists, or whether it could be created if it doesn't
|
||||
return d.exists() || d.mkpath(QStringLiteral("."));
|
||||
}
|
||||
|
||||
QString FileFunctions::EnsureFilenameExtension(QString fn, const QString &extension)
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#ifndef FILEFUNCTIONS_H
|
||||
#define FILEFUNCTIONS_H
|
||||
|
||||
#include <QDir>
|
||||
#include <QString>
|
||||
|
||||
#include "common/define.h"
|
||||
@@ -52,7 +53,7 @@ public:
|
||||
|
||||
static void CopyDirectory(const QString& source, const QString& dest, bool overwrite = false);
|
||||
|
||||
static bool DirectoryIsValid(const QString& dir, bool try_to_create);
|
||||
static bool DirectoryIsValid(const QDir& dir, bool try_to_create_if_not_exists = true);
|
||||
|
||||
/**
|
||||
* @brief Ensures a given filename has a certain extension
|
||||
|
||||
@@ -0,0 +1,469 @@
|
||||
#include "html.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QTextBlock>
|
||||
|
||||
#include "xmlutils.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
const QVector<QString> Html::kBlockTags = {
|
||||
QStringLiteral("p"),
|
||||
QStringLiteral("div")
|
||||
};
|
||||
|
||||
inline bool StrEquals(const QString &a, const QStringRef &b)
|
||||
{
|
||||
return !a.compare(b, Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
inline bool StrEquals(const QString &a, const QString &b)
|
||||
{
|
||||
return !a.compare(b, Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
inline bool StrEquals(const QStringRef &a, const QString &b)
|
||||
{
|
||||
return !a.compare(b, Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
inline bool StrEquals(const QStringRef &a, const QStringRef &b)
|
||||
{
|
||||
return !a.compare(b, Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
QString Html::DocToHtml(const QTextDocument *doc)
|
||||
{
|
||||
QString html;
|
||||
QXmlStreamWriter writer(&html);
|
||||
|
||||
//writer.setAutoFormatting(true);
|
||||
|
||||
for (auto it=doc->begin(); it!=doc->end(); it=it.next()) {
|
||||
WriteBlock(&writer, it);
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
struct HtmlNode {
|
||||
QString tag;
|
||||
QTextCharFormat format;
|
||||
};
|
||||
|
||||
QTextCharFormat MergeHtmlFormats(const QVector<HtmlNode> &stack)
|
||||
{
|
||||
QTextCharFormat f;
|
||||
|
||||
for (int i=0; i<stack.size(); i++) {
|
||||
f.merge(stack.at(i).format);
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
void Html::HtmlToDoc(QTextDocument *doc, const QString &html)
|
||||
{
|
||||
// Empty doc
|
||||
doc->clear();
|
||||
bool inside_block = true;
|
||||
|
||||
// Create cursor, which appears to be Qt's official way of inserting blocks and fragments
|
||||
QTextCursor c(doc);
|
||||
|
||||
QString wrapped = QStringLiteral("<html>").append(html).append("</html>");
|
||||
QXmlStreamReader reader(wrapped);
|
||||
|
||||
QVector<HtmlNode> fmt_stack;
|
||||
|
||||
QTextCharFormat default_fmt;
|
||||
default_fmt.setFontWeight(QFont::Normal);
|
||||
fmt_stack.append({QStringLiteral("html"), default_fmt});
|
||||
|
||||
QTextCharFormat current_fmt;
|
||||
|
||||
while (!reader.atEnd()) {
|
||||
reader.readNext();
|
||||
|
||||
if (reader.tokenType() == QXmlStreamReader::StartElement) {
|
||||
|
||||
QString tag = reader.name().toString().toLower();
|
||||
|
||||
fmt_stack.append({tag, ReadCharFormat(reader.attributes())});
|
||||
current_fmt = MergeHtmlFormats(fmt_stack);
|
||||
|
||||
if (kBlockTags.contains(tag)) {
|
||||
QTextBlockFormat block_fmt = ReadBlockFormat(reader.attributes());
|
||||
if (inside_block) {
|
||||
c.setBlockFormat(block_fmt);
|
||||
c.setBlockCharFormat(current_fmt);
|
||||
} else {
|
||||
c.insertBlock(block_fmt, current_fmt);
|
||||
inside_block = true;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (reader.tokenType() == QXmlStreamReader::Characters) {
|
||||
|
||||
QString characters = reader.text().toString();
|
||||
c.insertText(characters, current_fmt);
|
||||
|
||||
} else if (reader.tokenType() == QXmlStreamReader::EndElement) {
|
||||
|
||||
QString tag = reader.name().toString().toLower();
|
||||
|
||||
for (int i=fmt_stack.size()-1; i>=0; i--) {
|
||||
if (fmt_stack.at(i).tag == tag) {
|
||||
fmt_stack.removeAt(i);
|
||||
current_fmt = MergeHtmlFormats(fmt_stack);
|
||||
|
||||
if (kBlockTags.contains(tag)) {
|
||||
inside_block = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (reader.error()) {
|
||||
qCritical() << "Failed to parse HTML:" << reader.errorString();
|
||||
}
|
||||
}
|
||||
|
||||
void Html::WriteBlock(QXmlStreamWriter *writer, const QTextBlock &block)
|
||||
{
|
||||
writer->writeStartElement(QStringLiteral("p"));
|
||||
|
||||
const QTextBlockFormat &fmt = block.blockFormat();
|
||||
|
||||
// Write block alignment
|
||||
if (!(fmt.alignment() & Qt::AlignLeft)) {
|
||||
if (fmt.alignment() & Qt::AlignRight) {
|
||||
writer->writeAttribute(QStringLiteral("align"), QStringLiteral("right"));
|
||||
} else if (fmt.alignment() & Qt::AlignCenter) {
|
||||
writer->writeAttribute(QStringLiteral("align"), QStringLiteral("center"));
|
||||
} else if (fmt.alignment() & Qt::AlignJustify) {
|
||||
writer->writeAttribute(QStringLiteral("align"), QStringLiteral("justify"));
|
||||
}
|
||||
}
|
||||
|
||||
// RTL support
|
||||
if (block.textDirection() == Qt::RightToLeft) {
|
||||
writer->writeAttribute(QStringLiteral("dir"), QStringLiteral("rtl"));
|
||||
}
|
||||
|
||||
// Write CSS attributes
|
||||
QString style;
|
||||
|
||||
if (fmt.lineHeightType() != QTextBlockFormat::SingleHeight) {
|
||||
WriteCSSProperty(&style, QStringLiteral("line-height"), QStringLiteral("%1%").arg(fmt.lineHeight()));
|
||||
}
|
||||
|
||||
//WriteCharFormat(&style, block.charFormat());
|
||||
|
||||
if (!style.isEmpty()) {
|
||||
writer->writeAttribute(QStringLiteral("style"), style);
|
||||
}
|
||||
|
||||
auto it = block.begin();
|
||||
|
||||
if (it == block.end()) {
|
||||
// FIXME: Might not be necessary with our custom HTML implementation
|
||||
QString s;
|
||||
s.append(QChar::Nbsp);
|
||||
writer->writeCharacters(s);
|
||||
} else {
|
||||
for (; it!=block.end(); it++) {
|
||||
WriteFragment(writer, it.fragment());
|
||||
}
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // p
|
||||
}
|
||||
|
||||
void Html::WriteFragment(QXmlStreamWriter *writer, const QTextFragment &fragment)
|
||||
{
|
||||
const QTextCharFormat &fmt = fragment.charFormat();
|
||||
|
||||
writer->writeStartElement(QStringLiteral("span"));
|
||||
|
||||
// Write CSS attributes
|
||||
QString style;
|
||||
|
||||
WriteCharFormat(&style, fmt);
|
||||
|
||||
if (!style.isEmpty()) {
|
||||
writer->writeAttribute(QStringLiteral("style"), style);
|
||||
}
|
||||
|
||||
QStringList lines = fragment.text().split(QChar::LineSeparator);
|
||||
bool first_line = true;
|
||||
foreach (const QString &l, lines) {
|
||||
if (first_line) {
|
||||
first_line = false;
|
||||
} else {
|
||||
writer->writeEmptyElement(QStringLiteral("br"));
|
||||
}
|
||||
writer->writeCharacters(l);
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // span
|
||||
}
|
||||
|
||||
void Html::WriteCSSProperty(QString *style, const QString &key, const QStringList &values)
|
||||
{
|
||||
QString value;
|
||||
foreach (QString v, values) {
|
||||
if (v.contains(' ')) {
|
||||
v = QStringLiteral("'%1'").arg(v);
|
||||
}
|
||||
|
||||
AppendStringAutoSpace(&value, v);
|
||||
}
|
||||
|
||||
AppendStringAutoSpace(style, QStringLiteral("%1: %2;").arg(key, value));
|
||||
}
|
||||
|
||||
void Html::WriteCharFormat(QString *style, const QTextCharFormat &fmt)
|
||||
{
|
||||
if (!fmt.fontFamily().isEmpty()) {
|
||||
WriteCSSProperty(style, QStringLiteral("font-family"), fmt.fontFamily());
|
||||
}
|
||||
|
||||
if (fmt.hasProperty(QTextFormat::FontPointSize)) {
|
||||
WriteCSSProperty(style, QStringLiteral("font-size"), QStringLiteral("%1pt").arg(QString::number(fmt.fontPointSize())));
|
||||
}
|
||||
|
||||
if (fmt.hasProperty(QTextFormat::FontWeight)) {
|
||||
WriteCSSProperty(style, QStringLiteral("font-weight"), QString::number(fmt.fontWeight() * 8));
|
||||
}
|
||||
|
||||
if (fmt.hasProperty(QTextFormat::FontItalic)) {
|
||||
WriteCSSProperty(style, QStringLiteral("font-style"), fmt.fontItalic() ? QStringLiteral("italic") : QStringLiteral("normal"));
|
||||
}
|
||||
|
||||
if (fmt.hasProperty(QTextFormat::FontStyleName)) {
|
||||
WriteCSSProperty(style, QStringLiteral("-ove-font-style"), fmt.fontStyleName().toString());
|
||||
}
|
||||
|
||||
QStringList deco;
|
||||
|
||||
if (fmt.fontUnderline()) {
|
||||
deco.append(QStringLiteral("underline"));
|
||||
}
|
||||
|
||||
if (fmt.fontStrikeOut()) {
|
||||
deco.append(QStringLiteral("line-through"));
|
||||
}
|
||||
|
||||
if (fmt.fontOverline()) {
|
||||
deco.append(QStringLiteral("overline"));
|
||||
}
|
||||
|
||||
if (!deco.isEmpty()) {
|
||||
WriteCSSProperty(style, QStringLiteral("text-decoration"), deco);
|
||||
}
|
||||
|
||||
if (fmt.foreground().style() != Qt::NoBrush) {
|
||||
const QColor &color = fmt.foreground().color();
|
||||
QString cs;
|
||||
|
||||
if (color.alpha() == 255) {
|
||||
cs = color.name();
|
||||
} else if (color.alpha()) {
|
||||
cs = QStringLiteral("rgba(%1, %2, %3, %4)").arg(QString::number(color.red()), QString::number(color.green()), QString::number(color.blue()), QString::number(color.alphaF()));
|
||||
}
|
||||
|
||||
WriteCSSProperty(style, QStringLiteral("color"), cs);
|
||||
}
|
||||
|
||||
if (fmt.fontCapitalization() != QFont::MixedCase) {
|
||||
if (fmt.fontCapitalization() == QFont::SmallCaps) {
|
||||
WriteCSSProperty(style, QStringLiteral("font-variant"), QStringLiteral("small-caps"));
|
||||
// TODO: Add others
|
||||
}
|
||||
}
|
||||
|
||||
if (fmt.fontLetterSpacing() != 0.0) {
|
||||
WriteCSSProperty(style, QStringLiteral("letter-spacing"), QStringLiteral("%1%").arg(QString::number(fmt.fontLetterSpacing())));
|
||||
}
|
||||
|
||||
if (fmt.fontStretch() != 0) {
|
||||
WriteCSSProperty(style, QStringLiteral("font-stretch"), QStringLiteral("%1%").arg(QString::number(fmt.fontStretch())));
|
||||
}
|
||||
}
|
||||
|
||||
QTextCharFormat Html::ReadCharFormat(const QXmlStreamAttributes &attributes)
|
||||
{
|
||||
QTextCharFormat fmt;
|
||||
|
||||
foreach (const QXmlStreamAttribute &attr, attributes) {
|
||||
if (StrEquals(attr.name(), QStringLiteral("style"))) {
|
||||
auto css = GetCSSFromStyle(attr.value().toString());
|
||||
|
||||
for (auto it=css.begin(); it!=css.end(); it++) {
|
||||
const QString &first_val = it.value().first();
|
||||
|
||||
if (it.key() == QStringLiteral("font-family")) {
|
||||
fmt.setFontFamily(first_val);
|
||||
} else if (it.key() == QStringLiteral("font-size")) {
|
||||
if (first_val.endsWith(QStringLiteral("pt"), Qt::CaseInsensitive)) {
|
||||
fmt.setFontPointSize(first_val.chopped(2).toDouble());
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("font-weight")) {
|
||||
fmt.setFontWeight(first_val.toInt()/8);
|
||||
} else if (it.key() == QStringLiteral("font-style")) {
|
||||
fmt.setFontItalic(StrEquals(first_val, QStringLiteral("italic")));
|
||||
} else if (it.key() == QStringLiteral("text-decoration")) {
|
||||
foreach (const QString &v, it.value()) {
|
||||
if (StrEquals(v, QStringLiteral("underline"))) {
|
||||
fmt.setFontUnderline(true);
|
||||
} else if (StrEquals(v, QStringLiteral("line-through"))) {
|
||||
fmt.setFontStrikeOut(true);
|
||||
} else if (StrEquals(v, QStringLiteral("overline"))) {
|
||||
fmt.setFontOverline(true);
|
||||
}
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("color")) {
|
||||
if (first_val.startsWith(QStringLiteral("rgba"), Qt::CaseInsensitive)) {
|
||||
QString vals_only = first_val;
|
||||
vals_only.remove(QStringLiteral("rgba"));
|
||||
vals_only.remove(QStringLiteral("("));
|
||||
vals_only.remove(QStringLiteral(")"));
|
||||
QStringList rgba = vals_only.split(',');
|
||||
if (rgba.size() == 4) {
|
||||
QColor c;
|
||||
c.setRedF(rgba.at(0).toDouble());
|
||||
c.setGreenF(rgba.at(1).toDouble());
|
||||
c.setBlueF(rgba.at(2).toDouble());
|
||||
c.setAlphaF(rgba.at(3).toDouble());
|
||||
fmt.setForeground(c);
|
||||
}
|
||||
} else {
|
||||
fmt.setForeground(QColor(first_val));
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("font-variant")) {
|
||||
if (StrEquals(first_val, QStringLiteral("small-caps"))) {
|
||||
fmt.setFontCapitalization(QFont::SmallCaps);
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("letter-spacing")) {
|
||||
if (first_val.contains(QChar('%'))) {
|
||||
fmt.setFontLetterSpacing(first_val.chopped(1).toDouble());
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("font-stretch")) {
|
||||
if (first_val.contains(QChar('%'))) {
|
||||
fmt.setFontStretch(first_val.chopped(1).toInt());
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("-ove-font-style")) {
|
||||
fmt.setFontStyleName(first_val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return fmt;
|
||||
}
|
||||
|
||||
QTextBlockFormat Html::ReadBlockFormat(const QXmlStreamAttributes &attributes)
|
||||
{
|
||||
QTextBlockFormat block_fmt;
|
||||
|
||||
foreach (const QXmlStreamAttribute &attr, attributes) {
|
||||
if (StrEquals(attr.name(), QStringLiteral("align"))) {
|
||||
if (StrEquals(attr.value(), QStringLiteral("right"))) {
|
||||
block_fmt.setAlignment(Qt::AlignRight);
|
||||
} else if (StrEquals(attr.value(), QStringLiteral("center"))) {
|
||||
block_fmt.setAlignment(Qt::AlignCenter);
|
||||
} else if (StrEquals(attr.value(), QStringLiteral("justify"))) {
|
||||
block_fmt.setAlignment(Qt::AlignJustify);
|
||||
}
|
||||
} else if (StrEquals(attr.name(), QStringLiteral("dir"))) {
|
||||
if (StrEquals(attr.value(), QStringLiteral("rtl"))) {
|
||||
block_fmt.setLayoutDirection(Qt::RightToLeft);
|
||||
}
|
||||
} else if (StrEquals(attr.name(), QStringLiteral("style"))) {
|
||||
auto css = GetCSSFromStyle(attr.value().toString());
|
||||
|
||||
for (auto it=css.begin(); it!=css.end(); it++) {
|
||||
if (it.key() == QStringLiteral("line-height")) {
|
||||
const QString &first_val = it.value().constFirst();
|
||||
if (first_val.contains(QChar('%'))) {
|
||||
block_fmt.setLineHeight(first_val.chopped(1).toDouble(), QTextBlockFormat::ProportionalHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return block_fmt;
|
||||
}
|
||||
|
||||
void Html::AppendStringAutoSpace(QString *s, const QString &append)
|
||||
{
|
||||
if (!s->isEmpty()) {
|
||||
s->append(QChar(' '));
|
||||
}
|
||||
|
||||
s->append(append);
|
||||
}
|
||||
|
||||
QMap<QString, QStringList> Html::GetCSSFromStyle(const QString &s)
|
||||
{
|
||||
QMap<QString, QStringList> map;
|
||||
|
||||
QStringList list = s.split(QChar(';'));
|
||||
|
||||
foreach (const QString &a, list) {
|
||||
QStringList kv = a.split(QChar(':'));
|
||||
|
||||
if (kv.size() != 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// I'm sure there's regex that could do this, but I couldn't figure it out. It needs to split
|
||||
// by space EXCEPT within quotes OR double-quotes, and said quotes should be EXCLUDED from each
|
||||
// match. Also commas should be filtered out.
|
||||
QStringList values;
|
||||
const QString &val = kv.at(1);
|
||||
QChar in_quote = 0;
|
||||
QString current_str;
|
||||
for (int i=0; i<val.size(); i++) {
|
||||
const QChar ¤t_char = val.at(i);
|
||||
|
||||
if (!in_quote.isNull()) {
|
||||
// If inside quotes and character isn't quote, indiscriminately append char
|
||||
if (current_char == in_quote) {
|
||||
in_quote = 0;
|
||||
} else {
|
||||
current_str.append(current_char);
|
||||
}
|
||||
} else if (current_char.isSpace() || current_char == QChar(',')) {
|
||||
// Dump current
|
||||
if (!current_str.isEmpty()) {
|
||||
values.append(current_str);
|
||||
current_str.clear();
|
||||
}
|
||||
} else if (in_quote.isNull() && (current_char == QChar('\'') || current_char == QChar('"'))) {
|
||||
in_quote = current_char;
|
||||
} else {
|
||||
current_str.append(current_char);
|
||||
}
|
||||
}
|
||||
|
||||
if (!current_str.isEmpty()) {
|
||||
values.append(current_str);
|
||||
}
|
||||
|
||||
// Not sure if this will ever happen, but just in case, we will avoid assert failures with this
|
||||
if (values.isEmpty()) {
|
||||
values.append(QString());
|
||||
}
|
||||
|
||||
map[kv.at(0).trimmed().toLower()] = values;
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#ifndef HTML_H
|
||||
#define HTML_H
|
||||
|
||||
#include <QTextDocument>
|
||||
#include <QTextFragment>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QXmlStreamWriter>
|
||||
|
||||
namespace olive {
|
||||
|
||||
/**
|
||||
* @brief Functions for converting HTML to QTextDocument and vice versa
|
||||
*
|
||||
* Qt does contain its own functions for this, however they have some limitations. Some things that
|
||||
* we want to support (e.g. kerning/spacing and font stretch) are not implemented in Qt's
|
||||
* QTextHtmlExporter and QTextHtmlParser. Additionally, since these functions are not part of Qt's
|
||||
* public API, and make many references to other parts of Qt that are not part of the public API,
|
||||
* there is no way to subclass or extend their functionality without forking Qt as a whole.
|
||||
*
|
||||
* Therefore, it became necessary to write a custom class for the conversion so that we can
|
||||
* ensure support for the features we need.
|
||||
*
|
||||
* If someone wishes to extend this class for more feature support, feel free to open a pull
|
||||
* request. But this is NOT intended to be an exhaustive HTML implementation, and is primarily
|
||||
* designed to store rich text in a standard format for the purpose of text formatting for video.
|
||||
*/
|
||||
class Html {
|
||||
public:
|
||||
static QString DocToHtml(const QTextDocument *doc);
|
||||
|
||||
static void HtmlToDoc(QTextDocument *doc, const QString &html);
|
||||
|
||||
private:
|
||||
static void WriteBlock(QXmlStreamWriter *writer, const QTextBlock &block);
|
||||
|
||||
static void WriteFragment(QXmlStreamWriter *writer, const QTextFragment &fragment);
|
||||
|
||||
static void WriteCSSProperty(QString *style, const QString &key, const QStringList &value);
|
||||
static void WriteCSSProperty(QString *style, const QString &key, const QString &value)
|
||||
{
|
||||
WriteCSSProperty(style, key, QStringList({value}));
|
||||
}
|
||||
|
||||
static void WriteCharFormat(QString *style, const QTextCharFormat &fmt);
|
||||
|
||||
static QTextCharFormat ReadCharFormat(const QXmlStreamAttributes &attributes);
|
||||
|
||||
static QTextBlockFormat ReadBlockFormat(const QXmlStreamAttributes &attributes);
|
||||
|
||||
static void AppendStringAutoSpace(QString *s, const QString &append);
|
||||
|
||||
static QMap<QString, QStringList> GetCSSFromStyle(const QString &s);
|
||||
|
||||
static const QVector<QString> kBlockTags;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // HTML_H
|
||||
@@ -38,6 +38,13 @@ QFrame *QtUtils::CreateHorizontalLine()
|
||||
return horizontal_line;
|
||||
}
|
||||
|
||||
QFrame *QtUtils::CreateVerticalLine()
|
||||
{
|
||||
QFrame *l = CreateHorizontalLine();
|
||||
l->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
|
||||
return l;
|
||||
}
|
||||
|
||||
int QtUtils::MessageBox(QWidget *parent, QMessageBox::Icon icon, const QString &title, const QString &message, QMessageBox::StandardButtons buttons)
|
||||
{
|
||||
QMessageBox b(parent);
|
||||
|
||||
@@ -54,6 +54,8 @@ public:
|
||||
|
||||
static QFrame* CreateHorizontalLine();
|
||||
|
||||
static QFrame* CreateVerticalLine();
|
||||
|
||||
static int MessageBox(QWidget *parent, QMessageBox::Icon icon, const QString& title, const QString& message, QMessageBox::StandardButtons buttons = QMessageBox::Ok);
|
||||
|
||||
static QDateTime GetCreationDate(const QFileInfo &info);
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
template <typename T>
|
||||
inline T mid(T a, T b)
|
||||
{
|
||||
return (a + b) * 0.5;
|
||||
}
|
||||
|
||||
#endif // UTIL_H
|
||||
+1
-1
@@ -921,7 +921,7 @@ void Core::SaveAutorecovery()
|
||||
foreach (Project* p, open_projects_) {
|
||||
if (!p->has_autorecovery_been_saved()) {
|
||||
QDir project_autorecovery_dir(QDir(FileFunctions::GetAutoRecoveryRoot()).filePath(p->GetUuid().toString()));
|
||||
if (project_autorecovery_dir.mkpath(QStringLiteral("."))) {
|
||||
if (FileFunctions::DirectoryIsValid(project_autorecovery_dir)) {
|
||||
QString this_autorecovery_path = project_autorecovery_dir.filePath(QStringLiteral("%1.ove").arg(QString::number(QDateTime::currentSecsSinceEpoch())));
|
||||
|
||||
SaveProjectInternal(p, this_autorecovery_path);
|
||||
|
||||
@@ -312,7 +312,8 @@ void ExportDialog::StartExport()
|
||||
QFileInfo dir_info(file_info.path());
|
||||
|
||||
// If the directory does not exist, try to create it
|
||||
if (!QDir(file_info.path()).mkpath(QStringLiteral("."))) {
|
||||
QDir dest_dir(file_info.path());
|
||||
if (!FileFunctions::DirectoryIsValid(dest_dir)) {
|
||||
QtUtils::MessageBox(this, QMessageBox::Critical, tr("Failed to create output directory"),
|
||||
tr("The intended output directory doesn't exist and Olive couldn't create it. "
|
||||
"Please choose a different filename."));
|
||||
|
||||
@@ -96,7 +96,7 @@ bool PreferencesDiskTab::Validate()
|
||||
}
|
||||
|
||||
// Check validity of the new path
|
||||
if (!FileFunctions::DirectoryIsValid(disk_cache_location_->text(), true)) {
|
||||
if (!FileFunctions::DirectoryIsValid(disk_cache_location_->text())) {
|
||||
QMessageBox::critical(this,
|
||||
tr("Disk Cache"),
|
||||
tr("Failed to set disk cache location. Access was denied."));
|
||||
|
||||
@@ -186,7 +186,7 @@ void ProjectPropertiesDialog::accept()
|
||||
|
||||
bool ProjectPropertiesDialog::VerifyPathAndWarnIfBad(const QString &path)
|
||||
{
|
||||
if (!FileFunctions::DirectoryIsValid(path, true)) {
|
||||
if (!FileFunctions::DirectoryIsValid(path)) {
|
||||
QMessageBox mb(this);
|
||||
mb.setWindowModality(Qt::WindowModal);
|
||||
mb.setIcon(QMessageBox::Critical);
|
||||
|
||||
@@ -155,6 +155,34 @@ void SpeedDurationDialog::accept()
|
||||
|
||||
MultiUndoCommand *command = new MultiUndoCommand();
|
||||
|
||||
// Set duration values
|
||||
foreach (ClipBlock *c, clips_) {
|
||||
rational proposed_length = c->length();
|
||||
|
||||
if (dur_slider_->IsTristate()) {
|
||||
if (link_box_->isChecked() && !speed_slider_->IsTristate()) {
|
||||
proposed_length = GetLengthAdjustment(c->length(), c->speed(), speed_slider_->GetValue(), timebase_);
|
||||
}
|
||||
} else {
|
||||
proposed_length = dur_slider_->GetValue();
|
||||
}
|
||||
|
||||
if (proposed_length != c->length()) {
|
||||
// Clip length should ideally change, but check if there's "room" to do so
|
||||
if (proposed_length > c->length() && c->next()) {
|
||||
if (GapBlock *gap = dynamic_cast<GapBlock*>(c->next())) {
|
||||
proposed_length = qMin(proposed_length, gap->out() - c->in());
|
||||
} else {
|
||||
proposed_length = c->length();
|
||||
}
|
||||
}
|
||||
|
||||
if (proposed_length != c->length()) {
|
||||
command->add_child(new BlockTrimCommand(c->track(), c, proposed_length, Timeline::kTrimOut));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set speed values
|
||||
if (speed_slider_->IsTristate()) {
|
||||
if (link_box_->isChecked() && !dur_slider_->IsTristate()) {
|
||||
@@ -184,34 +212,6 @@ void SpeedDurationDialog::accept()
|
||||
}
|
||||
}
|
||||
|
||||
// Set duration values
|
||||
foreach (ClipBlock *c, clips_) {
|
||||
rational proposed_length = c->length();
|
||||
|
||||
if (dur_slider_->IsTristate()) {
|
||||
if (link_box_->isChecked() && !speed_slider_->IsTristate()) {
|
||||
proposed_length = GetLengthAdjustment(c->length(), c->speed(), speed_slider_->GetValue(), timebase_);
|
||||
}
|
||||
} else {
|
||||
proposed_length = dur_slider_->GetValue();
|
||||
}
|
||||
|
||||
if (proposed_length != c->length()) {
|
||||
// Clip length should ideally change, but check if there's "room" to do so
|
||||
if (proposed_length > c->length() && c->next()) {
|
||||
if (GapBlock *gap = dynamic_cast<GapBlock*>(c->next())) {
|
||||
proposed_length = qMin(proposed_length, gap->out() - c->in());
|
||||
} else {
|
||||
proposed_length = c->length();
|
||||
}
|
||||
}
|
||||
|
||||
if (proposed_length != c->length()) {
|
||||
command->add_child(new BlockTrimCommand(c->track(), c, proposed_length, Timeline::kTrimOut));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->push(command);
|
||||
|
||||
super::accept();
|
||||
|
||||
+1
-2
@@ -190,9 +190,8 @@ int main(int argc, char *argv[])
|
||||
//
|
||||
// https://bugreports.qt.io/browse/QTBUG-46140
|
||||
QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
|
||||
format.setVersion(2, 0);
|
||||
format.setVersion(3, 2);
|
||||
format.setProfile(QSurfaceFormat::CoreProfile);
|
||||
format.setOption(QSurfaceFormat::DeprecatedFunctions);
|
||||
|
||||
format.setDepthBufferSize(24);
|
||||
QSurfaceFormat::setDefaultFormat(format);
|
||||
|
||||
@@ -21,8 +21,10 @@ add_subdirectory(distort)
|
||||
add_subdirectory(effect)
|
||||
add_subdirectory(filter)
|
||||
add_subdirectory(generator)
|
||||
add_subdirectory(gizmo)
|
||||
add_subdirectory(group)
|
||||
add_subdirectory(input)
|
||||
add_subdirectory(keying)
|
||||
add_subdirectory(math)
|
||||
add_subdirectory(output)
|
||||
add_subdirectory(project)
|
||||
|
||||
@@ -54,7 +54,7 @@ QString PanNode::id() const
|
||||
|
||||
QVector<Node::CategoryID> PanNode::Category() const
|
||||
{
|
||||
return {kCategoryChannels};
|
||||
return {kCategoryFilter};
|
||||
}
|
||||
|
||||
QString PanNode::Description() const
|
||||
|
||||
@@ -95,7 +95,8 @@ void ClipBlock::set_length_and_media_out(const rational &length)
|
||||
|
||||
if (reverse()) {
|
||||
// Calculate media_in adjustment
|
||||
set_media_in(SequenceToMediaTime(length - this->length(), true));
|
||||
rational proposed_media_in = SequenceToMediaTime(this->length() - length, true);
|
||||
set_media_in(proposed_media_in);
|
||||
}
|
||||
|
||||
super::set_length_and_media_out(length);
|
||||
@@ -109,7 +110,14 @@ void ClipBlock::set_length_and_media_in(const rational &length)
|
||||
|
||||
if (!reverse()) {
|
||||
// Calculate media_in adjustment
|
||||
set_media_in(SequenceToMediaTime(this->length() - length));
|
||||
rational proposed_media_in = SequenceToMediaTime(this->length() - length, false, true);
|
||||
|
||||
waveform_.TrimIn(proposed_media_in - media_in());
|
||||
|
||||
set_media_in(proposed_media_in);
|
||||
} else {
|
||||
// Trim waveform out point
|
||||
waveform_.TrimIn(this->length() - length);
|
||||
}
|
||||
|
||||
super::set_length_and_media_in(length);
|
||||
@@ -125,7 +133,7 @@ void ClipBlock::set_media_in(const rational &media_in)
|
||||
SetStandardValue(kMediaInInput, QVariant::fromValue(media_in));
|
||||
}
|
||||
|
||||
rational ClipBlock::SequenceToMediaTime(const rational &sequence_time, bool ignore_reverse) const
|
||||
rational ClipBlock::SequenceToMediaTime(const rational &sequence_time, bool ignore_reverse, bool ignore_speed) const
|
||||
{
|
||||
// These constants are not considered "values" per se, so we don't modify them
|
||||
if (sequence_time == RATIONAL_MIN || sequence_time == RATIONAL_MAX) {
|
||||
@@ -134,19 +142,21 @@ rational ClipBlock::SequenceToMediaTime(const rational &sequence_time, bool igno
|
||||
|
||||
rational media_time = sequence_time;
|
||||
|
||||
double speed_value = speed();
|
||||
if (qIsNull(speed_value)) {
|
||||
// Effectively holds the frame at the in point
|
||||
media_time = 0;
|
||||
} else if (!qFuzzyCompare(speed_value, 1.0)) {
|
||||
// Multiply time
|
||||
media_time = rational::fromDouble(media_time.toDouble() * speed_value);
|
||||
}
|
||||
|
||||
if (reverse() && !ignore_reverse) {
|
||||
media_time = length() - media_time;
|
||||
}
|
||||
|
||||
if (!ignore_speed) {
|
||||
double speed_value = speed();
|
||||
if (qIsNull(speed_value)) {
|
||||
// Effectively holds the frame at the in point
|
||||
media_time = 0;
|
||||
} else if (!qFuzzyCompare(speed_value, 1.0)) {
|
||||
// Multiply time
|
||||
media_time = rational::fromDouble(media_time.toDouble() * speed_value);
|
||||
}
|
||||
}
|
||||
|
||||
media_time += media_in();
|
||||
|
||||
return media_time;
|
||||
@@ -161,10 +171,6 @@ rational ClipBlock::MediaToSequenceTime(const rational &media_time) const
|
||||
|
||||
rational sequence_time = media_time - media_in();
|
||||
|
||||
if (reverse()) {
|
||||
sequence_time = length() - sequence_time;
|
||||
}
|
||||
|
||||
double speed_value = speed();
|
||||
if (qIsNull(speed_value)) {
|
||||
// I don't know what to return here yet...
|
||||
@@ -174,6 +180,10 @@ rational ClipBlock::MediaToSequenceTime(const rational &media_time) const
|
||||
sequence_time = rational::fromDouble(sequence_time.toDouble() / speed_value);
|
||||
}
|
||||
|
||||
if (reverse()) {
|
||||
sequence_time = length() - sequence_time;
|
||||
}
|
||||
|
||||
return sequence_time;
|
||||
}
|
||||
|
||||
@@ -222,19 +232,6 @@ void ClipBlock::LinkChangeEvent()
|
||||
}
|
||||
}
|
||||
|
||||
void ClipBlock::InputValueChangedEvent(const QString &input, int element)
|
||||
{
|
||||
super::InputValueChangedEvent(input, element);
|
||||
|
||||
if (input == kMediaInInput) {
|
||||
// Shift waveform in the inverse that the media in moved
|
||||
rational diff = media_in() - last_media_in_;
|
||||
waveform_.TrimIn(diff);
|
||||
|
||||
last_media_in_ = media_in();
|
||||
}
|
||||
}
|
||||
|
||||
TimeRange ClipBlock::InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const
|
||||
{
|
||||
Q_UNUSED(element)
|
||||
|
||||
@@ -130,12 +130,10 @@ public:
|
||||
protected:
|
||||
virtual void LinkChangeEvent() override;
|
||||
|
||||
virtual void InputValueChangedEvent(const QString &input, int element) override;
|
||||
|
||||
virtual void Hash(QCryptographicHash &hash, const NodeGlobals &globals, const VideoParams& video_params) const override;
|
||||
|
||||
private:
|
||||
rational SequenceToMediaTime(const rational& sequence_time, bool ignore_reverse = false) const;
|
||||
rational SequenceToMediaTime(const rational& sequence_time, bool ignore_reverse = false, bool ignore_speed = false) const;
|
||||
|
||||
rational MediaToSequenceTime(const rational& media_time) const;
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ ColorManager::ColorManager() :
|
||||
|
||||
// Set config to our built-in default
|
||||
SetConfig(GetDefaultConfig());
|
||||
SetDefaultInputColorSpace(QStringLiteral("sRGB OETF"));
|
||||
SetDefaultInputColorSpace(config_->getCanonicalName(OCIO::ROLE_DEFAULT));
|
||||
}
|
||||
|
||||
OCIO::ConstConfigRcPtr ColorManager::GetConfig() const
|
||||
|
||||
@@ -41,6 +41,13 @@ CornerPinDistortNode::CornerPinDistortNode()
|
||||
AddInput(kTopRightInput, NodeValue::kVec2, QVector2D(0.0, 0.0));
|
||||
AddInput(kBottomRightInput, NodeValue::kVec2, QVector2D(0.0, 0.0));
|
||||
AddInput(kBottomLeftInput, NodeValue::kVec2, QVector2D(0.0, 0.0));
|
||||
|
||||
// Initiate gizmos
|
||||
gizmo_whole_rect_ = AddDraggableGizmo<PolygonGizmo>();
|
||||
gizmo_resize_handle_[0] = AddDraggableGizmo<PointGizmo>({NodeKeyframeTrackReference(NodeInput(this, kTopLeftInput), 0), NodeKeyframeTrackReference(NodeInput(this, kTopLeftInput), 1)});
|
||||
gizmo_resize_handle_[1] = AddDraggableGizmo<PointGizmo>({NodeKeyframeTrackReference(NodeInput(this, kTopRightInput), 0), NodeKeyframeTrackReference(NodeInput(this, kTopRightInput), 1)});
|
||||
gizmo_resize_handle_[2] = AddDraggableGizmo<PointGizmo>({NodeKeyframeTrackReference(NodeInput(this, kBottomRightInput), 0), NodeKeyframeTrackReference(NodeInput(this, kBottomRightInput), 1)});
|
||||
gizmo_resize_handle_[3] = AddDraggableGizmo<PointGizmo>({NodeKeyframeTrackReference(NodeInput(this, kBottomLeftInput), 0), NodeKeyframeTrackReference(NodeInput(this, kBottomLeftInput), 1)});
|
||||
}
|
||||
|
||||
void CornerPinDistortNode::Retranslate()
|
||||
@@ -97,18 +104,9 @@ void CornerPinDistortNode::Value(const NodeValueRow &value, const NodeGlobals &g
|
||||
ShaderCode CornerPinDistortNode::GetShaderCode(const QString &shader_id) const
|
||||
{
|
||||
Q_UNUSED(shader_id)
|
||||
QString frag = FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/cornerpin.frag"));
|
||||
QString vert = FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/cornerpin.vert"));
|
||||
|
||||
// HACK: No good, very bad hack
|
||||
#ifndef Q_OS_MAC
|
||||
frag.prepend(QStringLiteral("#version 130\n\n"));
|
||||
vert.prepend(QStringLiteral("#version 130\n\n"));
|
||||
#else
|
||||
vert.prepend(QStringLiteral("#extension GL_EXT_gpu_shader4 : require\n\n"));
|
||||
#endif
|
||||
|
||||
return ShaderCode(frag, vert);
|
||||
return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/cornerpin.frag")),
|
||||
FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/cornerpin.vert")));
|
||||
}
|
||||
|
||||
QPointF CornerPinDistortNode::ValueToPixel(int value, const NodeValueRow& row, const QVector2D &resolution) const
|
||||
@@ -136,14 +134,20 @@ QPointF CornerPinDistortNode::ValueToPixel(int value, const NodeValueRow& row, c
|
||||
}
|
||||
}
|
||||
|
||||
void CornerPinDistortNode::DrawGizmos(const NodeValueRow &row, const NodeGlobals &globals, QPainter *p)
|
||||
void CornerPinDistortNode::GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers)
|
||||
{
|
||||
DraggableGizmo *gizmo = static_cast<DraggableGizmo*>(sender());
|
||||
|
||||
if (gizmo != gizmo_whole_rect_) {
|
||||
gizmo->GetDraggers()[0].Drag(gizmo->GetDraggers()[0].GetStartValue().toDouble() + x);
|
||||
gizmo->GetDraggers()[1].Drag(gizmo->GetDraggers()[1].GetStartValue().toDouble() + y);
|
||||
}
|
||||
}
|
||||
|
||||
void CornerPinDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals)
|
||||
{
|
||||
const QVector2D &resolution = globals.resolution();
|
||||
|
||||
const double handle_radius = GetGizmoHandleRadius(p->transform());
|
||||
|
||||
p->setPen(QPen(Qt::white, 0));
|
||||
|
||||
QPointF top_left = ValueToPixel(0, row, resolution);
|
||||
QPointF top_right = ValueToPixel(1, row, resolution);
|
||||
QPointF bottom_right = ValueToPixel(2, row, resolution);
|
||||
@@ -156,71 +160,13 @@ void CornerPinDistortNode::DrawGizmos(const NodeValueRow &row, const NodeGlobals
|
||||
SetInputProperty(kBottomLeftInput, QStringLiteral("offset"), QVector2D(0.0, resolution.y()));
|
||||
|
||||
// Draw bounding box
|
||||
p->drawLine(QLineF(top_left, top_right));
|
||||
p->drawLine(QLineF(top_right, bottom_right));
|
||||
p->drawLine(QLineF(bottom_right, bottom_left));
|
||||
p->drawLine(QLineF(bottom_left, top_left));
|
||||
gizmo_whole_rect_->SetPolygon(QPolygonF({top_left, top_right, bottom_right, bottom_left, top_left}));
|
||||
|
||||
// Create handles
|
||||
gizmo_resize_handle_[0] = CreateGizmoHandleRect(top_left, handle_radius);
|
||||
gizmo_resize_handle_[1] = CreateGizmoHandleRect(top_right, handle_radius);
|
||||
gizmo_resize_handle_[2] = CreateGizmoHandleRect(bottom_right, handle_radius);
|
||||
gizmo_resize_handle_[3] = CreateGizmoHandleRect(bottom_left, handle_radius);
|
||||
|
||||
// Draw handles
|
||||
DrawAndExpandGizmoHandles(p, handle_radius, gizmo_resize_handle_, kGizmoCornerCount);
|
||||
}
|
||||
|
||||
bool CornerPinDistortNode::GizmoPress(const NodeValueRow &row, const NodeGlobals &globals, const QPointF &p)
|
||||
{
|
||||
bool gizmo_active[kGizmoCornerCount] = {false};
|
||||
|
||||
for (int i = 0; i < kGizmoCornerCount; i++) {
|
||||
gizmo_active[i] = gizmo_resize_handle_[i].contains(p);
|
||||
|
||||
if (gizmo_active[i]) {
|
||||
gizmo_drag_start_ = p;
|
||||
gizmo_res_ = globals.resolution();
|
||||
gizmo_drag_ = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CornerPinDistortNode::GizmoMove(const QPointF &p, const rational &time, const Qt::KeyboardModifiers &modifiers)
|
||||
{
|
||||
if (gizmo_dragger_.isEmpty()) {
|
||||
gizmo_dragger_.resize(2);
|
||||
if (gizmo_drag_ == 0) {
|
||||
gizmo_dragger_[0].Start(NodeKeyframeTrackReference(NodeInput(this, kTopLeftInput), 0), time);
|
||||
gizmo_dragger_[1].Start(NodeKeyframeTrackReference(NodeInput(this, kTopLeftInput), 1), time);
|
||||
}
|
||||
if (gizmo_drag_ == 1) {
|
||||
gizmo_dragger_[0].Start(NodeKeyframeTrackReference(NodeInput(this, kTopRightInput), 0), time);
|
||||
gizmo_dragger_[1].Start(NodeKeyframeTrackReference(NodeInput(this, kTopRightInput), 1), time);
|
||||
}
|
||||
if (gizmo_drag_ == 2) {
|
||||
gizmo_dragger_[0].Start(NodeKeyframeTrackReference(NodeInput(this, kBottomRightInput), 0), time);
|
||||
gizmo_dragger_[1].Start(NodeKeyframeTrackReference(NodeInput(this, kBottomRightInput), 1), time);
|
||||
}
|
||||
if (gizmo_drag_ == 3) {
|
||||
gizmo_dragger_[0].Start(NodeKeyframeTrackReference(NodeInput(this, kBottomLeftInput), 0), time);
|
||||
gizmo_dragger_[1].Start(NodeKeyframeTrackReference(NodeInput(this, kBottomLeftInput), 1), time);
|
||||
}
|
||||
}
|
||||
|
||||
QPointF diff = p - gizmo_drag_start_;
|
||||
gizmo_dragger_[0].Drag(gizmo_dragger_[0].GetStartValue().toDouble() + diff.x());
|
||||
gizmo_dragger_[1].Drag(gizmo_dragger_[1].GetStartValue().toDouble() + diff.y());
|
||||
}
|
||||
|
||||
void CornerPinDistortNode::GizmoRelease(MultiUndoCommand *command) {
|
||||
for (NodeInputDragger &i : gizmo_dragger_) {
|
||||
i.End(command);
|
||||
}
|
||||
gizmo_dragger_.clear();
|
||||
gizmo_resize_handle_[0]->SetPoint(top_left);
|
||||
gizmo_resize_handle_[1]->SetPoint(top_right);
|
||||
gizmo_resize_handle_[2]->SetPoint(bottom_right);
|
||||
gizmo_resize_handle_[3]->SetPoint(bottom_left);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
|
||||
#include <QVector2D>
|
||||
|
||||
#include "node/gizmo/point.h"
|
||||
#include "node/gizmo/polygon.h"
|
||||
#include "node/inputdragger.h"
|
||||
#include "node/node.h"
|
||||
|
||||
@@ -66,16 +68,7 @@ public:
|
||||
|
||||
virtual ShaderCode GetShaderCode(const QString &shader_id) const override;
|
||||
|
||||
virtual bool HasGizmos() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void DrawGizmos(const NodeValueRow& row, const NodeGlobals &globals, QPainter *p) override;
|
||||
|
||||
virtual bool GizmoPress(const NodeValueRow& row, const NodeGlobals &globals, const QPointF &p) override;
|
||||
virtual void GizmoMove(const QPointF &p, const rational &time, const Qt::KeyboardModifiers &modifiers) override;
|
||||
virtual void GizmoRelease(MultiUndoCommand *command) override;
|
||||
virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override;
|
||||
|
||||
/**
|
||||
* @brief Convenience function - converts the 2D slider values from being
|
||||
@@ -90,16 +83,14 @@ public:
|
||||
static const QString kBottomRightInput;
|
||||
static const QString kBottomLeftInput;
|
||||
|
||||
protected slots:
|
||||
virtual void GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers) override;
|
||||
|
||||
private:
|
||||
// Gizmo variables
|
||||
static const int kGizmoCornerCount = 4;
|
||||
QRectF gizmo_resize_handle_[kGizmoCornerCount];
|
||||
QRectF gizmo_whole_rect_;
|
||||
|
||||
int gizmo_drag_;
|
||||
QVector<NodeInputDragger> gizmo_dragger_;
|
||||
QPointF gizmo_drag_start_;
|
||||
QVector2D gizmo_res_;
|
||||
PointGizmo *gizmo_resize_handle_[kGizmoCornerCount];
|
||||
PolygonGizmo *gizmo_whole_rect_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
#include "cropdistortnode.h"
|
||||
|
||||
#include "common/lerp.h"
|
||||
#include "common/util.h"
|
||||
#include "core.h"
|
||||
#include "widget/slider/floatslider.h"
|
||||
|
||||
@@ -44,6 +44,18 @@ CropDistortNode::CropDistortNode()
|
||||
|
||||
AddInput(kFeatherInput, NodeValue::kFloat, 0.0);
|
||||
SetInputProperty(kFeatherInput, QStringLiteral("min"), 0.0);
|
||||
|
||||
// Initiate gizmos
|
||||
poly_gizmo_ = AddDraggableGizmo<PolygonGizmo>({kLeftInput, kTopInput, kRightInput, kBottomInput});
|
||||
|
||||
point_gizmo_[kGizmoScaleTopLeft] = AddDraggableGizmo<PointGizmo>({kLeftInput, kTopInput});
|
||||
point_gizmo_[kGizmoScaleTopCenter] = AddDraggableGizmo<PointGizmo>({kTopInput});
|
||||
point_gizmo_[kGizmoScaleTopRight] = AddDraggableGizmo<PointGizmo>({kRightInput, kTopInput});
|
||||
point_gizmo_[kGizmoScaleBottomLeft] = AddDraggableGizmo<PointGizmo>({kLeftInput, kBottomInput});
|
||||
point_gizmo_[kGizmoScaleBottomCenter] = AddDraggableGizmo<PointGizmo>({kBottomInput});
|
||||
point_gizmo_[kGizmoScaleBottomRight] = AddDraggableGizmo<PointGizmo>({kRightInput, kBottomInput});
|
||||
point_gizmo_[kGizmoScaleCenterLeft] = AddDraggableGizmo<PointGizmo>({kLeftInput});
|
||||
point_gizmo_[kGizmoScaleCenterRight] = AddDraggableGizmo<PointGizmo>({kRightInput});
|
||||
}
|
||||
|
||||
void CropDistortNode::Retranslate()
|
||||
@@ -81,164 +93,50 @@ ShaderCode CropDistortNode::GetShaderCode(const QString &shader_id) const
|
||||
return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/crop.frag")));
|
||||
}
|
||||
|
||||
void CropDistortNode::DrawGizmos(const NodeValueRow &row, const NodeGlobals &globals, QPainter *p)
|
||||
void CropDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals)
|
||||
{
|
||||
const QVector2D &resolution = globals.resolution();
|
||||
|
||||
const double handle_radius = GetGizmoHandleRadius(p->transform());
|
||||
|
||||
p->setPen(QPen(Qt::white, 0));
|
||||
|
||||
double left_pt = resolution.x() * row[kLeftInput].data().toDouble();
|
||||
double top_pt = resolution.y() * row[kTopInput].data().toDouble();
|
||||
double right_pt = resolution.x() * (1.0 - row[kRightInput].data().toDouble());
|
||||
double bottom_pt = resolution.y() * (1.0 - row[kBottomInput].data().toDouble());
|
||||
double center_x_pt = lerp(left_pt, right_pt, 0.5);
|
||||
double center_y_pt = lerp(top_pt, bottom_pt, 0.5);
|
||||
double center_x_pt = mid(left_pt, right_pt);
|
||||
double center_y_pt = mid(top_pt, bottom_pt);
|
||||
|
||||
gizmo_whole_rect_ = QRectF(left_pt, top_pt, right_pt - left_pt, bottom_pt - top_pt);
|
||||
p->drawRect(gizmo_whole_rect_);
|
||||
point_gizmo_[kGizmoScaleTopLeft]->SetPoint(QPointF(left_pt, top_pt));
|
||||
point_gizmo_[kGizmoScaleTopCenter]->SetPoint(QPointF(center_x_pt, top_pt));
|
||||
point_gizmo_[kGizmoScaleTopRight]->SetPoint(QPointF(right_pt, top_pt));
|
||||
point_gizmo_[kGizmoScaleBottomLeft]->SetPoint(QPointF(left_pt, bottom_pt));
|
||||
point_gizmo_[kGizmoScaleBottomCenter]->SetPoint(QPointF(center_x_pt, bottom_pt));
|
||||
point_gizmo_[kGizmoScaleBottomRight]->SetPoint(QPointF(right_pt, bottom_pt));
|
||||
point_gizmo_[kGizmoScaleCenterLeft]->SetPoint(QPointF(left_pt, center_y_pt));
|
||||
point_gizmo_[kGizmoScaleCenterRight]->SetPoint(QPointF(right_pt, center_y_pt));
|
||||
|
||||
gizmo_resize_handle_[kGizmoScaleTopLeft] = CreateGizmoHandleRect(QPointF(left_pt, top_pt), handle_radius);
|
||||
gizmo_resize_handle_[kGizmoScaleTopCenter] = CreateGizmoHandleRect(QPointF(center_x_pt, top_pt), handle_radius);
|
||||
gizmo_resize_handle_[kGizmoScaleTopRight] = CreateGizmoHandleRect(QPointF(right_pt, top_pt), handle_radius);
|
||||
gizmo_resize_handle_[kGizmoScaleBottomLeft] = CreateGizmoHandleRect(QPointF(left_pt, bottom_pt), handle_radius);
|
||||
gizmo_resize_handle_[kGizmoScaleBottomCenter] = CreateGizmoHandleRect(QPointF(center_x_pt, bottom_pt), handle_radius);
|
||||
gizmo_resize_handle_[kGizmoScaleBottomRight] = CreateGizmoHandleRect(QPointF(right_pt, bottom_pt), handle_radius);
|
||||
gizmo_resize_handle_[kGizmoScaleCenterLeft] = CreateGizmoHandleRect(QPointF(left_pt, center_y_pt), handle_radius);
|
||||
gizmo_resize_handle_[kGizmoScaleCenterRight] = CreateGizmoHandleRect(QPointF(right_pt, center_y_pt), handle_radius);
|
||||
|
||||
DrawAndExpandGizmoHandles(p, handle_radius, gizmo_resize_handle_, kGizmoScaleCount);
|
||||
poly_gizmo_->SetPolygon(QRectF(left_pt, top_pt, right_pt - left_pt, bottom_pt - top_pt));
|
||||
}
|
||||
|
||||
bool CropDistortNode::GizmoPress(const NodeValueRow &row, const NodeGlobals &globals, const QPointF &p)
|
||||
void CropDistortNode::GizmoDragMove(double x_diff, double y_diff, const Qt::KeyboardModifiers &modifiers)
|
||||
{
|
||||
bool found_handle = false;
|
||||
DraggableGizmo *gizmo = static_cast<DraggableGizmo*>(sender());
|
||||
|
||||
bool gizmo_active[kGizmoScaleCount] = {false};
|
||||
QVector2D res = gizmo->GetGlobals().resolution();
|
||||
x_diff /= res.x();
|
||||
y_diff /= res.y();
|
||||
|
||||
for (int i=0; i<kGizmoScaleCount; i++) {
|
||||
gizmo_active[i] = gizmo_resize_handle_[i].contains(p);
|
||||
|
||||
if (gizmo_active[i]) {
|
||||
found_handle = true;
|
||||
break;
|
||||
for (int j=0; j<gizmo->GetDraggers().size(); j++) {
|
||||
NodeInputDragger& i = gizmo->GetDraggers()[j];
|
||||
double s = i.GetStartValue().toDouble();
|
||||
if (i.GetInput().input().input() == kLeftInput) {
|
||||
i.Drag(s + x_diff);
|
||||
} else if (i.GetInput().input().input() == kTopInput) {
|
||||
i.Drag(s + y_diff);
|
||||
} else if (i.GetInput().input().input() == kRightInput) {
|
||||
i.Drag(s - x_diff);
|
||||
} else if (i.GetInput().input().input() == kBottomInput) {
|
||||
i.Drag(s - y_diff);
|
||||
}
|
||||
}
|
||||
|
||||
bool in_rect = found_handle ? false : gizmo_whole_rect_.contains(p);
|
||||
|
||||
gizmo_drag_ = kGizmoNone;
|
||||
|
||||
// Drag handle
|
||||
if (gizmo_active[kGizmoScaleTopLeft]
|
||||
|| gizmo_active[kGizmoScaleCenterLeft]
|
||||
|| gizmo_active[kGizmoScaleBottomLeft]
|
||||
|| in_rect) {
|
||||
gizmo_drag_ |= kGizmoLeft;
|
||||
|
||||
gizmo_start_.append(row[kLeftInput].data());
|
||||
}
|
||||
|
||||
if (gizmo_active[kGizmoScaleTopLeft]
|
||||
|| gizmo_active[kGizmoScaleTopCenter]
|
||||
|| gizmo_active[kGizmoScaleTopRight]
|
||||
|| in_rect) {
|
||||
gizmo_drag_ |= kGizmoTop;
|
||||
|
||||
gizmo_start_.append(row[kTopInput].data());
|
||||
}
|
||||
|
||||
if (gizmo_active[kGizmoScaleTopRight]
|
||||
|| gizmo_active[kGizmoScaleCenterRight]
|
||||
|| gizmo_active[kGizmoScaleBottomRight]
|
||||
|| in_rect) {
|
||||
gizmo_drag_ |= kGizmoRight;
|
||||
|
||||
gizmo_start_.append(row[kRightInput].data());
|
||||
}
|
||||
|
||||
if (gizmo_active[kGizmoScaleBottomLeft]
|
||||
|| gizmo_active[kGizmoScaleBottomCenter]
|
||||
|| gizmo_active[kGizmoScaleBottomRight]
|
||||
|| in_rect) {
|
||||
gizmo_drag_ |= kGizmoBottom;
|
||||
|
||||
gizmo_start_.append(row[kBottomInput].data());
|
||||
}
|
||||
|
||||
if (gizmo_drag_ > kGizmoNone) {
|
||||
gizmo_res_ = globals.resolution();
|
||||
gizmo_drag_start_ = p;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CropDistortNode::GizmoMove(const QPointF &p, const rational &time, const Qt::KeyboardModifiers &modifiers)
|
||||
{
|
||||
if (gizmo_dragger_.isEmpty()) {
|
||||
gizmo_dragger_.resize(gizmo_start_.size());
|
||||
|
||||
int counter = 0;
|
||||
|
||||
if (gizmo_drag_ & kGizmoLeft) {
|
||||
gizmo_dragger_[counter].Start(NodeInput(this, kLeftInput), time);
|
||||
counter++;
|
||||
}
|
||||
|
||||
if (gizmo_drag_ & kGizmoTop) {
|
||||
gizmo_dragger_[counter].Start(NodeInput(this, kTopInput), time);
|
||||
counter++;
|
||||
}
|
||||
|
||||
if (gizmo_drag_ & kGizmoRight) {
|
||||
gizmo_dragger_[counter].Start(NodeInput(this, kRightInput), time);
|
||||
counter++;
|
||||
}
|
||||
|
||||
if (gizmo_drag_ & kGizmoBottom) {
|
||||
gizmo_dragger_[counter].Start(NodeInput(this, kBottomInput), time);
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
int counter = 0;
|
||||
|
||||
double x_diff = (p.x() - gizmo_drag_start_.x()) / gizmo_res_.x();
|
||||
double y_diff = (p.y() - gizmo_drag_start_.y()) / gizmo_res_.y();
|
||||
|
||||
if (gizmo_drag_ & kGizmoLeft) {
|
||||
gizmo_dragger_[counter].Drag(gizmo_start_[counter].toDouble() + x_diff);
|
||||
counter++;
|
||||
}
|
||||
|
||||
if (gizmo_drag_ & kGizmoTop) {
|
||||
gizmo_dragger_[counter].Drag(gizmo_start_[counter].toDouble() + y_diff);
|
||||
counter++;
|
||||
}
|
||||
|
||||
if (gizmo_drag_ & kGizmoRight) {
|
||||
gizmo_dragger_[counter].Drag(gizmo_start_[counter].toDouble() - x_diff);
|
||||
counter++;
|
||||
}
|
||||
|
||||
if (gizmo_drag_ & kGizmoBottom) {
|
||||
gizmo_dragger_[counter].Drag(gizmo_start_[counter].toDouble() - y_diff);
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
void CropDistortNode::GizmoRelease(MultiUndoCommand *command)
|
||||
{
|
||||
for (NodeInputDragger& i : gizmo_dragger_) {
|
||||
i.End(command);
|
||||
}
|
||||
gizmo_dragger_.clear();
|
||||
|
||||
gizmo_start_.clear();
|
||||
}
|
||||
|
||||
void CropDistortNode::CreateCropSideInput(const QString &id)
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
|
||||
#include <QVector2D>
|
||||
|
||||
#include "node/gizmo/point.h"
|
||||
#include "node/gizmo/polygon.h"
|
||||
#include "node/inputdragger.h"
|
||||
#include "node/node.h"
|
||||
|
||||
@@ -67,16 +69,7 @@ public:
|
||||
|
||||
virtual ShaderCode GetShaderCode(const QString &shader_id) const override;
|
||||
|
||||
virtual bool HasGizmos() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void DrawGizmos(const NodeValueRow& row, const NodeGlobals &globals, QPainter *p) override;
|
||||
|
||||
virtual bool GizmoPress(const NodeValueRow& row, const NodeGlobals &globals, const QPointF &p) override;
|
||||
virtual void GizmoMove(const QPointF &p, const rational &time, const Qt::KeyboardModifiers &modifiers) override;
|
||||
virtual void GizmoRelease(MultiUndoCommand *command) override;
|
||||
virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override;
|
||||
|
||||
static const QString kTextureInput;
|
||||
static const QString kLeftInput;
|
||||
@@ -85,27 +78,15 @@ public:
|
||||
static const QString kBottomInput;
|
||||
static const QString kFeatherInput;
|
||||
|
||||
protected slots:
|
||||
virtual void GizmoDragMove(double delta_x, double delta_y, const Qt::KeyboardModifiers &modifiers) override;
|
||||
|
||||
private:
|
||||
void CreateCropSideInput(const QString& id);
|
||||
|
||||
// Gizmo variables
|
||||
QRectF gizmo_resize_handle_[kGizmoScaleCount];
|
||||
QRectF gizmo_whole_rect_;
|
||||
|
||||
enum GizmoDragDirection {
|
||||
kGizmoNone = 0x0,
|
||||
kGizmoLeft = 0x1,
|
||||
kGizmoTop = 0x2,
|
||||
kGizmoRight = 0x4,
|
||||
kGizmoBottom = 0x8,
|
||||
kGizmoRectangle = 0xFF
|
||||
};
|
||||
|
||||
int gizmo_drag_;
|
||||
QVector<NodeInputDragger> gizmo_dragger_;
|
||||
QVector<QVariant> gizmo_start_;
|
||||
QPointF gizmo_drag_start_;
|
||||
QVector2D gizmo_res_;
|
||||
PointGizmo *point_gizmo_[kGizmoScaleCount];
|
||||
PolygonGizmo *poly_gizmo_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -41,6 +41,29 @@ TransformDistortNode::TransformDistortNode()
|
||||
AddInput(kInterpolationInput, NodeValue::kCombo, 2);
|
||||
|
||||
PrependInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||
|
||||
// Initiate gizmos
|
||||
rotation_gizmo_ = AddDraggableGizmo<ScreenGizmo>();
|
||||
rotation_gizmo_->AddInput(NodeInput(this, kRotationInput));
|
||||
rotation_gizmo_->SetDragValueBehavior(ScreenGizmo::kAbsolute);
|
||||
|
||||
poly_gizmo_ = AddDraggableGizmo<PolygonGizmo>();
|
||||
poly_gizmo_->AddInput(NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 0));
|
||||
poly_gizmo_->AddInput(NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 1));
|
||||
|
||||
anchor_gizmo_ = AddDraggableGizmo<PointGizmo>();
|
||||
anchor_gizmo_->SetShape(PointGizmo::kAnchorPoint);
|
||||
anchor_gizmo_->AddInput(NodeKeyframeTrackReference(NodeInput(this, kAnchorInput), 0));
|
||||
anchor_gizmo_->AddInput(NodeKeyframeTrackReference(NodeInput(this, kAnchorInput), 1));
|
||||
anchor_gizmo_->AddInput(NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 0));
|
||||
anchor_gizmo_->AddInput(NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 1));
|
||||
|
||||
for (int i=0; i<kGizmoScaleCount; i++) {
|
||||
point_gizmo_[i] = AddDraggableGizmo<PointGizmo>();
|
||||
point_gizmo_[i]->AddInput(NodeKeyframeTrackReference(NodeInput(this, kScaleInput), 0));
|
||||
point_gizmo_[i]->AddInput(NodeKeyframeTrackReference(NodeInput(this, kScaleInput), 1));
|
||||
point_gizmo_[i]->SetDragValueBehavior(PointGizmo::kAbsolute);
|
||||
}
|
||||
}
|
||||
|
||||
void TransformDistortNode::Retranslate()
|
||||
@@ -101,218 +124,6 @@ ShaderCode TransformDistortNode::GetShaderCode(const QString &shader_id) const
|
||||
return ShaderCode();
|
||||
}
|
||||
|
||||
bool TransformDistortNode::GizmoPress(const NodeValueRow &row, const NodeGlobals &globals, const QPointF &p)
|
||||
{
|
||||
TexturePtr tex = row[kTextureInput].data().value<TexturePtr>();
|
||||
if (!tex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Store cursor position
|
||||
gizmo_drag_pos_ = p;
|
||||
|
||||
// Check scaling
|
||||
bool gizmo_scale_active[kGizmoScaleCount] = {false};
|
||||
bool scaling = false;
|
||||
|
||||
for (int i=0; i<kGizmoScaleCount; i++) {
|
||||
gizmo_scale_active[i] = gizmo_resize_handle_[i].contains(p);
|
||||
|
||||
if (gizmo_scale_active[i]) {
|
||||
scaling = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (scaling) {
|
||||
|
||||
// Dragging scale handle
|
||||
gizmo_start_ = {row[kScaleInput].data()};
|
||||
gizmo_drag_ = kScaleInput;
|
||||
|
||||
gizmo_scale_uniform_ = row[kUniformScaleInput].data().toBool();
|
||||
|
||||
if (gizmo_scale_active[kGizmoScaleTopLeft] || gizmo_scale_active[kGizmoScaleTopRight]
|
||||
|| gizmo_scale_active[kGizmoScaleBottomLeft] || gizmo_scale_active[kGizmoScaleBottomRight]) {
|
||||
gizmo_scale_axes_ = kGizmoScaleBoth;
|
||||
} else if (gizmo_scale_active[kGizmoScaleCenterLeft] || gizmo_scale_active[kGizmoScaleCenterRight]) {
|
||||
gizmo_scale_axes_ = kGizmoScaleXOnly;
|
||||
} else {
|
||||
gizmo_scale_axes_ = kGizmoScaleYOnly;
|
||||
}
|
||||
|
||||
// Store texture size
|
||||
VideoParams texture_params = tex->params();
|
||||
QVector2D texture_sz(texture_params.square_pixel_width(), texture_params.height());
|
||||
gizmo_scale_anchor_ = row[kAnchorInput].data().value<QVector2D>() + texture_sz/2;
|
||||
|
||||
if (gizmo_scale_active[kGizmoScaleTopRight]
|
||||
|| gizmo_scale_active[kGizmoScaleBottomRight]
|
||||
|| gizmo_scale_active[kGizmoScaleCenterRight]) {
|
||||
// Right handles, flip X axis
|
||||
gizmo_scale_anchor_.setX(texture_sz.x() - gizmo_scale_anchor_.x());
|
||||
}
|
||||
|
||||
if (gizmo_scale_active[kGizmoScaleBottomLeft]
|
||||
|| gizmo_scale_active[kGizmoScaleBottomRight]
|
||||
|| gizmo_scale_active[kGizmoScaleBottomCenter]) {
|
||||
// Bottom handles, flip Y axis
|
||||
gizmo_scale_anchor_.setY(texture_sz.y() - gizmo_scale_anchor_.y());
|
||||
}
|
||||
|
||||
// Store current matrix
|
||||
gizmo_matrix_ = GenerateMatrix(row, false, true, true, true);
|
||||
|
||||
return true;
|
||||
|
||||
} else if (gizmo_anchor_pt_.contains(p)) {
|
||||
|
||||
// Dragging the anchor point specifically
|
||||
gizmo_start_ = {row[kAnchorInput].data(),
|
||||
row[kPositionInput].data()};
|
||||
gizmo_drag_ = kAnchorInput;
|
||||
|
||||
// Store current matrix
|
||||
gizmo_matrix_ = GenerateMatrix(row, false, true, true, false);
|
||||
|
||||
return true;
|
||||
|
||||
} else if (gizmo_rect_.containsPoint(p, Qt::OddEvenFill)) {
|
||||
|
||||
// Dragging the main rectangle
|
||||
gizmo_start_ = {row[kPositionInput].data()};
|
||||
gizmo_drag_ = kPositionInput;
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
// Dragging rotation
|
||||
gizmo_start_ = {row[kRotationInput].data()};
|
||||
gizmo_drag_ = kRotationInput;
|
||||
gizmo_start_angle_ = qAtan2(gizmo_drag_pos_.y() - gizmo_anchor_pt_.center().y(),
|
||||
gizmo_drag_pos_.x() - gizmo_anchor_pt_.center().x());
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void TransformDistortNode::GizmoMove(const QPointF &p, const rational &time, const Qt::KeyboardModifiers &modifiers)
|
||||
{
|
||||
QPointF movement = (p - gizmo_drag_pos_);
|
||||
QVector2D vec_movement(movement);
|
||||
|
||||
if (gizmo_drag_ == kAnchorInput) {
|
||||
|
||||
// Dragging the anchor point around
|
||||
if (gizmo_dragger_.isEmpty()) {
|
||||
gizmo_dragger_.resize(4);
|
||||
gizmo_dragger_[0].Start(NodeKeyframeTrackReference(NodeInput(this, kAnchorInput), 0), time);
|
||||
gizmo_dragger_[1].Start(NodeKeyframeTrackReference(NodeInput(this, kAnchorInput), 1), time);
|
||||
gizmo_dragger_[2].Start(NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 0), time);
|
||||
gizmo_dragger_[3].Start(NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 1), time);
|
||||
}
|
||||
|
||||
QVector2D inverted_movement(gizmo_matrix_.toTransform().inverted().map(movement));
|
||||
QVector2D anchor_cont = gizmo_start_[0].value<QVector2D>() + inverted_movement;
|
||||
QVector2D position_cont = gizmo_start_[1].value<QVector2D>() + vec_movement;
|
||||
|
||||
gizmo_dragger_[0].Drag(anchor_cont.x());
|
||||
gizmo_dragger_[1].Drag(anchor_cont.y());
|
||||
gizmo_dragger_[2].Drag(position_cont.x());
|
||||
gizmo_dragger_[3].Drag(position_cont.y());
|
||||
|
||||
} else if (gizmo_drag_ == kPositionInput) {
|
||||
|
||||
// Dragging the main rectangle around
|
||||
if (gizmo_dragger_.isEmpty()) {
|
||||
gizmo_dragger_.resize(2);
|
||||
gizmo_dragger_[0].Start(NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 0), time);
|
||||
gizmo_dragger_[1].Start(NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 1), time);
|
||||
}
|
||||
|
||||
QVector2D position_cont = gizmo_start_[0].value<QVector2D>() + vec_movement;
|
||||
|
||||
gizmo_dragger_[0].Drag(position_cont.x());
|
||||
gizmo_dragger_[1].Drag(position_cont.y());
|
||||
|
||||
} else if (gizmo_drag_ == kScaleInput) {
|
||||
|
||||
// Dragging a resize handle
|
||||
if (gizmo_dragger_.isEmpty()) {
|
||||
if (gizmo_scale_uniform_ || gizmo_scale_axes_ == kGizmoScaleXOnly) {
|
||||
gizmo_dragger_.resize(1);
|
||||
gizmo_dragger_[0].Start(NodeKeyframeTrackReference(NodeInput(this, kScaleInput), 0), time);
|
||||
} else if (gizmo_scale_axes_ == kGizmoScaleYOnly) {
|
||||
gizmo_dragger_.resize(1);
|
||||
gizmo_dragger_[0].Start(NodeKeyframeTrackReference(NodeInput(this, kScaleInput), 1), time);
|
||||
} else {
|
||||
gizmo_dragger_.resize(2);
|
||||
gizmo_dragger_[0].Start(NodeKeyframeTrackReference(NodeInput(this, kScaleInput), 0), time);
|
||||
gizmo_dragger_[1].Start(NodeKeyframeTrackReference(NodeInput(this, kScaleInput), 1), time);
|
||||
}
|
||||
}
|
||||
|
||||
QPointF mouse_relative = gizmo_matrix_.toTransform().inverted().map(QPointF(p - gizmo_anchor_pt_.center()));
|
||||
|
||||
double x_scaled_movement = qAbs(mouse_relative.x() / gizmo_scale_anchor_.x());
|
||||
double y_scaled_movement = qAbs(mouse_relative.y() / gizmo_scale_anchor_.y());
|
||||
|
||||
switch (gizmo_scale_axes_) {
|
||||
case kGizmoScaleXOnly:
|
||||
gizmo_dragger_[0].Drag(x_scaled_movement);
|
||||
break;
|
||||
case kGizmoScaleYOnly:
|
||||
gizmo_dragger_[0].Drag(y_scaled_movement);
|
||||
break;
|
||||
case kGizmoScaleBoth:
|
||||
if (gizmo_scale_uniform_) {
|
||||
double distance = std::hypot(mouse_relative.x(), mouse_relative.y());
|
||||
double texture_diag = std::hypot(gizmo_scale_anchor_.x(), gizmo_scale_anchor_.y());
|
||||
|
||||
gizmo_dragger_[0].Drag(qAbs(distance / texture_diag));
|
||||
} else {
|
||||
gizmo_dragger_[0].Drag(x_scaled_movement);
|
||||
gizmo_dragger_[1].Drag(y_scaled_movement);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
} else if (gizmo_drag_ == kRotationInput) {
|
||||
|
||||
// Dragging outside the rectangle to rotate
|
||||
if (gizmo_dragger_.isEmpty()) {
|
||||
gizmo_dragger_.resize(1);
|
||||
gizmo_dragger_[0].Start(NodeInput(this, kRotationInput), time);
|
||||
}
|
||||
|
||||
double current_angle = qAtan2(p.y() - gizmo_anchor_pt_.center().y(),
|
||||
p.x() - gizmo_anchor_pt_.center().x());
|
||||
|
||||
double rotation_difference = (current_angle - gizmo_start_angle_) * 57.2958;
|
||||
|
||||
double rotation_cont = gizmo_start_[0].toDouble() + rotation_difference;
|
||||
|
||||
gizmo_dragger_[0].Drag(rotation_cont);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void TransformDistortNode::GizmoRelease(MultiUndoCommand *command)
|
||||
{
|
||||
for (NodeInputDragger& i : gizmo_dragger_) {
|
||||
i.End(command);
|
||||
}
|
||||
gizmo_dragger_.clear();
|
||||
|
||||
gizmo_start_.clear();
|
||||
|
||||
gizmo_drag_ = nullptr;
|
||||
}
|
||||
|
||||
void TransformDistortNode::Hash(QCryptographicHash &hash, const NodeGlobals &globals, const VideoParams &video_params) const
|
||||
{
|
||||
// If not connected to output, this will produce nothing
|
||||
@@ -342,6 +153,168 @@ void TransformDistortNode::Hash(QCryptographicHash &hash, const NodeGlobals &glo
|
||||
Node::Hash(out, GetValueHintForInput(kTextureInput), hash, globals, video_params);
|
||||
}
|
||||
|
||||
void TransformDistortNode::GizmoDragStart(const NodeValueRow &row, double x, double y, const rational &time)
|
||||
{
|
||||
DraggableGizmo *gizmo = static_cast<DraggableGizmo*>(sender());
|
||||
|
||||
if (gizmo == anchor_gizmo_) {
|
||||
|
||||
gizmo_inverted_transform_ = GenerateMatrix(row, false, true, true, false).toTransform().inverted();
|
||||
|
||||
} else if (IsAScaleGizmo(gizmo)) {
|
||||
|
||||
// Dragging scale handle
|
||||
TexturePtr tex = row[kTextureInput].data().value<TexturePtr>();
|
||||
if (!tex) {
|
||||
return;
|
||||
}
|
||||
|
||||
gizmo_scale_uniform_ = row[kUniformScaleInput].data().toBool();
|
||||
gizmo_anchor_pt_ = (row[kAnchorInput].data().value<QVector2D>() + gizmo->GetGlobals().resolution()/2).toPointF();
|
||||
|
||||
if (gizmo == point_gizmo_[kGizmoScaleTopLeft] || gizmo == point_gizmo_[kGizmoScaleTopRight]
|
||||
|| gizmo == point_gizmo_[kGizmoScaleBottomLeft] || gizmo == point_gizmo_[kGizmoScaleBottomRight]) {
|
||||
gizmo_scale_axes_ = kGizmoScaleBoth;
|
||||
} else if (gizmo == point_gizmo_[kGizmoScaleCenterLeft] || gizmo == point_gizmo_[kGizmoScaleCenterRight]) {
|
||||
gizmo_scale_axes_ = kGizmoScaleXOnly;
|
||||
} else {
|
||||
gizmo_scale_axes_ = kGizmoScaleYOnly;
|
||||
}
|
||||
|
||||
// Store texture size
|
||||
VideoParams texture_params = tex->params();
|
||||
QVector2D texture_sz(texture_params.square_pixel_width(), texture_params.height());
|
||||
gizmo_scale_anchor_ = row[kAnchorInput].data().value<QVector2D>() + texture_sz/2;
|
||||
|
||||
if (gizmo == point_gizmo_[kGizmoScaleTopRight]
|
||||
|| gizmo == point_gizmo_[kGizmoScaleBottomRight]
|
||||
|| gizmo == point_gizmo_[kGizmoScaleCenterRight]) {
|
||||
// Right handles, flip X axis
|
||||
gizmo_scale_anchor_.setX(texture_sz.x() - gizmo_scale_anchor_.x());
|
||||
}
|
||||
|
||||
if (gizmo == point_gizmo_[kGizmoScaleBottomLeft]
|
||||
|| gizmo == point_gizmo_[kGizmoScaleBottomRight]
|
||||
|| gizmo == point_gizmo_[kGizmoScaleBottomCenter]) {
|
||||
// Bottom handles, flip Y axis
|
||||
gizmo_scale_anchor_.setY(texture_sz.y() - gizmo_scale_anchor_.y());
|
||||
}
|
||||
|
||||
// Store current matrix
|
||||
gizmo_inverted_transform_ = GenerateMatrix(row, false, true, true, true).toTransform().inverted();
|
||||
|
||||
} else if (gizmo == rotation_gizmo_) {
|
||||
|
||||
gizmo_anchor_pt_ = (row[kAnchorInput].data().value<QVector2D>() + gizmo->GetGlobals().resolution()/2).toPointF();
|
||||
gizmo_start_angle_ = qAtan2(y - gizmo_anchor_pt_.y(), x - gizmo_anchor_pt_.x());
|
||||
gizmo_last_angle_ = gizmo_start_angle_;
|
||||
gizmo_last_alt_angle_ = qAtan2(x - gizmo_anchor_pt_.x(), y - gizmo_anchor_pt_.y());
|
||||
gizmo_rotate_wrap_ = 0;
|
||||
gizmo_rotate_last_dir_ = kDirectionNone;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void TransformDistortNode::GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers)
|
||||
{
|
||||
DraggableGizmo *gizmo = static_cast<DraggableGizmo*>(sender());
|
||||
|
||||
if (gizmo == poly_gizmo_) {
|
||||
|
||||
NodeInputDragger &x_drag = gizmo->GetDraggers()[0];
|
||||
NodeInputDragger &y_drag = gizmo->GetDraggers()[1];
|
||||
|
||||
x_drag.Drag(x_drag.GetStartValue().toDouble() + x);
|
||||
y_drag.Drag(y_drag.GetStartValue().toDouble() + y);
|
||||
|
||||
} else if (gizmo == anchor_gizmo_) {
|
||||
|
||||
NodeInputDragger &x_anchor_drag = gizmo->GetDraggers()[0];
|
||||
NodeInputDragger &y_anchor_drag = gizmo->GetDraggers()[1];
|
||||
NodeInputDragger &x_pos_drag = gizmo->GetDraggers()[2];
|
||||
NodeInputDragger &y_pos_drag = gizmo->GetDraggers()[3];
|
||||
|
||||
QPointF inverted_movement(gizmo_inverted_transform_.map(QPointF(x, y)));
|
||||
|
||||
x_anchor_drag.Drag(x_anchor_drag.GetStartValue().toDouble() + inverted_movement.x());
|
||||
y_anchor_drag.Drag(y_anchor_drag.GetStartValue().toDouble() + inverted_movement.y());
|
||||
x_pos_drag.Drag(x_pos_drag.GetStartValue().toDouble() + x);
|
||||
y_pos_drag.Drag(y_pos_drag.GetStartValue().toDouble() + y);
|
||||
|
||||
} else if (gizmo == rotation_gizmo_) {
|
||||
|
||||
double raw_angle = qAtan2(y - gizmo_anchor_pt_.y(), x - gizmo_anchor_pt_.x());
|
||||
double alt_angle = qAtan2(x - gizmo_anchor_pt_.x(), y - gizmo_anchor_pt_.y());
|
||||
|
||||
double current_angle = raw_angle;
|
||||
|
||||
// Detect rotation wrap around
|
||||
RotationDirection this_dir = GetDirectionFromAngles(gizmo_last_angle_, raw_angle);
|
||||
RotationDirection alt_dir = GetDirectionFromAngles(gizmo_last_alt_angle_, alt_angle);
|
||||
|
||||
if (gizmo_rotate_last_dir_ != kDirectionNone && this_dir != gizmo_rotate_last_dir_) {
|
||||
if (alt_dir == gizmo_rotate_last_alt_dir_) {
|
||||
if ((raw_angle - gizmo_last_angle_) < 0) {
|
||||
gizmo_rotate_wrap_++;
|
||||
} else {
|
||||
gizmo_rotate_wrap_--;
|
||||
}
|
||||
|
||||
this_dir = gizmo_rotate_last_dir_;
|
||||
alt_dir = gizmo_rotate_last_alt_dir_;
|
||||
}
|
||||
}
|
||||
|
||||
gizmo_rotate_last_dir_ = this_dir;
|
||||
gizmo_rotate_last_alt_dir_ = alt_dir;
|
||||
gizmo_last_angle_ = raw_angle;
|
||||
gizmo_last_alt_angle_ = alt_angle;
|
||||
|
||||
current_angle += M_PI*2*gizmo_rotate_wrap_;
|
||||
|
||||
// Convert radians to degrees
|
||||
double rotation_difference = (current_angle - gizmo_start_angle_) * 57.2958;
|
||||
|
||||
NodeInputDragger &d = gizmo->GetDraggers()[0];
|
||||
d.Drag(d.GetStartValue().toDouble() + rotation_difference);
|
||||
|
||||
} else if (IsAScaleGizmo(gizmo)) {
|
||||
|
||||
QPointF mouse_relative = gizmo_inverted_transform_.map(QPointF(x, y) - gizmo_anchor_pt_);
|
||||
|
||||
double x_scaled_movement = qAbs(mouse_relative.x() / gizmo_scale_anchor_.x());
|
||||
double y_scaled_movement = qAbs(mouse_relative.y() / gizmo_scale_anchor_.y());
|
||||
|
||||
NodeInputDragger &x_drag = gizmo->GetDraggers()[0];
|
||||
NodeInputDragger &y_drag = gizmo->GetDraggers()[1];
|
||||
|
||||
switch (gizmo_scale_axes_) {
|
||||
case kGizmoScaleXOnly:
|
||||
x_drag.Drag(x_scaled_movement);
|
||||
break;
|
||||
case kGizmoScaleYOnly:
|
||||
if (gizmo_scale_uniform_) {
|
||||
x_drag.Drag(y_scaled_movement);
|
||||
} else {
|
||||
y_drag.Drag(y_scaled_movement);
|
||||
}
|
||||
break;
|
||||
case kGizmoScaleBoth:
|
||||
if (gizmo_scale_uniform_) {
|
||||
double distance = std::hypot(mouse_relative.x(), mouse_relative.y());
|
||||
double texture_diag = std::hypot(gizmo_scale_anchor_.x(), gizmo_scale_anchor_.y());
|
||||
|
||||
x_drag.Drag(qAbs(distance / texture_diag));
|
||||
} else {
|
||||
x_drag.Drag(x_scaled_movement);
|
||||
y_drag.Drag(y_scaled_movement);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
QMatrix4x4 TransformDistortNode::AdjustMatrixByResolutions(const QMatrix4x4 &mat, const QVector2D &sequence_res, const QVector2D &texture_res, AutoScaleType autoscale_type)
|
||||
{
|
||||
// First, create an identity matrix
|
||||
@@ -387,33 +360,13 @@ QMatrix4x4 TransformDistortNode::AdjustMatrixByResolutions(const QMatrix4x4 &mat
|
||||
return adjusted_matrix;
|
||||
}
|
||||
|
||||
QPointF TransformDistortNode::CreateScalePoint(double x, double y, const QPointF &half_res, const QMatrix4x4 &mat)
|
||||
{
|
||||
return mat.map(QPointF(x, y)) + half_res;
|
||||
}
|
||||
|
||||
QMatrix4x4 TransformDistortNode::GenerateAutoScaledMatrix(const QMatrix4x4& generated_matrix, const NodeValueRow& value, const NodeGlobals &globals, const VideoParams& texture_params) const
|
||||
{
|
||||
const QVector2D &sequence_res = globals.resolution();
|
||||
QVector2D texture_res(texture_params.square_pixel_width(), texture_params.height());
|
||||
AutoScaleType autoscale = static_cast<AutoScaleType>(value[kAutoscaleInput].data().toInt());
|
||||
|
||||
return AdjustMatrixByResolutions(generated_matrix,
|
||||
sequence_res,
|
||||
texture_res,
|
||||
autoscale);
|
||||
}
|
||||
|
||||
void TransformDistortNode::DrawGizmos(const NodeValueRow &row, const NodeGlobals &globals, QPainter *p)
|
||||
void TransformDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals)
|
||||
{
|
||||
TexturePtr tex = row[kTextureInput].data().value<TexturePtr>();
|
||||
if (!tex) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 0 pen width is always 1px wide despite any transform
|
||||
p->setPen(QPen(Qt::white, 0));
|
||||
|
||||
// Get the sequence resolution
|
||||
const QVector2D &sequence_res = globals.resolution();
|
||||
QVector2D sequence_half_res = sequence_res * 0.5;
|
||||
@@ -441,14 +394,9 @@ void TransformDistortNode::DrawGizmos(const NodeValueRow &row, const NodeGlobals
|
||||
QPointF(-1, 1),
|
||||
QPointF(-1, -1)};
|
||||
QTransform rectangle_transform = rectangle_matrix.toTransform();
|
||||
gizmo_rect_ = rectangle_transform.map(points);
|
||||
gizmo_rect_.translate(sequence_half_res_pt);
|
||||
|
||||
// Draw rectangle
|
||||
p->drawPolyline(gizmo_rect_);
|
||||
|
||||
// Get handle size (relative to screen space rather than buffer space)
|
||||
const double resize_handle_rad = GetGizmoHandleRadius(p->transform());
|
||||
QPolygonF r = rectangle_transform.map(points);
|
||||
r.translate(sequence_half_res_pt);
|
||||
poly_gizmo_->SetPolygon(r);
|
||||
|
||||
// Draw anchor point
|
||||
QMatrix4x4 anchor_matrix;
|
||||
@@ -457,32 +405,17 @@ void TransformDistortNode::DrawGizmos(const NodeValueRow &row, const NodeGlobals
|
||||
sequence_res,
|
||||
tex_sz,
|
||||
autoscale);
|
||||
QPointF anchor_pt = anchor_matrix.toTransform().map(QPointF(0, 0)) + sequence_half_res_pt;
|
||||
const double anchor_pt_radius = resize_handle_rad * 2;
|
||||
|
||||
gizmo_anchor_pt_ = QRectF(anchor_pt.x() - anchor_pt_radius,
|
||||
anchor_pt.y() - anchor_pt_radius,
|
||||
anchor_pt_radius*2,
|
||||
anchor_pt_radius*2);
|
||||
|
||||
p->drawEllipse(anchor_pt, anchor_pt_radius, anchor_pt_radius);
|
||||
|
||||
p->drawLines({QLineF(anchor_pt.x() - anchor_pt_radius, anchor_pt.y(),
|
||||
anchor_pt.x() + anchor_pt_radius, anchor_pt.y()),
|
||||
QLineF(anchor_pt.x(), anchor_pt.y() - anchor_pt_radius,
|
||||
anchor_pt.x(), anchor_pt.y() + anchor_pt_radius)});
|
||||
anchor_gizmo_->SetPoint(anchor_matrix.toTransform().map(QPointF(0, 0)) + sequence_half_res_pt);
|
||||
|
||||
// Draw scale handles
|
||||
gizmo_resize_handle_[kGizmoScaleTopLeft] = CreateGizmoHandleRect(CreateScalePoint(-1, -1, sequence_half_res_pt, rectangle_matrix), resize_handle_rad);
|
||||
gizmo_resize_handle_[kGizmoScaleTopCenter] = CreateGizmoHandleRect(CreateScalePoint( 0, -1, sequence_half_res_pt, rectangle_matrix), resize_handle_rad);
|
||||
gizmo_resize_handle_[kGizmoScaleTopRight] = CreateGizmoHandleRect(CreateScalePoint( 1, -1, sequence_half_res_pt, rectangle_matrix), resize_handle_rad);
|
||||
gizmo_resize_handle_[kGizmoScaleBottomLeft] = CreateGizmoHandleRect(CreateScalePoint(-1, 1, sequence_half_res_pt, rectangle_matrix), resize_handle_rad);
|
||||
gizmo_resize_handle_[kGizmoScaleBottomCenter] = CreateGizmoHandleRect(CreateScalePoint( 0, 1, sequence_half_res_pt, rectangle_matrix), resize_handle_rad);
|
||||
gizmo_resize_handle_[kGizmoScaleBottomRight] = CreateGizmoHandleRect(CreateScalePoint( 1, 1, sequence_half_res_pt, rectangle_matrix), resize_handle_rad);
|
||||
gizmo_resize_handle_[kGizmoScaleCenterLeft] = CreateGizmoHandleRect(CreateScalePoint(-1, 0, sequence_half_res_pt, rectangle_matrix), resize_handle_rad);
|
||||
gizmo_resize_handle_[kGizmoScaleCenterRight] = CreateGizmoHandleRect(CreateScalePoint( 1, 0, sequence_half_res_pt, rectangle_matrix), resize_handle_rad);
|
||||
|
||||
DrawAndExpandGizmoHandles(p, resize_handle_rad, gizmo_resize_handle_, kGizmoScaleCount);
|
||||
point_gizmo_[kGizmoScaleTopLeft]->SetPoint(CreateScalePoint(-1, -1, sequence_half_res_pt, rectangle_matrix));
|
||||
point_gizmo_[kGizmoScaleTopCenter]->SetPoint(CreateScalePoint( 0, -1, sequence_half_res_pt, rectangle_matrix));
|
||||
point_gizmo_[kGizmoScaleTopRight]->SetPoint(CreateScalePoint( 1, -1, sequence_half_res_pt, rectangle_matrix));
|
||||
point_gizmo_[kGizmoScaleBottomLeft]->SetPoint(CreateScalePoint(-1, 1, sequence_half_res_pt, rectangle_matrix));
|
||||
point_gizmo_[kGizmoScaleBottomCenter]->SetPoint(CreateScalePoint( 0, 1, sequence_half_res_pt, rectangle_matrix));
|
||||
point_gizmo_[kGizmoScaleBottomRight]->SetPoint(CreateScalePoint( 1, 1, sequence_half_res_pt, rectangle_matrix));
|
||||
point_gizmo_[kGizmoScaleCenterLeft]->SetPoint(CreateScalePoint(-1, 0, sequence_half_res_pt, rectangle_matrix));
|
||||
point_gizmo_[kGizmoScaleCenterRight]->SetPoint(CreateScalePoint( 1, 0, sequence_half_res_pt, rectangle_matrix));
|
||||
|
||||
// Use offsets to make the appearance of values that start in the top left, even though we
|
||||
// really anchor around the center
|
||||
@@ -490,4 +423,37 @@ void TransformDistortNode::DrawGizmos(const NodeValueRow &row, const NodeGlobals
|
||||
SetInputProperty(kAnchorInput, QStringLiteral("offset"), tex_sz * 0.5);
|
||||
}
|
||||
|
||||
QPointF TransformDistortNode::CreateScalePoint(double x, double y, const QPointF &half_res, const QMatrix4x4 &mat)
|
||||
{
|
||||
return mat.map(QPointF(x, y)) + half_res;
|
||||
}
|
||||
|
||||
QMatrix4x4 TransformDistortNode::GenerateAutoScaledMatrix(const QMatrix4x4& generated_matrix, const NodeValueRow& value, const NodeGlobals &globals, const VideoParams& texture_params) const
|
||||
{
|
||||
const QVector2D &sequence_res = globals.resolution();
|
||||
QVector2D texture_res(texture_params.square_pixel_width(), texture_params.height());
|
||||
AutoScaleType autoscale = static_cast<AutoScaleType>(value[kAutoscaleInput].data().toInt());
|
||||
|
||||
return AdjustMatrixByResolutions(generated_matrix,
|
||||
sequence_res,
|
||||
texture_res,
|
||||
autoscale);
|
||||
}
|
||||
|
||||
bool TransformDistortNode::IsAScaleGizmo(NodeGizmo *g) const
|
||||
{
|
||||
for (int i=0; i<kGizmoScaleCount; i++) {
|
||||
if (point_gizmo_[i] == g) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
TransformDistortNode::RotationDirection TransformDistortNode::GetDirectionFromAngles(double last, double current)
|
||||
{
|
||||
return (current > last) ? kDirectionPositive : kDirectionNegative;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
#define TRANSFORMDISTORTNODE_H
|
||||
|
||||
#include "node/generator/matrix/matrix.h"
|
||||
#include "node/gizmo/point.h"
|
||||
#include "node/gizmo/polygon.h"
|
||||
#include "node/gizmo/screen.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -69,17 +72,6 @@ public:
|
||||
|
||||
virtual ShaderCode GetShaderCode(const QString& shader_id) const override;
|
||||
|
||||
virtual bool HasGizmos() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void DrawGizmos(const NodeValueRow &row, const NodeGlobals &globals, QPainter *p) override;
|
||||
|
||||
virtual bool GizmoPress(const NodeValueRow &row, const NodeGlobals &globals, const QPointF &p) override;
|
||||
virtual void GizmoMove(const QPointF &p, const rational &time, const Qt::KeyboardModifiers &modifiers) override;
|
||||
virtual void GizmoRelease(MultiUndoCommand *command) override;
|
||||
|
||||
enum AutoScaleType {
|
||||
kAutoScaleNone,
|
||||
kAutoScaleFit,
|
||||
@@ -92,6 +84,8 @@ public:
|
||||
const QVector2D& texture_res,
|
||||
AutoScaleType autoscale_type = kAutoScaleNone);
|
||||
|
||||
virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override;
|
||||
|
||||
static const QString kTextureInput;
|
||||
static const QString kAutoscaleInput;
|
||||
static const QString kInterpolationInput;
|
||||
@@ -99,18 +93,36 @@ public:
|
||||
protected:
|
||||
virtual void Hash(QCryptographicHash& hash, const NodeGlobals &globals, const VideoParams& video_params) const override;
|
||||
|
||||
protected slots:
|
||||
virtual void GizmoDragStart(const olive::NodeValueRow &row, double x, double y, const olive::rational &time) override;
|
||||
|
||||
virtual void GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers) override;
|
||||
|
||||
private:
|
||||
static QPointF CreateScalePoint(double x, double y, const QPointF& half_res, const QMatrix4x4& mat);
|
||||
|
||||
QMatrix4x4 GenerateAutoScaledMatrix(const QMatrix4x4 &generated_matrix, const NodeValueRow &db, const NodeGlobals &globals, const VideoParams &texture_params) const;
|
||||
|
||||
bool IsAScaleGizmo(NodeGizmo *g) const;
|
||||
|
||||
// Gizmo variables
|
||||
QString gizmo_drag_;
|
||||
QVector<QVariant> gizmo_start_;
|
||||
QVector<NodeInputDragger> gizmo_dragger_;
|
||||
QPointF gizmo_drag_pos_;
|
||||
double gizmo_start_angle_;
|
||||
QTransform gizmo_inverted_transform_;
|
||||
QPointF gizmo_anchor_pt_;
|
||||
bool gizmo_scale_uniform_;
|
||||
double gizmo_last_angle_;
|
||||
double gizmo_last_alt_angle_;
|
||||
int gizmo_rotate_wrap_;
|
||||
|
||||
enum RotationDirection {
|
||||
kDirectionNone,
|
||||
kDirectionPositive, // Clockwise
|
||||
kDirectionNegative // Counter-clockwise
|
||||
};
|
||||
|
||||
static RotationDirection GetDirectionFromAngles(double last, double current);
|
||||
RotationDirection gizmo_rotate_last_dir_;
|
||||
RotationDirection gizmo_rotate_last_alt_dir_;
|
||||
|
||||
enum GizmoScaleType {
|
||||
kGizmoScaleXOnly,
|
||||
@@ -119,13 +131,13 @@ private:
|
||||
};
|
||||
|
||||
GizmoScaleType gizmo_scale_axes_;
|
||||
QMatrix4x4 gizmo_matrix_;
|
||||
QVector2D gizmo_scale_anchor_;
|
||||
|
||||
// Gizmo on screen object storage
|
||||
QPolygonF gizmo_rect_;
|
||||
QRectF gizmo_anchor_pt_;
|
||||
QRectF gizmo_resize_handle_[kGizmoScaleCount];
|
||||
PointGizmo *point_gizmo_[kGizmoScaleCount];
|
||||
PointGizmo *anchor_gizmo_;
|
||||
PolygonGizmo *poly_gizmo_;
|
||||
ScreenGizmo *rotation_gizmo_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
#define super NodeGroup
|
||||
#define super Node
|
||||
|
||||
const QString OpacityEffect::kTextureInput = QStringLiteral("tex_in");
|
||||
const QString OpacityEffect::kValueInput = QStringLiteral("opacity_in");
|
||||
|
||||
OpacityEffect::OpacityEffect()
|
||||
{
|
||||
@@ -15,24 +18,44 @@ OpacityEffect::OpacityEffect()
|
||||
|
||||
SetNodePositionInContext(math, QPointF(0, 0));
|
||||
|
||||
tex_in_pass_ = AddInputPassthrough(NodeInput(math, MathNode::kParamAIn), InputFlags(kInputFlagNotKeyframable));
|
||||
SetInputDataType(tex_in_pass_, NodeValue::kTexture);
|
||||
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||
|
||||
value_in_pass_ = AddInputPassthrough(NodeInput(math, MathNode::kParamBIn));
|
||||
SetInputProperty(value_in_pass_, QStringLiteral("view"), FloatSlider::kPercentage);
|
||||
SetInputProperty(value_in_pass_, QStringLiteral("min"), 0.0);
|
||||
SetInputProperty(value_in_pass_, QStringLiteral("max"), 1.0);
|
||||
math->SetStandardValue(MathNode::kParamBIn, 1.0);
|
||||
|
||||
SetOutputPassthrough(math);
|
||||
AddInput(kValueInput, NodeValue::kFloat, 1.0);
|
||||
SetInputProperty(kValueInput, QStringLiteral("view"), FloatSlider::kPercentage);
|
||||
SetInputProperty(kValueInput, QStringLiteral("min"), 0.0);
|
||||
SetInputProperty(kValueInput, QStringLiteral("max"), 1.0);
|
||||
}
|
||||
|
||||
void OpacityEffect::Retranslate()
|
||||
{
|
||||
super::Retranslate();
|
||||
|
||||
SetInputName(tex_in_pass_, tr("Texture"));
|
||||
SetInputName(value_in_pass_, tr("Opacity"));
|
||||
SetInputName(kTextureInput, tr("Texture"));
|
||||
SetInputName(kValueInput, tr("Opacity"));
|
||||
}
|
||||
|
||||
ShaderCode OpacityEffect::GetShaderCode(const QString &shader_id) const
|
||||
{
|
||||
Q_UNUSED(shader_id)
|
||||
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/opacity.frag"));
|
||||
}
|
||||
|
||||
void OpacityEffect::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
ShaderJob job;
|
||||
|
||||
job.InsertValue(value);
|
||||
|
||||
// If there's no texture, no need to run an operation
|
||||
if (!job.GetValue(kTextureInput).data().isNull()) {
|
||||
if (!qFuzzyCompare(job.GetValue(kValueInput).data().toDouble(), 1.0)) {
|
||||
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||
table->Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||
} else {
|
||||
// 1.0 float is a no-op, so just push the texture
|
||||
table->Push(job.GetValue(kTextureInput));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
class OpacityEffect : public NodeGroup
|
||||
class OpacityEffect : public Node
|
||||
{
|
||||
public:
|
||||
OpacityEffect();
|
||||
@@ -21,12 +21,12 @@ public:
|
||||
|
||||
virtual QString id() const override
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.opacityeffect");
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.opacity");
|
||||
}
|
||||
|
||||
virtual QVector<CategoryID> Category() const override
|
||||
{
|
||||
return {kCategoryFilter, kCategoryVideoEffect};
|
||||
return {kCategoryFilter};
|
||||
}
|
||||
|
||||
virtual QString Description() const override
|
||||
@@ -36,9 +36,11 @@ public:
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
private:
|
||||
QString tex_in_pass_;
|
||||
QString value_in_pass_;
|
||||
virtual ShaderCode GetShaderCode(const QString &shader_id) const override;
|
||||
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
|
||||
|
||||
static const QString kTextureInput;
|
||||
static const QString kValueInput;
|
||||
|
||||
};
|
||||
|
||||
|
||||
+17
-7
@@ -41,8 +41,9 @@
|
||||
#include "generator/polygon/polygon.h"
|
||||
#include "generator/shape/shapenode.h"
|
||||
#include "generator/solid/solid.h"
|
||||
#include "generator/text/text.h"
|
||||
#include "generator/text/textlegacy.h"
|
||||
#include "generator/text/textv1.h"
|
||||
#include "generator/text/textv2.h"
|
||||
#include "generator/text/textv3.h"
|
||||
#include "filter/blur/blur.h"
|
||||
#include "filter/mosaic/mosaicfilternode.h"
|
||||
#include "filter/stroke/stroke.h"
|
||||
@@ -51,6 +52,8 @@
|
||||
#include "math/math/math.h"
|
||||
#include "math/merge/merge.h"
|
||||
#include "math/trigonometry/trigonometry.h"
|
||||
#include "keying/colordifferencekey/colordifferencekey.h"
|
||||
#include "keying/despill/despill.h"
|
||||
#include "output/track/track.h"
|
||||
#include "output/viewer/viewer.h"
|
||||
#include "project/folder/folder.h"
|
||||
@@ -74,7 +77,8 @@ void NodeFactory::Initialize()
|
||||
library_.append(created_node);
|
||||
}
|
||||
|
||||
hidden_.append(kTextGeneratorLegacy);
|
||||
hidden_.append(kTextGeneratorV1);
|
||||
hidden_.append(kTextGeneratorV2);
|
||||
hidden_.append(kGroupNode);
|
||||
}
|
||||
|
||||
@@ -231,10 +235,12 @@ Node *NodeFactory::CreateFromFactoryIndex(const NodeFactory::InternalID &id)
|
||||
return new MergeNode();
|
||||
case kStrokeFilter:
|
||||
return new StrokeFilterNode();
|
||||
case kTextGeneratorLegacy:
|
||||
return new TextGeneratorLegacy();
|
||||
case kTextGenerator:
|
||||
return new TextGenerator();
|
||||
case kTextGeneratorV1:
|
||||
return new TextGeneratorV1();
|
||||
case kTextGeneratorV2:
|
||||
return new TextGeneratorV2();
|
||||
case kTextGeneratorV3:
|
||||
return new TextGeneratorV3();
|
||||
case kCrossDissolveTransition:
|
||||
return new CrossDissolveTransition();
|
||||
case kDipToColorTransition:
|
||||
@@ -257,6 +263,10 @@ Node *NodeFactory::CreateFromFactoryIndex(const NodeFactory::InternalID &id)
|
||||
return new SubtitleBlock();
|
||||
case kShapeGenerator:
|
||||
return new ShapeNode();
|
||||
case kColorDifferenceKeyKeying:
|
||||
return new ColorDifferenceKeyNode();
|
||||
case kDespillKeying:
|
||||
return new DespillNode();
|
||||
case kGroupNode:
|
||||
return new NodeGroup();
|
||||
case kOpacityEffect:
|
||||
|
||||
+5
-2
@@ -48,8 +48,9 @@ public:
|
||||
kSolidGenerator,
|
||||
kMerge,
|
||||
kStrokeFilter,
|
||||
kTextGeneratorLegacy,
|
||||
kTextGenerator,
|
||||
kTextGeneratorV1,
|
||||
kTextGeneratorV2,
|
||||
kTextGeneratorV3,
|
||||
kCrossDissolveTransition,
|
||||
kDipToColorTransition,
|
||||
kMosaicFilter,
|
||||
@@ -61,6 +62,8 @@ public:
|
||||
kTimeRemapNode,
|
||||
kSubtitleBlock,
|
||||
kShapeGenerator,
|
||||
kColorDifferenceKeyKeying,
|
||||
kDespillKeying,
|
||||
kGroupNode,
|
||||
kOpacityEffect,
|
||||
kFlipDistort,
|
||||
|
||||
@@ -42,7 +42,7 @@ BlurFilterNode::BlurFilterNode()
|
||||
|
||||
AddInput(kVertInput, NodeValue::kBoolean, true);
|
||||
|
||||
AddInput(kRepeatEdgePixelsInput, NodeValue::kBoolean, false);
|
||||
AddInput(kRepeatEdgePixelsInput, NodeValue::kBoolean, true);
|
||||
}
|
||||
|
||||
Node *BlurFilterNode::copy() const
|
||||
|
||||
@@ -54,6 +54,9 @@ PolygonGenerator::PolygonGenerator()
|
||||
SetSplitStandardValueOnTrack(kPointsInput, 1, kBottomY, 3);
|
||||
SetSplitStandardValueOnTrack(kPointsInput, 0, -kMiddleX, 4);
|
||||
SetSplitStandardValueOnTrack(kPointsInput, 1, -kMiddleY, 4);
|
||||
|
||||
// Initiate gizmos
|
||||
poly_gizmo_ = new PathGizmo(this);
|
||||
}
|
||||
|
||||
Node *PolygonGenerator::copy() const
|
||||
@@ -149,139 +152,101 @@ void PolygonGenerator::GenerateFrame(FramePtr frame, const GenerateJob &job) con
|
||||
}
|
||||
}
|
||||
|
||||
bool PolygonGenerator::HasGizmos() const
|
||||
template<typename T>
|
||||
NodeGizmo *PolygonGenerator::CreateAppropriateGizmo()
|
||||
{
|
||||
return true;
|
||||
return new T(this);
|
||||
}
|
||||
|
||||
void PolygonGenerator::DrawGizmos(const NodeValueRow &row, const NodeGlobals &globals, QPainter *p)
|
||||
template<>
|
||||
NodeGizmo *PolygonGenerator::CreateAppropriateGizmo<PointGizmo>()
|
||||
{
|
||||
const double handle_radius = GetGizmoHandleRadius(p->transform());
|
||||
const double bezier_radius = handle_radius/2;
|
||||
return AddDraggableGizmo<PointGizmo>();
|
||||
}
|
||||
|
||||
p->setPen(Qt::white);
|
||||
p->setBrush(Qt::white);
|
||||
p->translate(globals.resolution_by_par().x()/2, globals.resolution_by_par().y()/2);
|
||||
template<typename T>
|
||||
void PolygonGenerator::ValidateGizmoVectorSize(QVector<T*> &vec, int new_sz)
|
||||
{
|
||||
int old_sz = vec.size();
|
||||
|
||||
if (old_sz != new_sz) {
|
||||
if (old_sz > new_sz) {
|
||||
for (int i=new_sz; i<old_sz; i++) {
|
||||
delete vec.at(i);
|
||||
}
|
||||
}
|
||||
|
||||
vec.resize(new_sz);
|
||||
|
||||
if (old_sz < new_sz) {
|
||||
for (int i=old_sz; i<new_sz; i++) {
|
||||
vec[i] = static_cast<T*>(CreateAppropriateGizmo<T>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PolygonGenerator::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals)
|
||||
{
|
||||
QPointF half_res(globals.resolution_by_par().x()/2, globals.resolution_by_par().y()/2);
|
||||
|
||||
QVector<NodeValue> points = row[kPointsInput].data().value< QVector<NodeValue> >();
|
||||
|
||||
gizmo_position_handles_.resize(points.size());
|
||||
gizmo_bezier_handles_.resize(points.size() * 2);
|
||||
int current_pos_sz = gizmo_position_handles_.size();
|
||||
|
||||
p->setPen(QPen(Qt::white, 0));
|
||||
p->setBrush(Qt::NoBrush);
|
||||
ValidateGizmoVectorSize(gizmo_position_handles_, points.size());
|
||||
ValidateGizmoVectorSize(gizmo_bezier_handles_, points.size() * 2);
|
||||
ValidateGizmoVectorSize(gizmo_bezier_lines_, points.size() * 2);
|
||||
|
||||
for (int i=current_pos_sz; i<gizmo_position_handles_.size(); i++) {
|
||||
gizmo_position_handles_.at(i)->AddInput(NodeKeyframeTrackReference(NodeInput(this, kPointsInput, i), 0));
|
||||
gizmo_position_handles_.at(i)->AddInput(NodeKeyframeTrackReference(NodeInput(this, kPointsInput, i), 1));
|
||||
|
||||
PointGizmo *bez_gizmo1 = gizmo_bezier_handles_.at(i*2+0);
|
||||
bez_gizmo1->AddInput(NodeKeyframeTrackReference(NodeInput(this, kPointsInput, i), 2));
|
||||
bez_gizmo1->AddInput(NodeKeyframeTrackReference(NodeInput(this, kPointsInput, i), 3));
|
||||
bez_gizmo1->SetShape(PointGizmo::kCircle);
|
||||
bez_gizmo1->SetSmaller(true);
|
||||
|
||||
PointGizmo *bez_gizmo2 = gizmo_bezier_handles_.at(i*2+1);
|
||||
bez_gizmo2->AddInput(NodeKeyframeTrackReference(NodeInput(this, kPointsInput, i), 4));
|
||||
bez_gizmo2->AddInput(NodeKeyframeTrackReference(NodeInput(this, kPointsInput, i), 5));
|
||||
bez_gizmo2->SetShape(PointGizmo::kCircle);
|
||||
bez_gizmo2->SetSmaller(true);
|
||||
}
|
||||
|
||||
if (!points.isEmpty()) {
|
||||
QVector<QLineF> lines(points.size() * 2);
|
||||
|
||||
for (int i=0; i<points.size(); i++) {
|
||||
const Bezier &pt = points.at(i).data().value<Bezier>();
|
||||
|
||||
QPointF main = pt.ToPointF();
|
||||
QPointF main = pt.ToPointF() + half_res;
|
||||
QPointF cp1 = main + pt.ControlPoint1ToPointF();
|
||||
QPointF cp2 = main + pt.ControlPoint2ToPointF();
|
||||
|
||||
gizmo_position_handles_[i] = CreateGizmoHandleRect(main, handle_radius);
|
||||
gizmo_position_handles_[i]->SetPoint(main);
|
||||
|
||||
gizmo_bezier_handles_[i*2] = CreateGizmoHandleRect(cp1, bezier_radius);
|
||||
lines[i*2] = QLineF(main, cp1);
|
||||
|
||||
gizmo_bezier_handles_[i*2+1] = CreateGizmoHandleRect(cp2, bezier_radius);
|
||||
lines[i*2+1] = QLineF(main, cp2);
|
||||
gizmo_bezier_handles_[i*2]->SetPoint(cp1);
|
||||
gizmo_bezier_lines_[i*2]->SetLine(QLineF(main, cp1));
|
||||
gizmo_bezier_handles_[i*2+1]->SetPoint(cp2);
|
||||
gizmo_bezier_lines_[i*2+1]->SetLine(QLineF(main, cp2));
|
||||
}
|
||||
|
||||
p->drawLines(lines);
|
||||
}
|
||||
|
||||
gizmo_polygon_path_ = GeneratePath(points);
|
||||
p->drawPath(gizmo_polygon_path_);
|
||||
|
||||
DrawAndExpandGizmoHandles(p, handle_radius, gizmo_position_handles_.data(), gizmo_position_handles_.size());
|
||||
DrawAndExpandGizmoHandles(p, handle_radius, gizmo_bezier_handles_.data(), gizmo_bezier_handles_.size());
|
||||
poly_gizmo_->SetPath(GeneratePath(points).translated(half_res));
|
||||
}
|
||||
|
||||
bool PolygonGenerator::GizmoPress(const NodeValueRow& row, const NodeGlobals &globals, const QPointF &p)
|
||||
void PolygonGenerator::GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers)
|
||||
{
|
||||
QPointF adjusted = p - (globals.resolution_by_par() / 2).toPointF();
|
||||
DraggableGizmo *gizmo = static_cast<DraggableGizmo*>(sender());
|
||||
|
||||
// First, look for main points
|
||||
|
||||
for (int i=0; i<gizmo_position_handles_.size(); i++) {
|
||||
if (gizmo_position_handles_.at(i).contains(adjusted)) {
|
||||
gizmo_x_active_.append(NodeKeyframeTrackReference(NodeInput(this, kPointsInput, i), 0));
|
||||
gizmo_y_active_.append(NodeKeyframeTrackReference(NodeInput(this, kPointsInput, i), 1));
|
||||
break;
|
||||
}
|
||||
if (gizmo == poly_gizmo_) {
|
||||
// FIXME: Drag all points
|
||||
} else {
|
||||
NodeInputDragger &x_drag = gizmo->GetDraggers()[0];
|
||||
NodeInputDragger &y_drag = gizmo->GetDraggers()[1];
|
||||
x_drag.Drag(x_drag.GetStartValue().toDouble() + x);
|
||||
y_drag.Drag(y_drag.GetStartValue().toDouble() + y);
|
||||
}
|
||||
|
||||
// Next, if no main points were found, look for beziers
|
||||
if (gizmo_x_active_.isEmpty() && gizmo_y_active_.isEmpty()) {
|
||||
for (int i=0; i<gizmo_bezier_handles_.size(); i++) {
|
||||
if (gizmo_bezier_handles_.at(i).contains(adjusted)) {
|
||||
int start = (i%2 == 0) ? 2 : 4;
|
||||
int element = i/2;
|
||||
gizmo_x_active_.append(NodeKeyframeTrackReference(NodeInput(this, kPointsInput, element), start + 0));
|
||||
gizmo_y_active_.append(NodeKeyframeTrackReference(NodeInput(this, kPointsInput, element), start + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, see if the cursor is inside the polygon
|
||||
if (gizmo_x_active_.isEmpty() && gizmo_y_active_.isEmpty()) {
|
||||
if (gizmo_polygon_path_.contains(adjusted)) {
|
||||
gizmo_x_active_.resize(gizmo_position_handles_.size());
|
||||
gizmo_y_active_.resize(gizmo_position_handles_.size());
|
||||
for (int i=0; i<gizmo_position_handles_.size(); i++) {
|
||||
gizmo_x_active_[i] = NodeKeyframeTrackReference(NodeInput(this, kPointsInput, i), 0);
|
||||
gizmo_y_active_[i] = NodeKeyframeTrackReference(NodeInput(this, kPointsInput, i), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gizmo_drag_start_ = p;
|
||||
|
||||
return !gizmo_x_active_.isEmpty() || !gizmo_y_active_.isEmpty();
|
||||
}
|
||||
|
||||
void PolygonGenerator::GizmoMove(const QPointF &p, const rational &time, const Qt::KeyboardModifiers &modifiers)
|
||||
{
|
||||
if (gizmo_x_draggers_.isEmpty() && gizmo_y_draggers_.isEmpty()) {
|
||||
gizmo_x_draggers_.resize(gizmo_x_active_.size());
|
||||
gizmo_y_draggers_.resize(gizmo_y_active_.size());
|
||||
for (int i=0; i<gizmo_x_active_.size(); i++) {
|
||||
gizmo_x_draggers_[i].Start(gizmo_x_active_.at(i), time);
|
||||
}
|
||||
for (int i=0; i<gizmo_y_active_.size(); i++) {
|
||||
gizmo_y_draggers_[i].Start(gizmo_y_active_.at(i), time);
|
||||
}
|
||||
}
|
||||
|
||||
QPointF diff = p - gizmo_drag_start_;
|
||||
|
||||
for (NodeInputDragger &dragger : gizmo_x_draggers_) {
|
||||
dragger.Drag(dragger.GetStartValue().toDouble() + diff.x());
|
||||
}
|
||||
|
||||
for (NodeInputDragger &dragger : gizmo_y_draggers_) {
|
||||
dragger.Drag(dragger.GetStartValue().toDouble() + diff.y());
|
||||
}
|
||||
}
|
||||
|
||||
void PolygonGenerator::GizmoRelease(MultiUndoCommand *command)
|
||||
{
|
||||
for (NodeInputDragger &dragger : gizmo_x_draggers_) {
|
||||
dragger.End(command);
|
||||
}
|
||||
gizmo_x_draggers_.clear();
|
||||
|
||||
for (NodeInputDragger &dragger : gizmo_y_draggers_) {
|
||||
dragger.End(command);
|
||||
}
|
||||
gizmo_y_draggers_.clear();
|
||||
|
||||
gizmo_x_active_.clear();
|
||||
gizmo_y_active_.clear();
|
||||
}
|
||||
|
||||
void PolygonGenerator::AddPointToPath(QPainterPath *path, const Bezier &before, const Bezier &after)
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
#include <QPainterPath>
|
||||
|
||||
#include "common/bezier.h"
|
||||
#include "node/gizmo/line.h"
|
||||
#include "node/gizmo/path.h"
|
||||
#include "node/gizmo/point.h"
|
||||
#include "node/node.h"
|
||||
#include "node/inputdragger.h"
|
||||
|
||||
@@ -50,30 +53,29 @@ public:
|
||||
|
||||
virtual void GenerateFrame(FramePtr frame, const GenerateJob &job) const override;
|
||||
|
||||
virtual bool HasGizmos() const override;
|
||||
virtual void DrawGizmos(const NodeValueRow& row, const NodeGlobals &globals, QPainter *p) override;
|
||||
|
||||
virtual bool GizmoPress(const NodeValueRow& row, const NodeGlobals &globals, const QPointF &p) override;
|
||||
virtual void GizmoMove(const QPointF &p, const rational &time, const Qt::KeyboardModifiers &modifiers) override;
|
||||
virtual void GizmoRelease(MultiUndoCommand *command) override;
|
||||
virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override;
|
||||
|
||||
static const QString kPointsInput;
|
||||
static const QString kColorInput;
|
||||
|
||||
protected slots:
|
||||
virtual void GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers) override;
|
||||
|
||||
private:
|
||||
static void AddPointToPath(QPainterPath *path, const Bezier &before, const Bezier &after);
|
||||
|
||||
static QPainterPath GeneratePath(const QVector<NodeValue> &points);
|
||||
|
||||
QPainterPath gizmo_polygon_path_;
|
||||
QVector<QRectF> gizmo_position_handles_;
|
||||
QVector<QRectF> gizmo_bezier_handles_;
|
||||
template<typename T>
|
||||
void ValidateGizmoVectorSize(QVector<T*> &vec, int new_sz);
|
||||
|
||||
QVector<NodeKeyframeTrackReference> gizmo_x_active_;
|
||||
QVector<NodeKeyframeTrackReference> gizmo_y_active_;
|
||||
QVector<NodeInputDragger> gizmo_x_draggers_;
|
||||
QVector<NodeInputDragger> gizmo_y_draggers_;
|
||||
QPointF gizmo_drag_start_;
|
||||
template<typename T>
|
||||
NodeGizmo *CreateAppropriateGizmo();
|
||||
|
||||
PathGizmo *poly_gizmo_;
|
||||
QVector<PointGizmo*> gizmo_position_handles_;
|
||||
QVector<PointGizmo*> gizmo_bezier_handles_;
|
||||
QVector<LineGizmo*> gizmo_bezier_lines_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include <QtMath>
|
||||
#include <QVector2D>
|
||||
|
||||
#include "common/lerp.h"
|
||||
#include "common/util.h"
|
||||
#include "core.h"
|
||||
|
||||
namespace olive {
|
||||
@@ -34,12 +34,30 @@ QString ShapeNodeBase::kPositionInput = QStringLiteral("pos_in");
|
||||
QString ShapeNodeBase::kSizeInput = QStringLiteral("size_in");
|
||||
QString ShapeNodeBase::kColorInput = QStringLiteral("color_in");
|
||||
|
||||
ShapeNodeBase::ShapeNodeBase()
|
||||
ShapeNodeBase::ShapeNodeBase(bool create_color_input)
|
||||
{
|
||||
AddInput(kPositionInput, NodeValue::kVec2, QVector2D(0, 0));
|
||||
AddInput(kSizeInput, NodeValue::kVec2, QVector2D(100, 100));
|
||||
SetInputProperty(kSizeInput, QStringLiteral("min"), QVector2D(0, 0));
|
||||
AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(1.0, 0.0, 0.0, 1.0)));
|
||||
|
||||
if (create_color_input) {
|
||||
AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(1.0, 0.0, 0.0, 1.0)));
|
||||
}
|
||||
|
||||
// Initiate gizmos
|
||||
QVector<NodeKeyframeTrackReference> pos_n_sz = {
|
||||
NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 0),
|
||||
NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 1),
|
||||
NodeKeyframeTrackReference(NodeInput(this, kSizeInput), 0),
|
||||
NodeKeyframeTrackReference(NodeInput(this, kSizeInput), 1)
|
||||
};
|
||||
poly_gizmo_ = AddDraggableGizmo<PolygonGizmo>({
|
||||
NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 0),
|
||||
NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 1),
|
||||
});
|
||||
for (int i=0; i<kGizmoScaleCount; i++) {
|
||||
point_gizmo_[i] = AddDraggableGizmo<PointGizmo>(pos_n_sz, PointGizmo::kAbsolute);
|
||||
}
|
||||
}
|
||||
|
||||
void ShapeNodeBase::Retranslate()
|
||||
@@ -48,20 +66,19 @@ void ShapeNodeBase::Retranslate()
|
||||
|
||||
SetInputName(kPositionInput, tr("Position"));
|
||||
SetInputName(kSizeInput, tr("Size"));
|
||||
SetInputName(kColorInput, tr("Color"));
|
||||
|
||||
if (HasInputWithID(kColorInput)) {
|
||||
SetInputName(kColorInput, tr("Color"));
|
||||
}
|
||||
}
|
||||
|
||||
void ShapeNodeBase::DrawGizmos(const NodeValueRow &row, const NodeGlobals &globals, QPainter *p)
|
||||
void ShapeNodeBase::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals)
|
||||
{
|
||||
// Use offsets to make the appearance of values that start in the top left, even though we
|
||||
// really anchor around the center
|
||||
QVector2D center_pt = globals.resolution() * 0.5;
|
||||
SetInputProperty(kPositionInput, QStringLiteral("offset"), center_pt);
|
||||
|
||||
const double handle_radius = GetGizmoHandleRadius(p->transform());
|
||||
|
||||
p->setPen(QPen(Qt::white, 0));
|
||||
|
||||
QVector2D pos = row[kPositionInput].data().value<QVector2D>();
|
||||
QVector2D sz = row[kSizeInput].data().value<QVector2D>();
|
||||
QVector2D half_sz = sz * 0.5;
|
||||
@@ -70,151 +87,73 @@ void ShapeNodeBase::DrawGizmos(const NodeValueRow &row, const NodeGlobals &globa
|
||||
double top_pt = pos.y() + center_pt.y() - half_sz.y();
|
||||
double right_pt = left_pt + sz.x();
|
||||
double bottom_pt = top_pt + sz.y();
|
||||
double center_x_pt = lerp(left_pt, right_pt, 0.5);
|
||||
double center_y_pt = lerp(top_pt, bottom_pt, 0.5);
|
||||
double center_x_pt = mid(left_pt, right_pt);
|
||||
double center_y_pt = mid(top_pt, bottom_pt);
|
||||
|
||||
gizmo_whole_rect_ = QRectF(left_pt, top_pt, right_pt - left_pt, bottom_pt - top_pt);
|
||||
p->drawRect(gizmo_whole_rect_);
|
||||
point_gizmo_[kGizmoScaleTopLeft]->SetPoint(QPointF(left_pt, top_pt));
|
||||
point_gizmo_[kGizmoScaleTopCenter]->SetPoint(QPointF(center_x_pt, top_pt));
|
||||
point_gizmo_[kGizmoScaleTopRight]->SetPoint(QPointF(right_pt, top_pt));
|
||||
point_gizmo_[kGizmoScaleBottomLeft]->SetPoint(QPointF(left_pt, bottom_pt));
|
||||
point_gizmo_[kGizmoScaleBottomCenter]->SetPoint(QPointF(center_x_pt, bottom_pt));
|
||||
point_gizmo_[kGizmoScaleBottomRight]->SetPoint(QPointF(right_pt, bottom_pt));
|
||||
point_gizmo_[kGizmoScaleCenterLeft]->SetPoint(QPointF(left_pt, center_y_pt));
|
||||
point_gizmo_[kGizmoScaleCenterRight]->SetPoint(QPointF(right_pt, center_y_pt));
|
||||
|
||||
gizmo_resize_handle_[kGizmoScaleTopLeft] = CreateGizmoHandleRect(QPointF(left_pt, top_pt), handle_radius);
|
||||
gizmo_resize_handle_[kGizmoScaleTopCenter] = CreateGizmoHandleRect(QPointF(center_x_pt, top_pt), handle_radius);
|
||||
gizmo_resize_handle_[kGizmoScaleTopRight] = CreateGizmoHandleRect(QPointF(right_pt, top_pt), handle_radius);
|
||||
gizmo_resize_handle_[kGizmoScaleBottomLeft] = CreateGizmoHandleRect(QPointF(left_pt, bottom_pt), handle_radius);
|
||||
gizmo_resize_handle_[kGizmoScaleBottomCenter] = CreateGizmoHandleRect(QPointF(center_x_pt, bottom_pt), handle_radius);
|
||||
gizmo_resize_handle_[kGizmoScaleBottomRight] = CreateGizmoHandleRect(QPointF(right_pt, bottom_pt), handle_radius);
|
||||
gizmo_resize_handle_[kGizmoScaleCenterLeft] = CreateGizmoHandleRect(QPointF(left_pt, center_y_pt), handle_radius);
|
||||
gizmo_resize_handle_[kGizmoScaleCenterRight] = CreateGizmoHandleRect(QPointF(right_pt, center_y_pt), handle_radius);
|
||||
|
||||
DrawAndExpandGizmoHandles(p, handle_radius, gizmo_resize_handle_, kGizmoScaleCount);
|
||||
poly_gizmo_->SetPolygon(QRectF(left_pt, top_pt, right_pt - left_pt, bottom_pt - top_pt));
|
||||
}
|
||||
|
||||
bool ShapeNodeBase::GizmoPress(const NodeValueRow &row, const NodeGlobals &globals, const QPointF &p)
|
||||
void ShapeNodeBase::GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers)
|
||||
{
|
||||
gizmo_drag_ = -1;
|
||||
DraggableGizmo *gizmo = static_cast<DraggableGizmo*>(sender());
|
||||
|
||||
// See if any of our resize handles have the pointer
|
||||
for (int i=0; i<kGizmoScaleCount; i++) {
|
||||
if (gizmo_resize_handle_[i].contains(p)) {
|
||||
gizmo_drag_ = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
NodeInputDragger &x_drag = gizmo->GetDraggers()[0];
|
||||
NodeInputDragger &y_drag = gizmo->GetDraggers()[1];
|
||||
|
||||
// See if the rect has the pointer
|
||||
if (gizmo_drag_ == -1 && gizmo_whole_rect_.contains(p)) {
|
||||
gizmo_drag_ = kGizmoWholeRect;
|
||||
}
|
||||
|
||||
if (gizmo_drag_ >= 0) {
|
||||
gizmo_pos_start_ = row[kPositionInput].data().value<QVector2D>();
|
||||
gizmo_sz_start_ = row[kSizeInput].data().value<QVector2D>();
|
||||
gizmo_drag_start_ = p;
|
||||
gizmo_half_res_ = globals.resolution()/2;
|
||||
|
||||
// Get aspect ratio (avoiding potential zero)
|
||||
if (qFuzzyIsNull(gizmo_sz_start_.y())) {
|
||||
gizmo_aspect_ratio_ = 0;
|
||||
} else {
|
||||
gizmo_aspect_ratio_ = gizmo_sz_start_.x() / gizmo_sz_start_.y();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
enum {
|
||||
kGDPosX,
|
||||
kGDPosY,
|
||||
kGDSzX,
|
||||
kGDSzY,
|
||||
kGDCount
|
||||
};
|
||||
|
||||
QVector2D ShapeNodeBase::GenerateGizmoAnchor(const QVector2D &pos, const QVector2D &size, int drag, QVector2D *pt = nullptr)
|
||||
{
|
||||
QVector2D anchor = pos;
|
||||
QVector2D half_sz = size/2;
|
||||
|
||||
if (drag == kGizmoScaleTopLeft || drag == kGizmoScaleCenterLeft || drag == kGizmoScaleBottomLeft) {
|
||||
anchor.setX(anchor.x() + half_sz.x());
|
||||
if (pt && pt->x() > anchor.x()) {
|
||||
pt->setX(anchor.x());
|
||||
}
|
||||
}
|
||||
|
||||
if (drag == kGizmoScaleTopRight || drag == kGizmoScaleCenterRight || drag == kGizmoScaleBottomRight) {
|
||||
anchor.setX(anchor.x() - half_sz.x());
|
||||
if (pt && pt->x() < anchor.x()) {
|
||||
pt->setX(anchor.x());
|
||||
}
|
||||
}
|
||||
|
||||
if (drag == kGizmoScaleTopLeft || drag == kGizmoScaleTopCenter || drag == kGizmoScaleTopRight) {
|
||||
anchor.setY(anchor.y() + half_sz.y());
|
||||
if (pt && pt->y() > anchor.y()) {
|
||||
pt->setY(anchor.y());
|
||||
}
|
||||
}
|
||||
|
||||
if (drag == kGizmoScaleBottomLeft || drag == kGizmoScaleBottomCenter || drag == kGizmoScaleBottomRight) {
|
||||
anchor.setY(anchor.y() - half_sz.y());
|
||||
if (pt && pt->y() < anchor.y()) {
|
||||
pt->setY(anchor.y());
|
||||
}
|
||||
}
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
void ShapeNodeBase::GizmoMove(const QPointF &p, const rational &time, const Qt::KeyboardModifiers &modifiers)
|
||||
{
|
||||
if (gizmo_dragger_.isEmpty()) {
|
||||
gizmo_dragger_.resize(kGDCount);
|
||||
gizmo_dragger_[kGDPosX].Start(NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 0), time);
|
||||
gizmo_dragger_[kGDPosY].Start(NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 1), time);
|
||||
gizmo_dragger_[kGDSzX].Start(NodeKeyframeTrackReference(NodeInput(this, kSizeInput), 0), time);
|
||||
gizmo_dragger_[kGDSzY].Start(NodeKeyframeTrackReference(NodeInput(this, kSizeInput), 1), time);
|
||||
}
|
||||
|
||||
bool from_center = modifiers & Qt::AltModifier;
|
||||
bool keep_ratio = modifiers & Qt::ShiftModifier;
|
||||
|
||||
QVector2D adjusted_pt(p.x(), p.y());
|
||||
QVector2D diff(adjusted_pt.x() - gizmo_drag_start_.x(), adjusted_pt.y() - gizmo_drag_start_.y());
|
||||
|
||||
if (gizmo_drag_ == kGizmoWholeRect) {
|
||||
// Simply move position by difference
|
||||
gizmo_dragger_[kGDPosX].Drag(gizmo_pos_start_.x() + diff.x());
|
||||
gizmo_dragger_[kGDPosY].Drag(gizmo_pos_start_.y() + diff.y());
|
||||
if (gizmo == poly_gizmo_) {
|
||||
x_drag.Drag(x_drag.GetStartValue().toDouble() + x);
|
||||
y_drag.Drag(y_drag.GetStartValue().toDouble() + y);
|
||||
} else {
|
||||
bool from_center = modifiers & Qt::AltModifier;
|
||||
bool keep_ratio = modifiers & Qt::ShiftModifier;
|
||||
|
||||
NodeInputDragger &w_drag = gizmo->GetDraggers()[2];
|
||||
NodeInputDragger &h_drag = gizmo->GetDraggers()[3];
|
||||
|
||||
QVector2D gizmo_sz_start(w_drag.GetStartValue().toDouble(), h_drag.GetStartValue().toDouble());
|
||||
QVector2D gizmo_pos_start(x_drag.GetStartValue().toDouble(), y_drag.GetStartValue().toDouble());
|
||||
QVector2D gizmo_half_res = gizmo->GetGlobals().resolution()/2;
|
||||
QVector2D adjusted_pt(x, y);
|
||||
QVector2D new_size;
|
||||
QVector2D new_pos;
|
||||
QVector2D anchor;
|
||||
static const int kXYCount = 2;
|
||||
bool negative[kXYCount] = {false};
|
||||
|
||||
double original_ratio;
|
||||
if (keep_ratio) {
|
||||
original_ratio = w_drag.GetStartValue().toDouble() / h_drag.GetStartValue().toDouble();
|
||||
}
|
||||
|
||||
// Calculate new size
|
||||
if (from_center) {
|
||||
// Calculate new size by using distance from center and doubling it
|
||||
new_size = (adjusted_pt - gizmo_half_res_ - gizmo_pos_start_) * 2;
|
||||
new_size = (adjusted_pt - gizmo_half_res - gizmo_pos_start) * 2;
|
||||
|
||||
if (gizmo_drag_ == kGizmoScaleTopCenter || gizmo_drag_ == kGizmoScaleTopLeft || gizmo_drag_ == kGizmoScaleTopRight) {
|
||||
if (IsGizmoTop(gizmo)) {
|
||||
new_size.setY(-new_size.y());
|
||||
}
|
||||
|
||||
if (gizmo_drag_ == kGizmoScaleTopLeft || gizmo_drag_ == kGizmoScaleCenterLeft || gizmo_drag_ == kGizmoScaleBottomLeft) {
|
||||
if (IsGizmoLeft(gizmo)) {
|
||||
new_size.setX(-new_size.x());
|
||||
}
|
||||
} else {
|
||||
// Calculate new size by using distance from "anchor" - i.e. the opposite point of the shape
|
||||
// from the gizmo being dragged
|
||||
adjusted_pt -= gizmo_half_res_;
|
||||
adjusted_pt -= gizmo_half_res;
|
||||
|
||||
anchor = GenerateGizmoAnchor(gizmo_pos_start_, gizmo_sz_start_, gizmo_drag_, &adjusted_pt) + gizmo_half_res_;
|
||||
anchor = GenerateGizmoAnchor(gizmo_pos_start, gizmo_sz_start, gizmo, &adjusted_pt) + gizmo_half_res;
|
||||
|
||||
adjusted_pt += gizmo_half_res_;
|
||||
adjusted_pt += gizmo_half_res;
|
||||
|
||||
// Calculate size and position
|
||||
new_size = adjusted_pt - anchor;
|
||||
@@ -229,32 +168,31 @@ void ShapeNodeBase::GizmoMove(const QPointF &p, const rational &time, const Qt::
|
||||
}
|
||||
|
||||
// Restrict sizes by constraints
|
||||
if (gizmo_drag_ == kGizmoScaleTopCenter || gizmo_drag_ == kGizmoScaleBottomCenter) {
|
||||
if (IsGizmoVerticalCenter(gizmo)) {
|
||||
if (keep_ratio) {
|
||||
// Calculate width from new height
|
||||
new_size.setX(new_size.y() * gizmo_aspect_ratio_);
|
||||
new_size.setX(new_size.y() * original_ratio);
|
||||
} else {
|
||||
// Constrain to original width
|
||||
new_size.setX(gizmo_sz_start_.x());
|
||||
new_size.setX(gizmo_sz_start.x());
|
||||
}
|
||||
}
|
||||
|
||||
if (gizmo_drag_ == kGizmoScaleCenterLeft || gizmo_drag_ == kGizmoScaleCenterRight) {
|
||||
if (IsGizmoHorizontalCenter(gizmo)) {
|
||||
if (keep_ratio) {
|
||||
// Calculate height from new width
|
||||
new_size.setY(new_size.x() / gizmo_aspect_ratio_);
|
||||
new_size.setY(new_size.x() / original_ratio);
|
||||
} else {
|
||||
// Constrain to original height
|
||||
new_size.setY(gizmo_sz_start_.y());
|
||||
new_size.setY(gizmo_sz_start.y());
|
||||
}
|
||||
}
|
||||
|
||||
if (gizmo_drag_ == kGizmoScaleTopLeft || gizmo_drag_ == kGizmoScaleTopRight
|
||||
|| gizmo_drag_ == kGizmoScaleBottomRight || gizmo_drag_ == kGizmoScaleBottomLeft) {
|
||||
if (IsGizmoCorner(gizmo)) {
|
||||
if (keep_ratio) {
|
||||
float hypot = std::hypot(new_size.x(), new_size.y());
|
||||
|
||||
float original_angle = std::atan2(gizmo_sz_start_.x(), gizmo_sz_start_.y());
|
||||
float original_angle = std::atan2(gizmo_sz_start.x(), gizmo_sz_start.y());
|
||||
|
||||
// Calculate new size based on original angle and hypotenuse
|
||||
new_size.setX(std::sin(original_angle) * hypot);
|
||||
@@ -264,7 +202,7 @@ void ShapeNodeBase::GizmoMove(const QPointF &p, const rational &time, const Qt::
|
||||
|
||||
// Calculate position
|
||||
if (from_center) {
|
||||
new_pos = gizmo_pos_start_;
|
||||
new_pos = gizmo_pos_start;
|
||||
} else {
|
||||
QVector2D using_size = new_size;
|
||||
|
||||
@@ -276,31 +214,94 @@ void ShapeNodeBase::GizmoMove(const QPointF &p, const rational &time, const Qt::
|
||||
}
|
||||
|
||||
// I'm pretty sure there's an algorithmic way of doing this, but I'm tired and this works
|
||||
if (gizmo_drag_ == kGizmoScaleCenterLeft || gizmo_drag_ == kGizmoScaleCenterRight) {
|
||||
if (IsGizmoHorizontalCenter(gizmo)) {
|
||||
using_size.setY(0);
|
||||
}
|
||||
|
||||
if (gizmo_drag_ == kGizmoScaleTopCenter || gizmo_drag_ == kGizmoScaleBottomCenter) {
|
||||
if (IsGizmoVerticalCenter(gizmo)) {
|
||||
using_size.setX(0);
|
||||
}
|
||||
|
||||
new_pos = GenerateGizmoAnchor(gizmo_pos_start_, gizmo_sz_start_, gizmo_drag_) + using_size / 2;
|
||||
new_pos = GenerateGizmoAnchor(gizmo_pos_start, gizmo_sz_start, gizmo) + using_size / 2;
|
||||
}
|
||||
|
||||
gizmo_dragger_[kGDPosX].Drag(new_pos.x());
|
||||
gizmo_dragger_[kGDPosY].Drag(new_pos.y());
|
||||
gizmo_dragger_[kGDSzX].Drag(new_size.x());
|
||||
gizmo_dragger_[kGDSzY].Drag(new_size.y());
|
||||
|
||||
x_drag.Drag(new_pos.x());
|
||||
y_drag.Drag(new_pos.y());
|
||||
w_drag.Drag(new_size.x());
|
||||
h_drag.Drag(new_size.y());
|
||||
}
|
||||
}
|
||||
|
||||
void ShapeNodeBase::GizmoRelease(MultiUndoCommand *command)
|
||||
QVector2D ShapeNodeBase::GenerateGizmoAnchor(const QVector2D &pos, const QVector2D &size, NodeGizmo *gizmo, QVector2D *pt) const
|
||||
{
|
||||
for (NodeInputDragger& i : gizmo_dragger_) {
|
||||
i.End(command);
|
||||
QVector2D anchor = pos;
|
||||
QVector2D half_sz = size/2;
|
||||
|
||||
if (IsGizmoLeft(gizmo)) {
|
||||
anchor.setX(anchor.x() + half_sz.x());
|
||||
if (pt && pt->x() > anchor.x()) {
|
||||
pt->setX(anchor.x());
|
||||
}
|
||||
}
|
||||
gizmo_dragger_.clear();
|
||||
|
||||
if (IsGizmoRight(gizmo)) {
|
||||
anchor.setX(anchor.x() - half_sz.x());
|
||||
if (pt && pt->x() < anchor.x()) {
|
||||
pt->setX(anchor.x());
|
||||
}
|
||||
}
|
||||
|
||||
if (IsGizmoTop(gizmo)) {
|
||||
anchor.setY(anchor.y() + half_sz.y());
|
||||
if (pt && pt->y() > anchor.y()) {
|
||||
pt->setY(anchor.y());
|
||||
}
|
||||
}
|
||||
|
||||
if (IsGizmoBottom(gizmo)) {
|
||||
anchor.setY(anchor.y() - half_sz.y());
|
||||
if (pt && pt->y() < anchor.y()) {
|
||||
pt->setY(anchor.y());
|
||||
}
|
||||
}
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
bool ShapeNodeBase::IsGizmoTop(NodeGizmo *g) const
|
||||
{
|
||||
return g == point_gizmo_[kGizmoScaleTopCenter] || g == point_gizmo_[kGizmoScaleTopLeft] || g == point_gizmo_[kGizmoScaleTopRight];
|
||||
}
|
||||
|
||||
bool ShapeNodeBase::IsGizmoBottom(NodeGizmo *g) const
|
||||
{
|
||||
return g == point_gizmo_[kGizmoScaleBottomCenter] || g == point_gizmo_[kGizmoScaleBottomLeft] || g == point_gizmo_[kGizmoScaleBottomRight];
|
||||
}
|
||||
|
||||
bool ShapeNodeBase::IsGizmoLeft(NodeGizmo *g) const
|
||||
{
|
||||
return g == point_gizmo_[kGizmoScaleTopLeft] || g == point_gizmo_[kGizmoScaleCenterLeft] || g == point_gizmo_[kGizmoScaleBottomLeft];
|
||||
}
|
||||
|
||||
bool ShapeNodeBase::IsGizmoRight(NodeGizmo *g) const
|
||||
{
|
||||
return g == point_gizmo_[kGizmoScaleTopRight] || g == point_gizmo_[kGizmoScaleCenterRight] || g == point_gizmo_[kGizmoScaleBottomRight];
|
||||
}
|
||||
|
||||
bool ShapeNodeBase::IsGizmoHorizontalCenter(NodeGizmo *g) const
|
||||
{
|
||||
return g == point_gizmo_[kGizmoScaleCenterLeft] || g == point_gizmo_[kGizmoScaleCenterRight];
|
||||
}
|
||||
|
||||
bool ShapeNodeBase::IsGizmoVerticalCenter(NodeGizmo *g) const
|
||||
{
|
||||
return g == point_gizmo_[kGizmoScaleTopCenter] || g == point_gizmo_[kGizmoScaleBottomCenter];
|
||||
}
|
||||
|
||||
bool ShapeNodeBase::IsGizmoCorner(NodeGizmo *g) const
|
||||
{
|
||||
return g == point_gizmo_[kGizmoScaleTopLeft] || g == point_gizmo_[kGizmoScaleTopRight]
|
||||
|| g == point_gizmo_[kGizmoScaleBottomRight] || g == point_gizmo_[kGizmoScaleBottomLeft];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#ifndef SHAPENODEBASE_H
|
||||
#define SHAPENODEBASE_H
|
||||
|
||||
#include "node/gizmo/point.h"
|
||||
#include "node/gizmo/polygon.h"
|
||||
#include "node/inputdragger.h"
|
||||
#include "node/node.h"
|
||||
|
||||
@@ -30,42 +32,42 @@ class ShapeNodeBase : public Node
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ShapeNodeBase();
|
||||
ShapeNodeBase(bool create_color_input = true);
|
||||
|
||||
NODE_DEFAULT_DESTRUCTOR(ShapeNodeBase)
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override;
|
||||
|
||||
static QString kPositionInput;
|
||||
static QString kSizeInput;
|
||||
static QString kColorInput;
|
||||
|
||||
virtual bool HasGizmos() const override
|
||||
protected:
|
||||
PolygonGizmo *poly_gizmo() const
|
||||
{
|
||||
return true;
|
||||
return poly_gizmo_;
|
||||
}
|
||||
|
||||
virtual void DrawGizmos(const NodeValueRow &row, const NodeGlobals &globals, QPainter *p) override;
|
||||
|
||||
virtual bool GizmoPress(const NodeValueRow& row, const NodeGlobals &globals, const QPointF &p) override;
|
||||
virtual void GizmoMove(const QPointF &p, const rational &time, const Qt::KeyboardModifiers &modifiers) override;
|
||||
virtual void GizmoRelease(MultiUndoCommand *command) override;
|
||||
protected slots:
|
||||
virtual void GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers) override;
|
||||
|
||||
private:
|
||||
static QVector2D GenerateGizmoAnchor(const QVector2D &pos, const QVector2D &size, int drag, QVector2D *pt);
|
||||
QVector2D GenerateGizmoAnchor(const QVector2D &pos, const QVector2D &size, NodeGizmo *gizmo, QVector2D *pt = nullptr) const;
|
||||
|
||||
bool IsGizmoTop(NodeGizmo *g) const;
|
||||
bool IsGizmoBottom(NodeGizmo *g) const;
|
||||
bool IsGizmoLeft(NodeGizmo *g) const;
|
||||
bool IsGizmoRight(NodeGizmo *g) const;
|
||||
bool IsGizmoHorizontalCenter(NodeGizmo *g) const;
|
||||
bool IsGizmoVerticalCenter(NodeGizmo *g) const;
|
||||
bool IsGizmoCorner(NodeGizmo *g) const;
|
||||
|
||||
// Gizmo variables
|
||||
static const int kGizmoWholeRect = kGizmoScaleCount;
|
||||
QRectF gizmo_resize_handle_[kGizmoScaleCount];
|
||||
QRectF gizmo_whole_rect_;
|
||||
|
||||
int gizmo_drag_;
|
||||
QVector<NodeInputDragger> gizmo_dragger_;
|
||||
QVector2D gizmo_pos_start_;
|
||||
QVector2D gizmo_sz_start_;
|
||||
QPointF gizmo_drag_start_;
|
||||
float gizmo_aspect_ratio_;
|
||||
QVector2D gizmo_half_res_;
|
||||
PointGizmo *point_gizmo_[kGizmoScaleCount];
|
||||
PolygonGizmo *poly_gizmo_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -16,9 +16,11 @@
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/generator/text/text.cpp
|
||||
node/generator/text/text.h
|
||||
node/generator/text/textlegacy.cpp
|
||||
node/generator/text/textlegacy.h
|
||||
node/generator/text/textv1.cpp
|
||||
node/generator/text/textv1.h
|
||||
node/generator/text/textv2.cpp
|
||||
node/generator/text/textv2.h
|
||||
node/generator/text/textv3.cpp
|
||||
node/generator/text/textv3.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
***/
|
||||
|
||||
#include "textlegacy.h"
|
||||
#include "textv1.h"
|
||||
|
||||
#include <QAbstractTextDocumentLayout>
|
||||
#include <QTextDocument>
|
||||
@@ -31,14 +31,14 @@ enum TextVerticalAlign {
|
||||
kVerticalAlignBottom,
|
||||
};
|
||||
|
||||
const QString TextGeneratorLegacy::kTextInput = QStringLiteral("text_in");
|
||||
const QString TextGeneratorLegacy::kHtmlInput = QStringLiteral("html_in");
|
||||
const QString TextGeneratorLegacy::kColorInput = QStringLiteral("color_in");
|
||||
const QString TextGeneratorLegacy::kVAlignInput = QStringLiteral("valign_in");
|
||||
const QString TextGeneratorLegacy::kFontInput = QStringLiteral("font_in");
|
||||
const QString TextGeneratorLegacy::kFontSizeInput = QStringLiteral("font_size_in");
|
||||
const QString TextGeneratorV1::kTextInput = QStringLiteral("text_in");
|
||||
const QString TextGeneratorV1::kHtmlInput = QStringLiteral("html_in");
|
||||
const QString TextGeneratorV1::kColorInput = QStringLiteral("color_in");
|
||||
const QString TextGeneratorV1::kVAlignInput = QStringLiteral("valign_in");
|
||||
const QString TextGeneratorV1::kFontInput = QStringLiteral("font_in");
|
||||
const QString TextGeneratorV1::kFontSizeInput = QStringLiteral("font_size_in");
|
||||
|
||||
TextGeneratorLegacy::TextGeneratorLegacy()
|
||||
TextGeneratorV1::TextGeneratorV1()
|
||||
{
|
||||
AddInput(kTextInput, NodeValue::kText, tr("Sample Text"));
|
||||
|
||||
@@ -53,27 +53,27 @@ TextGeneratorLegacy::TextGeneratorLegacy()
|
||||
AddInput(kFontSizeInput, NodeValue::kFloat, 72.0f);
|
||||
}
|
||||
|
||||
QString TextGeneratorLegacy::Name() const
|
||||
QString TextGeneratorV1::Name() const
|
||||
{
|
||||
return tr("Text (Legacy)");
|
||||
}
|
||||
|
||||
QString TextGeneratorLegacy::id() const
|
||||
QString TextGeneratorV1::id() const
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.textgenerator");
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> TextGeneratorLegacy::Category() const
|
||||
QVector<Node::CategoryID> TextGeneratorV1::Category() const
|
||||
{
|
||||
return {kCategoryGenerator};
|
||||
}
|
||||
|
||||
QString TextGeneratorLegacy::Description() const
|
||||
QString TextGeneratorV1::Description() const
|
||||
{
|
||||
return tr("Generate rich text.");
|
||||
}
|
||||
|
||||
void TextGeneratorLegacy::Retranslate()
|
||||
void TextGeneratorV1::Retranslate()
|
||||
{
|
||||
SetInputName(kTextInput, tr("Text"));
|
||||
SetInputName(kHtmlInput, tr("Enable HTML"));
|
||||
@@ -84,7 +84,7 @@ void TextGeneratorLegacy::Retranslate()
|
||||
SetComboBoxStrings(kVAlignInput, {tr("Top"), tr("Center"), tr("Bottom")});
|
||||
}
|
||||
|
||||
void TextGeneratorLegacy::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
void TextGeneratorV1::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
GenerateJob job;
|
||||
job.InsertValue(value);
|
||||
@@ -95,7 +95,7 @@ void TextGeneratorLegacy::Value(const NodeValueRow &value, const NodeGlobals &gl
|
||||
}
|
||||
}
|
||||
|
||||
void TextGeneratorLegacy::GenerateFrame(FramePtr frame, const GenerateJob& job) const
|
||||
void TextGeneratorV1::GenerateFrame(FramePtr frame, const GenerateJob& job) const
|
||||
{
|
||||
// This could probably be more optimized, but for now we use Qt to draw to a QImage.
|
||||
// QImages only support integer pixels and we use float pixels, so what we do here is draw onto
|
||||
@@ -18,21 +18,21 @@
|
||||
|
||||
***/
|
||||
|
||||
#ifndef TEXTGENERATORLEGACY_H
|
||||
#define TEXTGENERATORLEGACY_H
|
||||
#ifndef TEXTGENERATORV1_H
|
||||
#define TEXTGENERATORV1_H
|
||||
|
||||
#include "node/node.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class TextGeneratorLegacy : public Node
|
||||
class TextGeneratorV1 : public Node
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TextGeneratorLegacy();
|
||||
TextGeneratorV1();
|
||||
|
||||
NODE_DEFAULT_DESTRUCTOR(TextGeneratorLegacy)
|
||||
NODE_COPY_FUNCTION(TextGeneratorLegacy)
|
||||
NODE_DEFAULT_DESTRUCTOR(TextGeneratorV1)
|
||||
NODE_COPY_FUNCTION(TextGeneratorV1)
|
||||
|
||||
virtual QString Name() const override;
|
||||
virtual QString id() const override;
|
||||
@@ -56,4 +56,4 @@ public:
|
||||
|
||||
}
|
||||
|
||||
#endif // TEXTGENERATORLEGACY_H
|
||||
#endif // TEXTGENERATORV1_H
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
***/
|
||||
|
||||
#include "text.h"
|
||||
#include "textv2.h"
|
||||
|
||||
#include <QAbstractTextDocumentLayout>
|
||||
#include <QDateTime>
|
||||
@@ -37,13 +37,13 @@ enum TextVerticalAlign {
|
||||
kVerticalAlignBottom,
|
||||
};
|
||||
|
||||
const QString TextGenerator::kTextInput = QStringLiteral("text_in");
|
||||
const QString TextGenerator::kHtmlInput = QStringLiteral("html_in");
|
||||
const QString TextGenerator::kVAlignInput = QStringLiteral("valign_in");
|
||||
const QString TextGenerator::kFontInput = QStringLiteral("font_in");
|
||||
const QString TextGenerator::kFontSizeInput = QStringLiteral("font_size_in");
|
||||
const QString TextGeneratorV2::kTextInput = QStringLiteral("text_in");
|
||||
const QString TextGeneratorV2::kHtmlInput = QStringLiteral("html_in");
|
||||
const QString TextGeneratorV2::kVAlignInput = QStringLiteral("valign_in");
|
||||
const QString TextGeneratorV2::kFontInput = QStringLiteral("font_in");
|
||||
const QString TextGeneratorV2::kFontSizeInput = QStringLiteral("font_size_in");
|
||||
|
||||
TextGenerator::TextGenerator()
|
||||
TextGeneratorV2::TextGeneratorV2()
|
||||
{
|
||||
AddInput(kTextInput, NodeValue::kText, tr("Sample Text"));
|
||||
|
||||
@@ -59,27 +59,27 @@ TextGenerator::TextGenerator()
|
||||
SetStandardValue(kSizeInput, QVector2D(400, 300));
|
||||
}
|
||||
|
||||
QString TextGenerator::Name() const
|
||||
QString TextGeneratorV2::Name() const
|
||||
{
|
||||
return tr("Text");
|
||||
return tr("Text (Legacy)");
|
||||
}
|
||||
|
||||
QString TextGenerator::id() const
|
||||
QString TextGeneratorV2::id() const
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.text2");
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> TextGenerator::Category() const
|
||||
QVector<Node::CategoryID> TextGeneratorV2::Category() const
|
||||
{
|
||||
return {kCategoryGenerator};
|
||||
}
|
||||
|
||||
QString TextGenerator::Description() const
|
||||
QString TextGeneratorV2::Description() const
|
||||
{
|
||||
return tr("Generate rich text.");
|
||||
}
|
||||
|
||||
void TextGenerator::Retranslate()
|
||||
void TextGeneratorV2::Retranslate()
|
||||
{
|
||||
super::Retranslate();
|
||||
|
||||
@@ -91,7 +91,7 @@ void TextGenerator::Retranslate()
|
||||
SetComboBoxStrings(kVAlignInput, {tr("Top"), tr("Center"), tr("Bottom")});
|
||||
}
|
||||
|
||||
void TextGenerator::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
void TextGeneratorV2::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
GenerateJob job;
|
||||
job.InsertValue(value);
|
||||
@@ -103,7 +103,7 @@ void TextGenerator::Value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
}
|
||||
}
|
||||
|
||||
void TextGenerator::GenerateFrame(FramePtr frame, const GenerateJob& job) const
|
||||
void TextGeneratorV2::GenerateFrame(FramePtr frame, const GenerateJob& job) const
|
||||
{
|
||||
// This could probably be more optimized, but for now we use Qt to draw to a QImage.
|
||||
// QImages only support integer pixels and we use float pixels, so what we do here is draw onto
|
||||
@@ -18,21 +18,21 @@
|
||||
|
||||
***/
|
||||
|
||||
#ifndef TEXTGENERATOR_H
|
||||
#define TEXTGENERATOR_H
|
||||
#ifndef TEXTGENERATORV2_H
|
||||
#define TEXTGENERATORV2_H
|
||||
|
||||
#include "node/generator/shape/shapenodebase.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class TextGenerator : public ShapeNodeBase
|
||||
class TextGeneratorV2 : public ShapeNodeBase
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TextGenerator();
|
||||
TextGeneratorV2();
|
||||
|
||||
NODE_DEFAULT_DESTRUCTOR(TextGenerator)
|
||||
NODE_COPY_FUNCTION(TextGenerator)
|
||||
NODE_DEFAULT_DESTRUCTOR(TextGeneratorV2)
|
||||
NODE_COPY_FUNCTION(TextGeneratorV2)
|
||||
|
||||
virtual QString Name() const override;
|
||||
virtual QString id() const override;
|
||||
@@ -55,4 +55,4 @@ public:
|
||||
|
||||
}
|
||||
|
||||
#endif // TEXTGENERATOR_H
|
||||
#endif // TEXTGENERATORV2_H
|
||||
@@ -0,0 +1,140 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "textv3.h"
|
||||
|
||||
#include <QAbstractTextDocumentLayout>
|
||||
#include <QDateTime>
|
||||
#include <QTextDocument>
|
||||
|
||||
#include "common/functiontimer.h"
|
||||
#include "common/html.h"
|
||||
#include "node/project/project.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
#define super ShapeNodeBase
|
||||
|
||||
enum TextVerticalAlign {
|
||||
kVerticalAlignTop,
|
||||
kVerticalAlignCenter,
|
||||
kVerticalAlignBottom,
|
||||
};
|
||||
|
||||
const QString TextGeneratorV3::kTextInput = QStringLiteral("text_in");
|
||||
|
||||
TextGeneratorV3::TextGeneratorV3() :
|
||||
ShapeNodeBase(false)
|
||||
{
|
||||
AddInput(kTextInput, NodeValue::kText, QStringLiteral("<p style='font-size: 72pt; color: white;'>%1</p>").arg(tr("Sample Text")));
|
||||
|
||||
SetStandardValue(kSizeInput, QVector2D(400, 300));
|
||||
|
||||
text_gizmo_ = new TextGizmo(this);
|
||||
text_gizmo_->SetInput(NodeInput(this, kTextInput));
|
||||
}
|
||||
|
||||
QString TextGeneratorV3::Name() const
|
||||
{
|
||||
return tr("Text");
|
||||
}
|
||||
|
||||
QString TextGeneratorV3::id() const
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.text3");
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> TextGeneratorV3::Category() const
|
||||
{
|
||||
return {kCategoryGenerator};
|
||||
}
|
||||
|
||||
QString TextGeneratorV3::Description() const
|
||||
{
|
||||
return tr("Generate rich text.");
|
||||
}
|
||||
|
||||
void TextGeneratorV3::Retranslate()
|
||||
{
|
||||
super::Retranslate();
|
||||
|
||||
SetInputName(kTextInput, tr("Text"));
|
||||
}
|
||||
|
||||
void TextGeneratorV3::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
GenerateJob job;
|
||||
job.InsertValue(value);
|
||||
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||
job.SetRequestedFormat(VideoParams::kFormatUnsigned8);
|
||||
|
||||
// FIXME: Provide user override for this
|
||||
job.SetColorspace(project()->color_manager()->GetDefaultInputColorSpace());
|
||||
|
||||
if (!job.GetValue(kTextInput).data().toString().isEmpty()) {
|
||||
table->Push(NodeValue::kGenerateJob, QVariant::fromValue(job), this);
|
||||
}
|
||||
}
|
||||
|
||||
void TextGeneratorV3::GenerateFrame(FramePtr frame, const GenerateJob& job) const
|
||||
{
|
||||
QImage img(reinterpret_cast<uchar*>(frame->data()), frame->width(), frame->height(), frame->linesize_bytes(), QImage::Format_RGBA8888_Premultiplied);
|
||||
img.fill(Qt::transparent);
|
||||
|
||||
// 96 DPI in DPM (96 / 2.54 * 100)
|
||||
const int dpm = 3780;
|
||||
img.setDotsPerMeterX(dpm);
|
||||
img.setDotsPerMeterY(dpm);
|
||||
|
||||
QTextDocument text_doc;
|
||||
text_doc.documentLayout()->setPaintDevice(&img);
|
||||
|
||||
QString html = job.GetValue(kTextInput).data().toString();
|
||||
Html::HtmlToDoc(&text_doc, html);
|
||||
|
||||
QVector2D size = job.GetValue(kSizeInput).data().value<QVector2D>();
|
||||
text_doc.setTextWidth(size.x());
|
||||
|
||||
// Draw rich text onto image
|
||||
QPainter p(&img);
|
||||
p.scale(1.0 / frame->video_params().divider(), 1.0 / frame->video_params().divider());
|
||||
|
||||
QVector2D pos = job.GetValue(kPositionInput).data().value<QVector2D>();
|
||||
p.translate(pos.x() - size.x()/2, pos.y() - size.y()/2);
|
||||
p.translate(frame->video_params().width()/2, frame->video_params().height()/2);
|
||||
p.setClipRect(0, 0, size.x(), size.y());
|
||||
|
||||
// Ensure default text color is white
|
||||
QAbstractTextDocumentLayout::PaintContext ctx;
|
||||
ctx.palette.setColor(QPalette::Text, Qt::white);
|
||||
|
||||
text_doc.documentLayout()->draw(&p, ctx);
|
||||
}
|
||||
|
||||
void TextGeneratorV3::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals)
|
||||
{
|
||||
super::UpdateGizmoPositions(row, globals);
|
||||
|
||||
QRectF rect = poly_gizmo()->GetPolygon().boundingRect();
|
||||
text_gizmo_->SetRect(rect);
|
||||
text_gizmo_->SetHtml(row[kTextInput].data().toString());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef TEXTGENERATORV3_H
|
||||
#define TEXTGENERATORV3_H
|
||||
|
||||
#include "node/generator/shape/shapenodebase.h"
|
||||
#include "node/gizmo/text.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class TextGeneratorV3 : public ShapeNodeBase
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TextGeneratorV3();
|
||||
|
||||
NODE_DEFAULT_DESTRUCTOR(TextGeneratorV3)
|
||||
NODE_COPY_FUNCTION(TextGeneratorV3)
|
||||
|
||||
virtual QString Name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QVector<CategoryID> Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
|
||||
|
||||
virtual void GenerateFrame(FramePtr frame, const GenerateJob &job) const override;
|
||||
|
||||
virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override;
|
||||
|
||||
static const QString kTextInput;
|
||||
|
||||
private:
|
||||
TextGizmo *text_gizmo_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // TEXTGENERATORV3_H
|
||||
@@ -0,0 +1,36 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2021 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/gizmo/draggable.cpp
|
||||
node/gizmo/draggable.h
|
||||
node/gizmo/gizmo.cpp
|
||||
node/gizmo/gizmo.h
|
||||
node/gizmo/line.cpp
|
||||
node/gizmo/line.h
|
||||
node/gizmo/path.cpp
|
||||
node/gizmo/path.h
|
||||
node/gizmo/point.cpp
|
||||
node/gizmo/point.h
|
||||
node/gizmo/polygon.cpp
|
||||
node/gizmo/polygon.h
|
||||
node/gizmo/screen.cpp
|
||||
node/gizmo/screen.h
|
||||
node/gizmo/text.cpp
|
||||
node/gizmo/text.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,52 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "draggable.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
DraggableGizmo::DraggableGizmo(QObject *parent)
|
||||
: NodeGizmo{parent},
|
||||
drag_value_behavior_(kAbsolute)
|
||||
{
|
||||
}
|
||||
|
||||
void DraggableGizmo::DragStart(const NodeValueRow &row, double abs_x, double abs_y, const rational &time)
|
||||
{
|
||||
for (int i=0; i<draggers_.size(); i++) {
|
||||
draggers_[i].Start(inputs_[i], time);
|
||||
}
|
||||
|
||||
emit HandleStart(row, abs_x, abs_y, time);
|
||||
}
|
||||
|
||||
void DraggableGizmo::DragMove(double x, double y, const Qt::KeyboardModifiers &modifiers)
|
||||
{
|
||||
emit HandleMovement(x, y, modifiers);
|
||||
}
|
||||
|
||||
void DraggableGizmo::DragEnd(MultiUndoCommand *command)
|
||||
{
|
||||
for (int i=0; i<draggers_.size(); i++) {
|
||||
draggers_[i].End(command);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef DRAGGABLEGIZMO_H
|
||||
#define DRAGGABLEGIZMO_H
|
||||
|
||||
#include "common/rational.h"
|
||||
#include "gizmo.h"
|
||||
#include "node/inputdragger.h"
|
||||
#include "undo/undocommand.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class DraggableGizmo : public NodeGizmo
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/// Changes what the X/Y coordinates emitted from HandleMovement specify
|
||||
enum DragValueBehavior {
|
||||
/// X/Y will be the exact mouse coordinates (in sequence pixels)
|
||||
kAbsolute,
|
||||
|
||||
/// X/Y will be the movement since the last time HandleMovement was called
|
||||
kDeltaFromPrevious,
|
||||
|
||||
/// X/Y will be the movement from the start of the drag
|
||||
kDeltaFromStart
|
||||
};
|
||||
|
||||
explicit DraggableGizmo(QObject *parent = nullptr);
|
||||
|
||||
void DragStart(const NodeValueRow &row, double abs_x, double abs_y, const olive::rational &time);
|
||||
|
||||
void DragMove(double x, double y, const Qt::KeyboardModifiers &modifiers);
|
||||
|
||||
void DragEnd(olive::MultiUndoCommand *command);
|
||||
|
||||
void AddInput(const NodeKeyframeTrackReference &input)
|
||||
{
|
||||
inputs_.append(input);
|
||||
draggers_.append(NodeInputDragger());
|
||||
}
|
||||
|
||||
QVector<NodeInputDragger> &GetDraggers()
|
||||
{
|
||||
return draggers_;
|
||||
}
|
||||
|
||||
DragValueBehavior GetDragValueBehavior() const { return drag_value_behavior_; }
|
||||
void SetDragValueBehavior(DragValueBehavior d) { drag_value_behavior_ = d; }
|
||||
|
||||
signals:
|
||||
void HandleStart(const olive::NodeValueRow &row, double x, double y, const olive::rational &time);
|
||||
|
||||
void HandleMovement(double x, double y, const Qt::KeyboardModifiers &modifiers);
|
||||
|
||||
private:
|
||||
QVector<NodeKeyframeTrackReference> inputs_;
|
||||
|
||||
QVector<NodeInputDragger> draggers_;
|
||||
|
||||
DragValueBehavior drag_value_behavior_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // DRAGGABLEGIZMO_H
|
||||
@@ -0,0 +1,30 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "gizmo.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
NodeGizmo::NodeGizmo(QObject *parent)
|
||||
{
|
||||
setParent(parent);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef NODEGIZMO_H
|
||||
#define NODEGIZMO_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QPainter>
|
||||
|
||||
#include "node/globals.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class NodeGizmo : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit NodeGizmo(QObject *parent = nullptr);
|
||||
|
||||
virtual void Draw(QPainter *p) const {}
|
||||
|
||||
const NodeGlobals &GetGlobals() const { return globals_; }
|
||||
void SetGlobals(const NodeGlobals &globals) { globals_ = globals; }
|
||||
|
||||
signals:
|
||||
|
||||
private:
|
||||
NodeGlobals globals_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // NODEGIZMO_H
|
||||
@@ -0,0 +1,38 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "line.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
LineGizmo::LineGizmo(QObject *parent) :
|
||||
NodeGizmo(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void LineGizmo::Draw(QPainter *p) const
|
||||
{
|
||||
p->setBrush(Qt::NoBrush);
|
||||
p->setPen(QPen(Qt::white, 0));
|
||||
|
||||
p->drawLine(line_);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef LINEGIZMO_H
|
||||
#define LINEGIZMO_H
|
||||
|
||||
#include <QLineF>
|
||||
|
||||
#include "gizmo.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class LineGizmo : public NodeGizmo
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
LineGizmo(QObject *parent = nullptr);
|
||||
|
||||
const QLineF &GetLine() const { return line_; }
|
||||
void SetLine(const QLineF &line) { line_ = line; }
|
||||
|
||||
virtual void Draw(QPainter *p) const override;
|
||||
|
||||
private:
|
||||
QLineF line_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // LINEGIZMO_H
|
||||
@@ -0,0 +1,39 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "path.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
PathGizmo::PathGizmo(QObject *parent) :
|
||||
DraggableGizmo{parent}
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void PathGizmo::Draw(QPainter *p) const
|
||||
{
|
||||
p->setPen(QPen(Qt::white, 0));
|
||||
p->setBrush(Qt::NoBrush);
|
||||
|
||||
p->drawPath(path_);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef PATHGIZMO_H
|
||||
#define PATHGIZMO_H
|
||||
|
||||
#include <QPainterPath>
|
||||
|
||||
#include "draggable.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class PathGizmo : public DraggableGizmo
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PathGizmo(QObject *parent = nullptr);
|
||||
|
||||
const QPainterPath &GetPath() const { return path_; }
|
||||
void SetPath(const QPainterPath &path) { path_ = path; }
|
||||
|
||||
virtual void Draw(QPainter *p) const override;
|
||||
|
||||
private:
|
||||
QPainterPath path_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // PATHGIZMO_H
|
||||
@@ -0,0 +1,99 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "point.h"
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
namespace olive {
|
||||
|
||||
PointGizmo::PointGizmo(const Shape &shape, bool smaller, QObject *parent) :
|
||||
DraggableGizmo{parent},
|
||||
shape_(shape),
|
||||
smaller_(smaller)
|
||||
{
|
||||
}
|
||||
|
||||
PointGizmo::PointGizmo(const Shape &shape, QObject *parent) :
|
||||
PointGizmo(shape, false, parent)
|
||||
{
|
||||
}
|
||||
|
||||
PointGizmo::PointGizmo(QObject *parent) :
|
||||
PointGizmo(kSquare, parent)
|
||||
{
|
||||
}
|
||||
|
||||
void PointGizmo::Draw(QPainter *p) const
|
||||
{
|
||||
QRectF rect = GetDrawingRect(GetStandardRadius() / p->transform().m11());
|
||||
|
||||
if (shape_ != kAnchorPoint) {
|
||||
p->setPen(Qt::NoPen);
|
||||
p->setBrush(Qt::white);
|
||||
}
|
||||
|
||||
switch (shape_) {
|
||||
case kSquare:
|
||||
p->drawRect(rect);
|
||||
break;
|
||||
case kCircle:
|
||||
p->drawEllipse(rect);
|
||||
break;
|
||||
case kAnchorPoint:
|
||||
p->setPen(QPen(Qt::white, 0));
|
||||
p->setBrush(Qt::NoBrush);
|
||||
|
||||
p->drawEllipse(rect);
|
||||
p->drawLines({QPointF(rect.left(), rect.center().y()),
|
||||
QPointF(rect.right(), rect.center().y()),
|
||||
QPointF(rect.center().x(), rect.top()),
|
||||
QPointF(rect.center().x(), rect.bottom())});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QRectF PointGizmo::GetClickingRect(const QTransform &t) const
|
||||
{
|
||||
return GetDrawingRect(GetStandardRadius() / t.m11() * 1.5);
|
||||
}
|
||||
|
||||
double PointGizmo::GetStandardRadius()
|
||||
{
|
||||
return QFontMetrics(qApp->font()).height() * 0.25;
|
||||
}
|
||||
|
||||
QRectF PointGizmo::GetDrawingRect(double radius) const
|
||||
{
|
||||
if (shape_ == kAnchorPoint) {
|
||||
radius *= 2;
|
||||
}
|
||||
|
||||
if (smaller_) {
|
||||
radius *= 0.5;
|
||||
}
|
||||
|
||||
return QRectF(point_.x() - radius,
|
||||
point_.y() - radius,
|
||||
2*radius,
|
||||
2*radius);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef POINTGIZMO_H
|
||||
#define POINTGIZMO_H
|
||||
|
||||
#include <QPointF>
|
||||
|
||||
#include "draggable.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class PointGizmo : public DraggableGizmo
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Shape {
|
||||
kSquare,
|
||||
kCircle,
|
||||
kAnchorPoint
|
||||
};
|
||||
|
||||
explicit PointGizmo(const Shape &shape, bool smaller, QObject *parent = nullptr);
|
||||
explicit PointGizmo(const Shape &shape, QObject *parent = nullptr);
|
||||
explicit PointGizmo(QObject *parent = nullptr);
|
||||
|
||||
const Shape &GetShape() const { return shape_; }
|
||||
void SetShape(const Shape &s) { shape_ = s; }
|
||||
|
||||
const QPointF &GetPoint() const { return point_; }
|
||||
void SetPoint(const QPointF &pt) { point_ = pt; }
|
||||
|
||||
bool GetSmaller() const { return smaller_; }
|
||||
void SetSmaller(bool e) { smaller_ = e; }
|
||||
|
||||
virtual void Draw(QPainter *p) const override;
|
||||
|
||||
QRectF GetClickingRect(const QTransform &t) const;
|
||||
|
||||
private:
|
||||
static double GetStandardRadius();
|
||||
|
||||
QRectF GetDrawingRect(double radius) const;
|
||||
|
||||
Shape shape_;
|
||||
|
||||
QPointF point_;
|
||||
|
||||
bool smaller_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // POINTGIZMO_H
|
||||
@@ -0,0 +1,38 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "polygon.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
PolygonGizmo::PolygonGizmo(QObject *parent)
|
||||
: DraggableGizmo{parent}
|
||||
{
|
||||
}
|
||||
|
||||
void PolygonGizmo::Draw(QPainter *p) const
|
||||
{
|
||||
p->setPen(QPen(Qt::white, 0));
|
||||
p->setBrush(Qt::NoBrush);
|
||||
|
||||
p->drawPolyline(polygon_);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef POLYGONGIZMO_H
|
||||
#define POLYGONGIZMO_H
|
||||
|
||||
#include <QPolygonF>
|
||||
|
||||
#include "draggable.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class PolygonGizmo : public DraggableGizmo
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PolygonGizmo(QObject *parent = nullptr);
|
||||
|
||||
const QPolygonF &GetPolygon() const { return polygon_; }
|
||||
void SetPolygon(const QPolygonF &polygon) { polygon_ = polygon; }
|
||||
|
||||
virtual void Draw(QPainter *p) const override;
|
||||
|
||||
private:
|
||||
QPolygonF polygon_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // POLYGONGIZMO_H
|
||||
@@ -0,0 +1,31 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "screen.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
ScreenGizmo::ScreenGizmo(QObject *parent)
|
||||
: DraggableGizmo{parent}
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef SCREENGIZMO_H
|
||||
#define SCREENGIZMO_H
|
||||
|
||||
#include "draggable.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class ScreenGizmo : public DraggableGizmo
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ScreenGizmo(QObject *parent = nullptr);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // SCREENGIZMO_H
|
||||
@@ -0,0 +1,43 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "text.h"
|
||||
|
||||
#include "core.h"
|
||||
#include "undo/undocommand.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
TextGizmo::TextGizmo(QObject *parent)
|
||||
: NodeGizmo{parent}
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TextGizmo::UpdateInputHtml(const QString &s, const rational &time)
|
||||
{
|
||||
if (input_.IsValid()) {
|
||||
MultiUndoCommand *command = new MultiUndoCommand();
|
||||
Node::SetValueAtTime(input_.input(), time, s, input_.track(), command, true);
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef TEXTGIZMO_H
|
||||
#define TEXTGIZMO_H
|
||||
|
||||
#include "gizmo.h"
|
||||
#include "node/param.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class TextGizmo : public NodeGizmo
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TextGizmo(QObject *parent = nullptr);
|
||||
|
||||
const QRectF &GetRect() const { return rect_; }
|
||||
void SetRect(const QRectF &r) { rect_ = r; }
|
||||
|
||||
const QString &GetHtml() const { return text_; }
|
||||
void SetHtml(const QString &t) { text_ = t; }
|
||||
|
||||
void SetInput(const NodeKeyframeTrackReference &input) { input_ = input; }
|
||||
|
||||
void UpdateInputHtml(const QString &s, const rational &time);
|
||||
|
||||
private:
|
||||
QRectF rect_;
|
||||
|
||||
QString text_;
|
||||
|
||||
NodeKeyframeTrackReference input_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // TEXTGIZMO_H
|
||||
@@ -30,6 +30,8 @@ namespace olive {
|
||||
class NodeGlobals
|
||||
{
|
||||
public:
|
||||
NodeGlobals(){}
|
||||
|
||||
NodeGlobals(const QVector2D &resolution, const rational &pixel_aspect, const TimeRange &time) :
|
||||
resolution_(resolution),
|
||||
pixel_aspect_(pixel_aspect),
|
||||
|
||||
+35
-28
@@ -43,7 +43,7 @@ QString NodeGroup::id() const
|
||||
|
||||
QVector<Node::CategoryID> NodeGroup::Category() const
|
||||
{
|
||||
return {kCategoryGeneral};
|
||||
return {kCategoryUnknown};
|
||||
}
|
||||
|
||||
QString NodeGroup::Description() const
|
||||
@@ -58,23 +58,43 @@ void NodeGroup::Retranslate()
|
||||
}
|
||||
}
|
||||
|
||||
QString NodeGroup::AddInputPassthrough(const NodeInput &input, const InputFlags &flags)
|
||||
QString NodeGroup::AddInputPassthrough(const NodeInput &input, const QString &force_id)
|
||||
{
|
||||
Q_ASSERT(ContextContainsNode(input.node()));
|
||||
|
||||
for (auto it=input_passthroughs_.cbegin(); it!=input_passthroughs_.cend(); it++) {
|
||||
if (it.value() == input) {
|
||||
if (it->second == input) {
|
||||
// Already passing this input through
|
||||
return it.key();
|
||||
return it->first;
|
||||
}
|
||||
}
|
||||
|
||||
// Add input
|
||||
QString id = GetGroupInputIDFromInput(input);
|
||||
QString id;
|
||||
if (force_id.isEmpty()) {
|
||||
id = input.input();
|
||||
int i = 2;
|
||||
while (HasInputWithID(id)) {
|
||||
id = QStringLiteral("%1_%2").arg(input.name(), QString::number(i));
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
id = force_id;
|
||||
|
||||
AddInput(id, input.GetDataType(), input.GetDefaultValue(), input.GetFlags() | flags);
|
||||
bool already_exists = false;
|
||||
for (auto it=input_passthroughs_.cbegin(); it!=input_passthroughs_.cend(); it++) {
|
||||
if (it->first == id) {
|
||||
already_exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
input_passthroughs_.insert(id, input);
|
||||
Q_ASSERT(!already_exists);
|
||||
}
|
||||
|
||||
AddInput(id, input.GetDataType(), input.GetDefaultValue(), input.GetFlags());
|
||||
|
||||
input_passthroughs_.append({id, input});
|
||||
|
||||
emit InputPassthroughAdded(this, input);
|
||||
|
||||
@@ -83,11 +103,11 @@ QString NodeGroup::AddInputPassthrough(const NodeInput &input, const InputFlags
|
||||
|
||||
void NodeGroup::RemoveInputPassthrough(const NodeInput &input)
|
||||
{
|
||||
for (auto it=input_passthroughs_.cbegin(); it!=input_passthroughs_.cend(); it++) {
|
||||
if (it.value() == input) {
|
||||
RemoveInput(it.key());
|
||||
for (auto it=input_passthroughs_.begin(); it!=input_passthroughs_.end(); it++) {
|
||||
if (it->second == input) {
|
||||
RemoveInput(it->first);
|
||||
emit InputPassthroughRemoved(this, it->second);
|
||||
input_passthroughs_.erase(it);
|
||||
emit InputPassthroughRemoved(this, it.value());
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -102,23 +122,10 @@ void NodeGroup::SetOutputPassthrough(Node *node)
|
||||
emit OutputPassthroughChanged(this, output_passthrough_);
|
||||
}
|
||||
|
||||
QString NodeGroup::GetGroupInputIDFromInput(const NodeInput &input)
|
||||
{
|
||||
QCryptographicHash hash(QCryptographicHash::Sha1);
|
||||
|
||||
hash.addData(input.node()->GetUUID().toByteArray());
|
||||
|
||||
hash.addData(input.input().toUtf8());
|
||||
|
||||
hash.addData((const char*) &input.element(), sizeof(input.element()));
|
||||
|
||||
return QString::fromLatin1(hash.result().toHex());
|
||||
}
|
||||
|
||||
bool NodeGroup::ContainsInputPassthrough(const NodeInput &input) const
|
||||
{
|
||||
for (auto it=input_passthroughs_.cbegin(); it!=input_passthroughs_.cend(); it++) {
|
||||
if (it.value() == input) {
|
||||
if (it->second == input) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -135,7 +142,7 @@ QString NodeGroup::GetInputName(const QString &id) const
|
||||
}
|
||||
|
||||
// Call GetInputName of passed through node, which may be another group
|
||||
NodeInput pass = input_passthroughs_.value(id);
|
||||
NodeInput pass = GetInputFromID(id);
|
||||
return pass.node()->GetInputName(pass.input());
|
||||
}
|
||||
|
||||
@@ -149,7 +156,7 @@ NodeInput NodeGroup::ResolveInput(NodeInput input)
|
||||
bool NodeGroup::GetInner(NodeInput *input)
|
||||
{
|
||||
if (NodeGroup *g = dynamic_cast<NodeGroup*>(input->node())) {
|
||||
const NodeInput &passthrough = g->GetInputPassthroughs().value(input->input());
|
||||
const NodeInput &passthrough = g->GetInputFromID(input->input());
|
||||
input->set_node(passthrough.node());
|
||||
input->set_input(passthrough.input());
|
||||
return true;
|
||||
@@ -161,7 +168,7 @@ bool NodeGroup::GetInner(NodeInput *input)
|
||||
void NodeGroupAddInputPassthrough::redo()
|
||||
{
|
||||
if (!group_->ContainsInputPassthrough(input_)) {
|
||||
group_->AddInputPassthrough(input_);
|
||||
group_->AddInputPassthrough(input_, force_id_);
|
||||
actually_added_ = true;
|
||||
} else {
|
||||
actually_added_ = false;
|
||||
|
||||
+31
-9
@@ -41,7 +41,7 @@ public:
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
QString AddInputPassthrough(const NodeInput &input, const InputFlags &flags = InputFlags());
|
||||
QString AddInputPassthrough(const NodeInput &input, const QString &force_id = QString());
|
||||
|
||||
void RemoveInputPassthrough(const NodeInput &input);
|
||||
|
||||
@@ -52,9 +52,9 @@ public:
|
||||
|
||||
void SetOutputPassthrough(Node *node);
|
||||
|
||||
static QString GetGroupInputIDFromInput(const NodeInput &input);
|
||||
|
||||
const QHash<QString, NodeInput> &GetInputPassthroughs() const
|
||||
using InputPassthrough = QPair<QString, NodeInput>;
|
||||
using InputPassthroughs = QVector<InputPassthrough>;
|
||||
const InputPassthroughs &GetInputPassthroughs() const
|
||||
{
|
||||
return input_passthroughs_;
|
||||
}
|
||||
@@ -66,15 +66,35 @@ public:
|
||||
static NodeInput ResolveInput(NodeInput input);
|
||||
static bool GetInner(NodeInput *input);
|
||||
|
||||
QString GetIDOfPassthrough(const NodeInput &input) const
|
||||
{
|
||||
for (auto it=input_passthroughs_.cbegin(); it!=input_passthroughs_.cend(); it++) {
|
||||
if (it->second == input) {
|
||||
return it->first;
|
||||
}
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
NodeInput GetInputFromID(const QString &id) const
|
||||
{
|
||||
for (auto it=input_passthroughs_.cbegin(); it!=input_passthroughs_.cend(); it++) {
|
||||
if (it->first == id) {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
return NodeInput();
|
||||
}
|
||||
|
||||
signals:
|
||||
void InputPassthroughAdded(NodeGroup *group, const NodeInput &input);
|
||||
void InputPassthroughAdded(olive::NodeGroup *group, const olive::NodeInput &input);
|
||||
|
||||
void InputPassthroughRemoved(NodeGroup *group, const NodeInput &input);
|
||||
void InputPassthroughRemoved(olive::NodeGroup *group, const olive::NodeInput &input);
|
||||
|
||||
void OutputPassthroughChanged(NodeGroup *group, Node *output);
|
||||
void OutputPassthroughChanged(olive::NodeGroup *group, olive::Node *output);
|
||||
|
||||
private:
|
||||
QHash<QString, NodeInput> input_passthroughs_;
|
||||
InputPassthroughs input_passthroughs_;
|
||||
|
||||
Node *output_passthrough_;
|
||||
|
||||
@@ -83,7 +103,7 @@ private:
|
||||
class NodeGroupAddInputPassthrough : public UndoCommand
|
||||
{
|
||||
public:
|
||||
NodeGroupAddInputPassthrough(NodeGroup *group, const NodeInput &input) :
|
||||
NodeGroupAddInputPassthrough(NodeGroup *group, const NodeInput &input, const QString &force_id = QString()) :
|
||||
group_(group),
|
||||
input_(input),
|
||||
actually_added_(false)
|
||||
@@ -104,6 +124,8 @@ private:
|
||||
|
||||
NodeInput input_;
|
||||
|
||||
QString force_id_;
|
||||
|
||||
bool actually_added_;
|
||||
|
||||
};
|
||||
|
||||
@@ -45,7 +45,7 @@ QString TimeInput::id() const
|
||||
|
||||
QVector<Node::CategoryID> TimeInput::Category() const
|
||||
{
|
||||
return {kCategoryInput};
|
||||
return {kCategoryTime};
|
||||
}
|
||||
|
||||
QString TimeInput::Description() const
|
||||
|
||||
@@ -50,7 +50,7 @@ public:
|
||||
|
||||
virtual QVector<CategoryID> Category() const override
|
||||
{
|
||||
return {kCategoryInput};
|
||||
return {kCategoryGenerator};
|
||||
}
|
||||
|
||||
virtual QString Description() const override
|
||||
|
||||
@@ -51,6 +51,16 @@ public:
|
||||
return start_value_;
|
||||
}
|
||||
|
||||
const NodeKeyframeTrackReference &GetInput() const
|
||||
{
|
||||
return input_;
|
||||
}
|
||||
|
||||
const rational &GetTime() const
|
||||
{
|
||||
return time_;
|
||||
}
|
||||
|
||||
private:
|
||||
NodeKeyframeTrackReference input_;
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2021 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
add_subdirectory(colordifferencekey)
|
||||
add_subdirectory(despill)
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2021 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/keying/colordifferencekey/colordifferencekey.h
|
||||
node/keying/colordifferencekey/colordifferencekey.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,101 @@
|
||||
/***
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include "colordifferencekey.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
const QString ColorDifferenceKeyNode::kTextureInput = QStringLiteral("tex_in");
|
||||
const QString ColorDifferenceKeyNode::kGarbageMatteInput = QStringLiteral("garbage_in");
|
||||
const QString ColorDifferenceKeyNode::kCoreMatteInput = QStringLiteral("core_in");
|
||||
const QString ColorDifferenceKeyNode::kColorInput = QStringLiteral("color_in");
|
||||
const QString ColorDifferenceKeyNode::kShadowsInput = QStringLiteral("shadows_in");
|
||||
const QString ColorDifferenceKeyNode::kHighlightsInput = QStringLiteral("highlights_in");
|
||||
const QString ColorDifferenceKeyNode::kMaskOnlyInput = QStringLiteral("mask_only_in");
|
||||
|
||||
ColorDifferenceKeyNode::ColorDifferenceKeyNode() {
|
||||
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||
|
||||
AddInput(kGarbageMatteInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||
|
||||
AddInput(kCoreMatteInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||
|
||||
AddInput(kColorInput, NodeValue::kCombo, 0);
|
||||
|
||||
AddInput(kHighlightsInput, NodeValue::kFloat, 100.0f);
|
||||
SetInputProperty(kHighlightsInput, QStringLiteral("min"), 0.0);
|
||||
|
||||
AddInput(kShadowsInput, NodeValue::kFloat, 100.0f);
|
||||
SetInputProperty(kShadowsInput, QStringLiteral("min"), 0.0);
|
||||
|
||||
AddInput(kMaskOnlyInput, NodeValue::kBoolean, false);
|
||||
}
|
||||
|
||||
Node *ColorDifferenceKeyNode::copy() const
|
||||
{
|
||||
return new ColorDifferenceKeyNode();
|
||||
}
|
||||
|
||||
QString ColorDifferenceKeyNode::Name() const
|
||||
{
|
||||
return tr("Color Difference Key");
|
||||
}
|
||||
|
||||
QString ColorDifferenceKeyNode::id() const
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.colordifferencekey");
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> ColorDifferenceKeyNode::Category() const
|
||||
{
|
||||
return {kCategoryKeying};
|
||||
}
|
||||
|
||||
QString ColorDifferenceKeyNode::Description() const
|
||||
{
|
||||
return tr("A simple color key based on the distance of one color from other colors.");
|
||||
}
|
||||
|
||||
void ColorDifferenceKeyNode::Retranslate()
|
||||
{
|
||||
SetInputName(kTextureInput, tr("Input"));
|
||||
SetInputName(kGarbageMatteInput, tr("Garbage Matte"));
|
||||
SetInputName(kCoreMatteInput, tr("Core Matte"));
|
||||
SetInputName(kColorInput, tr("Key Color"));
|
||||
SetComboBoxStrings(kColorInput, {tr("Green"), tr("Blue")});
|
||||
SetInputName(kShadowsInput, tr("Shadows"));
|
||||
SetInputName(kHighlightsInput, tr("Highlights"));
|
||||
SetInputName(kMaskOnlyInput, tr("Show Mask Only"));
|
||||
}
|
||||
|
||||
ShaderCode ColorDifferenceKeyNode::GetShaderCode(const QString &shader_id) const
|
||||
{
|
||||
Q_UNUSED(shader_id)
|
||||
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/colordifferencekey.frag"));
|
||||
}
|
||||
|
||||
void ColorDifferenceKeyNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
ShaderJob job;
|
||||
job.InsertValue(value);
|
||||
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||
|
||||
// If there's no texture, no need to run an operation
|
||||
if (!job.GetValue(kTextureInput).data().isNull()) {
|
||||
table->Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace olive
|
||||
@@ -0,0 +1,51 @@
|
||||
/***
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#ifndef COLORDIFFERENCEKEYNODE_H
|
||||
#define COLORDIFFERENCEKEYNODE_H
|
||||
|
||||
#include "node/node.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class ColorDifferenceKeyNode : public Node {
|
||||
public:
|
||||
ColorDifferenceKeyNode();
|
||||
|
||||
virtual Node* copy() const override;
|
||||
|
||||
virtual QString Name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QVector<CategoryID> Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
virtual ShaderCode GetShaderCode(const QString& shader_id) const override;
|
||||
virtual void Value(const NodeValueRow& value, const NodeGlobals& globals, NodeValueTable* table) const override;
|
||||
|
||||
static const QString kTextureInput;
|
||||
static const QString kGarbageMatteInput;
|
||||
static const QString kCoreMatteInput;
|
||||
static const QString kColorInput;
|
||||
static const QString kShadowsInput;
|
||||
static const QString kHighlightsInput;
|
||||
static const QString kMaskOnlyInput;
|
||||
|
||||
};
|
||||
|
||||
} // namespace olive
|
||||
|
||||
#endif // COLORDIFFERENCEKEYNODE_H
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2021 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/keying/despill/despill.h
|
||||
node/keying/despill/despill.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,99 @@
|
||||
/***
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include "despill.h"
|
||||
|
||||
#include "node/project/project.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
const QString DespillNode::kTextureInput = QStringLiteral("tex_in");
|
||||
const QString DespillNode::kColorInput = QStringLiteral("color_in");
|
||||
const QString DespillNode::kMethodInput = QStringLiteral("method_in");
|
||||
const QString DespillNode::kPreserveLuminanceInput = QStringLiteral("preserve_luminance_input");
|
||||
|
||||
DespillNode::DespillNode()
|
||||
{
|
||||
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||
|
||||
AddInput(kColorInput, NodeValue::kCombo, 0);
|
||||
|
||||
AddInput(kMethodInput, NodeValue::kCombo, 0);
|
||||
|
||||
AddInput(kPreserveLuminanceInput, NodeValue::kBoolean, false);
|
||||
}
|
||||
|
||||
Node* DespillNode::copy() const
|
||||
{
|
||||
return new DespillNode();
|
||||
}
|
||||
|
||||
QString DespillNode::Name() const
|
||||
{
|
||||
return tr("Despill");
|
||||
}
|
||||
|
||||
QString DespillNode::id() const
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.despill");
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> DespillNode::Category() const
|
||||
{
|
||||
return {kCategoryKeying};
|
||||
}
|
||||
|
||||
QString DespillNode::Description() const
|
||||
{
|
||||
return tr("Selection of simple depsill operations");
|
||||
}
|
||||
|
||||
void DespillNode::Retranslate()
|
||||
{
|
||||
SetInputName(kTextureInput, tr("Input"));
|
||||
|
||||
SetInputName(kColorInput, tr("Key Color"));
|
||||
SetComboBoxStrings(kColorInput, {tr("Green"), tr("Blue")});
|
||||
|
||||
|
||||
SetInputName(kMethodInput, tr("Method"));
|
||||
SetComboBoxStrings(kMethodInput, {tr("Average"), tr("Double Red Average"), tr("Double Average"), tr("Limit")});
|
||||
|
||||
SetInputName(kPreserveLuminanceInput, tr("Preserve Luminance"));
|
||||
}
|
||||
|
||||
ShaderCode DespillNode::GetShaderCode(const QString& shader_id) const {
|
||||
Q_UNUSED(shader_id)
|
||||
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/despill.frag"));
|
||||
}
|
||||
|
||||
void DespillNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const {
|
||||
ShaderJob job;
|
||||
job.InsertValue(value);
|
||||
|
||||
// Set luma coefficients
|
||||
double luma_coeffs[3] = {0.0f, 0.0f, 0.0f};
|
||||
project()->color_manager()->GetDefaultLumaCoefs(luma_coeffs);
|
||||
job.InsertValue(QStringLiteral("luma_coeffs"),
|
||||
NodeValue(NodeValue::kVec3, QVector3D(luma_coeffs[0], luma_coeffs[1], luma_coeffs[2])));
|
||||
|
||||
// If there's no texture, no need to run an operation
|
||||
if (!job.GetValue(kTextureInput).data().isNull()) {
|
||||
table->Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace olive
|
||||
@@ -0,0 +1,50 @@
|
||||
/***
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#ifndef DESPILLNODE_H
|
||||
#define DESPILLNODE_H
|
||||
|
||||
#include "node/node.h"
|
||||
#include "node/color/colormanager/colormanager.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class DespillNode : public Node {
|
||||
public:
|
||||
DespillNode();
|
||||
|
||||
virtual Node* copy() const override;
|
||||
|
||||
virtual QString Name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QVector<CategoryID> Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
virtual ShaderCode GetShaderCode(const QString& shader_id) const override;
|
||||
virtual void Value(const NodeValueRow& value, const NodeGlobals& globals, NodeValueTable* table) const override;
|
||||
|
||||
static const QString kTextureInput;
|
||||
static const QString kColorInput;
|
||||
static const QString kMethodInput;
|
||||
static const QString kPreserveLuminanceInput;
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // namespace olive
|
||||
|
||||
#endif // DESPILLNODE_H
|
||||
@@ -48,14 +48,14 @@ ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, const Q
|
||||
const QString& mat_in = (type_a == NodeValue::kTexture) ? param_b_in : param_a_in;
|
||||
|
||||
// No-op frag shader (can we return QString() instead?)
|
||||
operation = QStringLiteral("texture2D(%1, ove_texcoord)").arg(tex_in);
|
||||
operation = QStringLiteral("texture(%1, ove_texcoord)").arg(tex_in);
|
||||
|
||||
vert = QStringLiteral("uniform mat4 %1;\n"
|
||||
"\n"
|
||||
"attribute vec4 a_position;\n"
|
||||
"attribute vec2 a_texcoord;\n"
|
||||
"in vec4 a_position;\n"
|
||||
"in vec2 a_texcoord;\n"
|
||||
"\n"
|
||||
"varying vec2 ove_texcoord;\n"
|
||||
"out vec2 ove_texcoord;\n"
|
||||
"\n"
|
||||
"void main() {\n"
|
||||
" gl_Position = %1 * a_position;\n"
|
||||
@@ -97,12 +97,13 @@ ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, const Q
|
||||
frag = QStringLiteral("uniform %1 %3;\n"
|
||||
"uniform %2 %4;\n"
|
||||
"\n"
|
||||
"varying vec2 ove_texcoord;\n"
|
||||
"in vec2 ove_texcoord;\n"
|
||||
"out vec4 frag_color;\n"
|
||||
"\n"
|
||||
"void main(void) {\n"
|
||||
" vec4 c = %5;\n"
|
||||
" c.a = clamp(c.a, 0.0, 1.0);\n" // Ensure alpha is between 0.0 and 1.0
|
||||
" gl_FragColor = c;\n"
|
||||
" frag_color = c;\n"
|
||||
"}\n").arg(GetShaderUniformType(type_a),
|
||||
GetShaderUniformType(type_b),
|
||||
param_a_in,
|
||||
@@ -129,7 +130,7 @@ QString MathNodeBase::GetShaderUniformType(const olive::NodeValue::Type &type)
|
||||
QString MathNodeBase::GetShaderVariableCall(const QString &input_id, const NodeValue::Type &type, const QString& coord_op)
|
||||
{
|
||||
if (type == NodeValue::kTexture) {
|
||||
return QStringLiteral("texture2D(%1, ove_texcoord%2)").arg(input_id, coord_op);
|
||||
return QStringLiteral("texture(%1, ove_texcoord%2)").arg(input_id, coord_op);
|
||||
}
|
||||
|
||||
return input_id;
|
||||
|
||||
+134
-82
@@ -50,7 +50,6 @@ Node::Node() :
|
||||
cache_result_(false),
|
||||
flags_(kNone)
|
||||
{
|
||||
uuid_ = QUuid::createUuid();
|
||||
}
|
||||
|
||||
Node::~Node()
|
||||
@@ -555,6 +554,37 @@ QVariant Node::GetSplitDefaultValueOnTrack(const QString &input, int track) cons
|
||||
}
|
||||
}
|
||||
|
||||
void Node::SetDefaultValue(const QString &input, const QVariant &val)
|
||||
{
|
||||
NodeValue::Type type = GetInputDataType(input);
|
||||
|
||||
SetSplitDefaultValue(input, NodeValue::split_normal_value_into_track_values(type, val));
|
||||
}
|
||||
|
||||
void Node::SetSplitDefaultValue(const QString &input, const SplitValue &val)
|
||||
{
|
||||
Input* i = GetInternalInputData(input);
|
||||
|
||||
if (i) {
|
||||
i->default_value = val;
|
||||
} else {
|
||||
ReportInvalidInput("set default value of", input);
|
||||
}
|
||||
}
|
||||
|
||||
void Node::SetSplitDefaultValueOnTrack(const QString &input, const QVariant &val, int track)
|
||||
{
|
||||
Input* i = GetInternalInputData(input);
|
||||
|
||||
if (i) {
|
||||
if (track < i->default_value.size()) {
|
||||
i->default_value[track] = val;
|
||||
}
|
||||
} else {
|
||||
ReportInvalidInput("set default value on track of", input);
|
||||
}
|
||||
}
|
||||
|
||||
const QVector<NodeKeyframeTrack> &Node::GetKeyframeTracks(const QString &input, int element) const
|
||||
{
|
||||
return GetImmediate(input, element)->keyframe_tracks();
|
||||
@@ -886,6 +916,17 @@ InputFlags Node::GetInputFlags(const QString &input) const
|
||||
}
|
||||
}
|
||||
|
||||
void Node::SetInputFlags(const QString &input, const InputFlags &f)
|
||||
{
|
||||
Input* i = GetInternalInputData(input);
|
||||
|
||||
if (i) {
|
||||
i->flags = f;
|
||||
} else {
|
||||
ReportInvalidInput("set flags of", input);
|
||||
}
|
||||
}
|
||||
|
||||
void Node::Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
// Do nothing
|
||||
@@ -991,12 +1032,44 @@ Node *Node::CopyNodeAndDependencyGraphMinusItemsInternal(QMap<Node*, Node*>& cre
|
||||
// Add to map
|
||||
created.insert(node, copy);
|
||||
|
||||
// Copy values to the clone
|
||||
CopyInputs(node, copy, false);
|
||||
|
||||
// Add it to the same graph
|
||||
command->add_child(new NodeAddCommand(node->parent(), copy));
|
||||
|
||||
// Copy context children
|
||||
const PositionMap &map = node->GetContextPositions();
|
||||
for (auto it=map.cbegin(); it!=map.cend(); it++) {
|
||||
// Add either the copy (if it exists) or the original node to the context
|
||||
Node *child;
|
||||
|
||||
if (it.key()->IsItem()) {
|
||||
child = it.key();
|
||||
} else {
|
||||
child = created.value(it.key());
|
||||
if (!child) {
|
||||
child = CopyNodeAndDependencyGraphMinusItemsInternal(created, it.key(), command);
|
||||
}
|
||||
}
|
||||
|
||||
command->add_child(new NodeSetPositionCommand(child, copy, it.value()));
|
||||
}
|
||||
|
||||
// If this is a group, copy input and output passthroughs
|
||||
if (NodeGroup *src_group = dynamic_cast<NodeGroup*>(node)) {
|
||||
NodeGroup *dst_group = static_cast<NodeGroup*>(copy);
|
||||
|
||||
for (auto it=src_group->GetInputPassthroughs().cbegin(); it!=src_group->GetInputPassthroughs().cend(); it++) {
|
||||
// This node should have been created by the context loop above
|
||||
NodeInput input = it->second;
|
||||
input.set_node(created.value(input.node()));
|
||||
command->add_child(new NodeGroupAddInputPassthrough(dst_group, input, it->first));
|
||||
}
|
||||
|
||||
command->add_child(new NodeGroupSetOutputPassthrough(dst_group, created.value(src_group->GetOutputPassthrough())));
|
||||
}
|
||||
|
||||
// Copy values to the clone
|
||||
command->add_child(new NodeCopyInputsCommand(node, copy, false));
|
||||
|
||||
// Go through input connections and copy if non-item and connect if item
|
||||
for (auto it=node->input_connections_.cbegin(); it!=node->input_connections_.cend(); it++) {
|
||||
NodeInput input = it->first;
|
||||
@@ -1009,23 +1082,17 @@ Node *Node::CopyNodeAndDependencyGraphMinusItemsInternal(QMap<Node*, Node*>& cre
|
||||
} else {
|
||||
// Non-item, we want to clone this too
|
||||
connected_copy = created.value(connected, nullptr);
|
||||
|
||||
if (!connected_copy) {
|
||||
connected_copy = CopyNodeAndDependencyGraphMinusItemsInternal(created, connected, command);
|
||||
}
|
||||
}
|
||||
|
||||
NodeInput copied_input(copy, input.input(), input.element());
|
||||
NodeInput copied_input = input;
|
||||
copied_input.set_node(copy);
|
||||
command->add_child(new NodeEdgeAddCommand(connected_copy, copied_input));
|
||||
command->add_child(new NodeSetValueHintCommand(copied_input, node->GetValueHintForInput(input.input(), input.element())));
|
||||
}
|
||||
|
||||
const PositionMap &map = node->GetContextPositions();
|
||||
for (auto it=map.cbegin(); it!=map.cend(); it++) {
|
||||
// Add either the copy (if it exists) or the original node to the context
|
||||
command->add_child(new NodeSetPositionCommand(created.value(it.key(), it.key()), copy, it.value()));
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
@@ -1045,8 +1112,7 @@ Node *Node::CopyNodeInGraph(Node *node, MultiUndoCommand *command)
|
||||
} else {
|
||||
copy = node->copy();
|
||||
|
||||
command->add_child(new NodeAddCommand(static_cast<NodeGraph*>(node->parent()),
|
||||
copy));
|
||||
command->add_child(new NodeAddCommand(static_cast<NodeGraph*>(node->parent()), copy));
|
||||
|
||||
command->add_child(new NodeCopyInputsCommand(node, copy, true));
|
||||
|
||||
@@ -1248,28 +1314,6 @@ void Node::IgnoreHashingFrom(const QString &input_id)
|
||||
ignore_when_hashing_.append(input_id);
|
||||
}
|
||||
|
||||
bool Node::HasGizmos() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void Node::DrawGizmos(const NodeValueRow &, const NodeGlobals &, QPainter *)
|
||||
{
|
||||
}
|
||||
|
||||
bool Node::GizmoPress(const NodeValueRow &, const NodeGlobals &, const QPointF &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void Node::GizmoMove(const QPointF &, const rational&, const Qt::KeyboardModifiers &)
|
||||
{
|
||||
}
|
||||
|
||||
void Node::GizmoRelease(MultiUndoCommand *)
|
||||
{
|
||||
}
|
||||
|
||||
const QString &Node::GetLabel() const
|
||||
{
|
||||
return label_;
|
||||
@@ -1324,6 +1368,11 @@ void Node::CopyInputs(const Node *source, Node *destination, bool include_connec
|
||||
Q_ASSERT(source->id() == destination->id());
|
||||
|
||||
foreach (const QString& input, source->inputs()) {
|
||||
// NOTE: This assert is to ensure that inputs in the source also exist in the destination, which
|
||||
// they should. If they don't and you hit this assert, check if you're handling group
|
||||
// passthroughs correctly.
|
||||
Q_ASSERT(destination->HasInputWithID(input));
|
||||
|
||||
CopyInput(source, destination, input, include_connections, true);
|
||||
}
|
||||
|
||||
@@ -1619,16 +1668,14 @@ void Node::DisconnectAll()
|
||||
QString Node::GetCategoryName(const CategoryID &c)
|
||||
{
|
||||
switch (c) {
|
||||
case kCategoryInput:
|
||||
return tr("Input");
|
||||
case kCategoryOutput:
|
||||
return tr("Output");
|
||||
case kCategoryGeneral:
|
||||
return tr("General");
|
||||
case kCategoryDistort:
|
||||
return tr("Distort");
|
||||
case kCategoryMath:
|
||||
return tr("Math");
|
||||
case kCategoryKeying:
|
||||
return tr("Keying");
|
||||
case kCategoryColor:
|
||||
return tr("Color");
|
||||
case kCategoryFilter:
|
||||
@@ -1637,16 +1684,12 @@ QString Node::GetCategoryName(const CategoryID &c)
|
||||
return tr("Timeline");
|
||||
case kCategoryGenerator:
|
||||
return tr("Generator");
|
||||
case kCategoryChannels:
|
||||
return tr("Channel");
|
||||
case kCategoryTransition:
|
||||
return tr("Transition");
|
||||
case kCategoryProject:
|
||||
return tr("Project");
|
||||
case kCategoryVideoEffect:
|
||||
return tr("Video Effect");
|
||||
case kCategoryAudioEffect:
|
||||
return tr("Audio Effect");
|
||||
case kCategoryTime:
|
||||
return tr("Time");
|
||||
case kCategoryUnknown:
|
||||
case kCategoryCount:
|
||||
break;
|
||||
@@ -1760,39 +1803,6 @@ void Node::ClearElement(const QString& input, int index)
|
||||
SetSplitStandardValue(input, GetSplitDefaultValue(input), index);
|
||||
}
|
||||
|
||||
QRectF Node::CreateGizmoHandleRect(const QPointF &pt, int radius)
|
||||
{
|
||||
return QRectF(pt.x() - radius,
|
||||
pt.y() - radius,
|
||||
2*radius,
|
||||
2*radius);
|
||||
}
|
||||
|
||||
double Node::GetGizmoHandleRadius(const QTransform &transform)
|
||||
{
|
||||
double raw_value = QFontMetrics(qApp->font()).height() * 0.25;
|
||||
|
||||
raw_value /= transform.m11();
|
||||
|
||||
return raw_value;
|
||||
}
|
||||
|
||||
void Node::DrawAndExpandGizmoHandles(QPainter *p, int handle_radius, QRectF *rects, int count)
|
||||
{
|
||||
p->setPen(Qt::NoPen);
|
||||
p->setBrush(Qt::white);
|
||||
|
||||
for (int i=0; i<count; i++) {
|
||||
QRectF& r = rects[i];
|
||||
|
||||
// Draw rect on screen
|
||||
p->drawRect(r);
|
||||
|
||||
// Extend rect so it's easier to drag with handle
|
||||
r.adjust(-handle_radius, -handle_radius, handle_radius, handle_radius);
|
||||
}
|
||||
}
|
||||
|
||||
void Node::InputValueChangedEvent(const QString &input, int element)
|
||||
{
|
||||
Q_UNUSED(input)
|
||||
@@ -1827,9 +1837,7 @@ void Node::childEvent(QChildEvent *event)
|
||||
{
|
||||
super::childEvent(event);
|
||||
|
||||
NodeKeyframe* key = dynamic_cast<NodeKeyframe*>(event->child());
|
||||
|
||||
if (key) {
|
||||
if (NodeKeyframe* key = dynamic_cast<NodeKeyframe*>(event->child())) {
|
||||
NodeInput i(this, key->input(), key->element());
|
||||
|
||||
if (event->type() == QEvent::ChildAdded) {
|
||||
@@ -1857,6 +1865,12 @@ void Node::childEvent(QChildEvent *event)
|
||||
GetImmediate(key->input(), key->element())->remove_keyframe(key);
|
||||
ParameterValueChanged(i, time_affected);
|
||||
}
|
||||
} else if (NodeGizmo *gizmo = dynamic_cast<NodeGizmo*>(event->child())) {
|
||||
if (event->type() == QEvent::ChildAdded) {
|
||||
gizmos_.append(gizmo);
|
||||
} else if (event->type() == QEvent::ChildRemoved) {
|
||||
gizmos_.removeOne(gizmo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1945,6 +1959,44 @@ void Node::InvalidateFromKeyframeTypeChanged()
|
||||
emit KeyframeTypeChanged(key);
|
||||
}
|
||||
|
||||
void Node::SetValueAtTime(const NodeInput &input, const rational &time, const QVariant &value, int track, MultiUndoCommand *command, bool insert_on_all_tracks_if_no_key)
|
||||
{
|
||||
if (input.IsKeyframing()) {
|
||||
rational node_time = time;
|
||||
|
||||
NodeKeyframe* existing_key = input.GetKeyframeAtTimeOnTrack(node_time, track);
|
||||
|
||||
if (existing_key) {
|
||||
command->add_child(new NodeParamSetKeyframeValueCommand(existing_key, value));
|
||||
} else {
|
||||
// No existing key, create a new one
|
||||
int nb_tracks = NodeValue::get_number_of_keyframe_tracks(input.node()->GetInputDataType(input.input()));
|
||||
for (int i=0; i<nb_tracks; i++) {
|
||||
QVariant track_value;
|
||||
|
||||
if (i == track) {
|
||||
track_value = value;
|
||||
} else if (!insert_on_all_tracks_if_no_key) {
|
||||
continue;
|
||||
} else {
|
||||
track_value = input.node()->GetSplitValueAtTimeOnTrack(input.input(), node_time, i, input.element());
|
||||
}
|
||||
|
||||
NodeKeyframe* new_key = new NodeKeyframe(node_time,
|
||||
track_value,
|
||||
input.node()->GetBestKeyframeTypeForTimeOnTrack(NodeKeyframeTrackReference(input, i), node_time),
|
||||
i,
|
||||
input.element(),
|
||||
input.input());
|
||||
|
||||
command->add_child(new NodeParamInsertKeyframeCommand(input.node(), new_key));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
command->add_child(new NodeParamSetStandardValueCommand(NodeKeyframeTrackReference(input, track), value));
|
||||
}
|
||||
}
|
||||
|
||||
Project *Node::ArrayInsertCommand::GetRelevantProject() const
|
||||
{
|
||||
return node_->project();
|
||||
|
||||
+51
-24
@@ -27,7 +27,6 @@
|
||||
#include <QObject>
|
||||
#include <QPainter>
|
||||
#include <QPointF>
|
||||
#include <QUuid>
|
||||
#include <QXmlStreamWriter>
|
||||
|
||||
#include "codec/frame.h"
|
||||
@@ -35,6 +34,7 @@
|
||||
#include "common/rational.h"
|
||||
#include "common/timerange.h"
|
||||
#include "common/xmlutils.h"
|
||||
#include "node/gizmo/draggable.h"
|
||||
#include "node/globals.h"
|
||||
#include "node/keyframe.h"
|
||||
#include "node/inputimmediate.h"
|
||||
@@ -78,20 +78,17 @@ public:
|
||||
enum CategoryID {
|
||||
kCategoryUnknown = -1,
|
||||
|
||||
kCategoryInput,
|
||||
kCategoryOutput,
|
||||
kCategoryGenerator,
|
||||
kCategoryMath,
|
||||
kCategoryKeying,
|
||||
kCategoryFilter,
|
||||
kCategoryColor,
|
||||
kCategoryGeneral,
|
||||
kCategoryTime,
|
||||
kCategoryTimeline,
|
||||
kCategoryChannels,
|
||||
kCategoryTransition,
|
||||
kCategoryDistort,
|
||||
kCategoryProject,
|
||||
kCategoryVideoEffect,
|
||||
kCategoryAudioEffect,
|
||||
|
||||
kCategoryCount
|
||||
};
|
||||
@@ -120,9 +117,6 @@ public:
|
||||
|
||||
Project* project() const;
|
||||
|
||||
const QUuid &GetUUID() const {return uuid_;}
|
||||
void SetUUID(const QUuid &uuid) {uuid_ = uuid;}
|
||||
|
||||
const uint64_t &GetFlags() const
|
||||
{
|
||||
return flags_;
|
||||
@@ -324,6 +318,8 @@ public:
|
||||
|
||||
virtual QString GetInputName(const QString& id) const;
|
||||
|
||||
void SetInputName(const QString& id, const QString& name);
|
||||
|
||||
bool IsInputHidden(const QString& input) const;
|
||||
bool IsInputConnectable(const QString& input) const;
|
||||
bool IsInputKeyframable(const QString& input) const;
|
||||
@@ -407,6 +403,10 @@ public:
|
||||
SplitValue GetSplitDefaultValue(const QString& input) const;
|
||||
QVariant GetSplitDefaultValueOnTrack(const QString& input, int track) const;
|
||||
|
||||
void SetDefaultValue(const QString& input, const QVariant &val);
|
||||
void SetSplitDefaultValue(const QString& input, const SplitValue &val);
|
||||
void SetSplitDefaultValueOnTrack(const QString& input, const QVariant &val, int track);
|
||||
|
||||
const QVector<NodeKeyframeTrack>& GetKeyframeTracks(const QString& input, int element) const;
|
||||
const QVector<NodeKeyframeTrack>& GetKeyframeTracks(const NodeInput& input) const
|
||||
{
|
||||
@@ -840,13 +840,17 @@ public:
|
||||
*/
|
||||
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const;
|
||||
|
||||
virtual bool HasGizmos() const;
|
||||
bool HasGizmos() const
|
||||
{
|
||||
return !gizmos_.isEmpty();
|
||||
}
|
||||
|
||||
virtual void DrawGizmos(const NodeValueRow& row, const NodeGlobals &globals, QPainter* p);
|
||||
const QVector<NodeGizmo*> &GetGizmos() const
|
||||
{
|
||||
return gizmos_;
|
||||
}
|
||||
|
||||
virtual bool GizmoPress(const NodeValueRow& row, const NodeGlobals &globals, const QPointF& p);
|
||||
virtual void GizmoMove(const QPointF& p, const rational &time, const Qt::KeyboardModifiers &modifiers);
|
||||
virtual void GizmoRelease(MultiUndoCommand *command);
|
||||
virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals){}
|
||||
|
||||
const QString& GetLabel() const;
|
||||
void SetLabel(const QString& s);
|
||||
@@ -941,6 +945,9 @@ public:
|
||||
};
|
||||
|
||||
InputFlags GetInputFlags(const QString& input) const;
|
||||
void SetInputFlags(const QString &input, const InputFlags &f);
|
||||
|
||||
static void SetValueAtTime(const NodeInput &input, const rational &time, const QVariant &value, int track, MultiUndoCommand *command, bool insert_on_all_tracks_if_no_key);
|
||||
|
||||
protected:
|
||||
virtual void Hash(QCryptographicHash& hash, const NodeGlobals &globals, const VideoParams& video_params) const;
|
||||
@@ -971,8 +978,6 @@ protected:
|
||||
|
||||
void RemoveInput(const QString& id);
|
||||
|
||||
void SetInputName(const QString& id, const QString& name);
|
||||
|
||||
void SetComboBoxStrings(const QString& id, const QStringList& strings)
|
||||
{
|
||||
SetInputProperty(id, QStringLiteral("combo_str"), strings);
|
||||
@@ -1008,12 +1013,6 @@ protected:
|
||||
kGizmoScaleCount,
|
||||
};
|
||||
|
||||
static QRectF CreateGizmoHandleRect(const QPointF& pt, int radius);
|
||||
|
||||
static double GetGizmoHandleRadius(const QTransform& transform);
|
||||
|
||||
static void DrawAndExpandGizmoHandles(QPainter* p, int handle_radius, QRectF* rects, int count);
|
||||
|
||||
virtual void LinkChangeEvent(){}
|
||||
|
||||
virtual void InputValueChangedEvent(const QString& input, int element);
|
||||
@@ -1038,6 +1037,34 @@ protected:
|
||||
flags_ = f;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T *AddDraggableGizmo(const QVector<NodeKeyframeTrackReference> &inputs = QVector<NodeKeyframeTrackReference>(), DraggableGizmo::DragValueBehavior behavior = DraggableGizmo::kDeltaFromStart)
|
||||
{
|
||||
T *gizmo = new T(this);
|
||||
gizmo->SetDragValueBehavior(behavior);
|
||||
foreach (const NodeKeyframeTrackReference &input, inputs) {
|
||||
gizmo->AddInput(input);
|
||||
}
|
||||
connect(gizmo, &DraggableGizmo::HandleStart, this, &Node::GizmoDragStart);
|
||||
connect(gizmo, &DraggableGizmo::HandleMovement, this, &Node::GizmoDragMove);
|
||||
return gizmo;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T *AddDraggableGizmo(const QStringList &inputs, DraggableGizmo::DragValueBehavior behavior = DraggableGizmo::kDeltaFromStart)
|
||||
{
|
||||
QVector<NodeKeyframeTrackReference> refs(inputs.size());
|
||||
for (int i=0; i<refs.size(); i++) {
|
||||
refs[i] = NodeInput(this, inputs[i]);
|
||||
}
|
||||
return AddDraggableGizmo<T>(refs, behavior);
|
||||
}
|
||||
|
||||
protected slots:
|
||||
virtual void GizmoDragStart(const olive::NodeValueRow &row, double x, double y, const olive::rational &time){}
|
||||
|
||||
virtual void GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers){}
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief Signal emitted when SetLabel() is called
|
||||
@@ -1311,10 +1338,10 @@ private:
|
||||
|
||||
PositionMap context_positions_;
|
||||
|
||||
QUuid uuid_;
|
||||
|
||||
uint64_t flags_;
|
||||
|
||||
QVector<NodeGizmo*> gizmos_;
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* @brief Slot when a keyframe's time changes to keep the keyframes correctly sorted by time
|
||||
|
||||
@@ -123,6 +123,15 @@ QVariant NodeInput::GetProperty(const QString &key) const
|
||||
}
|
||||
}
|
||||
|
||||
QHash<QString, QVariant> NodeInput::GetProperties() const
|
||||
{
|
||||
if (IsValid()) {
|
||||
return node_->GetInputProperties(input_);
|
||||
} else {
|
||||
return QHash<QString, QVariant>();
|
||||
}
|
||||
}
|
||||
|
||||
QVariant NodeInput::GetValueAtTime(const rational &time) const
|
||||
{
|
||||
if (IsValid()) {
|
||||
|
||||
+44
-5
@@ -52,11 +52,6 @@ public:
|
||||
f_ = flags;
|
||||
}
|
||||
|
||||
bool operator&(const InputFlag& f) const
|
||||
{
|
||||
return f_ & f;
|
||||
}
|
||||
|
||||
InputFlags operator|(const InputFlags &f) const
|
||||
{
|
||||
InputFlags i = *this;
|
||||
@@ -70,6 +65,49 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
InputFlags operator&(const InputFlags &f) const
|
||||
{
|
||||
InputFlags i = *this;
|
||||
i &= f;
|
||||
return i;
|
||||
}
|
||||
|
||||
InputFlags &operator&=(const InputFlags &f)
|
||||
{
|
||||
f_ &= f.f_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
InputFlags operator&(const InputFlag &f) const
|
||||
{
|
||||
InputFlags i = *this;
|
||||
i &= f;
|
||||
return i;
|
||||
}
|
||||
|
||||
InputFlags &operator&=(const InputFlag &f)
|
||||
{
|
||||
f_ &= f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
InputFlags operator~() const
|
||||
{
|
||||
InputFlags i = *this;
|
||||
i.f_ = ~i.f_;
|
||||
return i;
|
||||
}
|
||||
|
||||
inline operator bool() const
|
||||
{
|
||||
return f_;
|
||||
}
|
||||
|
||||
inline const uint64_t &value() const
|
||||
{
|
||||
return f_;
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t f_;
|
||||
|
||||
@@ -188,6 +226,7 @@ public:
|
||||
QStringList GetComboBoxStrings() const;
|
||||
|
||||
QVariant GetProperty(const QString& key) const;
|
||||
QHash<QString, QVariant> GetProperties() const;
|
||||
|
||||
QVariant GetValueAtTime(const rational& time) const;
|
||||
|
||||
|
||||
@@ -27,5 +27,7 @@ set(OLIVE_SOURCES
|
||||
node/project/serializer/serializer210907.h
|
||||
node/project/serializer/serializer211228.cpp
|
||||
node/project/serializer/serializer211228.h
|
||||
node/project/serializer/serializer220403.cpp
|
||||
node/project/serializer/serializer220403.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "serializer210528.h"
|
||||
#include "serializer210907.h"
|
||||
#include "serializer211228.h"
|
||||
#include "serializer220403.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -45,6 +46,7 @@ void ProjectSerializer::Initialize()
|
||||
instances_.append(new ProjectSerializer210528);
|
||||
instances_.append(new ProjectSerializer210907);
|
||||
instances_.append(new ProjectSerializer211228);
|
||||
instances_.append(new ProjectSerializer220403);
|
||||
}
|
||||
|
||||
void ProjectSerializer::Destroy()
|
||||
|
||||
@@ -178,8 +178,6 @@ void ProjectSerializer210528::LoadNode(Node *node, XMLNodeData &xml_node_data, Q
|
||||
xml_node_data.node_ptrs.insert(reader->readElementText().toULongLong(), node);
|
||||
} else if (reader->name() == QStringLiteral("label")) {
|
||||
node->SetLabel(reader->readElementText());
|
||||
} else if (reader->name() == QStringLiteral("uuid")) {
|
||||
node->SetUUID(QUuid::fromString(reader->readElementText()));
|
||||
} else if (reader->name() == QStringLiteral("color")) {
|
||||
node->SetOverrideColor(reader->readElementText().toInt());
|
||||
} else if (reader->name() == QStringLiteral("links")) {
|
||||
|
||||
@@ -178,8 +178,6 @@ void ProjectSerializer210907::LoadNode(Node *node, XMLNodeData &xml_node_data, Q
|
||||
xml_node_data.node_ptrs.insert(reader->readElementText().toULongLong(), node);
|
||||
} else if (reader->name() == QStringLiteral("label")) {
|
||||
node->SetLabel(reader->readElementText());
|
||||
} else if (reader->name() == QStringLiteral("uuid")) {
|
||||
node->SetUUID(QUuid::fromString(reader->readElementText()));
|
||||
} else if (reader->name() == QStringLiteral("color")) {
|
||||
node->SetOverrideColor(reader->readElementText().toInt());
|
||||
} else if (reader->name() == QStringLiteral("links")) {
|
||||
|
||||
@@ -214,80 +214,6 @@ ProjectSerializer211228::LoadData ProjectSerializer211228::Load(Project *project
|
||||
return load_data;
|
||||
}
|
||||
|
||||
void ProjectSerializer211228::Save(QXmlStreamWriter *writer, const SaveData &data, void *reserved) const
|
||||
{
|
||||
Project *project = data.GetProject();
|
||||
|
||||
writer->writeTextElement(QStringLiteral("uuid"), data.GetProject()->GetUuid().toString());
|
||||
|
||||
writer->writeStartElement(QStringLiteral("nodes"));
|
||||
|
||||
const QVector<Node*> &using_node_list = (data.GetOnlySerializeNodes().isEmpty()) ? project->nodes() : data.GetOnlySerializeNodes();
|
||||
|
||||
foreach (Node* node, using_node_list) {
|
||||
writer->writeStartElement(QStringLiteral("node"));
|
||||
|
||||
if (node == project->root()) {
|
||||
writer->writeAttribute(QStringLiteral("root"), QStringLiteral("1"));
|
||||
} else if (node == project->color_manager()) {
|
||||
writer->writeAttribute(QStringLiteral("cm"), QStringLiteral("1"));
|
||||
} else if (node == project->settings()) {
|
||||
writer->writeAttribute(QStringLiteral("settings"), QStringLiteral("1"));
|
||||
}
|
||||
|
||||
writer->writeAttribute(QStringLiteral("id"), node->id());
|
||||
|
||||
SaveNode(node, writer);
|
||||
|
||||
writer->writeEndElement(); // node
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // nodes
|
||||
|
||||
writer->writeStartElement(QStringLiteral("positions"));
|
||||
|
||||
foreach (Node* context, using_node_list) {
|
||||
const Node::PositionMap &map = context->GetContextPositions();
|
||||
|
||||
if (!map.isEmpty()) {
|
||||
writer->writeStartElement(QStringLiteral("context"));
|
||||
|
||||
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(context)));
|
||||
|
||||
for (auto jt=map.cbegin(); jt!=map.cend(); jt++) {
|
||||
if (data.GetOnlySerializeNodes().isEmpty() || data.GetOnlySerializeNodes().contains(jt.key())) {
|
||||
writer->writeStartElement(QStringLiteral("node"));
|
||||
SavePosition(writer, jt.key(), jt.value());
|
||||
writer->writeEndElement(); // node
|
||||
}
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // context
|
||||
}
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // positions
|
||||
|
||||
writer->writeStartElement(QStringLiteral("properties"));
|
||||
|
||||
for (auto it=data.GetProperties().cbegin(); it!=data.GetProperties().cend(); it++) {
|
||||
writer->writeStartElement(QStringLiteral("node"));
|
||||
|
||||
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(it.key())));
|
||||
|
||||
for (auto jt=it.value().cbegin(); jt!=it.value().cend(); jt++) {
|
||||
writer->writeTextElement(jt.key(), jt.value());
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // node
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // properties
|
||||
|
||||
// Save main window project layout
|
||||
project->GetLayoutInfo().toXml(writer);
|
||||
}
|
||||
|
||||
void ProjectSerializer211228::LoadNode(Node *node, XMLNodeData &xml_node_data, QXmlStreamReader *reader) const
|
||||
{
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
@@ -303,7 +229,7 @@ void ProjectSerializer211228::LoadNode(Node *node, XMLNodeData &xml_node_data, Q
|
||||
} else if (reader->name() == QStringLiteral("label")) {
|
||||
node->SetLabel(reader->readElementText());
|
||||
} else if (reader->name() == QStringLiteral("uuid")) {
|
||||
node->SetUUID(QUuid::fromString(reader->readElementText()));
|
||||
xml_node_data.node_uuids.insert(node, QUuid::fromString(reader->readElementText()));
|
||||
} else if (reader->name() == QStringLiteral("color")) {
|
||||
node->SetOverrideColor(reader->readElementText().toInt());
|
||||
} else if (reader->name() == QStringLiteral("links")) {
|
||||
@@ -373,61 +299,6 @@ void ProjectSerializer211228::LoadNode(Node *node, XMLNodeData &xml_node_data, Q
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectSerializer211228::SaveNode(Node *node, QXmlStreamWriter *writer) const
|
||||
{
|
||||
writer->writeTextElement(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(node)));
|
||||
|
||||
writer->writeTextElement(QStringLiteral("uuid"), node->GetUUID().toString());
|
||||
writer->writeTextElement(QStringLiteral("label"), node->GetLabel());
|
||||
writer->writeTextElement(QStringLiteral("color"), QString::number(node->GetOverrideColor()));
|
||||
|
||||
foreach (const QString& input, node->inputs()) {
|
||||
writer->writeStartElement(QStringLiteral("input"));
|
||||
|
||||
SaveInput(node, writer, input);
|
||||
|
||||
writer->writeEndElement(); // input
|
||||
}
|
||||
|
||||
writer->writeStartElement(QStringLiteral("links"));
|
||||
foreach (Node* link, node->links()) {
|
||||
writer->writeTextElement(QStringLiteral("link"), QString::number(reinterpret_cast<quintptr>(link)));
|
||||
}
|
||||
writer->writeEndElement(); // links
|
||||
|
||||
writer->writeStartElement(QStringLiteral("connections"));
|
||||
for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) {
|
||||
writer->writeStartElement(QStringLiteral("connection"));
|
||||
|
||||
writer->writeAttribute(QStringLiteral("input"), it->first.input());
|
||||
writer->writeAttribute(QStringLiteral("element"), QString::number(it->first.element()));
|
||||
|
||||
writer->writeTextElement(QStringLiteral("output"), QString::number(reinterpret_cast<quintptr>(it->second)));
|
||||
|
||||
writer->writeEndElement(); // connection
|
||||
}
|
||||
writer->writeEndElement(); // connections
|
||||
|
||||
writer->writeStartElement(QStringLiteral("hints"));
|
||||
for (auto it=node->GetValueHints().cbegin(); it!=node->GetValueHints().cend(); it++) {
|
||||
writer->writeStartElement(QStringLiteral("hint"));
|
||||
|
||||
writer->writeAttribute(QStringLiteral("input"), it.key().input);
|
||||
writer->writeAttribute(QStringLiteral("element"), QString::number(it.key().element));
|
||||
|
||||
SaveValueHint(&it.value(), writer);
|
||||
|
||||
writer->writeEndElement(); // hint
|
||||
}
|
||||
writer->writeEndElement();
|
||||
|
||||
writer->writeStartElement(QStringLiteral("custom"));
|
||||
|
||||
SaveNodeCustom(writer, node);
|
||||
|
||||
writer->writeEndElement(); // custom
|
||||
}
|
||||
|
||||
void ProjectSerializer211228::LoadInput(Node *node, QXmlStreamReader *reader, XMLNodeData &xml_node_data) const
|
||||
{
|
||||
QString param_id;
|
||||
@@ -485,33 +356,6 @@ void ProjectSerializer211228::LoadInput(Node *node, QXmlStreamReader *reader, XM
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectSerializer211228::SaveInput(Node *node, QXmlStreamWriter *writer, const QString &id) const
|
||||
{
|
||||
writer->writeAttribute(QStringLiteral("id"), id);
|
||||
|
||||
writer->writeStartElement(QStringLiteral("primary"));
|
||||
|
||||
SaveImmediate(writer, node, id, -1);
|
||||
|
||||
writer->writeEndElement(); // primary
|
||||
|
||||
writer->writeStartElement(QStringLiteral("subelements"));
|
||||
|
||||
int arr_sz = node->InputArraySize(id);
|
||||
|
||||
writer->writeAttribute(QStringLiteral("count"), QString::number(arr_sz));
|
||||
|
||||
for (int i=0; i<arr_sz; i++) {
|
||||
writer->writeStartElement(QStringLiteral("element"));
|
||||
|
||||
SaveImmediate(writer, node, id, i);
|
||||
|
||||
writer->writeEndElement(); // element
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // subelements
|
||||
}
|
||||
|
||||
void ProjectSerializer211228::LoadImmediate(QXmlStreamReader *reader, Node *node, const QString& input, int element, XMLNodeData &xml_node_data) const
|
||||
{
|
||||
Q_UNUSED(xml_node_data)
|
||||
@@ -629,69 +473,6 @@ void ProjectSerializer211228::LoadImmediate(QXmlStreamReader *reader, Node *node
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectSerializer211228::SaveImmediate(QXmlStreamWriter *writer, Node *node, const QString& input, int element) const
|
||||
{
|
||||
if (node->IsInputKeyframable(input)) {
|
||||
writer->writeTextElement(QStringLiteral("keyframing"), QString::number(node->IsInputKeyframing(input, element)));
|
||||
}
|
||||
|
||||
NodeValue::Type data_type = node->GetInputDataType(input);
|
||||
|
||||
// Write standard value
|
||||
writer->writeStartElement(QStringLiteral("standard"));
|
||||
|
||||
foreach (const QVariant& v, node->GetSplitStandardValue(input, element)) {
|
||||
writer->writeStartElement(QStringLiteral("track"));
|
||||
|
||||
if (data_type == NodeValue::kVideoParams) {
|
||||
v.value<VideoParams>().Save(writer);
|
||||
} else if (data_type == NodeValue::kAudioParams) {
|
||||
v.value<AudioParams>().Save(writer);
|
||||
} else {
|
||||
writer->writeCharacters(NodeValue::ValueToString(data_type, v, true));
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // track
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // standard
|
||||
|
||||
// Write keyframes
|
||||
writer->writeStartElement(QStringLiteral("keyframes"));
|
||||
|
||||
for (const NodeKeyframeTrack& track : node->GetKeyframeTracks(input, element)) {
|
||||
writer->writeStartElement(QStringLiteral("track"));
|
||||
|
||||
for (NodeKeyframe* key : track) {
|
||||
writer->writeStartElement(QStringLiteral("key"));
|
||||
|
||||
writer->writeAttribute(QStringLiteral("input"), key->input());
|
||||
writer->writeAttribute(QStringLiteral("time"), key->time().toString());
|
||||
writer->writeAttribute(QStringLiteral("type"), QString::number(key->type()));
|
||||
writer->writeAttribute(QStringLiteral("inhandlex"), QString::number(key->bezier_control_in().x()));
|
||||
writer->writeAttribute(QStringLiteral("inhandley"), QString::number(key->bezier_control_in().y()));
|
||||
writer->writeAttribute(QStringLiteral("outhandlex"), QString::number(key->bezier_control_out().x()));
|
||||
writer->writeAttribute(QStringLiteral("outhandley"), QString::number(key->bezier_control_out().y()));
|
||||
|
||||
writer->writeCharacters(NodeValue::ValueToString(data_type, key->value(), true));
|
||||
|
||||
writer->writeEndElement(); // key
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // track
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // keyframes
|
||||
|
||||
if (data_type == NodeValue::kColor) {
|
||||
// Save color management information
|
||||
writer->writeTextElement(QStringLiteral("csinput"), node->GetInputProperty(input, QStringLiteral("col_input")).toString());
|
||||
writer->writeTextElement(QStringLiteral("csdisplay"), node->GetInputProperty(input, QStringLiteral("col_display")).toString());
|
||||
writer->writeTextElement(QStringLiteral("csview"), node->GetInputProperty(input, QStringLiteral("col_view")).toString());
|
||||
writer->writeTextElement(QStringLiteral("cslook"), node->GetInputProperty(input, QStringLiteral("col_look")).toString());
|
||||
}
|
||||
}
|
||||
|
||||
bool ProjectSerializer211228::LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, Node::Position *pos) const
|
||||
{
|
||||
bool got_node_ptr = false;
|
||||
@@ -723,15 +504,6 @@ bool ProjectSerializer211228::LoadPosition(QXmlStreamReader *reader, quintptr *n
|
||||
return got_node_ptr && got_pos_x && got_pos_y;
|
||||
}
|
||||
|
||||
void ProjectSerializer211228::SavePosition(QXmlStreamWriter *writer, Node *node, const Node::Position &pos) const
|
||||
{
|
||||
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(node)));
|
||||
|
||||
writer->writeTextElement(QStringLiteral("x"), QString::number(pos.position.x()));
|
||||
writer->writeTextElement(QStringLiteral("y"), QString::number(pos.position.y()));
|
||||
writer->writeTextElement(QStringLiteral("expanded"), QString::number(pos.expanded));
|
||||
}
|
||||
|
||||
void ProjectSerializer211228::PostConnect(const XMLNodeData &xml_node_data) const
|
||||
{
|
||||
foreach (const XMLNodeData::SerializedConnection& con, xml_node_data.desired_connections) {
|
||||
@@ -828,36 +600,6 @@ void ProjectSerializer211228::LoadNodeCustom(QXmlStreamReader *reader, Node *nod
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectSerializer211228::SaveNodeCustom(QXmlStreamWriter *writer, Node *node) const
|
||||
{
|
||||
if (ViewerOutput *viewer = dynamic_cast<ViewerOutput*>(node)) {
|
||||
// Write TimelinePoints
|
||||
writer->writeStartElement(QStringLiteral("points"));
|
||||
SaveTimelinePoints(writer, viewer->GetTimelinePoints());
|
||||
writer->writeEndElement(); // points
|
||||
|
||||
if (Footage *footage = dynamic_cast<Footage*>(node)) {
|
||||
writer->writeTextElement(QStringLiteral("timestamp"), QString::number(footage->timestamp()));
|
||||
}
|
||||
} else if (Track *track = dynamic_cast<Track*>(node)) {
|
||||
writer->writeTextElement(QStringLiteral("height"), QString::number(track->GetTrackHeight()));
|
||||
} else if (NodeGroup *group = dynamic_cast<NodeGroup*>(node)) {
|
||||
writer->writeStartElement(QStringLiteral("inputpassthroughs"));
|
||||
|
||||
foreach (const NodeInput &ip, group->GetInputPassthroughs()) {
|
||||
writer->writeStartElement(QStringLiteral("inputpassthrough"));
|
||||
writer->writeTextElement(QStringLiteral("node"), QString::number(reinterpret_cast<quintptr>(ip.node())));
|
||||
writer->writeTextElement(QStringLiteral("input"), ip.input());
|
||||
writer->writeTextElement(QStringLiteral("element"), QString::number(ip.element()));
|
||||
writer->writeEndElement(); // input
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // inputpassthroughs
|
||||
|
||||
writer->writeTextElement(QStringLiteral("outputpassthrough"), QString::number(reinterpret_cast<quintptr>(group->GetOutputPassthrough())));
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectSerializer211228::LoadTimelinePoints(QXmlStreamReader *reader, TimelinePoints *points) const
|
||||
{
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
@@ -871,17 +613,6 @@ void ProjectSerializer211228::LoadTimelinePoints(QXmlStreamReader *reader, Timel
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectSerializer211228::SaveTimelinePoints(QXmlStreamWriter *writer, TimelinePoints *points) const
|
||||
{
|
||||
writer->writeStartElement(QStringLiteral("workarea"));
|
||||
SaveWorkArea(writer, points->workarea());
|
||||
writer->writeEndElement(); // workarea
|
||||
|
||||
writer->writeStartElement(QStringLiteral("markers"));
|
||||
SaveMarkerList(writer, points->markers());
|
||||
writer->writeEndElement(); // markers
|
||||
}
|
||||
|
||||
void ProjectSerializer211228::LoadWorkArea(QXmlStreamReader *reader, TimelineWorkArea *workarea) const
|
||||
{
|
||||
rational range_in = workarea->in();
|
||||
@@ -906,13 +637,6 @@ void ProjectSerializer211228::LoadWorkArea(QXmlStreamReader *reader, TimelineWor
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
|
||||
void ProjectSerializer211228::SaveWorkArea(QXmlStreamWriter *writer, TimelineWorkArea *workarea) const
|
||||
{
|
||||
writer->writeAttribute(QStringLiteral("enabled"), QString::number(workarea->enabled()));
|
||||
writer->writeAttribute(QStringLiteral("in"), workarea->in().toString());
|
||||
writer->writeAttribute(QStringLiteral("out"), workarea->out().toString());
|
||||
}
|
||||
|
||||
void ProjectSerializer211228::LoadMarkerList(QXmlStreamReader *reader, TimelineMarkerList *markers) const
|
||||
{
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
@@ -937,20 +661,6 @@ void ProjectSerializer211228::LoadMarkerList(QXmlStreamReader *reader, TimelineM
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectSerializer211228::SaveMarkerList(QXmlStreamWriter *writer, TimelineMarkerList *markers) const
|
||||
{
|
||||
foreach (TimelineMarker* marker, markers->list()) {
|
||||
writer->writeStartElement(QStringLiteral("marker"));
|
||||
|
||||
writer->writeAttribute(QStringLiteral("name"), marker->name());
|
||||
|
||||
writer->writeAttribute(QStringLiteral("in"), marker->time().in().toString());
|
||||
writer->writeAttribute(QStringLiteral("out"), marker->time().out().toString());
|
||||
|
||||
writer->writeEndElement(); // marker
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectSerializer211228::LoadValueHint(Node::ValueHint *hint, QXmlStreamReader *reader) const
|
||||
{
|
||||
QVector<NodeValue::Type> types;
|
||||
@@ -976,19 +686,4 @@ void ProjectSerializer211228::LoadValueHint(Node::ValueHint *hint, QXmlStreamRea
|
||||
hint->set_type(types);
|
||||
}
|
||||
|
||||
void ProjectSerializer211228::SaveValueHint(const Node::ValueHint *hint, QXmlStreamWriter *writer) const
|
||||
{
|
||||
writer->writeStartElement(QStringLiteral("types"));
|
||||
|
||||
for (auto it=hint->types().cbegin(); it!=hint->types().cend(); it++) {
|
||||
writer->writeTextElement(QStringLiteral("type"), QString::number(*it));
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // types
|
||||
|
||||
writer->writeTextElement(QStringLiteral("index"), QString::number(hint->index()));
|
||||
|
||||
writer->writeTextElement(QStringLiteral("tag"), hint->tag());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,8 +33,6 @@ public:
|
||||
protected:
|
||||
virtual LoadData Load(Project *project, QXmlStreamReader *reader, void *reserved) const override;
|
||||
|
||||
virtual void Save(QXmlStreamWriter *writer, const SaveData &data, void *reserved) const override;
|
||||
|
||||
virtual uint Version() const override
|
||||
{
|
||||
return 211228;
|
||||
@@ -64,47 +62,30 @@ private:
|
||||
QList<BlockLink> block_links;
|
||||
QVector<GroupLink> group_input_links;
|
||||
QHash<NodeGroup*, quintptr> group_output_links;
|
||||
QHash<Node*, QUuid> node_uuids;
|
||||
|
||||
};
|
||||
|
||||
void LoadNode(Node *node, XMLNodeData &xml_node_data, QXmlStreamReader *reader) const;
|
||||
|
||||
void SaveNode(Node *node, QXmlStreamWriter *writer) const;
|
||||
|
||||
void LoadInput(Node *node, QXmlStreamReader* reader, XMLNodeData &xml_node_data) const;
|
||||
|
||||
void SaveInput(Node *node, QXmlStreamWriter* writer, const QString& id) const;
|
||||
|
||||
void LoadImmediate(QXmlStreamReader *reader, Node *node, const QString& input, int element, XMLNodeData& xml_node_data) const;
|
||||
|
||||
void SaveImmediate(QXmlStreamWriter *writer, Node *node, const QString &input, int element) const;
|
||||
|
||||
bool LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, Node::Position *pos) const;
|
||||
|
||||
void SavePosition(QXmlStreamWriter *writer, Node *node, const Node::Position &pos) const;
|
||||
|
||||
void PostConnect(const XMLNodeData &xml_node_data) const;
|
||||
|
||||
void LoadNodeCustom(QXmlStreamReader *reader, Node *node, XMLNodeData &xml_node_data) const;
|
||||
|
||||
void SaveNodeCustom(QXmlStreamWriter *writer, Node *node) const;
|
||||
|
||||
void LoadTimelinePoints(QXmlStreamReader *reader, TimelinePoints *points) const;
|
||||
|
||||
void SaveTimelinePoints(QXmlStreamWriter *writer, TimelinePoints *points) const;
|
||||
|
||||
void LoadWorkArea(QXmlStreamReader *reader, TimelineWorkArea *workarea) const;
|
||||
|
||||
void SaveWorkArea(QXmlStreamWriter *writer, TimelineWorkArea *workarea) const;
|
||||
|
||||
void LoadMarkerList(QXmlStreamReader *reader, TimelineMarkerList *markers) const;
|
||||
|
||||
void SaveMarkerList(QXmlStreamWriter *writer, TimelineMarkerList *markers) const;
|
||||
|
||||
void LoadValueHint(Node::ValueHint *hint, QXmlStreamReader *reader) const;
|
||||
|
||||
void SaveValueHint(const Node::ValueHint *hint, QXmlStreamWriter *writer) const;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,119 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef SERIALIZER220403_H
|
||||
#define SERIALIZER220403_H
|
||||
|
||||
#include "serializer.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class ProjectSerializer220403 : public ProjectSerializer
|
||||
{
|
||||
public:
|
||||
ProjectSerializer220403() = default;
|
||||
|
||||
protected:
|
||||
virtual LoadData Load(Project *project, QXmlStreamReader *reader, void *reserved) const override;
|
||||
|
||||
virtual void Save(QXmlStreamWriter *writer, const SaveData &data, void *reserved) const override;
|
||||
|
||||
virtual uint Version() const override
|
||||
{
|
||||
return 220403;
|
||||
}
|
||||
|
||||
private:
|
||||
struct XMLNodeData {
|
||||
struct SerializedConnection {
|
||||
NodeInput input;
|
||||
quintptr output_node;
|
||||
};
|
||||
|
||||
struct BlockLink {
|
||||
Node* block;
|
||||
quintptr link;
|
||||
};
|
||||
|
||||
struct GroupLink {
|
||||
NodeGroup *group;
|
||||
QString passthrough_id;
|
||||
quintptr input_node;
|
||||
QString input_id;
|
||||
int input_element;
|
||||
QString custom_name;
|
||||
InputFlags custom_flags;
|
||||
NodeValue::Type data_type;
|
||||
QVariant default_val;
|
||||
QHash<QString, QVariant> custom_properties;
|
||||
};
|
||||
|
||||
QHash<quintptr, Node*> node_ptrs;
|
||||
QList<SerializedConnection> desired_connections;
|
||||
QList<BlockLink> block_links;
|
||||
QVector<GroupLink> group_input_links;
|
||||
QHash<NodeGroup*, quintptr> group_output_links;
|
||||
QHash<Node*, QUuid> node_uuids;
|
||||
|
||||
};
|
||||
|
||||
void LoadNode(Node *node, XMLNodeData &xml_node_data, QXmlStreamReader *reader) const;
|
||||
|
||||
void SaveNode(Node *node, QXmlStreamWriter *writer) const;
|
||||
|
||||
void LoadInput(Node *node, QXmlStreamReader* reader, XMLNodeData &xml_node_data) const;
|
||||
|
||||
void SaveInput(Node *node, QXmlStreamWriter* writer, const QString& id) const;
|
||||
|
||||
void LoadImmediate(QXmlStreamReader *reader, Node *node, const QString& input, int element, XMLNodeData& xml_node_data) const;
|
||||
|
||||
void SaveImmediate(QXmlStreamWriter *writer, Node *node, const QString &input, int element) const;
|
||||
|
||||
bool LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, Node::Position *pos) const;
|
||||
|
||||
void SavePosition(QXmlStreamWriter *writer, Node *node, const Node::Position &pos) const;
|
||||
|
||||
void PostConnect(const XMLNodeData &xml_node_data) const;
|
||||
|
||||
void LoadNodeCustom(QXmlStreamReader *reader, Node *node, XMLNodeData &xml_node_data) const;
|
||||
|
||||
void SaveNodeCustom(QXmlStreamWriter *writer, Node *node) const;
|
||||
|
||||
void LoadTimelinePoints(QXmlStreamReader *reader, TimelinePoints *points) const;
|
||||
|
||||
void SaveTimelinePoints(QXmlStreamWriter *writer, TimelinePoints *points) const;
|
||||
|
||||
void LoadWorkArea(QXmlStreamReader *reader, TimelineWorkArea *workarea) const;
|
||||
|
||||
void SaveWorkArea(QXmlStreamWriter *writer, TimelineWorkArea *workarea) const;
|
||||
|
||||
void LoadMarkerList(QXmlStreamReader *reader, TimelineMarkerList *markers) const;
|
||||
|
||||
void SaveMarkerList(QXmlStreamWriter *writer, TimelineMarkerList *markers) const;
|
||||
|
||||
void LoadValueHint(Node::ValueHint *hint, QXmlStreamReader *reader) const;
|
||||
|
||||
void SaveValueHint(const Node::ValueHint *hint, QXmlStreamWriter *writer) const;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // SERIALIZER220403_H
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
|
||||
virtual QVector<CategoryID> Category() const override
|
||||
{
|
||||
return {kCategoryGeneral};
|
||||
return {kCategoryTime};
|
||||
}
|
||||
|
||||
virtual QString Description() const override
|
||||
|
||||
@@ -56,7 +56,7 @@ QString TimeRemapNode::id() const
|
||||
|
||||
QVector<Node::CategoryID> TimeRemapNode::Category() const
|
||||
{
|
||||
return {kCategoryGeneral};
|
||||
return {kCategoryTime};
|
||||
}
|
||||
|
||||
QString TimeRemapNode::Description() const
|
||||
|
||||
@@ -141,6 +141,7 @@ QByteArray NodeValue::ValueToBytes(NodeValue::Type type, const QVariant &value)
|
||||
case kShaderJob:
|
||||
case kSampleJob:
|
||||
case kGenerateJob:
|
||||
case kDataTypeCount:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -357,12 +358,78 @@ QString NodeValue::GetPrettyDataTypeName(Type type)
|
||||
case kShaderJob:
|
||||
case kSampleJob:
|
||||
case kGenerateJob:
|
||||
case kDataTypeCount:
|
||||
break;
|
||||
}
|
||||
|
||||
return QCoreApplication::translate("NodeValue", "Unknown");
|
||||
}
|
||||
|
||||
QString NodeValue::GetDataTypeName(Type type)
|
||||
{
|
||||
switch (type) {
|
||||
case kNone:
|
||||
return QStringLiteral("none");
|
||||
case kInt:
|
||||
return QStringLiteral("int");
|
||||
case kCombo:
|
||||
return QStringLiteral("combo");
|
||||
case kFloat:
|
||||
return QStringLiteral("float");
|
||||
case kRational:
|
||||
return QStringLiteral("rational");
|
||||
case kBoolean:
|
||||
return QStringLiteral("bool");
|
||||
case kColor:
|
||||
return QStringLiteral("color");
|
||||
case kMatrix:
|
||||
return QStringLiteral("matrix");
|
||||
case kText:
|
||||
return QStringLiteral("text");
|
||||
case kFont:
|
||||
return QStringLiteral("font");
|
||||
case kFile:
|
||||
return QStringLiteral("file");
|
||||
case kTexture:
|
||||
return QStringLiteral("texture");
|
||||
case kSamples:
|
||||
return QStringLiteral("samples");
|
||||
case kVec2:
|
||||
return QStringLiteral("vec2");
|
||||
case kVec3:
|
||||
return QStringLiteral("vec3");
|
||||
case kVec4:
|
||||
return QStringLiteral("vec4");
|
||||
case kBezier:
|
||||
return QStringLiteral("bezier");
|
||||
case kVideoParams:
|
||||
return QStringLiteral("vparam");
|
||||
case kAudioParams:
|
||||
return QStringLiteral("aparam");
|
||||
case kFootageJob:
|
||||
case kShaderJob:
|
||||
case kSampleJob:
|
||||
case kGenerateJob:
|
||||
case kDataTypeCount:
|
||||
break;
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
NodeValue::Type NodeValue::GetDataTypeFromName(const QString &n)
|
||||
{
|
||||
// Slow but easy to maintain
|
||||
for (int i=0; i<kDataTypeCount; i++) {
|
||||
Type t = static_cast<Type>(i);
|
||||
if (GetDataTypeName(t) == n) {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
return NodeValue::kNone;
|
||||
}
|
||||
|
||||
NodeValue NodeValueTable::GetWithMeta(const QVector<NodeValue::Type> &type, const QString &tag) const
|
||||
{
|
||||
int value_index = GetValueIndex(type, tag);
|
||||
|
||||
+12
-1
@@ -25,6 +25,8 @@
|
||||
#include <QVariant>
|
||||
#include <QVector>
|
||||
|
||||
#include "undo/undocommand.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class Node;
|
||||
@@ -204,7 +206,12 @@ public:
|
||||
* take place. This value will usually be taken from a table and a kSamples value will be
|
||||
* pushed to take its place.
|
||||
*/
|
||||
kGenerateJob
|
||||
kGenerateJob,
|
||||
|
||||
/**
|
||||
* End of list
|
||||
*/
|
||||
kDataTypeCount
|
||||
};
|
||||
|
||||
static const QVector<Type> kNumber;
|
||||
@@ -259,6 +266,9 @@ public:
|
||||
|
||||
static QString GetPrettyDataTypeName(Type type);
|
||||
|
||||
static QString GetDataTypeName(Type type);
|
||||
static NodeValue::Type GetDataTypeFromName(const QString &n);
|
||||
|
||||
static QString ValueToString(Type data_type, const QVariant& value, bool value_is_a_key_track);
|
||||
|
||||
static QVariant StringToValue(Type data_type, const QString &string, bool value_is_a_key_track);
|
||||
@@ -285,6 +295,7 @@ public:
|
||||
|| type == kVec2
|
||||
|| type == kVec3
|
||||
|| type == kVec4
|
||||
|| type == kBezier
|
||||
|| type == kColor
|
||||
|| type == kRational;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,8 @@ NodePanel::NodePanel(QWidget *parent) :
|
||||
// Connect node view signals to this panel - MAY REMOVE
|
||||
connect(node_widget_->view(), &NodeView::NodesSelected, this, &NodePanel::NodesSelected);
|
||||
connect(node_widget_->view(), &NodeView::NodesDeselected, this, &NodePanel::NodesDeselected);
|
||||
connect(node_widget_->view(), &NodeView::NodeGroupOpenRequested, this, &NodePanel::NodeGroupOpenRequested);
|
||||
connect(node_widget_->view(), &NodeView::NodeGroupOpened, this, &NodePanel::NodeGroupOpened);
|
||||
connect(node_widget_->view(), &NodeView::NodeGroupClosed, this, &NodePanel::NodeGroupClosed);
|
||||
|
||||
// Set it as the main widget of this panel
|
||||
SetWidgetWithPadding(node_widget_);
|
||||
|
||||
+6
-10
@@ -40,10 +40,9 @@ public:
|
||||
return node_widget_;
|
||||
}
|
||||
|
||||
const QVector<Node*> &GetContexts() const
|
||||
{
|
||||
return node_widget_->view()->GetContexts();
|
||||
}
|
||||
const QVector<Node*> &GetContexts() const { return node_widget_->view()->GetContexts(); }
|
||||
|
||||
bool IsGroupOverlay() const { return node_widget_->view()->IsGroupOverlay(); }
|
||||
|
||||
void SetContexts(const QVector<Node*> &nodes)
|
||||
{
|
||||
@@ -55,11 +54,6 @@ public:
|
||||
node_widget_->view()->CloseContextsBelongingToProject(project);
|
||||
}
|
||||
|
||||
const QVector<Node*> &GetCurrentContexts() const
|
||||
{
|
||||
return node_widget_->view()->GetCurrentContexts();
|
||||
}
|
||||
|
||||
virtual void SelectAll() override
|
||||
{
|
||||
node_widget_->view()->SelectAll();
|
||||
@@ -122,7 +116,9 @@ signals:
|
||||
|
||||
void NodesDeselected(const QVector<Node*>& nodes);
|
||||
|
||||
void NodeGroupOpenRequested(NodeGroup *group);
|
||||
void NodeGroupOpened(NodeGroup *group);
|
||||
|
||||
void NodeGroupClosed();
|
||||
|
||||
private:
|
||||
virtual void Retranslate() override
|
||||
|
||||
@@ -43,16 +43,6 @@ public:
|
||||
return GetParamView()->GetContexts();
|
||||
}
|
||||
|
||||
void SetCreateCheckBoxes(NodeParamViewCheckBoxBehavior e)
|
||||
{
|
||||
GetParamView()->SetCreateCheckBoxes(e);
|
||||
}
|
||||
|
||||
void SetIgnoreNodeFlags(bool e)
|
||||
{
|
||||
GetParamView()->SetIgnoreNodeFlags(e);
|
||||
}
|
||||
|
||||
void CloseContextsBelongingToProject(Project *p)
|
||||
{
|
||||
GetParamView()->CloseContextsBelongingToProject(p);
|
||||
|
||||
@@ -44,7 +44,7 @@ DiskManager::DiskManager()
|
||||
QString default_dir = default_disk_cache_file.readAll();
|
||||
|
||||
if (!default_dir.isEmpty()) {
|
||||
if (FileFunctions::DirectoryIsValid(default_dir, true)) {
|
||||
if (FileFunctions::DirectoryIsValid(default_dir)) {
|
||||
GetOpenFolder(default_dir);
|
||||
} else {
|
||||
QMessageBox::warning(nullptr,
|
||||
@@ -180,7 +180,7 @@ void DiskManager::ShowDiskCacheSettingsDialog(DiskCacheFolder *folder, QWidget *
|
||||
|
||||
void DiskManager::ShowDiskCacheSettingsDialog(const QString &path, QWidget *parent)
|
||||
{
|
||||
if (!FileFunctions::DirectoryIsValid(path, true)) {
|
||||
if (!FileFunctions::DirectoryIsValid(path)) {
|
||||
QMessageBox::critical(parent, tr("Disk Cache Error"),
|
||||
tr("Failed to open disk cache at \"%1\". Try a different folder.").arg(path));
|
||||
return;
|
||||
@@ -274,7 +274,7 @@ void DiskCacheFolder::SetPath(const QString &path)
|
||||
|
||||
// Attempt to load existing index file from path
|
||||
QDir path_dir(path_);
|
||||
path_dir.mkpath(QStringLiteral("."));
|
||||
FileFunctions::DirectoryIsValid(path_dir);
|
||||
|
||||
index_path_ = path_dir.filePath(QStringLiteral("index"));
|
||||
|
||||
|
||||
@@ -394,8 +394,8 @@ bool FrameHashCache::SaveCacheFrame(const QString &filename, char *data, const V
|
||||
|
||||
// Ensure directory is created
|
||||
QDir cache_dir = QFileInfo(filename).dir();
|
||||
if (!cache_dir.exists()) {
|
||||
cache_dir.mkpath(".");
|
||||
if (!FileFunctions::DirectoryIsValid(cache_dir)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Floating point types are stored in EXR
|
||||
|
||||
@@ -48,11 +48,16 @@ public:
|
||||
|
||||
void SetRequestedFormat(VideoParams::Format f) { requested_format_ = f; }
|
||||
|
||||
const QString &GetColorspace() const { return colorspace_; }
|
||||
void SetColorspace(const QString &s) { colorspace_ = s; }
|
||||
|
||||
private:
|
||||
AlphaChannelSetting alpha_channel_required_;
|
||||
|
||||
VideoParams::Format requested_format_;
|
||||
|
||||
QString colorspace_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# Price, Nick Shaw, and Timothy
|
||||
# Lottes.
|
||||
|
||||
ocio_profile_version: 1
|
||||
ocio_profile_version: 2
|
||||
|
||||
search_path: "luts:looks"
|
||||
strictparsing: true
|
||||
@@ -16,7 +16,7 @@ luma: [0.2126, 0.7152, 0.0722]
|
||||
description: A filmlike dynamic range encoding set for Blender
|
||||
|
||||
roles:
|
||||
default: Linear
|
||||
default: sRGB OETF
|
||||
reference: Linear
|
||||
scene_linear: Linear
|
||||
data: Non-Colour Data
|
||||
@@ -28,6 +28,7 @@ roles:
|
||||
color_picking: sRGB OETF
|
||||
texture_paint: sRGB OETF
|
||||
matte_paint: Filmic Log Encoding
|
||||
cie_xyz_d65_interchange: CIE-XYZ D65
|
||||
|
||||
displays:
|
||||
sRGB:
|
||||
@@ -49,6 +50,8 @@ displays:
|
||||
active_displays: [sRGB, BT.1886, Apple Display P3, None]
|
||||
#active_views: [Filmic Log Encoding Base, sRGB OETF, Non-Colour Data, Linear Raw, No View]
|
||||
|
||||
inactive_colorspaces: [CIE-XYZ D65]
|
||||
|
||||
colorspaces:
|
||||
- !<ColorSpace>
|
||||
name: Linear
|
||||
@@ -61,6 +64,20 @@ colorspaces:
|
||||
allocation: lg2
|
||||
allocationvars: [-12.4739311883, 12.5260688117, 0.00392156862]
|
||||
|
||||
- !<ColorSpace>
|
||||
name: CIE-XYZ D65
|
||||
family: display
|
||||
equalitygroup: ""
|
||||
bitdepth: 32f
|
||||
description: |
|
||||
Linear CIE XYZ space with D65 white point
|
||||
isdata: false
|
||||
allocation: lg2
|
||||
allocationvars: [-12.4739311883, 12.5260688117, 0.00392156862]
|
||||
to_reference: !<GroupTransform>
|
||||
children:
|
||||
- !<MatrixTransform> {matrix: [0.4124, 0.3576, 0.1805, 0, 0.2126, 0.7152, 0.0722, 0, 0.0193, 0.1192, 0.9505, 0, 0, 0, 0, 1], direction: inverse}
|
||||
|
||||
- !<ColorSpace>
|
||||
name: Filmic Log Encoding
|
||||
family:
|
||||
@@ -248,6 +265,22 @@ colorspaces:
|
||||
- !<FileTransform> {src: V3_LogC_400_to_linear.spi1d, interpolation: linear}
|
||||
- !<MatrixTransform> {matrix: [1.617523, -0.537287, -0.080237, 0, -0.070573, 1.334613, -0.26404, 0, -0.021102, -0.226954, 1.248056, 0, 0, 0, 0, 1]}
|
||||
|
||||
- !<ColorSpace>
|
||||
name: Blackmagic Film Wide Gamut (Gen 5)
|
||||
family: Camera Footage
|
||||
equalitygroup: ""
|
||||
bitdepth: 32f
|
||||
description: |
|
||||
Blackmagic Film Wide Gamut (Gen 5)
|
||||
isdata: false
|
||||
allocation: uniform
|
||||
allocationvars: [0, 1]
|
||||
to_reference: !<GroupTransform>
|
||||
children:
|
||||
- !<FileTransform> {src: Blackmagic_FilmWideGamut_Gen5_to_linear.spi1d, interpolation: linear}
|
||||
- !<MatrixTransform> {matrix: [0.606530, 0.220408, 0.123479, 0, 0.267989, 0.832731, -0.100720, 0, -0.029442, -0.086611, 1.204861, 0, 0, 0, 0, 1]}
|
||||
- !<ColorSpaceTransform> {src: CIE-XYZ D65, dst: reference}
|
||||
|
||||
- !<ColorSpace>
|
||||
name: Non-Colour Data
|
||||
family:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -523,6 +523,7 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video
|
||||
case NodeValue::kFootageJob:
|
||||
case NodeValue::kBezier:
|
||||
case NodeValue::kNone:
|
||||
case NodeValue::kDataTypeCount:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -539,13 +540,15 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video
|
||||
GLenum target = (texture && texture->type() == Texture::k3D) ? GL_TEXTURE_3D : GL_TEXTURE_2D;
|
||||
functions_->glBindTexture(target, tex_id);
|
||||
|
||||
PrepareInputTexture(target, t.interpolation);
|
||||
if (tex_id) {
|
||||
PrepareInputTexture(target, t.interpolation);
|
||||
|
||||
if (texture && texture->channel_count() == 1 && destination_params.channel_count() != 1) {
|
||||
// Interpret this texture as a grayscale texture
|
||||
functions_->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_RED);
|
||||
functions_->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_RED);
|
||||
functions_->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
|
||||
if (texture->channel_count() == 1 && destination_params.channel_count() != 1) {
|
||||
// Interpret this texture as a grayscale texture
|
||||
functions_->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_RED);
|
||||
functions_->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_RED);
|
||||
functions_->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -807,6 +810,10 @@ void OpenGLRenderer::PrepareInputTexture(GLenum target, Texture::Interpolation i
|
||||
|
||||
functions_->glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
functions_->glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
if (target == GL_TEXTURE_3D) {
|
||||
functions_->glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLRenderer::ClearDestinationInternal(double r, double g, double b, double a)
|
||||
@@ -883,14 +890,9 @@ GLuint OpenGLRenderer::GetCachedTexture(int width, int height, int depth, VideoP
|
||||
GLuint OpenGLRenderer::CompileShader(GLenum type, const QString &code)
|
||||
{
|
||||
static const QString shader_preamble =
|
||||
#ifndef Q_OS_MAC
|
||||
// Use appropriate GL ES 2.0 shader header
|
||||
QStringLiteral("#version 100\n\n"
|
||||
// Use appropriate GL 3.2 shader header
|
||||
QStringLiteral("#version 150\n\n"
|
||||
"precision highp float;\n\n");
|
||||
#else
|
||||
// Use desktop GL equivalent header because apparently macOS doesn't support the ES header
|
||||
QStringLiteral("#version 120\n\n");
|
||||
#endif
|
||||
|
||||
QString complete_code;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user