Merge branch 'master' into pr/1717
This commit is contained in:
+82
-27
@@ -50,7 +50,7 @@ jobs:
|
|||||||
${{ matrix.cmake-gen }}>
|
${{ matrix.cmake-gen }}>
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: olivevideoeditor/ci-olive:2021.4
|
image: olivevideoeditor/ci-olive:2022.1
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Source Code
|
- name: Checkout Source Code
|
||||||
@@ -285,11 +285,20 @@ jobs:
|
|||||||
os-arch: x86_64
|
os-arch: x86_64
|
||||||
os: macos-10.15
|
os: macos-10.15
|
||||||
cmake-gen: Ninja
|
cmake-gen: Ninja
|
||||||
|
min-deploy: 10.13
|
||||||
|
- build-type: RelWithDebInfo
|
||||||
|
compiler-name: Clang LLVM
|
||||||
|
os-name: macOS
|
||||||
|
os-arch: arm64
|
||||||
|
os: macos-11.0
|
||||||
|
cmake-gen: Ninja
|
||||||
|
min-deploy: 11.0
|
||||||
env:
|
env:
|
||||||
DEP_LOCATION: /opt/olive-editor
|
DEP_LOCATION: /opt/olive-editor
|
||||||
name: |
|
name: |
|
||||||
${{ matrix.os-name }}
|
${{ matrix.os-name }}
|
||||||
<${{ matrix.compiler-name }},
|
<${{ matrix.os-arch }},
|
||||||
|
${{ matrix.compiler-name }},
|
||||||
${{ matrix.build-type }},
|
${{ matrix.build-type }},
|
||||||
${{ matrix.cmake-gen }}>
|
${{ matrix.cmake-gen }}>
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
@@ -314,15 +323,16 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
working-directory: ${{ runner.workspace }}
|
working-directory: ${{ runner.workspace }}
|
||||||
run: |
|
run: |
|
||||||
$DOWNLOAD_TOOL https://github.com/olive-editor/dependencies/releases/download/continuous/olive-dep-mac-x86_64.tar.gz
|
$DOWNLOAD_TOOL https://github.com/olive-editor/dependencies/releases/download/continuous/olive-dep-mac-${{ matrix.os-arch }}.tar.gz
|
||||||
sudo tar xzf olive-dep-mac-x86_64.tar.gz -C /
|
sudo tar xzf olive-dep-mac-${{ matrix.os-arch }}.tar.gz -C /
|
||||||
|
|
||||||
- name: Acquire Google Crashpad
|
- name: Acquire Google Crashpad
|
||||||
shell: bash
|
shell: bash
|
||||||
working-directory: ${{ runner.workspace }}
|
working-directory: ${{ runner.workspace }}
|
||||||
run: |
|
run: |
|
||||||
$DOWNLOAD_TOOL https://github.com/olive-editor/crashpad/releases/download/continuous/crashpad-mac-x86_64.tar.gz
|
$DOWNLOAD_TOOL https://github.com/olive-editor/crashpad/releases/download/continuous/crashpad-mac-${{ matrix.os-arch }}.tar.gz
|
||||||
sudo tar xzf crashpad-mac-x86_64.tar.gz -C /
|
sudo tar xzf crashpad-mac-${{ matrix.os-arch }}.tar.gz -C /
|
||||||
|
if: matrix.os-arch == 'x86_64' # TEMPORARY: Crashpad isn't compiling on ARM64 yet for some reason
|
||||||
|
|
||||||
- name: Generate Patreon List
|
- name: Generate Patreon List
|
||||||
env:
|
env:
|
||||||
@@ -341,7 +351,8 @@ jobs:
|
|||||||
brew install ninja
|
brew install ninja
|
||||||
PATH=$DEP_LOCATION:$DEP_LOCATION/bin:$DEP_LOCATION/include:$DEP_LOCATION/lib:$DEP_LOCATION/crashpad:$PATH \
|
PATH=$DEP_LOCATION:$DEP_LOCATION/bin:$DEP_LOCATION/include:$DEP_LOCATION/lib:$DEP_LOCATION/crashpad:$PATH \
|
||||||
cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} \
|
cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} \
|
||||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 -G "${{ matrix.cmake-gen }}"
|
-DCMAKE_OSX_DEPLOYMENT_TARGET=${{ matrix.min-deploy }} -G "${{ matrix.cmake-gen }}" \
|
||||||
|
-DCMAKE_OSX_ARCHITECTURES="${{ matrix.os-arch }}"
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
working-directory: ${{ runner.workspace }}/build
|
working-directory: ${{ runner.workspace }}/build
|
||||||
@@ -354,34 +365,78 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
ctest -C ${{ matrix.build-type }} -V
|
ctest -C ${{ matrix.build-type }} -V
|
||||||
|
if: matrix.os-arch == 'x86_64' # ARM64 tests naturally won't be able to run on x86_64 runners
|
||||||
|
|
||||||
- name: Create Package
|
- name: Create Package
|
||||||
working-directory: ${{ runner.workspace }}/build
|
working-directory: ${{ runner.workspace }}/build
|
||||||
env:
|
|
||||||
BUNDLE_NAME: "Olive.app"
|
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
# Use macdeployqt and macdeployqt to bundle dependencies
|
BUNDLE_NAME="Olive.app"
|
||||||
|
brew install dylibbundler
|
||||||
|
|
||||||
|
if [ "${{ matrix.os-arch }}" == "x86_64" ]
|
||||||
|
then
|
||||||
|
DYLIBBUNDLER_EXTRA_ARGS="-x $BUNDLE_NAME/Contents/MacOS/olive-crashhandler"
|
||||||
|
fi
|
||||||
|
|
||||||
mv app/$BUNDLE_NAME .
|
mv app/$BUNDLE_NAME .
|
||||||
$DEP_LOCATION/bin/macdeployqt $BUNDLE_NAME -executable=$BUNDLE_NAME/Contents/MacOS/olive-crashhandler
|
mkdir $BUNDLE_NAME/Contents/Frameworks
|
||||||
$DOWNLOAD_TOOL https://github.com/arl/macdeployqtfix/raw/master/macdeployqtfix.py
|
dylibbundler -b -ns -x "$BUNDLE_NAME/Contents/MacOS/Olive" -s "$DEP_LOCATION/lib" -d "$BUNDLE_NAME/Contents/Frameworks" -p "@executable_path/../Frameworks" $DYLIBBUNDLER_EXTRA_ARGS
|
||||||
python2 macdeployqtfix.py $BUNDLE_NAME/Contents/MacOS/Olive $VCPKG_ROOT/installed/x64-osx
|
|
||||||
|
|
||||||
# Manual corrections
|
# Copy Qt frameworks and plugins
|
||||||
for f in $BUNDLE_NAME/Contents/Frameworks/*.dylib
|
cp -Ra $DEP_LOCATION/lib/Qt*.framework $BUNDLE_NAME/Contents/Frameworks
|
||||||
do
|
cp -Ra $DEP_LOCATION/plugins $BUNDLE_NAME/Contents
|
||||||
for r in $(otool -L $f | grep "@rpath" | awk '{print $1;}')
|
|
||||||
do
|
|
||||||
install_name_tool -change $r ${r//rpath/loader_path} $f
|
|
||||||
done
|
|
||||||
done
|
|
||||||
|
|
||||||
# Crashpad symbols
|
# HACK: On x86_64, dylibbundler doesn't resolve this symlink. Weirdly it does on ARM64,
|
||||||
$DEP_LOCATION/bin/dump_syms $BUNDLE_NAME/Contents/MacOS/Olive > Olive.sym
|
# but perhaps I'll bring it up with them soon.
|
||||||
SYM_HEADER=($(head -n 1 Olive.sym)) # Read first line of symbol file
|
cp -a $BUNDLE_NAME/Contents/Frameworks/libpng16.16.37.0.dylib $BUNDLE_NAME/Contents/Frameworks/libpng16.16.dylib
|
||||||
SYM_DIR=$BUNDLE_NAME/Contents/Resources/symbols/Olive/${SYM_HEADER[3]}
|
|
||||||
mkdir -p "$SYM_DIR"
|
if [ "${{ matrix.os-arch }}" == "x86_64" ]
|
||||||
mv Olive.sym "$SYM_DIR"
|
then
|
||||||
|
# Crashpad symbols
|
||||||
|
$DEP_LOCATION/bin/dump_syms $BUNDLE_NAME/Contents/MacOS/Olive > Olive.sym
|
||||||
|
SYM_HEADER=($(head -n 1 Olive.sym)) # Read first line of symbol file
|
||||||
|
SYM_DIR=$BUNDLE_NAME/Contents/Resources/symbols/Olive/${SYM_HEADER[3]}
|
||||||
|
mkdir -p "$SYM_DIR"
|
||||||
|
mv Olive.sym "$SYM_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Sign Application
|
||||||
|
working-directory: ${{ runner.workspace }}/build
|
||||||
|
shell: bash
|
||||||
|
if: github.event_name == 'push'
|
||||||
|
env:
|
||||||
|
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
|
||||||
|
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
|
||||||
|
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
BUNDLE_NAME="Olive.app"
|
||||||
|
|
||||||
|
# Install certificate
|
||||||
|
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
|
||||||
|
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
|
||||||
|
|
||||||
|
# import certificate from secrets
|
||||||
|
echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode --output $CERTIFICATE_PATH
|
||||||
|
|
||||||
|
# create temporary keychain
|
||||||
|
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
|
||||||
|
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
|
||||||
|
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
|
||||||
|
|
||||||
|
# import certificate to keychain
|
||||||
|
security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
|
||||||
|
security list-keychain -d user -s $KEYCHAIN_PATH
|
||||||
|
|
||||||
|
# HACK: Remove unsignable frameworks
|
||||||
|
rm -r $BUNDLE_NAME/Contents/Frameworks/QtUiPlugin.framework
|
||||||
|
if [ "${{ matrix.os-arch }}" == "arm64" ]
|
||||||
|
then
|
||||||
|
rm -r $BUNDLE_NAME/Contents/Frameworks/QtZlib.framework
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Sign application
|
||||||
|
codesign --deep --sign "Developer ID Application: Olive Studios LLC" $BUNDLE_NAME
|
||||||
|
|
||||||
- name: Deploy Packages
|
- name: Deploy Packages
|
||||||
working-directory: ${{ runner.workspace }}/build
|
working-directory: ${{ runner.workspace }}/build
|
||||||
|
|||||||
+1
-1
@@ -74,7 +74,7 @@ find_package(OpenGL REQUIRED)
|
|||||||
list(APPEND OLIVE_LIBRARIES OpenGL::GL)
|
list(APPEND OLIVE_LIBRARIES OpenGL::GL)
|
||||||
|
|
||||||
# Link OpenColorIO
|
# 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_LIBRARIES ${OCIO_LIBRARIES})
|
||||||
list(APPEND OLIVE_INCLUDE_DIRS ${OCIO_INCLUDE_DIRS})
|
list(APPEND OLIVE_INCLUDE_DIRS ${OCIO_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ set(OLIVE_SOURCES
|
|||||||
audio/audiovisualwaveform.h
|
audio/audiovisualwaveform.h
|
||||||
audio/packedprocessor.cpp
|
audio/packedprocessor.cpp
|
||||||
audio/packedprocessor.h
|
audio/packedprocessor.h
|
||||||
|
audio/planarprocessor.cpp
|
||||||
|
audio/planarprocessor.h
|
||||||
audio/tempoprocessor.cpp
|
audio/tempoprocessor.cpp
|
||||||
audio/tempoprocessor.h
|
audio/tempoprocessor.h
|
||||||
PARENT_SCOPE
|
PARENT_SCOPE
|
||||||
|
|||||||
@@ -76,17 +76,12 @@ QByteArray PackedProcessor::Convert(SampleBufferPtr planar)
|
|||||||
return QByteArray();
|
return QByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
int nb_channels = planar->audio_params().channel_count();
|
|
||||||
|
|
||||||
QByteArray output(planar->audio_params().samples_to_bytes(nb_samples), Qt::Uninitialized);
|
QByteArray output(planar->audio_params().samples_to_bytes(nb_samples), Qt::Uninitialized);
|
||||||
uint8_t *output_data = reinterpret_cast<uint8_t*>(output.data());
|
uint8_t *output_data = reinterpret_cast<uint8_t*>(output.data());
|
||||||
|
|
||||||
QVector<const uint8_t*> input_arrays(nb_channels);
|
int ret = swr_convert(swr_ctx_, &output_data, nb_samples,
|
||||||
for (int i=0; i<nb_channels; i++) {
|
const_cast<const uint8_t**>(reinterpret_cast<uint8_t**>(planar->to_raw_ptrs())),
|
||||||
input_arrays[i] = reinterpret_cast<const uint8_t*>(planar->data(i));
|
nb_samples);
|
||||||
}
|
|
||||||
|
|
||||||
int ret = swr_convert(swr_ctx_, &output_data, nb_samples, input_arrays.data(), nb_samples);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
char buf[200];
|
char buf[200];
|
||||||
av_strerror(ret, buf, 200);
|
av_strerror(ret, buf, 200);
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
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 "planarprocessor.h"
|
||||||
|
|
||||||
|
#include "common/ffmpegutils.h"
|
||||||
|
|
||||||
|
namespace olive {
|
||||||
|
|
||||||
|
PlanarProcessor::PlanarProcessor() :
|
||||||
|
swr_ctx_(nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
PlanarProcessor::~PlanarProcessor()
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PlanarProcessor::Open(const AudioParams ¶ms)
|
||||||
|
{
|
||||||
|
if (IsOpen()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
swr_ctx_ = swr_alloc_set_opts(nullptr,
|
||||||
|
params.channel_layout(),
|
||||||
|
FFmpegUtils::GetFFmpegSampleFormat(params.format(), true),
|
||||||
|
params.sample_rate(),
|
||||||
|
params.channel_layout(),
|
||||||
|
FFmpegUtils::GetFFmpegSampleFormat(params.format(), false),
|
||||||
|
params.sample_rate(),
|
||||||
|
0,
|
||||||
|
nullptr);
|
||||||
|
|
||||||
|
if (!swr_ctx_) {
|
||||||
|
qCritical() << "Failed to allocate resample context";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (swr_init(swr_ctx_) < 0) {
|
||||||
|
qCritical() << "Failed to init resample context";
|
||||||
|
swr_free(&swr_ctx_);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
params_ = params;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SampleBufferPtr PlanarProcessor::Convert(const QByteArray &packed)
|
||||||
|
{
|
||||||
|
if (!IsOpen()) {
|
||||||
|
qCritical() << "Tried to convert while closed";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packed.isEmpty()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nb_samples_per_channel = params_.bytes_to_samples(packed.size());
|
||||||
|
|
||||||
|
SampleBufferPtr output = SampleBuffer::CreateAllocated(params_, nb_samples_per_channel);
|
||||||
|
|
||||||
|
const uint8_t *input = reinterpret_cast<const uint8_t*>(packed.constData());
|
||||||
|
int ret = swr_convert(swr_ctx_,
|
||||||
|
reinterpret_cast<uint8_t**>(output->to_raw_ptrs()), nb_samples_per_channel,
|
||||||
|
&input, nb_samples_per_channel);
|
||||||
|
if (ret < 0) {
|
||||||
|
char buf[200];
|
||||||
|
av_strerror(ret, buf, 200);
|
||||||
|
qDebug() << "Planar processor failed with error:" << buf << ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlanarProcessor::Close()
|
||||||
|
{
|
||||||
|
if (swr_ctx_) {
|
||||||
|
swr_free(&swr_ctx_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
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 PLANARPROCESSOR_H
|
||||||
|
#define PLANARPROCESSOR_H
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <libswresample/swresample.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "codec/samplebuffer.h"
|
||||||
|
#include "render/audioparams.h"
|
||||||
|
|
||||||
|
namespace olive {
|
||||||
|
|
||||||
|
class PlanarProcessor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PlanarProcessor();
|
||||||
|
|
||||||
|
~PlanarProcessor();
|
||||||
|
|
||||||
|
DISABLE_COPY_MOVE(PlanarProcessor)
|
||||||
|
|
||||||
|
bool Open(const AudioParams ¶ms);
|
||||||
|
|
||||||
|
SampleBufferPtr Convert(const QByteArray &packed);
|
||||||
|
|
||||||
|
void Close();
|
||||||
|
|
||||||
|
bool IsOpen() const
|
||||||
|
{
|
||||||
|
return swr_ctx_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
SwrContext *swr_ctx_;
|
||||||
|
|
||||||
|
AudioParams params_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // PLANARPROCESSOR_H
|
||||||
@@ -36,7 +36,6 @@ TempoProcessor::TempoProcessor() :
|
|||||||
filter_graph_(nullptr),
|
filter_graph_(nullptr),
|
||||||
buffersrc_ctx_(nullptr),
|
buffersrc_ctx_(nullptr),
|
||||||
buffersink_ctx_(nullptr),
|
buffersink_ctx_(nullptr),
|
||||||
processed_frame_(nullptr),
|
|
||||||
open_(false)
|
open_(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -150,47 +149,42 @@ bool TempoProcessor::Open(const AudioParams ¶ms, const double& speed)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TempoProcessor::Push(const char *data, int length)
|
void TempoProcessor::Push(const QByteArray &packed)
|
||||||
{
|
{
|
||||||
if (flushed_) {
|
if (!IsOpen()) {
|
||||||
if (length > 0) {
|
qWarning() << "Tried to push to closed TempoProcessor";
|
||||||
qCritical() << "Tried to push" << length << "bytes after TempoProcessor was closed";
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AVFrame* src_frame;
|
if (flushed_) {
|
||||||
|
qWarning() << "Tried to push to flushed TempoProcessor";
|
||||||
if (length == 0) {
|
return;
|
||||||
// No audio data, flush the last out of the filter graph
|
|
||||||
src_frame = nullptr;
|
|
||||||
flushed_ = true;
|
|
||||||
} else {
|
|
||||||
src_frame = av_frame_alloc();
|
|
||||||
|
|
||||||
if (!src_frame) {
|
|
||||||
qCritical() << "Failed to allocate source frame";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate a buffer for the number of samples we got
|
|
||||||
src_frame->sample_rate = params_.sample_rate();
|
|
||||||
src_frame->format = FFmpegUtils::GetFFmpegSampleFormat(params_.format());
|
|
||||||
src_frame->channel_layout = params_.channel_layout();
|
|
||||||
src_frame->nb_samples = params_.bytes_to_samples(length);
|
|
||||||
src_frame->pts = timestamp_;
|
|
||||||
timestamp_ += src_frame->nb_samples;
|
|
||||||
|
|
||||||
if (av_frame_get_buffer(src_frame, 0) < 0) {
|
|
||||||
qCritical() << "Failed to allocate buffer for source frame";
|
|
||||||
av_frame_free(&src_frame);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy buffer from data array to frame
|
|
||||||
memcpy(src_frame->data[0], data, static_cast<size_t>(length));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AVFrame* src_frame = av_frame_alloc();
|
||||||
|
|
||||||
|
if (!src_frame) {
|
||||||
|
qCritical() << "Failed to allocate source frame";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate a buffer for the number of samples we got
|
||||||
|
src_frame->sample_rate = params_.sample_rate();
|
||||||
|
src_frame->format = FFmpegUtils::GetFFmpegSampleFormat(params_.format());
|
||||||
|
src_frame->channel_layout = params_.channel_layout();
|
||||||
|
src_frame->nb_samples = params_.bytes_to_samples(packed.size());
|
||||||
|
src_frame->pts = timestamp_;
|
||||||
|
timestamp_ += src_frame->nb_samples;
|
||||||
|
|
||||||
|
if (av_frame_get_buffer(src_frame, 0) < 0) {
|
||||||
|
qCritical() << "Failed to allocate buffer for source frame";
|
||||||
|
av_frame_free(&src_frame);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy buffer from data array to frame
|
||||||
|
memcpy(src_frame->data[0], packed, packed.size());
|
||||||
|
|
||||||
int ret = av_buffersrc_add_frame_flags(buffersrc_ctx_, src_frame, AV_BUFFERSRC_FLAG_KEEP_REF);
|
int ret = av_buffersrc_add_frame_flags(buffersrc_ctx_, src_frame, AV_BUFFERSRC_FLAG_KEEP_REF);
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@@ -202,46 +196,45 @@ void TempoProcessor::Push(const char *data, int length)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int TempoProcessor::Pull(char *data, int max_length)
|
void TempoProcessor::Flush()
|
||||||
{
|
{
|
||||||
if (!processed_frame_) {
|
if (!flushed_) {
|
||||||
processed_frame_ = av_frame_alloc();
|
int ret = av_buffersrc_add_frame_flags(buffersrc_ctx_, nullptr, AV_BUFFERSRC_FLAG_KEEP_REF);
|
||||||
|
|
||||||
// Try to pull samples from the buffersink
|
|
||||||
int ret = av_buffersink_get_frame(buffersink_ctx_, processed_frame_);
|
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
// We couldn't pull for some reason, if the error was EAGAIN, we just need to send more samples. Otherwise the
|
qCritical() << "Failed to feed buffer source" << ret;
|
||||||
// error might be fatal...
|
}
|
||||||
if (ret != AVERROR(EAGAIN)) {
|
flushed_ = true;
|
||||||
qCritical() << "Failed to pull from buffersink" << ret;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
av_frame_free(&processed_frame_);
|
QByteArray TempoProcessor::Pull()
|
||||||
|
{
|
||||||
|
QByteArray b;
|
||||||
|
AVFrame *processed_frame = av_frame_alloc();
|
||||||
|
|
||||||
return 0;
|
// Try to pull samples from the buffersink
|
||||||
|
int ret = av_buffersink_get_frame(buffersink_ctx_, processed_frame);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
// We couldn't pull for some reason, if the error was EAGAIN, we just need to send more samples. Otherwise the
|
||||||
|
// error might be fatal...
|
||||||
|
if (ret != AVERROR(EAGAIN)) {
|
||||||
|
qCritical() << "Failed to pull from buffersink" << ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
processed_frame_byte_index_ = 0;
|
av_frame_free(&processed_frame);
|
||||||
processed_frame_max_bytes_ = params_.samples_to_bytes(processed_frame_->nb_samples);
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine how many bytes we should copy into the data array
|
b.resize(params_.samples_to_bytes(processed_frame->nb_samples));
|
||||||
int copy_length = qMin(max_length, processed_frame_max_bytes_ - processed_frame_byte_index_);
|
|
||||||
|
|
||||||
// Copy the bytes
|
// Copy the bytes
|
||||||
memcpy(data, processed_frame_->data[0] + processed_frame_byte_index_, static_cast<size_t>(copy_length));
|
memcpy(b.data(), processed_frame->data[0], b.size());
|
||||||
|
|
||||||
// Add the copied amount to the current index
|
|
||||||
processed_frame_byte_index_ += copy_length;
|
|
||||||
|
|
||||||
// If the index has reached the limit of this processed frame, we can dispose of the frame now
|
// If the index has reached the limit of this processed frame, we can dispose of the frame now
|
||||||
if (processed_frame_byte_index_ == processed_frame_max_bytes_) {
|
av_frame_free(&processed_frame);
|
||||||
av_frame_free(&processed_frame_);
|
|
||||||
processed_frame_ = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return copy_length;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TempoProcessor::Close()
|
void TempoProcessor::Close()
|
||||||
@@ -253,11 +246,6 @@ void TempoProcessor::Close()
|
|||||||
filter_graph_ = nullptr;
|
filter_graph_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (processed_frame_) {
|
|
||||||
av_frame_free(&processed_frame_);
|
|
||||||
processed_frame_ = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffersrc_ctx_ = nullptr;
|
buffersrc_ctx_ = nullptr;
|
||||||
buffersink_ctx_ = nullptr;
|
buffersink_ctx_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,9 +52,11 @@ public:
|
|||||||
|
|
||||||
bool Open(const AudioParams& params, const double &speed);
|
bool Open(const AudioParams& params, const double &speed);
|
||||||
|
|
||||||
void Push(const char *data, int length);
|
void Push(const QByteArray &packed);
|
||||||
|
|
||||||
int Pull(char* data, int max_length);
|
void Flush();
|
||||||
|
|
||||||
|
QByteArray Pull();
|
||||||
|
|
||||||
void Close();
|
void Close();
|
||||||
|
|
||||||
@@ -67,10 +69,6 @@ private:
|
|||||||
|
|
||||||
AVFilterContext* buffersink_ctx_;
|
AVFilterContext* buffersink_ctx_;
|
||||||
|
|
||||||
AVFrame* processed_frame_;
|
|
||||||
int processed_frame_byte_index_;
|
|
||||||
int processed_frame_max_bytes_;
|
|
||||||
|
|
||||||
AudioParams params_;
|
AudioParams params_;
|
||||||
|
|
||||||
int64_t timestamp_;
|
int64_t timestamp_;
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ QString ExportCodec::GetCodecName(ExportCodec::Codec c)
|
|||||||
return tr("PNG");
|
return tr("PNG");
|
||||||
case kCodecProRes:
|
case kCodecProRes:
|
||||||
return tr("ProRes");
|
return tr("ProRes");
|
||||||
|
case kCodecCineform:
|
||||||
|
return tr("Cineform");
|
||||||
case kCodecTIFF:
|
case kCodecTIFF:
|
||||||
return tr("TIFF");
|
return tr("TIFF");
|
||||||
case kCodecMP2:
|
case kCodecMP2:
|
||||||
@@ -79,6 +81,7 @@ bool ExportCodec::IsCodecAStillImage(ExportCodec::Codec c)
|
|||||||
case kCodecH264rgb:
|
case kCodecH264rgb:
|
||||||
case kCodecH265:
|
case kCodecH265:
|
||||||
case kCodecProRes:
|
case kCodecProRes:
|
||||||
|
case kCodecCineform:
|
||||||
case kCodecMP2:
|
case kCodecMP2:
|
||||||
case kCodecMP3:
|
case kCodecMP3:
|
||||||
case kCodecAAC:
|
case kCodecAAC:
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ public:
|
|||||||
kCodecOpenEXR,
|
kCodecOpenEXR,
|
||||||
kCodecPNG,
|
kCodecPNG,
|
||||||
kCodecProRes,
|
kCodecProRes,
|
||||||
|
kCodecCineform,
|
||||||
kCodecTIFF,
|
kCodecTIFF,
|
||||||
kCodecVP9,
|
kCodecVP9,
|
||||||
|
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ QList<ExportCodec::Codec> ExportFormat::GetVideoCodecs(ExportFormat::Format f)
|
|||||||
case kFormatTIFF:
|
case kFormatTIFF:
|
||||||
return {ExportCodec::kCodecTIFF};
|
return {ExportCodec::kCodecTIFF};
|
||||||
case kFormatQuickTime:
|
case kFormatQuickTime:
|
||||||
return {ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb, ExportCodec::kCodecH265, ExportCodec::kCodecProRes};
|
return {ExportCodec::kCodecH264, ExportCodec::kCodecH264rgb, ExportCodec::kCodecH265, ExportCodec::kCodecProRes, ExportCodec::kCodecCineform};
|
||||||
case kFormatWebM:
|
case kFormatWebM:
|
||||||
return {ExportCodec::kCodecVP9};
|
return {ExportCodec::kCodecVP9};
|
||||||
case kFormatOgg:
|
case kFormatOgg:
|
||||||
|
|||||||
@@ -268,7 +268,7 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, const QAtomicIn
|
|||||||
AVStream* avstream = fmt_ctx->streams[i];
|
AVStream* avstream = fmt_ctx->streams[i];
|
||||||
|
|
||||||
// Find decoder for this stream, if it exists we can proceed
|
// Find decoder for this stream, if it exists we can proceed
|
||||||
AVCodec* decoder = avcodec_find_decoder(avstream->codecpar->codec_id);
|
const AVCodec* decoder = avcodec_find_decoder(avstream->codecpar->codec_id);
|
||||||
|
|
||||||
if (decoder
|
if (decoder
|
||||||
&& (avstream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO
|
&& (avstream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO
|
||||||
@@ -323,7 +323,7 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, const QAtomicIn
|
|||||||
int64_t new_dur;
|
int64_t new_dur;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
new_dur = frame->pts;
|
new_dur = frame->best_effort_timestamp;
|
||||||
} while (instance.GetFrame(pkt, frame) >= 0);
|
} while (instance.GetFrame(pkt, frame) >= 0);
|
||||||
|
|
||||||
avstream->duration = new_dur;
|
avstream->duration = new_dur;
|
||||||
@@ -388,7 +388,7 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, const QAtomicIn
|
|||||||
int64_t new_dur;
|
int64_t new_dur;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
new_dur = frame->pts;
|
new_dur = frame->best_effort_timestamp;
|
||||||
} while (instance.GetFrame(pkt, frame) >= 0);
|
} while (instance.GetFrame(pkt, frame) >= 0);
|
||||||
|
|
||||||
avstream->duration = new_dur;
|
avstream->duration = new_dur;
|
||||||
@@ -540,7 +540,7 @@ bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames, cons
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
SignalProcessingProgress(frame->pts, duration);
|
SignalProcessingProgress(frame->best_effort_timestamp, duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
wave_out.close();
|
wave_out.close();
|
||||||
@@ -733,7 +733,7 @@ FFmpegFramePool::ElementPtr FFmpegDecoder::RetrieveFrame(const rational& time, c
|
|||||||
if (still_seeking) {
|
if (still_seeking) {
|
||||||
// Handle a failure to seek (occurs on some media)
|
// Handle a failure to seek (occurs on some media)
|
||||||
// We'll only be here if the frame cache was emptied earlier
|
// 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_);
|
seek_ts = qMax(min_seek, seek_ts - second_ts_);
|
||||||
instance_.Seek(seek_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);
|
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
|
// 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
|
// Store frame before just in case
|
||||||
FFmpegFramePool::ElementPtr previous;
|
FFmpegFramePool::ElementPtr previous;
|
||||||
@@ -1010,7 +1010,7 @@ bool FFmpegDecoder::Instance::Open(const char *filename, int stream_index)
|
|||||||
avstream_ = fmt_ctx_->streams[stream_index];
|
avstream_ = fmt_ctx_->streams[stream_index];
|
||||||
|
|
||||||
// Find decoder
|
// Find decoder
|
||||||
AVCodec* codec = avcodec_find_decoder(avstream_->codecpar->codec_id);
|
const AVCodec* codec = avcodec_find_decoder(avstream_->codecpar->codec_id);
|
||||||
|
|
||||||
// Handle failure to find decoder
|
// Handle failure to find decoder
|
||||||
if (codec == nullptr) {
|
if (codec == nullptr) {
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ QStringList FFmpegEncoder::GetPixelFormatsForCodec(ExportCodec::Codec c) const
|
|||||||
{
|
{
|
||||||
QStringList pix_fmts;
|
QStringList pix_fmts;
|
||||||
|
|
||||||
AVCodec* codec_info = GetEncoder(c);
|
const AVCodec* codec_info = GetEncoder(c);
|
||||||
|
|
||||||
if (codec_info) {
|
if (codec_info) {
|
||||||
for (int i=0; codec_info->pix_fmts[i]!=-1; i++) {
|
for (int i=0; codec_info->pix_fmts[i]!=-1; i++) {
|
||||||
@@ -570,7 +570,7 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Find encoder
|
// Find encoder
|
||||||
AVCodec* encoder = GetEncoder(codec);
|
const AVCodec* encoder = GetEncoder(codec);
|
||||||
if (!encoder) {
|
if (!encoder) {
|
||||||
SetError(tr("Failed to find codec for 0x%1").arg(codec, 16));
|
SetError(tr("Failed to find codec for 0x%1").arg(codec, 16));
|
||||||
return false;
|
return false;
|
||||||
@@ -669,7 +669,7 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FFmpegEncoder::InitializeCodecContext(AVStream **stream, AVCodecContext **codec_ctx, AVCodec* codec)
|
bool FFmpegEncoder::InitializeCodecContext(AVStream **stream, AVCodecContext **codec_ctx, const AVCodec* codec)
|
||||||
{
|
{
|
||||||
*stream = avformat_new_stream(fmt_ctx_, nullptr);
|
*stream = avformat_new_stream(fmt_ctx_, nullptr);
|
||||||
if (!(*stream)) {
|
if (!(*stream)) {
|
||||||
@@ -687,7 +687,7 @@ bool FFmpegEncoder::InitializeCodecContext(AVStream **stream, AVCodecContext **c
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FFmpegEncoder::SetupCodecContext(AVStream* stream, AVCodecContext* codec_ctx, AVCodec* codec)
|
bool FFmpegEncoder::SetupCodecContext(AVStream* stream, AVCodecContext* codec_ctx, const AVCodec* codec)
|
||||||
{
|
{
|
||||||
int error_code;
|
int error_code;
|
||||||
|
|
||||||
@@ -833,7 +833,7 @@ bool FFmpegEncoder::InitializeResampleContext(SampleBufferPtr audio)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
AVCodec *FFmpegEncoder::GetEncoder(ExportCodec::Codec c)
|
const AVCodec *FFmpegEncoder::GetEncoder(ExportCodec::Codec c)
|
||||||
{
|
{
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case ExportCodec::kCodecH264:
|
case ExportCodec::kCodecH264:
|
||||||
@@ -844,6 +844,8 @@ AVCodec *FFmpegEncoder::GetEncoder(ExportCodec::Codec c)
|
|||||||
return avcodec_find_encoder(AV_CODEC_ID_DNXHD);
|
return avcodec_find_encoder(AV_CODEC_ID_DNXHD);
|
||||||
case ExportCodec::kCodecProRes:
|
case ExportCodec::kCodecProRes:
|
||||||
return avcodec_find_encoder(AV_CODEC_ID_PRORES);
|
return avcodec_find_encoder(AV_CODEC_ID_PRORES);
|
||||||
|
case ExportCodec::kCodecCineform:
|
||||||
|
return avcodec_find_encoder(AV_CODEC_ID_CFHD);
|
||||||
case ExportCodec::kCodecH265:
|
case ExportCodec::kCodecH265:
|
||||||
return avcodec_find_encoder(AV_CODEC_ID_HEVC);
|
return avcodec_find_encoder(AV_CODEC_ID_HEVC);
|
||||||
case ExportCodec::kCodecVP9:
|
case ExportCodec::kCodecVP9:
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#define FFMPEGENCODER_H
|
#define FFMPEGENCODER_H
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
#include <libavcodec/avcodec.h>
|
||||||
#include <libavformat/avformat.h>
|
#include <libavformat/avformat.h>
|
||||||
#include <libswscale/swscale.h>
|
#include <libswscale/swscale.h>
|
||||||
#include <libswresample/swresample.h>
|
#include <libswresample/swresample.h>
|
||||||
@@ -69,15 +70,15 @@ private:
|
|||||||
bool WriteAVFrame(AVFrame* frame, AVCodecContext *codec_ctx, AVStream *stream);
|
bool WriteAVFrame(AVFrame* frame, AVCodecContext *codec_ctx, AVStream *stream);
|
||||||
|
|
||||||
bool InitializeStream(enum AVMediaType type, AVStream** stream, AVCodecContext** codec_ctx, const ExportCodec::Codec &codec);
|
bool InitializeStream(enum AVMediaType type, AVStream** stream, AVCodecContext** codec_ctx, const ExportCodec::Codec &codec);
|
||||||
bool InitializeCodecContext(AVStream** stream, AVCodecContext** codec_ctx, AVCodec* codec);
|
bool InitializeCodecContext(AVStream** stream, AVCodecContext** codec_ctx, const AVCodec* codec);
|
||||||
bool SetupCodecContext(AVStream *stream, AVCodecContext *codec_ctx, AVCodec *codec);
|
bool SetupCodecContext(AVStream *stream, AVCodecContext *codec_ctx, const AVCodec *codec);
|
||||||
|
|
||||||
void FlushEncoders();
|
void FlushEncoders();
|
||||||
void FlushCodecCtx(AVCodecContext* codec_ctx, AVStream *stream);
|
void FlushCodecCtx(AVCodecContext* codec_ctx, AVStream *stream);
|
||||||
|
|
||||||
bool InitializeResampleContext(SampleBufferPtr audio);
|
bool InitializeResampleContext(SampleBufferPtr audio);
|
||||||
|
|
||||||
static AVCodec *GetEncoder(ExportCodec::Codec c);
|
static const AVCodec *GetEncoder(ExportCodec::Codec c);
|
||||||
|
|
||||||
AVFormatContext* fmt_ctx_;
|
AVFormatContext* fmt_ctx_;
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ set(OLIVE_SOURCES
|
|||||||
common/flipmodifiers.cpp
|
common/flipmodifiers.cpp
|
||||||
common/flipmodifiers.h
|
common/flipmodifiers.h
|
||||||
common/functiontimer.h
|
common/functiontimer.h
|
||||||
|
common/html.cpp
|
||||||
|
common/html.h
|
||||||
common/jobtime.cpp
|
common/jobtime.cpp
|
||||||
common/jobtime.h
|
common/jobtime.h
|
||||||
common/lerp.h
|
common/lerp.h
|
||||||
@@ -61,6 +63,7 @@ set(OLIVE_SOURCES
|
|||||||
common/timerange.cpp
|
common/timerange.cpp
|
||||||
common/timerange.h
|
common/timerange.h
|
||||||
common/tohex.h
|
common/tohex.h
|
||||||
|
common/util.h
|
||||||
common/xmlutils.cpp
|
common/xmlutils.cpp
|
||||||
common/xmlutils.h
|
common/xmlutils.h
|
||||||
PARENT_SCOPE
|
PARENT_SCOPE
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#define FFMPEGABSTRACTION_H
|
#define FFMPEGABSTRACTION_H
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
#include <libavcodec/avcodec.h>
|
||||||
#include <libavformat/avformat.h>
|
#include <libavformat/avformat.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -139,7 +139,10 @@ void FileFunctions::CopyDirectory(const QString &source, const QString &dest, bo
|
|||||||
} else {
|
} else {
|
||||||
// Copy file
|
// Copy file
|
||||||
if (overwrite && QFile::exists(dest_file_path)) {
|
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);
|
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
|
// Return whether the directory exists, or whether it could be created if it doesn't
|
||||||
if (dir.isEmpty()) {
|
return d.exists() || d.mkpath(QStringLiteral("."));
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString FileFunctions::EnsureFilenameExtension(QString fn, const QString &extension)
|
QString FileFunctions::EnsureFilenameExtension(QString fn, const QString &extension)
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#ifndef FILEFUNCTIONS_H
|
#ifndef FILEFUNCTIONS_H
|
||||||
#define FILEFUNCTIONS_H
|
#define FILEFUNCTIONS_H
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
#include "common/define.h"
|
#include "common/define.h"
|
||||||
@@ -52,7 +53,7 @@ public:
|
|||||||
|
|
||||||
static void CopyDirectory(const QString& source, const QString& dest, bool overwrite = false);
|
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
|
* @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;
|
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)
|
int QtUtils::MessageBox(QWidget *parent, QMessageBox::Icon icon, const QString &title, const QString &message, QMessageBox::StandardButtons buttons)
|
||||||
{
|
{
|
||||||
QMessageBox b(parent);
|
QMessageBox b(parent);
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ public:
|
|||||||
|
|
||||||
static QFrame* CreateHorizontalLine();
|
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 int MessageBox(QWidget *parent, QMessageBox::Icon icon, const QString& title, const QString& message, QMessageBox::StandardButtons buttons = QMessageBox::Ok);
|
||||||
|
|
||||||
static QDateTime GetCreationDate(const QFileInfo &info);
|
static QDateTime GetCreationDate(const QFileInfo &info);
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ void rational::fix_signs()
|
|||||||
|
|
||||||
void rational::reduce()
|
void rational::reduce()
|
||||||
{
|
{
|
||||||
if (!isNull()) {
|
if (!isNull() && denom_ != 1) {
|
||||||
// Euclidean often fails if numbers are negative, we abs it and re-neg it later if necessary
|
// Euclidean often fails if numbers are negative, we abs it and re-neg it later if necessary
|
||||||
bool neg = numer_ < 0;
|
bool neg = numer_ < 0;
|
||||||
|
|
||||||
@@ -186,6 +186,8 @@ const rational& rational::operator=(const rational &rhs)
|
|||||||
|
|
||||||
const rational& rational::operator+=(const rational &rhs)
|
const rational& rational::operator+=(const rational &rhs)
|
||||||
{
|
{
|
||||||
|
Q_ASSERT(*this != RATIONAL_MIN && *this != RATIONAL_MAX && rhs != RATIONAL_MIN && rhs != RATIONAL_MAX);
|
||||||
|
|
||||||
if (!isNaN()) {
|
if (!isNaN()) {
|
||||||
if (rhs.isNaN()) {
|
if (rhs.isNaN()) {
|
||||||
// Set to NaN
|
// Set to NaN
|
||||||
@@ -209,6 +211,8 @@ const rational& rational::operator+=(const rational &rhs)
|
|||||||
|
|
||||||
const rational& rational::operator-=(const rational &rhs)
|
const rational& rational::operator-=(const rational &rhs)
|
||||||
{
|
{
|
||||||
|
Q_ASSERT(*this != RATIONAL_MIN && *this != RATIONAL_MAX && rhs != RATIONAL_MIN && rhs != RATIONAL_MAX);
|
||||||
|
|
||||||
if (!isNaN()) {
|
if (!isNaN()) {
|
||||||
if (rhs.isNaN()) {
|
if (rhs.isNaN()) {
|
||||||
// Set to NaN
|
// Set to NaN
|
||||||
@@ -232,6 +236,8 @@ const rational& rational::operator-=(const rational &rhs)
|
|||||||
|
|
||||||
const rational& rational::operator/=(const rational &rhs)
|
const rational& rational::operator/=(const rational &rhs)
|
||||||
{
|
{
|
||||||
|
Q_ASSERT(*this != RATIONAL_MIN && *this != RATIONAL_MAX && rhs != RATIONAL_MIN && rhs != RATIONAL_MAX);
|
||||||
|
|
||||||
if (!isNaN()) {
|
if (!isNaN()) {
|
||||||
if (rhs.isNaN()) {
|
if (rhs.isNaN()) {
|
||||||
// Set to NaN
|
// Set to NaN
|
||||||
@@ -250,6 +256,8 @@ const rational& rational::operator/=(const rational &rhs)
|
|||||||
|
|
||||||
const rational& rational::operator*=(const rational &rhs)
|
const rational& rational::operator*=(const rational &rhs)
|
||||||
{
|
{
|
||||||
|
Q_ASSERT(*this != RATIONAL_MIN && *this != RATIONAL_MAX && rhs != RATIONAL_MIN && rhs != RATIONAL_MAX);
|
||||||
|
|
||||||
if (!isNaN()) {
|
if (!isNaN()) {
|
||||||
if (rhs.isNaN()) {
|
if (rhs.isNaN()) {
|
||||||
denom_ = 0;
|
denom_ = 0;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//Copyright 2015 Adam Quintero
|
//Copyright 2015 Adam Quintero
|
||||||
//This program is distributed under the terms of the GNU General Public License.
|
//This program is distributed under the terms of the GNU General Public License.
|
||||||
|
|
||||||
// Adapted by MattKC for the Olive Video Editor (2019)
|
// Adapted by MattKC for the Olive Video Editor (2019-2022)
|
||||||
|
|
||||||
#ifndef RATIONAL_H
|
#ifndef RATIONAL_H
|
||||||
#define RATIONAL_H
|
#define RATIONAL_H
|
||||||
|
|||||||
@@ -157,10 +157,12 @@ int64_t Timecode::timecode_to_timestamp(const QString &timecode, const rational
|
|||||||
// Convert values to integers
|
// Convert values to integers
|
||||||
QList<int64_t> timecode_numbers;
|
QList<int64_t> timecode_numbers;
|
||||||
|
|
||||||
|
bool negative = timecode.trimmed().startsWith('-');
|
||||||
|
|
||||||
foreach (const QString& element, timecode_split) {
|
foreach (const QString& element, timecode_split) {
|
||||||
valid = true;
|
valid = true;
|
||||||
|
|
||||||
timecode_numbers.append((element.isEmpty()) ? 0 : element.toLong(&valid));
|
timecode_numbers.append((element.isEmpty()) ? 0 : qAbs(element.toLong(&valid)));
|
||||||
|
|
||||||
// If element cannot be converted to a number,
|
// If element cannot be converted to a number,
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
@@ -203,6 +205,9 @@ int64_t Timecode::timecode_to_timestamp(const QString &timecode, const rational
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ok) *ok = true;
|
if (ok) *ok = true;
|
||||||
|
|
||||||
|
if (negative) timestamp = -timestamp;
|
||||||
|
|
||||||
return timestamp;
|
return timestamp;
|
||||||
}
|
}
|
||||||
case kMilliseconds:
|
case kMilliseconds:
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ const rational &TimeRange::out() const
|
|||||||
|
|
||||||
const rational &TimeRange::length() const
|
const rational &TimeRange::length() const
|
||||||
{
|
{
|
||||||
|
Q_ASSERT(!length_.isNaN());
|
||||||
return length_;
|
return length_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,7 +174,11 @@ void TimeRange::normalize()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Calculate length
|
// Calculate length
|
||||||
length_ = out_ - in_;
|
if (out_ == RATIONAL_MIN || out_ == RATIONAL_MAX || in_ == RATIONAL_MIN || in_ == RATIONAL_MAX) {
|
||||||
|
length_ = rational::NaN;
|
||||||
|
} else {
|
||||||
|
length_ = out_ - in_;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TimeRangeList::insert(const TimeRangeList &list_to_add)
|
void TimeRangeList::insert(const TimeRangeList &list_to_add)
|
||||||
|
|||||||
@@ -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_) {
|
foreach (Project* p, open_projects_) {
|
||||||
if (!p->has_autorecovery_been_saved()) {
|
if (!p->has_autorecovery_been_saved()) {
|
||||||
QDir project_autorecovery_dir(QDir(FileFunctions::GetAutoRecoveryRoot()).filePath(p->GetUuid().toString()));
|
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())));
|
QString this_autorecovery_path = project_autorecovery_dir.filePath(QStringLiteral("%1.ove").arg(QString::number(QDateTime::currentSecsSinceEpoch())));
|
||||||
|
|
||||||
SaveProjectInternal(p, this_autorecovery_path);
|
SaveProjectInternal(p, this_autorecovery_path);
|
||||||
|
|||||||
@@ -16,11 +16,15 @@
|
|||||||
|
|
||||||
set(OLIVE_SOURCES
|
set(OLIVE_SOURCES
|
||||||
${OLIVE_SOURCES}
|
${OLIVE_SOURCES}
|
||||||
dialog/export/codec/codecsection.h
|
dialog/export/codec/cineformsection.cpp
|
||||||
|
dialog/export/codec/cineformsection.h
|
||||||
dialog/export/codec/codecsection.cpp
|
dialog/export/codec/codecsection.cpp
|
||||||
dialog/export/codec/h264section.h
|
dialog/export/codec/codecsection.h
|
||||||
|
dialog/export/codec/codecstack.cpp
|
||||||
|
dialog/export/codec/codecstack.h
|
||||||
dialog/export/codec/h264section.cpp
|
dialog/export/codec/h264section.cpp
|
||||||
dialog/export/codec/imagesection.h
|
dialog/export/codec/h264section.h
|
||||||
dialog/export/codec/imagesection.cpp
|
dialog/export/codec/imagesection.cpp
|
||||||
|
dialog/export/codec/imagesection.h
|
||||||
PARENT_SCOPE
|
PARENT_SCOPE
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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/>.
|
||||||
|
|
||||||
|
***/
|
||||||
|
|
||||||
|
#include "cineformsection.h"
|
||||||
|
|
||||||
|
#include <QGridLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
|
||||||
|
namespace olive {
|
||||||
|
|
||||||
|
CineformSection::CineformSection(QWidget *parent) :
|
||||||
|
CodecSection(parent)
|
||||||
|
{
|
||||||
|
QGridLayout *layout = new QGridLayout(this);
|
||||||
|
|
||||||
|
layout->setMargin(0);
|
||||||
|
|
||||||
|
int row = 0;
|
||||||
|
|
||||||
|
layout->addWidget(new QLabel(tr("Quality:")), row, 0);
|
||||||
|
|
||||||
|
quality_combobox_ = new QComboBox();
|
||||||
|
|
||||||
|
/* Correspond to the following indexes for FFmpeg
|
||||||
|
*
|
||||||
|
* -quality <int> E..V....... set quality (from 0 to 12) (default film3+)
|
||||||
|
* film3+ 0 E..V.......
|
||||||
|
* film3 1 E..V.......
|
||||||
|
* film2+ 2 E..V.......
|
||||||
|
* film2 3 E..V.......
|
||||||
|
* film1.5 4 E..V.......
|
||||||
|
* film1+ 5 E..V.......
|
||||||
|
* film1 6 E..V.......
|
||||||
|
* high+ 7 E..V.......
|
||||||
|
* high 8 E..V.......
|
||||||
|
* medium+ 9 E..V.......
|
||||||
|
* medium 10 E..V.......
|
||||||
|
* low+ 11 E..V.......
|
||||||
|
* low 12 E..V.......
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
quality_combobox_->addItem(tr("Film Scan 3+"));
|
||||||
|
quality_combobox_->addItem(tr("Film Scan 3"));
|
||||||
|
quality_combobox_->addItem(tr("Film Scan 2+"));
|
||||||
|
quality_combobox_->addItem(tr("Film Scan 2"));
|
||||||
|
quality_combobox_->addItem(tr("Film Scan 1.5"));
|
||||||
|
quality_combobox_->addItem(tr("Film Scan 1+"));
|
||||||
|
quality_combobox_->addItem(tr("Film Scan 1"));
|
||||||
|
quality_combobox_->addItem(tr("High+"));
|
||||||
|
quality_combobox_->addItem(tr("High"));
|
||||||
|
quality_combobox_->addItem(tr("Medium+"));
|
||||||
|
quality_combobox_->addItem(tr("Medium"));
|
||||||
|
quality_combobox_->addItem(tr("Low+"));
|
||||||
|
quality_combobox_->addItem(tr("Low"));
|
||||||
|
|
||||||
|
// Default to "medium"
|
||||||
|
quality_combobox_->setCurrentIndex(10);
|
||||||
|
|
||||||
|
layout->addWidget(quality_combobox_, row, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CineformSection::AddOpts(EncodingParams *params)
|
||||||
|
{
|
||||||
|
params->set_video_option(QStringLiteral("quality"), QString::number(quality_combobox_->currentIndex()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
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 CINEFORMSECTION_H
|
||||||
|
#define CINEFORMSECTION_H
|
||||||
|
|
||||||
|
#include <QComboBox>
|
||||||
|
|
||||||
|
#include "codecsection.h"
|
||||||
|
|
||||||
|
namespace olive {
|
||||||
|
|
||||||
|
class CineformSection : public CodecSection
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
CineformSection(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
virtual void AddOpts(EncodingParams* params) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QComboBox *quality_combobox_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CINEFORMSECTION_H
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
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 "codecstack.h"
|
||||||
|
|
||||||
|
namespace olive {
|
||||||
|
|
||||||
|
#define super QStackedWidget
|
||||||
|
|
||||||
|
CodecStack::CodecStack(QWidget *parent)
|
||||||
|
: super{parent}
|
||||||
|
{
|
||||||
|
connect(this, &CodecStack::currentChanged, this, &CodecStack::OnChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodecStack::addWidget(QWidget *widget)
|
||||||
|
{
|
||||||
|
super::addWidget(widget);
|
||||||
|
|
||||||
|
OnChange(currentIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodecStack::OnChange(int index)
|
||||||
|
{
|
||||||
|
for (int i=0; i<count(); i++) {
|
||||||
|
if (i == index) {
|
||||||
|
widget(i)->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||||
|
} else {
|
||||||
|
widget(i)->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
|
||||||
|
}
|
||||||
|
widget(i)->adjustSize();
|
||||||
|
}
|
||||||
|
adjustSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
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 CODECSTACK_H
|
||||||
|
#define CODECSTACK_H
|
||||||
|
|
||||||
|
#include <QStackedWidget>
|
||||||
|
|
||||||
|
namespace olive {
|
||||||
|
|
||||||
|
class CodecStack : public QStackedWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit CodecStack(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
void addWidget(QWidget *widget);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void OnChange(int index);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CODECSTACK_H
|
||||||
@@ -42,6 +42,31 @@ H264Section::H264Section(int default_crf, QWidget *parent) :
|
|||||||
layout->setMargin(0);
|
layout->setMargin(0);
|
||||||
|
|
||||||
int row = 0;
|
int row = 0;
|
||||||
|
layout->addWidget(new QLabel(tr("Encode Speed:")), row, 0);
|
||||||
|
|
||||||
|
|
||||||
|
preset_combobox_ = new QComboBox();
|
||||||
|
preset_combobox_->setToolTip(tr("This setting allows you to tweak the ratio of export speed to compression quality. \n\n"
|
||||||
|
"If using Constant Rate Factor, slower speeds will result in smaller file sizes for the same quality. \n\n"
|
||||||
|
"If using Target Bit Rate or Target File Size, slower speeds will result in higher quality for the same bitrate/filesize. \n\n"
|
||||||
|
"This setting is equivalent to the `preset` setting in libx264."));
|
||||||
|
|
||||||
|
preset_combobox_->addItem(tr("Ultra Fast"));
|
||||||
|
preset_combobox_->addItem(tr("Super Fast"));
|
||||||
|
preset_combobox_->addItem(tr("Very Fast"));
|
||||||
|
preset_combobox_->addItem(tr("Faster"));
|
||||||
|
preset_combobox_->addItem(tr("Fast"));
|
||||||
|
preset_combobox_->addItem(tr("Medium"));
|
||||||
|
preset_combobox_->addItem(tr("Slow"));
|
||||||
|
preset_combobox_->addItem(tr("Slower"));
|
||||||
|
preset_combobox_->addItem(tr("Very Slow"));
|
||||||
|
|
||||||
|
//Default to "medium"
|
||||||
|
preset_combobox_->setCurrentIndex(5);
|
||||||
|
|
||||||
|
layout->addWidget(preset_combobox_, row, 1);
|
||||||
|
|
||||||
|
row++;
|
||||||
|
|
||||||
layout->addWidget(new QLabel(tr("Compression Method:")), row, 0);
|
layout->addWidget(new QLabel(tr("Compression Method:")), row, 0);
|
||||||
|
|
||||||
@@ -110,6 +135,8 @@ void H264Section::AddOpts(EncodingParams *params)
|
|||||||
params->set_video_buffer_size(2000000);
|
params->set_video_buffer_size(2000000);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
params->set_video_option(QStringLiteral("preset"), QString::number(preset_combobox_->currentIndex()));
|
||||||
}
|
}
|
||||||
|
|
||||||
H264CRFSection::H264CRFSection(int default_crf, QWidget *parent) :
|
H264CRFSection::H264CRFSection(int default_crf, QWidget *parent) :
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
#include <QSlider>
|
#include <QSlider>
|
||||||
#include <QStackedWidget>
|
#include <QStackedWidget>
|
||||||
|
#include <QComboBox>
|
||||||
|
|
||||||
#include "codecsection.h"
|
#include "codecsection.h"
|
||||||
#include "widget/slider/floatslider.h"
|
#include "widget/slider/floatslider.h"
|
||||||
@@ -111,6 +112,7 @@ private:
|
|||||||
|
|
||||||
H264FileSizeSection* filesize_section_;
|
H264FileSizeSection* filesize_section_;
|
||||||
|
|
||||||
|
QComboBox *preset_combobox_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class H265Section : public H264Section
|
class H265Section : public H264Section
|
||||||
|
|||||||
@@ -312,7 +312,8 @@ void ExportDialog::StartExport()
|
|||||||
QFileInfo dir_info(file_info.path());
|
QFileInfo dir_info(file_info.path());
|
||||||
|
|
||||||
// If the directory does not exist, try to create it
|
// 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"),
|
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. "
|
tr("The intended output directory doesn't exist and Olive couldn't create it. "
|
||||||
"Please choose a different filename."));
|
"Please choose a different filename."));
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ ExportAdvancedVideoDialog::ExportAdvancedVideoDialog(const QList<QString> &pix_f
|
|||||||
thread_slider_ = new IntegerSlider();
|
thread_slider_ = new IntegerSlider();
|
||||||
thread_slider_->SetMinimum(0);
|
thread_slider_->SetMinimum(0);
|
||||||
thread_slider_->SetDefaultValue(0);
|
thread_slider_->SetDefaultValue(0);
|
||||||
|
thread_slider_->InsertLabelSubstitution(0, tr("Auto"));
|
||||||
performance_layout->addWidget(thread_slider_, row, 1);
|
performance_layout->addWidget(thread_slider_, row, 1);
|
||||||
|
|
||||||
row++;
|
row++;
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ QWidget *ExportVideoTab::SetupCodecSection()
|
|||||||
|
|
||||||
row++;
|
row++;
|
||||||
|
|
||||||
codec_stack_ = new QStackedWidget();
|
codec_stack_ = new CodecStack();
|
||||||
codec_layout->addWidget(codec_stack_, row, 0, 1, 2);
|
codec_layout->addWidget(codec_stack_, row, 0, 1, 2);
|
||||||
|
|
||||||
image_section_ = new ImageSection();
|
image_section_ = new ImageSection();
|
||||||
@@ -185,6 +185,9 @@ QWidget *ExportVideoTab::SetupCodecSection()
|
|||||||
h265_section_ = new H265Section();
|
h265_section_ = new H265Section();
|
||||||
codec_stack_->addWidget(h265_section_);
|
codec_stack_->addWidget(h265_section_);
|
||||||
|
|
||||||
|
cineform_section_ = new CineformSection();
|
||||||
|
codec_stack_->addWidget(cineform_section_);
|
||||||
|
|
||||||
row++;
|
row++;
|
||||||
|
|
||||||
QPushButton* advanced_btn = new QPushButton(tr("Advanced"));
|
QPushButton* advanced_btn = new QPushButton(tr("Advanced"));
|
||||||
@@ -240,6 +243,9 @@ void ExportVideoTab::VideoCodecChanged()
|
|||||||
case ExportCodec::kCodecH265:
|
case ExportCodec::kCodecH265:
|
||||||
SetCodecSection(h265_section_);
|
SetCodecSection(h265_section_);
|
||||||
break;
|
break;
|
||||||
|
case ExportCodec::kCodecCineform:
|
||||||
|
SetCodecSection(cineform_section_);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
SetCodecSection(ExportCodec::IsCodecAStillImage(codec) ? image_section_ : nullptr);
|
SetCodecSection(ExportCodec::IsCodecAStillImage(codec) ? image_section_ : nullptr);
|
||||||
}
|
}
|
||||||
@@ -251,7 +257,6 @@ void ExportVideoTab::VideoCodecChanged()
|
|||||||
} else {
|
} else {
|
||||||
pix_fmt_.clear();
|
pix_fmt_.clear();
|
||||||
}
|
}
|
||||||
qDebug() << "Set default pix fmt" << pix_fmt_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportVideoTab::SetTime(const rational &time)
|
void ExportVideoTab::SetTime(const rational &time)
|
||||||
|
|||||||
@@ -26,6 +26,8 @@
|
|||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
#include "common/rational.h"
|
#include "common/rational.h"
|
||||||
|
#include "dialog/export/codec/cineformsection.h"
|
||||||
|
#include "dialog/export/codec/codecstack.h"
|
||||||
#include "dialog/export/codec/h264section.h"
|
#include "dialog/export/codec/h264section.h"
|
||||||
#include "dialog/export/codec/imagesection.h"
|
#include "dialog/export/codec/imagesection.h"
|
||||||
#include "node/color/colormanager/colormanager.h"
|
#include "node/color/colormanager/colormanager.h"
|
||||||
@@ -155,10 +157,11 @@ private:
|
|||||||
QCheckBox* maintain_aspect_checkbox_;
|
QCheckBox* maintain_aspect_checkbox_;
|
||||||
QComboBox* scaling_method_combobox_;
|
QComboBox* scaling_method_combobox_;
|
||||||
|
|
||||||
QStackedWidget* codec_stack_;
|
CodecStack* codec_stack_;
|
||||||
ImageSection* image_section_;
|
ImageSection* image_section_;
|
||||||
H264Section* h264_section_;
|
H264Section* h264_section_;
|
||||||
H264Section* h265_section_;
|
H264Section* h265_section_;
|
||||||
|
CineformSection *cineform_section_;
|
||||||
|
|
||||||
ColorSpaceChooser* color_space_chooser_;
|
ColorSpaceChooser* color_space_chooser_;
|
||||||
|
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ bool PreferencesDiskTab::Validate()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check validity of the new path
|
// Check validity of the new path
|
||||||
if (!FileFunctions::DirectoryIsValid(disk_cache_location_->text(), true)) {
|
if (!FileFunctions::DirectoryIsValid(disk_cache_location_->text())) {
|
||||||
QMessageBox::critical(this,
|
QMessageBox::critical(this,
|
||||||
tr("Disk Cache"),
|
tr("Disk Cache"),
|
||||||
tr("Failed to set disk cache location. Access was denied."));
|
tr("Failed to set disk cache location. Access was denied."));
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ void ProjectPropertiesDialog::accept()
|
|||||||
|
|
||||||
bool ProjectPropertiesDialog::VerifyPathAndWarnIfBad(const QString &path)
|
bool ProjectPropertiesDialog::VerifyPathAndWarnIfBad(const QString &path)
|
||||||
{
|
{
|
||||||
if (!FileFunctions::DirectoryIsValid(path, true)) {
|
if (!FileFunctions::DirectoryIsValid(path)) {
|
||||||
QMessageBox mb(this);
|
QMessageBox mb(this);
|
||||||
mb.setWindowModality(Qt::WindowModal);
|
mb.setWindowModality(Qt::WindowModal);
|
||||||
mb.setIcon(QMessageBox::Critical);
|
mb.setIcon(QMessageBox::Critical);
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
#include "common/filefunctions.h"
|
#include "common/filefunctions.h"
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
|
#include "render/audioparams.h"
|
||||||
#include "render/videoparams.h"
|
#include "render/videoparams.h"
|
||||||
#include "ui/icons/icons.h"
|
#include "ui/icons/icons.h"
|
||||||
#include "widget/menu/menu.h"
|
#include "widget/menu/menu.h"
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include <QDialogButtonBox>
|
#include <QDialogButtonBox>
|
||||||
#include <QGridLayout>
|
#include <QGridLayout>
|
||||||
|
#include <QGroupBox>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
@@ -39,58 +40,82 @@ SpeedDurationDialog::SpeedDurationDialog(const QVector<ClipBlock *> &clips, cons
|
|||||||
{
|
{
|
||||||
setWindowTitle(tr("Speed/Duration"));
|
setWindowTitle(tr("Speed/Duration"));
|
||||||
|
|
||||||
QGridLayout *layout = new QGridLayout(this);
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||||
|
|
||||||
int row = 0;
|
{
|
||||||
|
QGroupBox *speed_group = new QGroupBox();
|
||||||
|
layout->addWidget(speed_group);
|
||||||
|
|
||||||
layout->addWidget(new QLabel(tr("Speed:")), row, 0);
|
QGridLayout *speed_layout = new QGridLayout(speed_group);
|
||||||
|
|
||||||
speed_slider_ = new FloatSlider();
|
int row = 0;
|
||||||
speed_slider_->SetDisplayType(FloatSlider::kPercentage);
|
|
||||||
connect(speed_slider_, &FloatSlider::ValueChanged, this, &SpeedDurationDialog::SpeedChanged);
|
|
||||||
layout->addWidget(speed_slider_, row, 1);
|
|
||||||
|
|
||||||
row++;
|
speed_layout->addWidget(new QLabel(tr("Speed:")), row, 0);
|
||||||
|
|
||||||
layout->addWidget(new QLabel(tr("Duration:")), row, 0);
|
speed_slider_ = new FloatSlider();
|
||||||
|
speed_slider_->SetDisplayType(FloatSlider::kPercentage);
|
||||||
|
connect(speed_slider_, &FloatSlider::ValueChanged, this, &SpeedDurationDialog::SpeedChanged);
|
||||||
|
speed_layout->addWidget(speed_slider_, row, 1);
|
||||||
|
|
||||||
dur_slider_ = new RationalSlider();
|
row++;
|
||||||
dur_slider_->SetTimebase(timebase);
|
|
||||||
dur_slider_->SetDisplayType(RationalSlider::kTime);
|
|
||||||
connect(dur_slider_, &RationalSlider::ValueChanged, this, &SpeedDurationDialog::DurationChanged);
|
|
||||||
layout->addWidget(dur_slider_, row, 1);
|
|
||||||
|
|
||||||
row++;
|
speed_layout->addWidget(new QLabel(tr("Duration:")), row, 0);
|
||||||
|
|
||||||
link_box_ = new QCheckBox(tr("Link Speed and Duration"));
|
dur_slider_ = new RationalSlider();
|
||||||
link_box_->setChecked(true);
|
dur_slider_->SetTimebase(timebase);
|
||||||
layout->addWidget(link_box_, row, 0, 1, 2);
|
dur_slider_->SetDisplayType(RationalSlider::kTime);
|
||||||
|
connect(dur_slider_, &RationalSlider::ValueChanged, this, &SpeedDurationDialog::DurationChanged);
|
||||||
|
speed_layout->addWidget(dur_slider_, row, 1);
|
||||||
|
|
||||||
row++;
|
row++;
|
||||||
|
|
||||||
|
link_box_ = new QCheckBox(tr("Link Speed and Duration"));
|
||||||
|
link_box_->setChecked(true);
|
||||||
|
speed_layout->addWidget(link_box_, row, 0, 1, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
reverse_box_ = new QCheckBox(tr("Reverse"));
|
||||||
|
layout->addWidget(reverse_box_);
|
||||||
|
|
||||||
|
maintain_audio_pitch_box_ = new QCheckBox(tr("Maintain Audio Pitch"));
|
||||||
|
layout->addWidget(maintain_audio_pitch_box_);
|
||||||
|
|
||||||
ripple_box_ = new QCheckBox(tr("Ripple Trailing Clips"));
|
ripple_box_ = new QCheckBox(tr("Ripple Trailing Clips"));
|
||||||
layout->addWidget(ripple_box_, row, 0, 1, 2);
|
layout->addWidget(ripple_box_);
|
||||||
|
|
||||||
row++;
|
|
||||||
|
|
||||||
QDialogButtonBox *btns = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
QDialogButtonBox *btns = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||||
btns->setCenterButtons(true);
|
btns->setCenterButtons(true);
|
||||||
connect(btns, &QDialogButtonBox::accepted, this, &SpeedDurationDialog::accept);
|
connect(btns, &QDialogButtonBox::accepted, this, &SpeedDurationDialog::accept);
|
||||||
connect(btns, &QDialogButtonBox::rejected, this, &SpeedDurationDialog::reject);
|
connect(btns, &QDialogButtonBox::rejected, this, &SpeedDurationDialog::reject);
|
||||||
layout->addWidget(btns, row, 0, 1, 2);
|
layout->addWidget(btns);
|
||||||
|
|
||||||
// Determine which speed value to use
|
// Determine which speed value to use
|
||||||
start_speed_ = clips.first()->speed();
|
start_speed_ = clips.first()->speed();
|
||||||
start_duration_ = clips.first()->length();
|
start_duration_ = clips.first()->length();
|
||||||
|
start_reverse_ = clips.first()->reverse();
|
||||||
|
start_maintain_audio_pitch_ = clips.first()->maintain_audio_pitch();
|
||||||
for (int i=1; i<clips.size(); i++) {
|
for (int i=1; i<clips.size(); i++) {
|
||||||
if (!qIsNaN(start_speed_) && !qFuzzyCompare(start_speed_, clips.at(i)->speed())) {
|
ClipBlock *c = clips.at(i);
|
||||||
|
|
||||||
|
if (!qIsNaN(start_speed_) && !qFuzzyCompare(start_speed_, c->speed())) {
|
||||||
// Speed differs per clip
|
// Speed differs per clip
|
||||||
start_speed_ = qSNaN();
|
start_speed_ = qSNaN();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (start_duration_ != -1 && clips.at(i)->length() != start_duration_) {
|
if (start_duration_ != -1 && c->length() != start_duration_) {
|
||||||
start_duration_ = -1;
|
start_duration_ = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Yes, in theory a bool should only ever be 0 or 1 anyway, but MSVC complained and it is
|
||||||
|
// *possible* that a bool could be something else, so this code is safer
|
||||||
|
int clip_reverse = c->reverse() ? 1 : 0;
|
||||||
|
int clip_maintain_pitch = c->maintain_audio_pitch() ? 1 : 0;
|
||||||
|
if (start_reverse_ != -1 && clip_reverse != start_reverse_) {
|
||||||
|
start_reverse_ = -1;
|
||||||
|
}
|
||||||
|
if (start_maintain_audio_pitch_ != -1 && clip_maintain_pitch != start_maintain_audio_pitch_) {
|
||||||
|
start_maintain_audio_pitch_ = -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (qIsNaN(start_speed_)) {
|
if (qIsNaN(start_speed_)) {
|
||||||
@@ -104,6 +129,18 @@ SpeedDurationDialog::SpeedDurationDialog(const QVector<ClipBlock *> &clips, cons
|
|||||||
} else {
|
} else {
|
||||||
dur_slider_->SetValue(start_duration_);
|
dur_slider_->SetValue(start_duration_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (start_reverse_ == -1) {
|
||||||
|
reverse_box_->setTristate();
|
||||||
|
} else {
|
||||||
|
reverse_box_->setChecked(start_reverse_);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start_maintain_audio_pitch_ == -1) {
|
||||||
|
maintain_audio_pitch_box_->setTristate();
|
||||||
|
} else {
|
||||||
|
maintain_audio_pitch_box_->setChecked(start_maintain_audio_pitch_);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpeedDurationDialog::accept()
|
void SpeedDurationDialog::accept()
|
||||||
@@ -118,21 +155,6 @@ void SpeedDurationDialog::accept()
|
|||||||
|
|
||||||
MultiUndoCommand *command = new MultiUndoCommand();
|
MultiUndoCommand *command = new MultiUndoCommand();
|
||||||
|
|
||||||
// Set speed values
|
|
||||||
if (speed_slider_->IsTristate()) {
|
|
||||||
if (link_box_->isChecked() && !dur_slider_->IsTristate()) {
|
|
||||||
// Automatically determine speed from duration
|
|
||||||
foreach (ClipBlock *c, clips_) {
|
|
||||||
command->add_child(new NodeParamSetStandardValueCommand(NodeKeyframeTrackReference(NodeInput(c, ClipBlock::kSpeedInput)), GetSpeedAdjustment(c->speed(), c->length(), dur_slider_->GetValue())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Set speeds to value of slider
|
|
||||||
foreach (ClipBlock *c, clips_) {
|
|
||||||
command->add_child(new NodeParamSetStandardValueCommand(NodeKeyframeTrackReference(NodeInput(c, ClipBlock::kSpeedInput)), speed_slider_->GetValue()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set duration values
|
// Set duration values
|
||||||
foreach (ClipBlock *c, clips_) {
|
foreach (ClipBlock *c, clips_) {
|
||||||
rational proposed_length = c->length();
|
rational proposed_length = c->length();
|
||||||
@@ -161,6 +183,35 @@ void SpeedDurationDialog::accept()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set speed values
|
||||||
|
if (speed_slider_->IsTristate()) {
|
||||||
|
if (link_box_->isChecked() && !dur_slider_->IsTristate()) {
|
||||||
|
// Automatically determine speed from duration
|
||||||
|
foreach (ClipBlock *c, clips_) {
|
||||||
|
command->add_child(new NodeParamSetStandardValueCommand(NodeKeyframeTrackReference(NodeInput(c, ClipBlock::kSpeedInput)), GetSpeedAdjustment(c->speed(), c->length(), dur_slider_->GetValue())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Set speeds to value of slider
|
||||||
|
foreach (ClipBlock *c, clips_) {
|
||||||
|
command->add_child(new NodeParamSetStandardValueCommand(NodeKeyframeTrackReference(NodeInput(c, ClipBlock::kSpeedInput)), speed_slider_->GetValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set reverse values
|
||||||
|
if (!reverse_box_->isTristate()) {
|
||||||
|
foreach (ClipBlock *c, clips_) {
|
||||||
|
command->add_child(new NodeParamSetStandardValueCommand(NodeKeyframeTrackReference(NodeInput(c, ClipBlock::kReverseInput)), reverse_box_->isChecked()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set reverse values
|
||||||
|
if (!maintain_audio_pitch_box_->isTristate()) {
|
||||||
|
foreach (ClipBlock *c, clips_) {
|
||||||
|
command->add_child(new NodeParamSetStandardValueCommand(NodeKeyframeTrackReference(NodeInput(c, ClipBlock::kMaintainAudioPitchInput)), maintain_audio_pitch_box_->isChecked()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Core::instance()->undo_stack()->push(command);
|
Core::instance()->undo_stack()->push(command);
|
||||||
|
|
||||||
super::accept();
|
super::accept();
|
||||||
|
|||||||
@@ -56,8 +56,16 @@ private:
|
|||||||
|
|
||||||
QCheckBox *link_box_;
|
QCheckBox *link_box_;
|
||||||
|
|
||||||
|
QCheckBox *reverse_box_;
|
||||||
|
|
||||||
|
QCheckBox *maintain_audio_pitch_box_;
|
||||||
|
|
||||||
QCheckBox *ripple_box_;
|
QCheckBox *ripple_box_;
|
||||||
|
|
||||||
|
int start_reverse_;
|
||||||
|
|
||||||
|
int start_maintain_audio_pitch_;
|
||||||
|
|
||||||
double start_speed_;
|
double start_speed_;
|
||||||
|
|
||||||
rational start_duration_;
|
rational start_duration_;
|
||||||
|
|||||||
+1
-2
@@ -190,9 +190,8 @@ int main(int argc, char *argv[])
|
|||||||
//
|
//
|
||||||
// https://bugreports.qt.io/browse/QTBUG-46140
|
// https://bugreports.qt.io/browse/QTBUG-46140
|
||||||
QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
|
QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
|
||||||
format.setVersion(2, 0);
|
format.setVersion(3, 2);
|
||||||
format.setProfile(QSurfaceFormat::CoreProfile);
|
format.setProfile(QSurfaceFormat::CoreProfile);
|
||||||
format.setOption(QSurfaceFormat::DeprecatedFunctions);
|
|
||||||
|
|
||||||
format.setDepthBufferSize(24);
|
format.setDepthBufferSize(24);
|
||||||
QSurfaceFormat::setDefaultFormat(format);
|
QSurfaceFormat::setDefaultFormat(format);
|
||||||
|
|||||||
@@ -21,8 +21,10 @@ add_subdirectory(distort)
|
|||||||
add_subdirectory(effect)
|
add_subdirectory(effect)
|
||||||
add_subdirectory(filter)
|
add_subdirectory(filter)
|
||||||
add_subdirectory(generator)
|
add_subdirectory(generator)
|
||||||
|
add_subdirectory(gizmo)
|
||||||
add_subdirectory(group)
|
add_subdirectory(group)
|
||||||
add_subdirectory(input)
|
add_subdirectory(input)
|
||||||
|
add_subdirectory(keying)
|
||||||
add_subdirectory(math)
|
add_subdirectory(math)
|
||||||
add_subdirectory(output)
|
add_subdirectory(output)
|
||||||
add_subdirectory(project)
|
add_subdirectory(project)
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ namespace olive {
|
|||||||
const QString PanNode::kSamplesInput = QStringLiteral("samples_in");
|
const QString PanNode::kSamplesInput = QStringLiteral("samples_in");
|
||||||
const QString PanNode::kPanningInput = QStringLiteral("panning_in");
|
const QString PanNode::kPanningInput = QStringLiteral("panning_in");
|
||||||
|
|
||||||
|
#define super Node
|
||||||
|
|
||||||
PanNode::PanNode()
|
PanNode::PanNode()
|
||||||
{
|
{
|
||||||
AddInput(kSamplesInput, NodeValue::kSamples, InputFlags(kInputFlagNotKeyframable));
|
AddInput(kSamplesInput, NodeValue::kSamples, InputFlags(kInputFlagNotKeyframable));
|
||||||
@@ -35,6 +37,9 @@ PanNode::PanNode()
|
|||||||
SetInputProperty(kPanningInput, QStringLiteral("min"), -1.0);
|
SetInputProperty(kPanningInput, QStringLiteral("min"), -1.0);
|
||||||
SetInputProperty(kPanningInput, QStringLiteral("max"), 1.0);
|
SetInputProperty(kPanningInput, QStringLiteral("max"), 1.0);
|
||||||
SetInputProperty(kPanningInput, QStringLiteral("view"), FloatSlider::kPercentage);
|
SetInputProperty(kPanningInput, QStringLiteral("view"), FloatSlider::kPercentage);
|
||||||
|
|
||||||
|
SetFlags(kAudioEffect);
|
||||||
|
SetEffectInput(kSamplesInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
Node *PanNode::copy() const
|
Node *PanNode::copy() const
|
||||||
@@ -54,7 +59,7 @@ QString PanNode::id() const
|
|||||||
|
|
||||||
QVector<Node::CategoryID> PanNode::Category() const
|
QVector<Node::CategoryID> PanNode::Category() const
|
||||||
{
|
{
|
||||||
return {kCategoryChannels};
|
return {kCategoryFilter};
|
||||||
}
|
}
|
||||||
|
|
||||||
QString PanNode::Description() const
|
QString PanNode::Description() const
|
||||||
@@ -115,6 +120,8 @@ void PanNode::ProcessSamples(const NodeValueRow &values, const SampleBufferPtr i
|
|||||||
|
|
||||||
void PanNode::Retranslate()
|
void PanNode::Retranslate()
|
||||||
{
|
{
|
||||||
|
super::Retranslate();
|
||||||
|
|
||||||
SetInputName(kSamplesInput, tr("Samples"));
|
SetInputName(kSamplesInput, tr("Samples"));
|
||||||
SetInputName(kPanningInput, tr("Pan"));
|
SetInputName(kPanningInput, tr("Pan"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ namespace olive {
|
|||||||
const QString VolumeNode::kSamplesInput = QStringLiteral("samples_in");
|
const QString VolumeNode::kSamplesInput = QStringLiteral("samples_in");
|
||||||
const QString VolumeNode::kVolumeInput = QStringLiteral("volume_in");
|
const QString VolumeNode::kVolumeInput = QStringLiteral("volume_in");
|
||||||
|
|
||||||
|
#define super MathNodeBase
|
||||||
|
|
||||||
VolumeNode::VolumeNode()
|
VolumeNode::VolumeNode()
|
||||||
{
|
{
|
||||||
AddInput(kSamplesInput, NodeValue::kSamples, InputFlags(kInputFlagNotKeyframable));
|
AddInput(kSamplesInput, NodeValue::kSamples, InputFlags(kInputFlagNotKeyframable));
|
||||||
@@ -34,6 +36,9 @@ VolumeNode::VolumeNode()
|
|||||||
AddInput(kVolumeInput, NodeValue::kFloat, 1.0);
|
AddInput(kVolumeInput, NodeValue::kFloat, 1.0);
|
||||||
SetInputProperty(kVolumeInput, QStringLiteral("min"), 0.0);
|
SetInputProperty(kVolumeInput, QStringLiteral("min"), 0.0);
|
||||||
SetInputProperty(kVolumeInput, QStringLiteral("view"), FloatSlider::kDecibel);
|
SetInputProperty(kVolumeInput, QStringLiteral("view"), FloatSlider::kDecibel);
|
||||||
|
|
||||||
|
SetFlags(kAudioEffect);
|
||||||
|
SetEffectInput(kSamplesInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
Node *VolumeNode::copy() const
|
Node *VolumeNode::copy() const
|
||||||
@@ -80,6 +85,8 @@ void VolumeNode::ProcessSamples(const NodeValueRow &values, const SampleBufferPt
|
|||||||
|
|
||||||
void VolumeNode::Retranslate()
|
void VolumeNode::Retranslate()
|
||||||
{
|
{
|
||||||
|
super::Retranslate();
|
||||||
|
|
||||||
SetInputName(kSamplesInput, tr("Samples"));
|
SetInputName(kSamplesInput, tr("Samples"));
|
||||||
SetInputName(kVolumeInput, tr("Volume"));
|
SetInputName(kVolumeInput, tr("Volume"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ namespace olive {
|
|||||||
#define super Node
|
#define super Node
|
||||||
|
|
||||||
const QString Block::kLengthInput = QStringLiteral("length_in");
|
const QString Block::kLengthInput = QStringLiteral("length_in");
|
||||||
const QString Block::kEnabledInput = QStringLiteral("enabled_in");
|
|
||||||
|
|
||||||
Block::Block() :
|
Block::Block() :
|
||||||
previous_(nullptr),
|
previous_(nullptr),
|
||||||
@@ -46,8 +45,6 @@ Block::Block() :
|
|||||||
SetInputProperty(kLengthInput, QStringLiteral("viewlock"), true);
|
SetInputProperty(kLengthInput, QStringLiteral("viewlock"), true);
|
||||||
IgnoreHashingFrom(kLengthInput);
|
IgnoreHashingFrom(kLengthInput);
|
||||||
|
|
||||||
AddInput(kEnabledInput, NodeValue::kBoolean, true, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
|
||||||
|
|
||||||
SetFlags(kDontShowInParamView);
|
SetFlags(kDontShowInParamView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -118,7 +118,6 @@ public:
|
|||||||
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element = -1, InvalidateCacheOptions options = InvalidateCacheOptions()) override;
|
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element = -1, InvalidateCacheOptions options = InvalidateCacheOptions()) override;
|
||||||
|
|
||||||
static const QString kLengthInput;
|
static const QString kLengthInput;
|
||||||
static const QString kEnabledInput;
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ const QString ClipBlock::kBufferIn = QStringLiteral("buffer_in");
|
|||||||
const QString ClipBlock::kMediaInInput = QStringLiteral("media_in_in");
|
const QString ClipBlock::kMediaInInput = QStringLiteral("media_in_in");
|
||||||
const QString ClipBlock::kSpeedInput = QStringLiteral("speed_in");
|
const QString ClipBlock::kSpeedInput = QStringLiteral("speed_in");
|
||||||
const QString ClipBlock::kReverseInput = QStringLiteral("reverse_in");
|
const QString ClipBlock::kReverseInput = QStringLiteral("reverse_in");
|
||||||
|
const QString ClipBlock::kMaintainAudioPitchInput = QStringLiteral("maintain_audio_pitch_in");
|
||||||
|
|
||||||
ClipBlock::ClipBlock() :
|
ClipBlock::ClipBlock() :
|
||||||
in_transition_(nullptr),
|
in_transition_(nullptr),
|
||||||
@@ -52,8 +53,12 @@ ClipBlock::ClipBlock() :
|
|||||||
AddInput(kReverseInput, NodeValue::kBoolean, false, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
AddInput(kReverseInput, NodeValue::kBoolean, false, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
||||||
IgnoreHashingFrom(kReverseInput);
|
IgnoreHashingFrom(kReverseInput);
|
||||||
|
|
||||||
|
AddInput(kMaintainAudioPitchInput, NodeValue::kBoolean, false, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
||||||
|
|
||||||
PrependInput(kBufferIn, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable));
|
PrependInput(kBufferIn, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable));
|
||||||
SetValueHintForInput(kBufferIn, ValueHint(NodeValue::kBuffer));
|
SetValueHintForInput(kBufferIn, ValueHint(NodeValue::kBuffer));
|
||||||
|
|
||||||
|
SetEffectInput(kBufferIn);
|
||||||
}
|
}
|
||||||
|
|
||||||
Node *ClipBlock::copy() const
|
Node *ClipBlock::copy() const
|
||||||
@@ -92,7 +97,8 @@ void ClipBlock::set_length_and_media_out(const rational &length)
|
|||||||
|
|
||||||
if (reverse()) {
|
if (reverse()) {
|
||||||
// Calculate media_in adjustment
|
// 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);
|
super::set_length_and_media_out(length);
|
||||||
@@ -106,7 +112,14 @@ void ClipBlock::set_length_and_media_in(const rational &length)
|
|||||||
|
|
||||||
if (!reverse()) {
|
if (!reverse()) {
|
||||||
// Calculate media_in adjustment
|
// 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);
|
super::set_length_and_media_in(length);
|
||||||
@@ -122,7 +135,7 @@ void ClipBlock::set_media_in(const rational &media_in)
|
|||||||
SetStandardValue(kMediaInInput, QVariant::fromValue(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
|
// These constants are not considered "values" per se, so we don't modify them
|
||||||
if (sequence_time == RATIONAL_MIN || sequence_time == RATIONAL_MAX) {
|
if (sequence_time == RATIONAL_MIN || sequence_time == RATIONAL_MAX) {
|
||||||
@@ -131,19 +144,21 @@ rational ClipBlock::SequenceToMediaTime(const rational &sequence_time, bool igno
|
|||||||
|
|
||||||
rational media_time = sequence_time;
|
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) {
|
if (reverse() && !ignore_reverse) {
|
||||||
media_time = length() - media_time;
|
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();
|
media_time += media_in();
|
||||||
|
|
||||||
return media_time;
|
return media_time;
|
||||||
@@ -158,10 +173,6 @@ rational ClipBlock::MediaToSequenceTime(const rational &media_time) const
|
|||||||
|
|
||||||
rational sequence_time = media_time - media_in();
|
rational sequence_time = media_time - media_in();
|
||||||
|
|
||||||
if (reverse()) {
|
|
||||||
sequence_time = length() - sequence_time;
|
|
||||||
}
|
|
||||||
|
|
||||||
double speed_value = speed();
|
double speed_value = speed();
|
||||||
if (qIsNull(speed_value)) {
|
if (qIsNull(speed_value)) {
|
||||||
// I don't know what to return here yet...
|
// I don't know what to return here yet...
|
||||||
@@ -171,6 +182,10 @@ rational ClipBlock::MediaToSequenceTime(const rational &media_time) const
|
|||||||
sequence_time = rational::fromDouble(sequence_time.toDouble() / speed_value);
|
sequence_time = rational::fromDouble(sequence_time.toDouble() / speed_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (reverse()) {
|
||||||
|
sequence_time = length() - sequence_time;
|
||||||
|
}
|
||||||
|
|
||||||
return sequence_time;
|
return sequence_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,19 +234,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
|
TimeRange ClipBlock::InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const
|
||||||
{
|
{
|
||||||
Q_UNUSED(element)
|
Q_UNUSED(element)
|
||||||
|
|||||||
@@ -71,6 +71,21 @@ public:
|
|||||||
return GetStandardValue(kReverseInput).toBool();
|
return GetStandardValue(kReverseInput).toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_reverse(bool e)
|
||||||
|
{
|
||||||
|
SetStandardValue(kReverseInput, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool maintain_audio_pitch() const
|
||||||
|
{
|
||||||
|
return GetStandardValue(kMaintainAudioPitchInput).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_maintain_audio_pitch(bool e)
|
||||||
|
{
|
||||||
|
SetStandardValue(kMaintainAudioPitchInput, e);
|
||||||
|
}
|
||||||
|
|
||||||
TransitionBlock* in_transition()
|
TransitionBlock* in_transition()
|
||||||
{
|
{
|
||||||
return in_transition_;
|
return in_transition_;
|
||||||
@@ -110,16 +125,15 @@ public:
|
|||||||
static const QString kMediaInInput;
|
static const QString kMediaInInput;
|
||||||
static const QString kSpeedInput;
|
static const QString kSpeedInput;
|
||||||
static const QString kReverseInput;
|
static const QString kReverseInput;
|
||||||
|
static const QString kMaintainAudioPitchInput;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void LinkChangeEvent() override;
|
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;
|
virtual void Hash(QCryptographicHash &hash, const NodeGlobals &globals, const VideoParams& video_params) const override;
|
||||||
|
|
||||||
private:
|
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;
|
rational MediaToSequenceTime(const rational& media_time) const;
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ const QString ColorManager::kConfigFilenameIn = QStringLiteral("config");
|
|||||||
const QString ColorManager::kDefaultColorspaceIn = QStringLiteral("default_input");
|
const QString ColorManager::kDefaultColorspaceIn = QStringLiteral("default_input");
|
||||||
const QString ColorManager::kReferenceSpaceIn = QStringLiteral("reference_space");
|
const QString ColorManager::kReferenceSpaceIn = QStringLiteral("reference_space");
|
||||||
|
|
||||||
|
#define super Node
|
||||||
|
|
||||||
OCIO::ConstConfigRcPtr ColorManager::default_config_ = nullptr;
|
OCIO::ConstConfigRcPtr ColorManager::default_config_ = nullptr;
|
||||||
|
|
||||||
ColorManager::ColorManager() :
|
ColorManager::ColorManager() :
|
||||||
@@ -50,7 +52,7 @@ ColorManager::ColorManager() :
|
|||||||
|
|
||||||
// Set config to our built-in default
|
// Set config to our built-in default
|
||||||
SetConfig(GetDefaultConfig());
|
SetConfig(GetDefaultConfig());
|
||||||
SetDefaultInputColorSpace(QStringLiteral("sRGB OETF"));
|
SetDefaultInputColorSpace(config_->getCanonicalName(OCIO::ROLE_DEFAULT));
|
||||||
}
|
}
|
||||||
|
|
||||||
OCIO::ConstConfigRcPtr ColorManager::GetConfig() const
|
OCIO::ConstConfigRcPtr ColorManager::GetConfig() const
|
||||||
@@ -253,6 +255,8 @@ void ColorManager::GetDefaultLumaCoefs(double *rgb) const
|
|||||||
|
|
||||||
void ColorManager::Retranslate()
|
void ColorManager::Retranslate()
|
||||||
{
|
{
|
||||||
|
super::Retranslate();
|
||||||
|
|
||||||
SetInputName(kConfigFilenameIn, tr("Configuration"));
|
SetInputName(kConfigFilenameIn, tr("Configuration"));
|
||||||
SetInputName(kDefaultColorspaceIn, tr("Default Input"));
|
SetInputName(kDefaultColorspaceIn, tr("Default Input"));
|
||||||
SetInputName(kReferenceSpaceIn, tr("Reference Space"));
|
SetInputName(kReferenceSpaceIn, tr("Reference Space"));
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
add_subdirectory(cornerpin)
|
||||||
add_subdirectory(crop)
|
add_subdirectory(crop)
|
||||||
add_subdirectory(flip)
|
add_subdirectory(flip)
|
||||||
add_subdirectory(transform)
|
add_subdirectory(transform)
|
||||||
|
|||||||
@@ -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/distort/cornerpin/cornerpindistortnode.cpp
|
||||||
|
node/distort/cornerpin/cornerpindistortnode.h
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
@@ -0,0 +1,179 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
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 "cornerpindistortnode.h"
|
||||||
|
|
||||||
|
#include "common/lerp.h"
|
||||||
|
#include "core.h"
|
||||||
|
#include "widget/slider/floatslider.h"
|
||||||
|
|
||||||
|
namespace olive {
|
||||||
|
|
||||||
|
const QString CornerPinDistortNode::kTextureInput = QStringLiteral("tex_in");
|
||||||
|
const QString CornerPinDistortNode::kTopLeftInput = QStringLiteral("top_left_in");
|
||||||
|
const QString CornerPinDistortNode::kTopRightInput = QStringLiteral("top_right_in");
|
||||||
|
const QString CornerPinDistortNode::kBottomRightInput = QStringLiteral("bottom_right_in");
|
||||||
|
const QString CornerPinDistortNode::kBottomLeftInput = QStringLiteral("bottom_left_in");
|
||||||
|
const QString CornerPinDistortNode::kPerspectiveInput = QStringLiteral("perspective_in");
|
||||||
|
|
||||||
|
#define super Node
|
||||||
|
|
||||||
|
CornerPinDistortNode::CornerPinDistortNode()
|
||||||
|
{
|
||||||
|
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||||
|
AddInput(kPerspectiveInput, NodeValue::kBoolean, true);
|
||||||
|
AddInput(kTopLeftInput, NodeValue::kVec2, QVector2D(0.0, 0.0));
|
||||||
|
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)});
|
||||||
|
|
||||||
|
SetFlags(kVideoEffect);
|
||||||
|
SetEffectInput(kTextureInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CornerPinDistortNode::Retranslate()
|
||||||
|
{
|
||||||
|
super::Retranslate();
|
||||||
|
|
||||||
|
SetInputName(kTextureInput, tr("Texture"));
|
||||||
|
SetInputName(kPerspectiveInput, tr("Perspective"));
|
||||||
|
SetInputName(kTopLeftInput, tr("Top Left"));
|
||||||
|
SetInputName(kTopRightInput, tr("Top Right"));
|
||||||
|
SetInputName(kBottomRightInput, tr("Bottom Right"));
|
||||||
|
SetInputName(kBottomLeftInput, tr("Bottom Left"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CornerPinDistortNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||||
|
{
|
||||||
|
ShaderJob job;
|
||||||
|
job.InsertValue(value);
|
||||||
|
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||||
|
job.InsertValue(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, globals.resolution(), this));
|
||||||
|
|
||||||
|
// Convert slider values to their pixel values and then convert to clip space (-1.0 ... 1.0) for overriding the
|
||||||
|
// vertex coordinates.
|
||||||
|
const QVector2D &resolution = globals.resolution();
|
||||||
|
QVector2D half_resolution = resolution * 0.5;
|
||||||
|
QVector2D top_left = QVector2D(ValueToPixel(0, value, resolution)) / half_resolution - QVector2D(1.0, 1.0);
|
||||||
|
QVector2D top_right = QVector2D(ValueToPixel(1, value, resolution)) / half_resolution - QVector2D(1.0, 1.0);
|
||||||
|
QVector2D bottom_right = QVector2D(ValueToPixel(2, value, resolution)) / half_resolution - QVector2D(1.0, 1.0);
|
||||||
|
QVector2D bottom_left = QVector2D(ValueToPixel(3, value, resolution)) / half_resolution - QVector2D(1.0, 1.0);
|
||||||
|
|
||||||
|
// Override default vertex coordinates.
|
||||||
|
QVector<float> adjusted_vertices = {top_left.x(), top_left.y(), 0.0f,
|
||||||
|
top_right.x(), top_right.y(), 0.0f,
|
||||||
|
bottom_right.x(), bottom_right.y(), 0.0f,
|
||||||
|
|
||||||
|
top_left.x(), top_left.y(), 0.0f,
|
||||||
|
bottom_left.x(), bottom_left.y(), 0.0f,
|
||||||
|
bottom_right.x(), bottom_right.y(), 0.0f};
|
||||||
|
job.SetVertexCoordinates(adjusted_vertices);
|
||||||
|
|
||||||
|
// If no texture do nothing
|
||||||
|
if (!job.GetValue(kTextureInput).data().isNull()) {
|
||||||
|
// In the special case that all sliders are in their default position just
|
||||||
|
// push the texture.
|
||||||
|
if (!(job.GetValue(kTopLeftInput).data().value<QVector2D>().isNull()
|
||||||
|
&& job.GetValue(kTopRightInput).data().value<QVector2D>().isNull() &&
|
||||||
|
job.GetValue(kBottomRightInput).data().value<QVector2D>().isNull() &&
|
||||||
|
job.GetValue(kBottomLeftInput).data().value<QVector2D>().isNull())) {
|
||||||
|
table->Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||||
|
} else {
|
||||||
|
table->Push(NodeValue::kTexture, job.GetValue(kTextureInput).data(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderCode CornerPinDistortNode::GetShaderCode(const QString &shader_id) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(shader_id)
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
Q_ASSERT(value >= 0 && value <= 3);
|
||||||
|
switch (value) {
|
||||||
|
case 0: // Top left
|
||||||
|
return QPointF(row[kTopLeftInput].data().value<QVector2D>().x(),
|
||||||
|
row[kTopLeftInput].data().value<QVector2D>().y());
|
||||||
|
break;
|
||||||
|
case 1: // Top right
|
||||||
|
return QPointF(resolution.x() + row[kTopRightInput].data().value<QVector2D>().x(),
|
||||||
|
row[kTopRightInput].data().value<QVector2D>().y());
|
||||||
|
break;
|
||||||
|
case 2: // Bottom right
|
||||||
|
return QPointF(resolution.x() + row[kBottomRightInput].data().value<QVector2D>().x(),
|
||||||
|
resolution.y() + row[kBottomRightInput].data().value<QVector2D>().y());
|
||||||
|
break;
|
||||||
|
case 3: //Bottom left
|
||||||
|
return QPointF(row[kBottomLeftInput].data().value<QVector2D>().x(),
|
||||||
|
row[kBottomLeftInput].data().value<QVector2D>().y() + resolution.y());
|
||||||
|
break;
|
||||||
|
default: // We should never get here
|
||||||
|
return QPointF();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
QPointF top_left = ValueToPixel(0, row, resolution);
|
||||||
|
QPointF top_right = ValueToPixel(1, row, resolution);
|
||||||
|
QPointF bottom_right = ValueToPixel(2, row, resolution);
|
||||||
|
QPointF bottom_left = ValueToPixel(3, row, resolution);
|
||||||
|
|
||||||
|
// Add the correct offset to each slider
|
||||||
|
SetInputProperty(kTopLeftInput, QStringLiteral("offset"), QVector2D(0.0, 0.0));
|
||||||
|
SetInputProperty(kTopRightInput, QStringLiteral("offset"), QVector2D(resolution.x() , 0.0));
|
||||||
|
SetInputProperty(kBottomRightInput, QStringLiteral("offset"), resolution);
|
||||||
|
SetInputProperty(kBottomLeftInput, QStringLiteral("offset"), QVector2D(0.0, resolution.y()));
|
||||||
|
|
||||||
|
// Draw bounding box
|
||||||
|
gizmo_whole_rect_->SetPolygon(QPolygonF({top_left, top_right, bottom_right, bottom_left, top_left}));
|
||||||
|
|
||||||
|
// Create handles
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
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 CORNERPINDISTORTNODE_H
|
||||||
|
#define CORNERPINDISTORTNODE_H
|
||||||
|
|
||||||
|
#include <QVector2D>
|
||||||
|
|
||||||
|
#include "node/gizmo/point.h"
|
||||||
|
#include "node/gizmo/polygon.h"
|
||||||
|
#include "node/inputdragger.h"
|
||||||
|
#include "node/node.h"
|
||||||
|
|
||||||
|
namespace olive {
|
||||||
|
class CornerPinDistortNode : public Node
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
CornerPinDistortNode();
|
||||||
|
|
||||||
|
NODE_DEFAULT_DESTRUCTOR(CornerPinDistortNode)
|
||||||
|
|
||||||
|
virtual Node* copy() const override
|
||||||
|
{
|
||||||
|
return new CornerPinDistortNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual QString Name() const override
|
||||||
|
{
|
||||||
|
return tr("Corner Pin");
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual QString id() const override
|
||||||
|
{
|
||||||
|
return QStringLiteral("org.olivevideoeditor.Olive.cornerpin");
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual QVector<CategoryID> Category() const override
|
||||||
|
{
|
||||||
|
return {kCategoryDistort};
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual QString Description() const override
|
||||||
|
{
|
||||||
|
return tr("Distort the image by dragging the corners.");
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Retranslate() override;
|
||||||
|
|
||||||
|
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
|
||||||
|
|
||||||
|
virtual ShaderCode GetShaderCode(const QString &shader_id) const override;
|
||||||
|
|
||||||
|
virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convenience function - converts the 2D slider values from being
|
||||||
|
* an offset to the actual pixel value.
|
||||||
|
*/
|
||||||
|
QPointF ValueToPixel(int value, const NodeValueRow &row, const QVector2D &resolution) const;
|
||||||
|
|
||||||
|
static const QString kTextureInput;
|
||||||
|
static const QString kPerspectiveInput;
|
||||||
|
static const QString kTopLeftInput;
|
||||||
|
static const QString kTopRightInput;
|
||||||
|
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;
|
||||||
|
PointGizmo *gizmo_resize_handle_[kGizmoCornerCount];
|
||||||
|
PolygonGizmo *gizmo_whole_rect_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif // CORNERPINDISTORTNODE_H
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
#include "cropdistortnode.h"
|
#include "cropdistortnode.h"
|
||||||
|
|
||||||
#include "common/lerp.h"
|
#include "common/util.h"
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
#include "widget/slider/floatslider.h"
|
#include "widget/slider/floatslider.h"
|
||||||
|
|
||||||
@@ -33,6 +33,8 @@ const QString CropDistortNode::kRightInput = QStringLiteral("right_in");
|
|||||||
const QString CropDistortNode::kBottomInput = QStringLiteral("bottom_in");
|
const QString CropDistortNode::kBottomInput = QStringLiteral("bottom_in");
|
||||||
const QString CropDistortNode::kFeatherInput = QStringLiteral("feather_in");
|
const QString CropDistortNode::kFeatherInput = QStringLiteral("feather_in");
|
||||||
|
|
||||||
|
#define super Node
|
||||||
|
|
||||||
CropDistortNode::CropDistortNode()
|
CropDistortNode::CropDistortNode()
|
||||||
{
|
{
|
||||||
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||||
@@ -44,10 +46,27 @@ CropDistortNode::CropDistortNode()
|
|||||||
|
|
||||||
AddInput(kFeatherInput, NodeValue::kFloat, 0.0);
|
AddInput(kFeatherInput, NodeValue::kFloat, 0.0);
|
||||||
SetInputProperty(kFeatherInput, QStringLiteral("min"), 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});
|
||||||
|
|
||||||
|
SetFlags(kVideoEffect);
|
||||||
|
SetEffectInput(kTextureInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CropDistortNode::Retranslate()
|
void CropDistortNode::Retranslate()
|
||||||
{
|
{
|
||||||
|
super::Retranslate();
|
||||||
|
|
||||||
SetInputName(kTextureInput, tr("Texture"));
|
SetInputName(kTextureInput, tr("Texture"));
|
||||||
SetInputName(kLeftInput, tr("Left"));
|
SetInputName(kLeftInput, tr("Left"));
|
||||||
SetInputName(kTopInput, tr("Top"));
|
SetInputName(kTopInput, tr("Top"));
|
||||||
@@ -81,164 +100,50 @@ ShaderCode CropDistortNode::GetShaderCode(const QString &shader_id) const
|
|||||||
return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/crop.frag")));
|
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 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 left_pt = resolution.x() * row[kLeftInput].data().toDouble();
|
||||||
double top_pt = resolution.y() * row[kTopInput].data().toDouble();
|
double top_pt = resolution.y() * row[kTopInput].data().toDouble();
|
||||||
double right_pt = resolution.x() * (1.0 - row[kRightInput].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 bottom_pt = resolution.y() * (1.0 - row[kBottomInput].data().toDouble());
|
||||||
double center_x_pt = lerp(left_pt, right_pt, 0.5);
|
double center_x_pt = mid(left_pt, right_pt);
|
||||||
double center_y_pt = lerp(top_pt, bottom_pt, 0.5);
|
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);
|
point_gizmo_[kGizmoScaleTopLeft]->SetPoint(QPointF(left_pt, top_pt));
|
||||||
p->drawRect(gizmo_whole_rect_);
|
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);
|
poly_gizmo_->SetPolygon(QRectF(left_pt, top_pt, right_pt - left_pt, bottom_pt - top_pt));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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++) {
|
for (int j=0; j<gizmo->GetDraggers().size(); j++) {
|
||||||
gizmo_active[i] = gizmo_resize_handle_[i].contains(p);
|
NodeInputDragger& i = gizmo->GetDraggers()[j];
|
||||||
|
double s = i.GetStartValue().toDouble();
|
||||||
if (gizmo_active[i]) {
|
if (i.GetInput().input().input() == kLeftInput) {
|
||||||
found_handle = true;
|
i.Drag(s + x_diff);
|
||||||
break;
|
} 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)
|
void CropDistortNode::CreateCropSideInput(const QString &id)
|
||||||
|
|||||||
@@ -23,6 +23,8 @@
|
|||||||
|
|
||||||
#include <QVector2D>
|
#include <QVector2D>
|
||||||
|
|
||||||
|
#include "node/gizmo/point.h"
|
||||||
|
#include "node/gizmo/polygon.h"
|
||||||
#include "node/inputdragger.h"
|
#include "node/inputdragger.h"
|
||||||
#include "node/node.h"
|
#include "node/node.h"
|
||||||
|
|
||||||
@@ -67,16 +69,7 @@ public:
|
|||||||
|
|
||||||
virtual ShaderCode GetShaderCode(const QString &shader_id) const override;
|
virtual ShaderCode GetShaderCode(const QString &shader_id) const override;
|
||||||
|
|
||||||
virtual bool HasGizmos() const override
|
virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) 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;
|
|
||||||
|
|
||||||
static const QString kTextureInput;
|
static const QString kTextureInput;
|
||||||
static const QString kLeftInput;
|
static const QString kLeftInput;
|
||||||
@@ -85,27 +78,15 @@ public:
|
|||||||
static const QString kBottomInput;
|
static const QString kBottomInput;
|
||||||
static const QString kFeatherInput;
|
static const QString kFeatherInput;
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
virtual void GizmoDragMove(double delta_x, double delta_y, const Qt::KeyboardModifiers &modifiers) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void CreateCropSideInput(const QString& id);
|
void CreateCropSideInput(const QString& id);
|
||||||
|
|
||||||
// Gizmo variables
|
// Gizmo variables
|
||||||
QRectF gizmo_resize_handle_[kGizmoScaleCount];
|
PointGizmo *point_gizmo_[kGizmoScaleCount];
|
||||||
QRectF gizmo_whole_rect_;
|
PolygonGizmo *poly_gizmo_;
|
||||||
|
|
||||||
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_;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ const QString FlipDistortNode::kTextureInput = QStringLiteral("tex_in");
|
|||||||
const QString FlipDistortNode::kHorizontalInput = QStringLiteral("horiz_in");
|
const QString FlipDistortNode::kHorizontalInput = QStringLiteral("horiz_in");
|
||||||
const QString FlipDistortNode::kVerticalInput = QStringLiteral("vert_in");
|
const QString FlipDistortNode::kVerticalInput = QStringLiteral("vert_in");
|
||||||
|
|
||||||
|
#define super Node
|
||||||
|
|
||||||
FlipDistortNode::FlipDistortNode()
|
FlipDistortNode::FlipDistortNode()
|
||||||
{
|
{
|
||||||
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||||
@@ -33,6 +35,9 @@ FlipDistortNode::FlipDistortNode()
|
|||||||
AddInput(kHorizontalInput, NodeValue::kBoolean, false);
|
AddInput(kHorizontalInput, NodeValue::kBoolean, false);
|
||||||
|
|
||||||
AddInput(kVerticalInput, NodeValue::kBoolean, false);
|
AddInput(kVerticalInput, NodeValue::kBoolean, false);
|
||||||
|
|
||||||
|
SetFlags(kVideoEffect);
|
||||||
|
SetEffectInput(kTextureInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
Node* FlipDistortNode::copy() const
|
Node* FlipDistortNode::copy() const
|
||||||
@@ -62,6 +67,8 @@ QString FlipDistortNode::Description() const
|
|||||||
|
|
||||||
void FlipDistortNode::Retranslate()
|
void FlipDistortNode::Retranslate()
|
||||||
{
|
{
|
||||||
|
super::Retranslate();
|
||||||
|
|
||||||
SetInputName(kTextureInput, tr("Input"));
|
SetInputName(kTextureInput, tr("Input"));
|
||||||
SetInputName(kHorizontalInput, tr("Horizontal"));
|
SetInputName(kHorizontalInput, tr("Horizontal"));
|
||||||
SetInputName(kVerticalInput, tr("Vertical"));
|
SetInputName(kVerticalInput, tr("Vertical"));
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ const QString TransformDistortNode::kTextureInput = QStringLiteral("tex_in");
|
|||||||
const QString TransformDistortNode::kAutoscaleInput = QStringLiteral("autoscale_in");
|
const QString TransformDistortNode::kAutoscaleInput = QStringLiteral("autoscale_in");
|
||||||
const QString TransformDistortNode::kInterpolationInput = QStringLiteral("interpolation_in");
|
const QString TransformDistortNode::kInterpolationInput = QStringLiteral("interpolation_in");
|
||||||
|
|
||||||
#define super Node
|
#define super MatrixGenerator
|
||||||
|
|
||||||
TransformDistortNode::TransformDistortNode()
|
TransformDistortNode::TransformDistortNode()
|
||||||
{
|
{
|
||||||
@@ -41,11 +41,37 @@ TransformDistortNode::TransformDistortNode()
|
|||||||
AddInput(kInterpolationInput, NodeValue::kCombo, 2);
|
AddInput(kInterpolationInput, NodeValue::kCombo, 2);
|
||||||
|
|
||||||
PrependInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetFlags(kVideoEffect);
|
||||||
|
SetEffectInput(kTextureInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransformDistortNode::Retranslate()
|
void TransformDistortNode::Retranslate()
|
||||||
{
|
{
|
||||||
MatrixGenerator::Retranslate();
|
super::Retranslate();
|
||||||
|
|
||||||
SetInputName(kAutoscaleInput, tr("Auto-Scale"));
|
SetInputName(kAutoscaleInput, tr("Auto-Scale"));
|
||||||
SetInputName(kTextureInput, tr("Texture"));
|
SetInputName(kTextureInput, tr("Texture"));
|
||||||
@@ -101,218 +127,6 @@ ShaderCode TransformDistortNode::GetShaderCode(const QString &shader_id) const
|
|||||||
return ShaderCode();
|
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
|
void TransformDistortNode::Hash(QCryptographicHash &hash, const NodeGlobals &globals, const VideoParams &video_params) const
|
||||||
{
|
{
|
||||||
// If not connected to output, this will produce nothing
|
// If not connected to output, this will produce nothing
|
||||||
@@ -339,7 +153,169 @@ void TransformDistortNode::Hash(QCryptographicHash &hash, const NodeGlobals &glo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Node::Hash(out, GetValueHintForInput(kTextureInput), hash, globals, video_params);
|
super::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)
|
QMatrix4x4 TransformDistortNode::AdjustMatrixByResolutions(const QMatrix4x4 &mat, const QVector2D &sequence_res, const QVector2D &texture_res, AutoScaleType autoscale_type)
|
||||||
@@ -387,33 +363,13 @@ QMatrix4x4 TransformDistortNode::AdjustMatrixByResolutions(const QMatrix4x4 &mat
|
|||||||
return adjusted_matrix;
|
return adjusted_matrix;
|
||||||
}
|
}
|
||||||
|
|
||||||
QPointF TransformDistortNode::CreateScalePoint(double x, double y, const QPointF &half_res, const QMatrix4x4 &mat)
|
void TransformDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals)
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
TexturePtr tex = row[kTextureInput].data().value<TexturePtr>();
|
TexturePtr tex = row[kTextureInput].data().value<TexturePtr>();
|
||||||
if (!tex) {
|
if (!tex) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0 pen width is always 1px wide despite any transform
|
|
||||||
p->setPen(QPen(Qt::white, 0));
|
|
||||||
|
|
||||||
// Get the sequence resolution
|
// Get the sequence resolution
|
||||||
const QVector2D &sequence_res = globals.resolution();
|
const QVector2D &sequence_res = globals.resolution();
|
||||||
QVector2D sequence_half_res = sequence_res * 0.5;
|
QVector2D sequence_half_res = sequence_res * 0.5;
|
||||||
@@ -441,14 +397,9 @@ void TransformDistortNode::DrawGizmos(const NodeValueRow &row, const NodeGlobals
|
|||||||
QPointF(-1, 1),
|
QPointF(-1, 1),
|
||||||
QPointF(-1, -1)};
|
QPointF(-1, -1)};
|
||||||
QTransform rectangle_transform = rectangle_matrix.toTransform();
|
QTransform rectangle_transform = rectangle_matrix.toTransform();
|
||||||
gizmo_rect_ = rectangle_transform.map(points);
|
QPolygonF r = rectangle_transform.map(points);
|
||||||
gizmo_rect_.translate(sequence_half_res_pt);
|
r.translate(sequence_half_res_pt);
|
||||||
|
poly_gizmo_->SetPolygon(r);
|
||||||
// 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());
|
|
||||||
|
|
||||||
// Draw anchor point
|
// Draw anchor point
|
||||||
QMatrix4x4 anchor_matrix;
|
QMatrix4x4 anchor_matrix;
|
||||||
@@ -457,32 +408,17 @@ void TransformDistortNode::DrawGizmos(const NodeValueRow &row, const NodeGlobals
|
|||||||
sequence_res,
|
sequence_res,
|
||||||
tex_sz,
|
tex_sz,
|
||||||
autoscale);
|
autoscale);
|
||||||
QPointF anchor_pt = anchor_matrix.toTransform().map(QPointF(0, 0)) + sequence_half_res_pt;
|
anchor_gizmo_->SetPoint(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)});
|
|
||||||
|
|
||||||
// Draw scale handles
|
// Draw scale handles
|
||||||
gizmo_resize_handle_[kGizmoScaleTopLeft] = CreateGizmoHandleRect(CreateScalePoint(-1, -1, sequence_half_res_pt, rectangle_matrix), resize_handle_rad);
|
point_gizmo_[kGizmoScaleTopLeft]->SetPoint(CreateScalePoint(-1, -1, sequence_half_res_pt, rectangle_matrix));
|
||||||
gizmo_resize_handle_[kGizmoScaleTopCenter] = CreateGizmoHandleRect(CreateScalePoint( 0, -1, sequence_half_res_pt, rectangle_matrix), resize_handle_rad);
|
point_gizmo_[kGizmoScaleTopCenter]->SetPoint(CreateScalePoint( 0, -1, sequence_half_res_pt, rectangle_matrix));
|
||||||
gizmo_resize_handle_[kGizmoScaleTopRight] = CreateGizmoHandleRect(CreateScalePoint( 1, -1, sequence_half_res_pt, rectangle_matrix), resize_handle_rad);
|
point_gizmo_[kGizmoScaleTopRight]->SetPoint(CreateScalePoint( 1, -1, sequence_half_res_pt, rectangle_matrix));
|
||||||
gizmo_resize_handle_[kGizmoScaleBottomLeft] = CreateGizmoHandleRect(CreateScalePoint(-1, 1, sequence_half_res_pt, rectangle_matrix), resize_handle_rad);
|
point_gizmo_[kGizmoScaleBottomLeft]->SetPoint(CreateScalePoint(-1, 1, sequence_half_res_pt, rectangle_matrix));
|
||||||
gizmo_resize_handle_[kGizmoScaleBottomCenter] = CreateGizmoHandleRect(CreateScalePoint( 0, 1, sequence_half_res_pt, rectangle_matrix), resize_handle_rad);
|
point_gizmo_[kGizmoScaleBottomCenter]->SetPoint(CreateScalePoint( 0, 1, sequence_half_res_pt, rectangle_matrix));
|
||||||
gizmo_resize_handle_[kGizmoScaleBottomRight] = CreateGizmoHandleRect(CreateScalePoint( 1, 1, sequence_half_res_pt, rectangle_matrix), resize_handle_rad);
|
point_gizmo_[kGizmoScaleBottomRight]->SetPoint(CreateScalePoint( 1, 1, sequence_half_res_pt, rectangle_matrix));
|
||||||
gizmo_resize_handle_[kGizmoScaleCenterLeft] = CreateGizmoHandleRect(CreateScalePoint(-1, 0, sequence_half_res_pt, rectangle_matrix), resize_handle_rad);
|
point_gizmo_[kGizmoScaleCenterLeft]->SetPoint(CreateScalePoint(-1, 0, sequence_half_res_pt, rectangle_matrix));
|
||||||
gizmo_resize_handle_[kGizmoScaleCenterRight] = CreateGizmoHandleRect(CreateScalePoint( 1, 0, sequence_half_res_pt, rectangle_matrix), resize_handle_rad);
|
point_gizmo_[kGizmoScaleCenterRight]->SetPoint(CreateScalePoint( 1, 0, sequence_half_res_pt, rectangle_matrix));
|
||||||
|
|
||||||
DrawAndExpandGizmoHandles(p, resize_handle_rad, gizmo_resize_handle_, kGizmoScaleCount);
|
|
||||||
|
|
||||||
// Use offsets to make the appearance of values that start in the top left, even though we
|
// Use offsets to make the appearance of values that start in the top left, even though we
|
||||||
// really anchor around the center
|
// really anchor around the center
|
||||||
@@ -490,4 +426,37 @@ void TransformDistortNode::DrawGizmos(const NodeValueRow &row, const NodeGlobals
|
|||||||
SetInputProperty(kAnchorInput, QStringLiteral("offset"), tex_sz * 0.5);
|
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
|
#define TRANSFORMDISTORTNODE_H
|
||||||
|
|
||||||
#include "node/generator/matrix/matrix.h"
|
#include "node/generator/matrix/matrix.h"
|
||||||
|
#include "node/gizmo/point.h"
|
||||||
|
#include "node/gizmo/polygon.h"
|
||||||
|
#include "node/gizmo/screen.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
@@ -69,17 +72,6 @@ public:
|
|||||||
|
|
||||||
virtual ShaderCode GetShaderCode(const QString& shader_id) const override;
|
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 {
|
enum AutoScaleType {
|
||||||
kAutoScaleNone,
|
kAutoScaleNone,
|
||||||
kAutoScaleFit,
|
kAutoScaleFit,
|
||||||
@@ -92,6 +84,8 @@ public:
|
|||||||
const QVector2D& texture_res,
|
const QVector2D& texture_res,
|
||||||
AutoScaleType autoscale_type = kAutoScaleNone);
|
AutoScaleType autoscale_type = kAutoScaleNone);
|
||||||
|
|
||||||
|
virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override;
|
||||||
|
|
||||||
static const QString kTextureInput;
|
static const QString kTextureInput;
|
||||||
static const QString kAutoscaleInput;
|
static const QString kAutoscaleInput;
|
||||||
static const QString kInterpolationInput;
|
static const QString kInterpolationInput;
|
||||||
@@ -99,18 +93,36 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
virtual void Hash(QCryptographicHash& hash, const NodeGlobals &globals, const VideoParams& video_params) const override;
|
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:
|
private:
|
||||||
static QPointF CreateScalePoint(double x, double y, const QPointF& half_res, const QMatrix4x4& mat);
|
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;
|
QMatrix4x4 GenerateAutoScaledMatrix(const QMatrix4x4 &generated_matrix, const NodeValueRow &db, const NodeGlobals &globals, const VideoParams &texture_params) const;
|
||||||
|
|
||||||
|
bool IsAScaleGizmo(NodeGizmo *g) const;
|
||||||
|
|
||||||
// Gizmo variables
|
// Gizmo variables
|
||||||
QString gizmo_drag_;
|
|
||||||
QVector<QVariant> gizmo_start_;
|
|
||||||
QVector<NodeInputDragger> gizmo_dragger_;
|
|
||||||
QPointF gizmo_drag_pos_;
|
|
||||||
double gizmo_start_angle_;
|
double gizmo_start_angle_;
|
||||||
|
QTransform gizmo_inverted_transform_;
|
||||||
|
QPointF gizmo_anchor_pt_;
|
||||||
bool gizmo_scale_uniform_;
|
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 {
|
enum GizmoScaleType {
|
||||||
kGizmoScaleXOnly,
|
kGizmoScaleXOnly,
|
||||||
@@ -119,13 +131,13 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
GizmoScaleType gizmo_scale_axes_;
|
GizmoScaleType gizmo_scale_axes_;
|
||||||
QMatrix4x4 gizmo_matrix_;
|
|
||||||
QVector2D gizmo_scale_anchor_;
|
QVector2D gizmo_scale_anchor_;
|
||||||
|
|
||||||
// Gizmo on screen object storage
|
// Gizmo on screen object storage
|
||||||
QPolygonF gizmo_rect_;
|
PointGizmo *point_gizmo_[kGizmoScaleCount];
|
||||||
QRectF gizmo_anchor_pt_;
|
PointGizmo *anchor_gizmo_;
|
||||||
QRectF gizmo_resize_handle_[kGizmoScaleCount];
|
PolygonGizmo *poly_gizmo_;
|
||||||
|
ScreenGizmo *rotation_gizmo_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,10 @@
|
|||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
#define super NodeGroup
|
#define super Node
|
||||||
|
|
||||||
|
const QString OpacityEffect::kTextureInput = QStringLiteral("tex_in");
|
||||||
|
const QString OpacityEffect::kValueInput = QStringLiteral("opacity_in");
|
||||||
|
|
||||||
OpacityEffect::OpacityEffect()
|
OpacityEffect::OpacityEffect()
|
||||||
{
|
{
|
||||||
@@ -15,24 +18,47 @@ OpacityEffect::OpacityEffect()
|
|||||||
|
|
||||||
SetNodePositionInContext(math, QPointF(0, 0));
|
SetNodePositionInContext(math, QPointF(0, 0));
|
||||||
|
|
||||||
tex_in_pass_ = AddInputPassthrough(NodeInput(math, MathNode::kParamAIn), InputFlags(kInputFlagNotKeyframable));
|
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||||
SetInputDataType(tex_in_pass_, NodeValue::kTexture);
|
|
||||||
|
|
||||||
value_in_pass_ = AddInputPassthrough(NodeInput(math, MathNode::kParamBIn));
|
AddInput(kValueInput, NodeValue::kFloat, 1.0);
|
||||||
SetInputProperty(value_in_pass_, QStringLiteral("view"), FloatSlider::kPercentage);
|
SetInputProperty(kValueInput, QStringLiteral("view"), FloatSlider::kPercentage);
|
||||||
SetInputProperty(value_in_pass_, QStringLiteral("min"), 0.0);
|
SetInputProperty(kValueInput, QStringLiteral("min"), 0.0);
|
||||||
SetInputProperty(value_in_pass_, QStringLiteral("max"), 1.0);
|
SetInputProperty(kValueInput, QStringLiteral("max"), 1.0);
|
||||||
math->SetStandardValue(MathNode::kParamBIn, 1.0);
|
|
||||||
|
|
||||||
SetOutputPassthrough(math);
|
SetFlags(kVideoEffect);
|
||||||
|
SetEffectInput(kTextureInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpacityEffect::Retranslate()
|
void OpacityEffect::Retranslate()
|
||||||
{
|
{
|
||||||
super::Retranslate();
|
super::Retranslate();
|
||||||
|
|
||||||
SetInputName(tex_in_pass_, tr("Texture"));
|
SetInputName(kTextureInput, tr("Texture"));
|
||||||
SetInputName(value_in_pass_, tr("Opacity"));
|
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 {
|
namespace olive {
|
||||||
|
|
||||||
class OpacityEffect : public NodeGroup
|
class OpacityEffect : public Node
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
OpacityEffect();
|
OpacityEffect();
|
||||||
@@ -21,12 +21,12 @@ public:
|
|||||||
|
|
||||||
virtual QString id() const override
|
virtual QString id() const override
|
||||||
{
|
{
|
||||||
return QStringLiteral("org.olivevideoeditor.Olive.opacityeffect");
|
return QStringLiteral("org.olivevideoeditor.Olive.opacity");
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual QVector<CategoryID> Category() const override
|
virtual QVector<CategoryID> Category() const override
|
||||||
{
|
{
|
||||||
return {kCategoryFilter, kCategoryVideoEffect};
|
return {kCategoryFilter};
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual QString Description() const override
|
virtual QString Description() const override
|
||||||
@@ -36,9 +36,11 @@ public:
|
|||||||
|
|
||||||
virtual void Retranslate() override;
|
virtual void Retranslate() override;
|
||||||
|
|
||||||
private:
|
virtual ShaderCode GetShaderCode(const QString &shader_id) const override;
|
||||||
QString tex_in_pass_;
|
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
|
||||||
QString value_in_pass_;
|
|
||||||
|
static const QString kTextureInput;
|
||||||
|
static const QString kValueInput;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+28
-8
@@ -29,6 +29,7 @@
|
|||||||
#include "block/subtitle/subtitle.h"
|
#include "block/subtitle/subtitle.h"
|
||||||
#include "block/transition/crossdissolve/crossdissolvetransition.h"
|
#include "block/transition/crossdissolve/crossdissolvetransition.h"
|
||||||
#include "block/transition/diptocolor/diptocolortransition.h"
|
#include "block/transition/diptocolor/diptocolortransition.h"
|
||||||
|
#include "distort/cornerpin/cornerpindistortnode.h"
|
||||||
#include "distort/crop/cropdistortnode.h"
|
#include "distort/crop/cropdistortnode.h"
|
||||||
#include "distort/flip/flipdistortnode.h"
|
#include "distort/flip/flipdistortnode.h"
|
||||||
#include "distort/transform/transformdistortnode.h"
|
#include "distort/transform/transformdistortnode.h"
|
||||||
@@ -38,8 +39,9 @@
|
|||||||
#include "generator/polygon/polygon.h"
|
#include "generator/polygon/polygon.h"
|
||||||
#include "generator/shape/shapenode.h"
|
#include "generator/shape/shapenode.h"
|
||||||
#include "generator/solid/solid.h"
|
#include "generator/solid/solid.h"
|
||||||
#include "generator/text/text.h"
|
#include "generator/text/textv1.h"
|
||||||
#include "generator/text/textlegacy.h"
|
#include "generator/text/textv2.h"
|
||||||
|
#include "generator/text/textv3.h"
|
||||||
#include "filter/blur/blur.h"
|
#include "filter/blur/blur.h"
|
||||||
#include "filter/mosaic/mosaicfilternode.h"
|
#include "filter/mosaic/mosaicfilternode.h"
|
||||||
#include "filter/stroke/stroke.h"
|
#include "filter/stroke/stroke.h"
|
||||||
@@ -48,11 +50,14 @@
|
|||||||
#include "math/math/math.h"
|
#include "math/math/math.h"
|
||||||
#include "math/merge/merge.h"
|
#include "math/merge/merge.h"
|
||||||
#include "math/trigonometry/trigonometry.h"
|
#include "math/trigonometry/trigonometry.h"
|
||||||
|
#include "keying/colordifferencekey/colordifferencekey.h"
|
||||||
|
#include "keying/despill/despill.h"
|
||||||
#include "output/track/track.h"
|
#include "output/track/track.h"
|
||||||
#include "output/viewer/viewer.h"
|
#include "output/viewer/viewer.h"
|
||||||
#include "project/folder/folder.h"
|
#include "project/folder/folder.h"
|
||||||
#include "project/footage/footage.h"
|
#include "project/footage/footage.h"
|
||||||
#include "project/sequence/sequence.h"
|
#include "project/sequence/sequence.h"
|
||||||
|
#include "time/timeoffset/timeoffsetnode.h"
|
||||||
#include "time/timeremap/timeremap.h"
|
#include "time/timeremap/timeremap.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
@@ -70,7 +75,8 @@ void NodeFactory::Initialize()
|
|||||||
library_.append(created_node);
|
library_.append(created_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
hidden_.append(kTextGeneratorLegacy);
|
hidden_.append(kTextGeneratorV1);
|
||||||
|
hidden_.append(kTextGeneratorV2);
|
||||||
hidden_.append(kGroupNode);
|
hidden_.append(kGroupNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +86,7 @@ void NodeFactory::Destroy()
|
|||||||
library_.clear();
|
library_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu *NodeFactory::CreateMenu(QWidget* parent, bool create_none_item, Node::CategoryID restrict_to)
|
Menu *NodeFactory::CreateMenu(QWidget* parent, bool create_none_item, Node::CategoryID restrict_to, uint64_t restrict_flags)
|
||||||
{
|
{
|
||||||
Menu* menu = new Menu(parent);
|
Menu* menu = new Menu(parent);
|
||||||
menu->setToolTipsVisible(true);
|
menu->setToolTipsVisible(true);
|
||||||
@@ -93,6 +99,10 @@ Menu *NodeFactory::CreateMenu(QWidget* parent, bool create_none_item, Node::Cate
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (restrict_flags && !(n->GetFlags() & restrict_flags)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (hidden_.contains(i)) {
|
if (hidden_.contains(i)) {
|
||||||
// Skip this node
|
// Skip this node
|
||||||
continue;
|
continue;
|
||||||
@@ -227,10 +237,12 @@ Node *NodeFactory::CreateFromFactoryIndex(const NodeFactory::InternalID &id)
|
|||||||
return new MergeNode();
|
return new MergeNode();
|
||||||
case kStrokeFilter:
|
case kStrokeFilter:
|
||||||
return new StrokeFilterNode();
|
return new StrokeFilterNode();
|
||||||
case kTextGeneratorLegacy:
|
case kTextGeneratorV1:
|
||||||
return new TextGeneratorLegacy();
|
return new TextGeneratorV1();
|
||||||
case kTextGenerator:
|
case kTextGeneratorV2:
|
||||||
return new TextGenerator();
|
return new TextGeneratorV2();
|
||||||
|
case kTextGeneratorV3:
|
||||||
|
return new TextGeneratorV3();
|
||||||
case kCrossDissolveTransition:
|
case kCrossDissolveTransition:
|
||||||
return new CrossDissolveTransition();
|
return new CrossDissolveTransition();
|
||||||
case kDipToColorTransition:
|
case kDipToColorTransition:
|
||||||
@@ -253,6 +265,10 @@ Node *NodeFactory::CreateFromFactoryIndex(const NodeFactory::InternalID &id)
|
|||||||
return new SubtitleBlock();
|
return new SubtitleBlock();
|
||||||
case kShapeGenerator:
|
case kShapeGenerator:
|
||||||
return new ShapeNode();
|
return new ShapeNode();
|
||||||
|
case kColorDifferenceKeyKeying:
|
||||||
|
return new ColorDifferenceKeyNode();
|
||||||
|
case kDespillKeying:
|
||||||
|
return new DespillNode();
|
||||||
case kGroupNode:
|
case kGroupNode:
|
||||||
return new NodeGroup();
|
return new NodeGroup();
|
||||||
case kOpacityEffect:
|
case kOpacityEffect:
|
||||||
@@ -261,6 +277,10 @@ Node *NodeFactory::CreateFromFactoryIndex(const NodeFactory::InternalID &id)
|
|||||||
return new FlipDistortNode();
|
return new FlipDistortNode();
|
||||||
case kNoiseGenerator:
|
case kNoiseGenerator:
|
||||||
return new NoiseGeneratorNode();
|
return new NoiseGeneratorNode();
|
||||||
|
case kTimeOffsetNode:
|
||||||
|
return new TimeOffsetNode();
|
||||||
|
case kCornerPinDistort:
|
||||||
|
return new CornerPinDistortNode();
|
||||||
|
|
||||||
case kInternalNodeCount:
|
case kInternalNodeCount:
|
||||||
break;
|
break;
|
||||||
|
|||||||
+8
-3
@@ -48,8 +48,9 @@ public:
|
|||||||
kSolidGenerator,
|
kSolidGenerator,
|
||||||
kMerge,
|
kMerge,
|
||||||
kStrokeFilter,
|
kStrokeFilter,
|
||||||
kTextGeneratorLegacy,
|
kTextGeneratorV1,
|
||||||
kTextGenerator,
|
kTextGeneratorV2,
|
||||||
|
kTextGeneratorV3,
|
||||||
kCrossDissolveTransition,
|
kCrossDissolveTransition,
|
||||||
kDipToColorTransition,
|
kDipToColorTransition,
|
||||||
kMosaicFilter,
|
kMosaicFilter,
|
||||||
@@ -61,10 +62,14 @@ public:
|
|||||||
kTimeRemapNode,
|
kTimeRemapNode,
|
||||||
kSubtitleBlock,
|
kSubtitleBlock,
|
||||||
kShapeGenerator,
|
kShapeGenerator,
|
||||||
|
kColorDifferenceKeyKeying,
|
||||||
|
kDespillKeying,
|
||||||
kGroupNode,
|
kGroupNode,
|
||||||
kOpacityEffect,
|
kOpacityEffect,
|
||||||
kFlipDistort,
|
kFlipDistort,
|
||||||
kNoiseGenerator,
|
kNoiseGenerator,
|
||||||
|
kTimeOffsetNode,
|
||||||
|
kCornerPinDistort,
|
||||||
|
|
||||||
// Count value
|
// Count value
|
||||||
kInternalNodeCount
|
kInternalNodeCount
|
||||||
@@ -76,7 +81,7 @@ public:
|
|||||||
|
|
||||||
static void Destroy();
|
static void Destroy();
|
||||||
|
|
||||||
static Menu* CreateMenu(QWidget *parent, bool create_none_item = false, Node::CategoryID restrict_to = Node::kCategoryUnknown);
|
static Menu* CreateMenu(QWidget *parent, bool create_none_item = false, Node::CategoryID restrict_to = Node::kCategoryUnknown, uint64_t restrict_flags = 0);
|
||||||
|
|
||||||
static Node* CreateFromMenuAction(QAction* action);
|
static Node* CreateFromMenuAction(QAction* action);
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ const QString BlurFilterNode::kHorizInput = QStringLiteral("horiz_in");
|
|||||||
const QString BlurFilterNode::kVertInput = QStringLiteral("vert_in");
|
const QString BlurFilterNode::kVertInput = QStringLiteral("vert_in");
|
||||||
const QString BlurFilterNode::kRepeatEdgePixelsInput = QStringLiteral("repeat_edge_pixels_in");
|
const QString BlurFilterNode::kRepeatEdgePixelsInput = QStringLiteral("repeat_edge_pixels_in");
|
||||||
|
|
||||||
|
#define super Node
|
||||||
|
|
||||||
BlurFilterNode::BlurFilterNode()
|
BlurFilterNode::BlurFilterNode()
|
||||||
{
|
{
|
||||||
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||||
@@ -42,7 +44,10 @@ BlurFilterNode::BlurFilterNode()
|
|||||||
|
|
||||||
AddInput(kVertInput, NodeValue::kBoolean, true);
|
AddInput(kVertInput, NodeValue::kBoolean, true);
|
||||||
|
|
||||||
AddInput(kRepeatEdgePixelsInput, NodeValue::kBoolean, false);
|
AddInput(kRepeatEdgePixelsInput, NodeValue::kBoolean, true);
|
||||||
|
|
||||||
|
SetFlags(kVideoEffect);
|
||||||
|
SetEffectInput(kTextureInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
Node *BlurFilterNode::copy() const
|
Node *BlurFilterNode::copy() const
|
||||||
@@ -72,6 +77,8 @@ QString BlurFilterNode::Description() const
|
|||||||
|
|
||||||
void BlurFilterNode::Retranslate()
|
void BlurFilterNode::Retranslate()
|
||||||
{
|
{
|
||||||
|
super::Retranslate();
|
||||||
|
|
||||||
SetInputName(kTextureInput, tr("Input"));
|
SetInputName(kTextureInput, tr("Input"));
|
||||||
SetInputName(kMethodInput, tr("Method"));
|
SetInputName(kMethodInput, tr("Method"));
|
||||||
SetComboBoxStrings(kMethodInput, { tr("Box"), tr("Gaussian") });
|
SetComboBoxStrings(kMethodInput, { tr("Box"), tr("Gaussian") });
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ const QString MosaicFilterNode::kTextureInput = QStringLiteral("tex_in");
|
|||||||
const QString MosaicFilterNode::kHorizInput = QStringLiteral("horiz_in");
|
const QString MosaicFilterNode::kHorizInput = QStringLiteral("horiz_in");
|
||||||
const QString MosaicFilterNode::kVertInput = QStringLiteral("vert_in");
|
const QString MosaicFilterNode::kVertInput = QStringLiteral("vert_in");
|
||||||
|
|
||||||
|
#define super Node
|
||||||
|
|
||||||
MosaicFilterNode::MosaicFilterNode()
|
MosaicFilterNode::MosaicFilterNode()
|
||||||
{
|
{
|
||||||
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||||
@@ -35,10 +37,15 @@ MosaicFilterNode::MosaicFilterNode()
|
|||||||
|
|
||||||
AddInput(kVertInput, NodeValue::kFloat, 18.0);
|
AddInput(kVertInput, NodeValue::kFloat, 18.0);
|
||||||
SetInputProperty(kVertInput, QStringLiteral("min"), 1.0);
|
SetInputProperty(kVertInput, QStringLiteral("min"), 1.0);
|
||||||
|
|
||||||
|
SetFlags(kVideoEffect);
|
||||||
|
SetEffectInput(kTextureInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MosaicFilterNode::Retranslate()
|
void MosaicFilterNode::Retranslate()
|
||||||
{
|
{
|
||||||
|
super::Retranslate();
|
||||||
|
|
||||||
SetInputName(kTextureInput, tr("Texture"));
|
SetInputName(kTextureInput, tr("Texture"));
|
||||||
SetInputName(kHorizInput, tr("Horizontal"));
|
SetInputName(kHorizInput, tr("Horizontal"));
|
||||||
SetInputName(kVertInput, tr("Vertical"));
|
SetInputName(kVertInput, tr("Vertical"));
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ const QString StrokeFilterNode::kRadiusInput = QStringLiteral("radius_in");
|
|||||||
const QString StrokeFilterNode::kOpacityInput = QStringLiteral("opacity_in");
|
const QString StrokeFilterNode::kOpacityInput = QStringLiteral("opacity_in");
|
||||||
const QString StrokeFilterNode::kInnerInput = QStringLiteral("inner_in");
|
const QString StrokeFilterNode::kInnerInput = QStringLiteral("inner_in");
|
||||||
|
|
||||||
|
#define super Node
|
||||||
|
|
||||||
StrokeFilterNode::StrokeFilterNode()
|
StrokeFilterNode::StrokeFilterNode()
|
||||||
{
|
{
|
||||||
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||||
@@ -46,6 +48,9 @@ StrokeFilterNode::StrokeFilterNode()
|
|||||||
SetInputProperty(kOpacityInput, QStringLiteral("max"), 1.0f);
|
SetInputProperty(kOpacityInput, QStringLiteral("max"), 1.0f);
|
||||||
|
|
||||||
AddInput(kInnerInput, NodeValue::kBoolean, false);
|
AddInput(kInnerInput, NodeValue::kBoolean, false);
|
||||||
|
|
||||||
|
SetFlags(kVideoEffect);
|
||||||
|
SetEffectInput(kTextureInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
Node *StrokeFilterNode::copy() const
|
Node *StrokeFilterNode::copy() const
|
||||||
@@ -75,6 +80,8 @@ QString StrokeFilterNode::Description() const
|
|||||||
|
|
||||||
void StrokeFilterNode::Retranslate()
|
void StrokeFilterNode::Retranslate()
|
||||||
{
|
{
|
||||||
|
super::Retranslate();
|
||||||
|
|
||||||
SetInputName(kTextureInput, tr("Input"));
|
SetInputName(kTextureInput, tr("Input"));
|
||||||
SetInputName(kColorInput, tr("Color"));
|
SetInputName(kColorInput, tr("Color"));
|
||||||
SetInputName(kRadiusInput, tr("Radius"));
|
SetInputName(kRadiusInput, tr("Radius"));
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ const QString MatrixGenerator::kScaleInput = QStringLiteral("scale_in");
|
|||||||
const QString MatrixGenerator::kUniformScaleInput = QStringLiteral("uniform_scale_in");
|
const QString MatrixGenerator::kUniformScaleInput = QStringLiteral("uniform_scale_in");
|
||||||
const QString MatrixGenerator::kAnchorInput = QStringLiteral("anchor_in");
|
const QString MatrixGenerator::kAnchorInput = QStringLiteral("anchor_in");
|
||||||
|
|
||||||
|
#define super Node
|
||||||
|
|
||||||
MatrixGenerator::MatrixGenerator()
|
MatrixGenerator::MatrixGenerator()
|
||||||
{
|
{
|
||||||
AddInput(kPositionInput, NodeValue::kVec2, QVector2D(0.0, 0.0));
|
AddInput(kPositionInput, NodeValue::kVec2, QVector2D(0.0, 0.0));
|
||||||
@@ -81,6 +83,8 @@ QString MatrixGenerator::Description() const
|
|||||||
|
|
||||||
void MatrixGenerator::Retranslate()
|
void MatrixGenerator::Retranslate()
|
||||||
{
|
{
|
||||||
|
super::Retranslate();
|
||||||
|
|
||||||
SetInputName(kPositionInput, tr("Position"));
|
SetInputName(kPositionInput, tr("Position"));
|
||||||
SetInputName(kRotationInput, tr("Rotation"));
|
SetInputName(kRotationInput, tr("Rotation"));
|
||||||
SetInputName(kScaleInput, tr("Scale"));
|
SetInputName(kScaleInput, tr("Scale"));
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ namespace olive {
|
|||||||
const QString NoiseGeneratorNode::kColorInput = QStringLiteral("color_in");
|
const QString NoiseGeneratorNode::kColorInput = QStringLiteral("color_in");
|
||||||
const QString NoiseGeneratorNode::kStrengthInput = QStringLiteral("strength_in");
|
const QString NoiseGeneratorNode::kStrengthInput = QStringLiteral("strength_in");
|
||||||
|
|
||||||
|
#define super Node
|
||||||
|
|
||||||
NoiseGeneratorNode::NoiseGeneratorNode()
|
NoiseGeneratorNode::NoiseGeneratorNode()
|
||||||
{
|
{
|
||||||
AddInput(kStrengthInput, NodeValue::kFloat, 20);
|
AddInput(kStrengthInput, NodeValue::kFloat, 20);
|
||||||
@@ -59,6 +61,8 @@ QString NoiseGeneratorNode::Description() const
|
|||||||
|
|
||||||
void NoiseGeneratorNode::Retranslate()
|
void NoiseGeneratorNode::Retranslate()
|
||||||
{
|
{
|
||||||
|
super::Retranslate();
|
||||||
|
|
||||||
SetInputName(kStrengthInput, tr("Strength"));
|
SetInputName(kStrengthInput, tr("Strength"));
|
||||||
SetInputName(kColorInput, tr("Color"));
|
SetInputName(kColorInput, tr("Color"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ namespace olive {
|
|||||||
const QString PolygonGenerator::kPointsInput = QStringLiteral("points_in");
|
const QString PolygonGenerator::kPointsInput = QStringLiteral("points_in");
|
||||||
const QString PolygonGenerator::kColorInput = QStringLiteral("color_in");
|
const QString PolygonGenerator::kColorInput = QStringLiteral("color_in");
|
||||||
|
|
||||||
|
#define super Node
|
||||||
|
|
||||||
PolygonGenerator::PolygonGenerator()
|
PolygonGenerator::PolygonGenerator()
|
||||||
{
|
{
|
||||||
AddInput(kPointsInput, NodeValue::kBezier, QVector2D(0, 0), InputFlags(kInputFlagArray));
|
AddInput(kPointsInput, NodeValue::kBezier, QVector2D(0, 0), InputFlags(kInputFlagArray));
|
||||||
@@ -54,6 +56,9 @@ PolygonGenerator::PolygonGenerator()
|
|||||||
SetSplitStandardValueOnTrack(kPointsInput, 1, kBottomY, 3);
|
SetSplitStandardValueOnTrack(kPointsInput, 1, kBottomY, 3);
|
||||||
SetSplitStandardValueOnTrack(kPointsInput, 0, -kMiddleX, 4);
|
SetSplitStandardValueOnTrack(kPointsInput, 0, -kMiddleX, 4);
|
||||||
SetSplitStandardValueOnTrack(kPointsInput, 1, -kMiddleY, 4);
|
SetSplitStandardValueOnTrack(kPointsInput, 1, -kMiddleY, 4);
|
||||||
|
|
||||||
|
// Initiate gizmos
|
||||||
|
poly_gizmo_ = new PathGizmo(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Node *PolygonGenerator::copy() const
|
Node *PolygonGenerator::copy() const
|
||||||
@@ -83,6 +88,8 @@ QString PolygonGenerator::Description() const
|
|||||||
|
|
||||||
void PolygonGenerator::Retranslate()
|
void PolygonGenerator::Retranslate()
|
||||||
{
|
{
|
||||||
|
super::Retranslate();
|
||||||
|
|
||||||
SetInputName(kPointsInput, tr("Points"));
|
SetInputName(kPointsInput, tr("Points"));
|
||||||
SetInputName(kColorInput, tr("Color"));
|
SetInputName(kColorInput, tr("Color"));
|
||||||
}
|
}
|
||||||
@@ -149,139 +156,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());
|
return AddDraggableGizmo<PointGizmo>();
|
||||||
const double bezier_radius = handle_radius/2;
|
}
|
||||||
|
|
||||||
p->setPen(Qt::white);
|
template<typename T>
|
||||||
p->setBrush(Qt::white);
|
void PolygonGenerator::ValidateGizmoVectorSize(QVector<T*> &vec, int new_sz)
|
||||||
p->translate(globals.resolution_by_par().x()/2, globals.resolution_by_par().y()/2);
|
{
|
||||||
|
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> >();
|
QVector<NodeValue> points = row[kPointsInput].data().value< QVector<NodeValue> >();
|
||||||
|
|
||||||
gizmo_position_handles_.resize(points.size());
|
int current_pos_sz = gizmo_position_handles_.size();
|
||||||
gizmo_bezier_handles_.resize(points.size() * 2);
|
|
||||||
|
|
||||||
p->setPen(QPen(Qt::white, 0));
|
ValidateGizmoVectorSize(gizmo_position_handles_, points.size());
|
||||||
p->setBrush(Qt::NoBrush);
|
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()) {
|
if (!points.isEmpty()) {
|
||||||
QVector<QLineF> lines(points.size() * 2);
|
|
||||||
|
|
||||||
for (int i=0; i<points.size(); i++) {
|
for (int i=0; i<points.size(); i++) {
|
||||||
const Bezier &pt = points.at(i).data().value<Bezier>();
|
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 cp1 = main + pt.ControlPoint1ToPointF();
|
||||||
QPointF cp2 = main + pt.ControlPoint2ToPointF();
|
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);
|
gizmo_bezier_handles_[i*2]->SetPoint(cp1);
|
||||||
lines[i*2] = QLineF(main, cp1);
|
gizmo_bezier_lines_[i*2]->SetLine(QLineF(main, cp1));
|
||||||
|
gizmo_bezier_handles_[i*2+1]->SetPoint(cp2);
|
||||||
gizmo_bezier_handles_[i*2+1] = CreateGizmoHandleRect(cp2, bezier_radius);
|
gizmo_bezier_lines_[i*2+1]->SetLine(QLineF(main, cp2));
|
||||||
lines[i*2+1] = QLineF(main, cp2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p->drawLines(lines);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gizmo_polygon_path_ = GeneratePath(points);
|
poly_gizmo_->SetPath(GeneratePath(points).translated(half_res));
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
if (gizmo == poly_gizmo_) {
|
||||||
|
// FIXME: Drag all points
|
||||||
for (int i=0; i<gizmo_position_handles_.size(); i++) {
|
} else {
|
||||||
if (gizmo_position_handles_.at(i).contains(adjusted)) {
|
NodeInputDragger &x_drag = gizmo->GetDraggers()[0];
|
||||||
gizmo_x_active_.append(NodeKeyframeTrackReference(NodeInput(this, kPointsInput, i), 0));
|
NodeInputDragger &y_drag = gizmo->GetDraggers()[1];
|
||||||
gizmo_y_active_.append(NodeKeyframeTrackReference(NodeInput(this, kPointsInput, i), 1));
|
x_drag.Drag(x_drag.GetStartValue().toDouble() + x);
|
||||||
break;
|
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)
|
void PolygonGenerator::AddPointToPath(QPainterPath *path, const Bezier &before, const Bezier &after)
|
||||||
|
|||||||
@@ -24,6 +24,9 @@
|
|||||||
#include <QPainterPath>
|
#include <QPainterPath>
|
||||||
|
|
||||||
#include "common/bezier.h"
|
#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/node.h"
|
||||||
#include "node/inputdragger.h"
|
#include "node/inputdragger.h"
|
||||||
|
|
||||||
@@ -50,30 +53,29 @@ public:
|
|||||||
|
|
||||||
virtual void GenerateFrame(FramePtr frame, const GenerateJob &job) const override;
|
virtual void GenerateFrame(FramePtr frame, const GenerateJob &job) const override;
|
||||||
|
|
||||||
virtual bool HasGizmos() const override;
|
virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) 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;
|
|
||||||
|
|
||||||
static const QString kPointsInput;
|
static const QString kPointsInput;
|
||||||
static const QString kColorInput;
|
static const QString kColorInput;
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
virtual void GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void AddPointToPath(QPainterPath *path, const Bezier &before, const Bezier &after);
|
static void AddPointToPath(QPainterPath *path, const Bezier &before, const Bezier &after);
|
||||||
|
|
||||||
static QPainterPath GeneratePath(const QVector<NodeValue> &points);
|
static QPainterPath GeneratePath(const QVector<NodeValue> &points);
|
||||||
|
|
||||||
QPainterPath gizmo_polygon_path_;
|
template<typename T>
|
||||||
QVector<QRectF> gizmo_position_handles_;
|
void ValidateGizmoVectorSize(QVector<T*> &vec, int new_sz);
|
||||||
QVector<QRectF> gizmo_bezier_handles_;
|
|
||||||
|
|
||||||
QVector<NodeKeyframeTrackReference> gizmo_x_active_;
|
template<typename T>
|
||||||
QVector<NodeKeyframeTrackReference> gizmo_y_active_;
|
NodeGizmo *CreateAppropriateGizmo();
|
||||||
QVector<NodeInputDragger> gizmo_x_draggers_;
|
|
||||||
QVector<NodeInputDragger> gizmo_y_draggers_;
|
PathGizmo *poly_gizmo_;
|
||||||
QPointF gizmo_drag_start_;
|
QVector<PointGizmo*> gizmo_position_handles_;
|
||||||
|
QVector<PointGizmo*> gizmo_bezier_handles_;
|
||||||
|
QVector<LineGizmo*> gizmo_bezier_lines_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
#include <QtMath>
|
#include <QtMath>
|
||||||
#include <QVector2D>
|
#include <QVector2D>
|
||||||
|
|
||||||
#include "common/lerp.h"
|
#include "common/util.h"
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
@@ -34,12 +34,30 @@ QString ShapeNodeBase::kPositionInput = QStringLiteral("pos_in");
|
|||||||
QString ShapeNodeBase::kSizeInput = QStringLiteral("size_in");
|
QString ShapeNodeBase::kSizeInput = QStringLiteral("size_in");
|
||||||
QString ShapeNodeBase::kColorInput = QStringLiteral("color_in");
|
QString ShapeNodeBase::kColorInput = QStringLiteral("color_in");
|
||||||
|
|
||||||
ShapeNodeBase::ShapeNodeBase()
|
ShapeNodeBase::ShapeNodeBase(bool create_color_input)
|
||||||
{
|
{
|
||||||
AddInput(kPositionInput, NodeValue::kVec2, QVector2D(0, 0));
|
AddInput(kPositionInput, NodeValue::kVec2, QVector2D(0, 0));
|
||||||
AddInput(kSizeInput, NodeValue::kVec2, QVector2D(100, 100));
|
AddInput(kSizeInput, NodeValue::kVec2, QVector2D(100, 100));
|
||||||
SetInputProperty(kSizeInput, QStringLiteral("min"), QVector2D(0, 0));
|
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()
|
void ShapeNodeBase::Retranslate()
|
||||||
@@ -48,20 +66,19 @@ void ShapeNodeBase::Retranslate()
|
|||||||
|
|
||||||
SetInputName(kPositionInput, tr("Position"));
|
SetInputName(kPositionInput, tr("Position"));
|
||||||
SetInputName(kSizeInput, tr("Size"));
|
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
|
// Use offsets to make the appearance of values that start in the top left, even though we
|
||||||
// really anchor around the center
|
// really anchor around the center
|
||||||
QVector2D center_pt = globals.resolution() * 0.5;
|
QVector2D center_pt = globals.resolution() * 0.5;
|
||||||
SetInputProperty(kPositionInput, QStringLiteral("offset"), center_pt);
|
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 pos = row[kPositionInput].data().value<QVector2D>();
|
||||||
QVector2D sz = row[kSizeInput].data().value<QVector2D>();
|
QVector2D sz = row[kSizeInput].data().value<QVector2D>();
|
||||||
QVector2D half_sz = sz * 0.5;
|
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 top_pt = pos.y() + center_pt.y() - half_sz.y();
|
||||||
double right_pt = left_pt + sz.x();
|
double right_pt = left_pt + sz.x();
|
||||||
double bottom_pt = top_pt + sz.y();
|
double bottom_pt = top_pt + sz.y();
|
||||||
double center_x_pt = lerp(left_pt, right_pt, 0.5);
|
double center_x_pt = mid(left_pt, right_pt);
|
||||||
double center_y_pt = lerp(top_pt, bottom_pt, 0.5);
|
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);
|
point_gizmo_[kGizmoScaleTopLeft]->SetPoint(QPointF(left_pt, top_pt));
|
||||||
p->drawRect(gizmo_whole_rect_);
|
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);
|
poly_gizmo_->SetPolygon(QRectF(left_pt, top_pt, right_pt - left_pt, bottom_pt - top_pt));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
NodeInputDragger &x_drag = gizmo->GetDraggers()[0];
|
||||||
for (int i=0; i<kGizmoScaleCount; i++) {
|
NodeInputDragger &y_drag = gizmo->GetDraggers()[1];
|
||||||
if (gizmo_resize_handle_[i].contains(p)) {
|
|
||||||
gizmo_drag_ = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// See if the rect has the pointer
|
if (gizmo == poly_gizmo_) {
|
||||||
if (gizmo_drag_ == -1 && gizmo_whole_rect_.contains(p)) {
|
x_drag.Drag(x_drag.GetStartValue().toDouble() + x);
|
||||||
gizmo_drag_ = kGizmoWholeRect;
|
y_drag.Drag(y_drag.GetStartValue().toDouble() + y);
|
||||||
}
|
|
||||||
|
|
||||||
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());
|
|
||||||
} else {
|
} 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_size;
|
||||||
QVector2D new_pos;
|
QVector2D new_pos;
|
||||||
QVector2D anchor;
|
QVector2D anchor;
|
||||||
static const int kXYCount = 2;
|
static const int kXYCount = 2;
|
||||||
bool negative[kXYCount] = {false};
|
bool negative[kXYCount] = {false};
|
||||||
|
|
||||||
|
double original_ratio;
|
||||||
|
if (keep_ratio) {
|
||||||
|
original_ratio = w_drag.GetStartValue().toDouble() / h_drag.GetStartValue().toDouble();
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate new size
|
// Calculate new size
|
||||||
if (from_center) {
|
if (from_center) {
|
||||||
// Calculate new size by using distance from center and doubling it
|
// 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());
|
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());
|
new_size.setX(-new_size.x());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Calculate new size by using distance from "anchor" - i.e. the opposite point of the shape
|
// Calculate new size by using distance from "anchor" - i.e. the opposite point of the shape
|
||||||
// from the gizmo being dragged
|
// 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
|
// Calculate size and position
|
||||||
new_size = adjusted_pt - anchor;
|
new_size = adjusted_pt - anchor;
|
||||||
@@ -229,32 +168,31 @@ void ShapeNodeBase::GizmoMove(const QPointF &p, const rational &time, const Qt::
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Restrict sizes by constraints
|
// Restrict sizes by constraints
|
||||||
if (gizmo_drag_ == kGizmoScaleTopCenter || gizmo_drag_ == kGizmoScaleBottomCenter) {
|
if (IsGizmoVerticalCenter(gizmo)) {
|
||||||
if (keep_ratio) {
|
if (keep_ratio) {
|
||||||
// Calculate width from new height
|
// Calculate width from new height
|
||||||
new_size.setX(new_size.y() * gizmo_aspect_ratio_);
|
new_size.setX(new_size.y() * original_ratio);
|
||||||
} else {
|
} else {
|
||||||
// Constrain to original width
|
// 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) {
|
if (keep_ratio) {
|
||||||
// Calculate height from new width
|
// Calculate height from new width
|
||||||
new_size.setY(new_size.x() / gizmo_aspect_ratio_);
|
new_size.setY(new_size.x() / original_ratio);
|
||||||
} else {
|
} else {
|
||||||
// Constrain to original height
|
// 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
|
if (IsGizmoCorner(gizmo)) {
|
||||||
|| gizmo_drag_ == kGizmoScaleBottomRight || gizmo_drag_ == kGizmoScaleBottomLeft) {
|
|
||||||
if (keep_ratio) {
|
if (keep_ratio) {
|
||||||
float hypot = std::hypot(new_size.x(), new_size.y());
|
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
|
// Calculate new size based on original angle and hypotenuse
|
||||||
new_size.setX(std::sin(original_angle) * hypot);
|
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
|
// Calculate position
|
||||||
if (from_center) {
|
if (from_center) {
|
||||||
new_pos = gizmo_pos_start_;
|
new_pos = gizmo_pos_start;
|
||||||
} else {
|
} else {
|
||||||
QVector2D using_size = new_size;
|
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
|
// 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);
|
using_size.setY(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gizmo_drag_ == kGizmoScaleTopCenter || gizmo_drag_ == kGizmoScaleBottomCenter) {
|
if (IsGizmoVerticalCenter(gizmo)) {
|
||||||
using_size.setX(0);
|
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());
|
x_drag.Drag(new_pos.x());
|
||||||
gizmo_dragger_[kGDPosY].Drag(new_pos.y());
|
y_drag.Drag(new_pos.y());
|
||||||
gizmo_dragger_[kGDSzX].Drag(new_size.x());
|
w_drag.Drag(new_size.x());
|
||||||
gizmo_dragger_[kGDSzY].Drag(new_size.y());
|
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_) {
|
QVector2D anchor = pos;
|
||||||
i.End(command);
|
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
|
#ifndef SHAPENODEBASE_H
|
||||||
#define SHAPENODEBASE_H
|
#define SHAPENODEBASE_H
|
||||||
|
|
||||||
|
#include "node/gizmo/point.h"
|
||||||
|
#include "node/gizmo/polygon.h"
|
||||||
#include "node/inputdragger.h"
|
#include "node/inputdragger.h"
|
||||||
#include "node/node.h"
|
#include "node/node.h"
|
||||||
|
|
||||||
@@ -30,42 +32,42 @@ class ShapeNodeBase : public Node
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
ShapeNodeBase();
|
ShapeNodeBase(bool create_color_input = true);
|
||||||
|
|
||||||
NODE_DEFAULT_DESTRUCTOR(ShapeNodeBase)
|
NODE_DEFAULT_DESTRUCTOR(ShapeNodeBase)
|
||||||
|
|
||||||
virtual void Retranslate() override;
|
virtual void Retranslate() override;
|
||||||
|
|
||||||
|
virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override;
|
||||||
|
|
||||||
static QString kPositionInput;
|
static QString kPositionInput;
|
||||||
static QString kSizeInput;
|
static QString kSizeInput;
|
||||||
static QString kColorInput;
|
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;
|
protected slots:
|
||||||
|
virtual void GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers) 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;
|
|
||||||
|
|
||||||
private:
|
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
|
// Gizmo variables
|
||||||
static const int kGizmoWholeRect = kGizmoScaleCount;
|
static const int kGizmoWholeRect = kGizmoScaleCount;
|
||||||
QRectF gizmo_resize_handle_[kGizmoScaleCount];
|
PointGizmo *point_gizmo_[kGizmoScaleCount];
|
||||||
QRectF gizmo_whole_rect_;
|
PolygonGizmo *poly_gizmo_;
|
||||||
|
|
||||||
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_;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ namespace olive {
|
|||||||
|
|
||||||
const QString SolidGenerator::kColorInput = QStringLiteral("color_in");
|
const QString SolidGenerator::kColorInput = QStringLiteral("color_in");
|
||||||
|
|
||||||
|
#define super Node
|
||||||
|
|
||||||
SolidGenerator::SolidGenerator()
|
SolidGenerator::SolidGenerator()
|
||||||
{
|
{
|
||||||
// Default to a color that isn't black
|
// Default to a color that isn't black
|
||||||
@@ -59,6 +61,8 @@ QString SolidGenerator::Description() const
|
|||||||
|
|
||||||
void SolidGenerator::Retranslate()
|
void SolidGenerator::Retranslate()
|
||||||
{
|
{
|
||||||
|
super::Retranslate();
|
||||||
|
|
||||||
SetInputName(kColorInput, tr("Color"));
|
SetInputName(kColorInput, tr("Color"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,9 +16,11 @@
|
|||||||
|
|
||||||
set(OLIVE_SOURCES
|
set(OLIVE_SOURCES
|
||||||
${OLIVE_SOURCES}
|
${OLIVE_SOURCES}
|
||||||
node/generator/text/text.cpp
|
node/generator/text/textv1.cpp
|
||||||
node/generator/text/text.h
|
node/generator/text/textv1.h
|
||||||
node/generator/text/textlegacy.cpp
|
node/generator/text/textv2.cpp
|
||||||
node/generator/text/textlegacy.h
|
node/generator/text/textv2.h
|
||||||
|
node/generator/text/textv3.cpp
|
||||||
|
node/generator/text/textv3.h
|
||||||
PARENT_SCOPE
|
PARENT_SCOPE
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
***/
|
***/
|
||||||
|
|
||||||
#include "textlegacy.h"
|
#include "textv1.h"
|
||||||
|
|
||||||
#include <QAbstractTextDocumentLayout>
|
#include <QAbstractTextDocumentLayout>
|
||||||
#include <QTextDocument>
|
#include <QTextDocument>
|
||||||
@@ -31,14 +31,16 @@ enum TextVerticalAlign {
|
|||||||
kVerticalAlignBottom,
|
kVerticalAlignBottom,
|
||||||
};
|
};
|
||||||
|
|
||||||
const QString TextGeneratorLegacy::kTextInput = QStringLiteral("text_in");
|
const QString TextGeneratorV1::kTextInput = QStringLiteral("text_in");
|
||||||
const QString TextGeneratorLegacy::kHtmlInput = QStringLiteral("html_in");
|
const QString TextGeneratorV1::kHtmlInput = QStringLiteral("html_in");
|
||||||
const QString TextGeneratorLegacy::kColorInput = QStringLiteral("color_in");
|
const QString TextGeneratorV1::kColorInput = QStringLiteral("color_in");
|
||||||
const QString TextGeneratorLegacy::kVAlignInput = QStringLiteral("valign_in");
|
const QString TextGeneratorV1::kVAlignInput = QStringLiteral("valign_in");
|
||||||
const QString TextGeneratorLegacy::kFontInput = QStringLiteral("font_in");
|
const QString TextGeneratorV1::kFontInput = QStringLiteral("font_in");
|
||||||
const QString TextGeneratorLegacy::kFontSizeInput = QStringLiteral("font_size_in");
|
const QString TextGeneratorV1::kFontSizeInput = QStringLiteral("font_size_in");
|
||||||
|
|
||||||
TextGeneratorLegacy::TextGeneratorLegacy()
|
#define super Node
|
||||||
|
|
||||||
|
TextGeneratorV1::TextGeneratorV1()
|
||||||
{
|
{
|
||||||
AddInput(kTextInput, NodeValue::kText, tr("Sample Text"));
|
AddInput(kTextInput, NodeValue::kText, tr("Sample Text"));
|
||||||
|
|
||||||
@@ -53,28 +55,30 @@ TextGeneratorLegacy::TextGeneratorLegacy()
|
|||||||
AddInput(kFontSizeInput, NodeValue::kFloat, 72.0f);
|
AddInput(kFontSizeInput, NodeValue::kFloat, 72.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TextGeneratorLegacy::Name() const
|
QString TextGeneratorV1::Name() const
|
||||||
{
|
{
|
||||||
return tr("Text (Legacy)");
|
return tr("Text (Legacy)");
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TextGeneratorLegacy::id() const
|
QString TextGeneratorV1::id() const
|
||||||
{
|
{
|
||||||
return QStringLiteral("org.olivevideoeditor.Olive.textgenerator");
|
return QStringLiteral("org.olivevideoeditor.Olive.textgenerator");
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<Node::CategoryID> TextGeneratorLegacy::Category() const
|
QVector<Node::CategoryID> TextGeneratorV1::Category() const
|
||||||
{
|
{
|
||||||
return {kCategoryGenerator};
|
return {kCategoryGenerator};
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TextGeneratorLegacy::Description() const
|
QString TextGeneratorV1::Description() const
|
||||||
{
|
{
|
||||||
return tr("Generate rich text.");
|
return tr("Generate rich text.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextGeneratorLegacy::Retranslate()
|
void TextGeneratorV1::Retranslate()
|
||||||
{
|
{
|
||||||
|
super::Retranslate();
|
||||||
|
|
||||||
SetInputName(kTextInput, tr("Text"));
|
SetInputName(kTextInput, tr("Text"));
|
||||||
SetInputName(kHtmlInput, tr("Enable HTML"));
|
SetInputName(kHtmlInput, tr("Enable HTML"));
|
||||||
SetInputName(kFontInput, tr("Font"));
|
SetInputName(kFontInput, tr("Font"));
|
||||||
@@ -84,7 +88,7 @@ void TextGeneratorLegacy::Retranslate()
|
|||||||
SetComboBoxStrings(kVAlignInput, {tr("Top"), tr("Center"), tr("Bottom")});
|
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;
|
GenerateJob job;
|
||||||
job.InsertValue(value);
|
job.InsertValue(value);
|
||||||
@@ -95,7 +99,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.
|
// 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
|
// 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
|
#ifndef TEXTGENERATORV1_H
|
||||||
#define TEXTGENERATORLEGACY_H
|
#define TEXTGENERATORV1_H
|
||||||
|
|
||||||
#include "node/node.h"
|
#include "node/node.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
class TextGeneratorLegacy : public Node
|
class TextGeneratorV1 : public Node
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
TextGeneratorLegacy();
|
TextGeneratorV1();
|
||||||
|
|
||||||
NODE_DEFAULT_DESTRUCTOR(TextGeneratorLegacy)
|
NODE_DEFAULT_DESTRUCTOR(TextGeneratorV1)
|
||||||
NODE_COPY_FUNCTION(TextGeneratorLegacy)
|
NODE_COPY_FUNCTION(TextGeneratorV1)
|
||||||
|
|
||||||
virtual QString Name() const override;
|
virtual QString Name() const override;
|
||||||
virtual QString id() 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 <QAbstractTextDocumentLayout>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
@@ -37,13 +37,13 @@ enum TextVerticalAlign {
|
|||||||
kVerticalAlignBottom,
|
kVerticalAlignBottom,
|
||||||
};
|
};
|
||||||
|
|
||||||
const QString TextGenerator::kTextInput = QStringLiteral("text_in");
|
const QString TextGeneratorV2::kTextInput = QStringLiteral("text_in");
|
||||||
const QString TextGenerator::kHtmlInput = QStringLiteral("html_in");
|
const QString TextGeneratorV2::kHtmlInput = QStringLiteral("html_in");
|
||||||
const QString TextGenerator::kVAlignInput = QStringLiteral("valign_in");
|
const QString TextGeneratorV2::kVAlignInput = QStringLiteral("valign_in");
|
||||||
const QString TextGenerator::kFontInput = QStringLiteral("font_in");
|
const QString TextGeneratorV2::kFontInput = QStringLiteral("font_in");
|
||||||
const QString TextGenerator::kFontSizeInput = QStringLiteral("font_size_in");
|
const QString TextGeneratorV2::kFontSizeInput = QStringLiteral("font_size_in");
|
||||||
|
|
||||||
TextGenerator::TextGenerator()
|
TextGeneratorV2::TextGeneratorV2()
|
||||||
{
|
{
|
||||||
AddInput(kTextInput, NodeValue::kText, tr("Sample Text"));
|
AddInput(kTextInput, NodeValue::kText, tr("Sample Text"));
|
||||||
|
|
||||||
@@ -59,27 +59,27 @@ TextGenerator::TextGenerator()
|
|||||||
SetStandardValue(kSizeInput, QVector2D(400, 300));
|
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");
|
return QStringLiteral("org.olivevideoeditor.Olive.text2");
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<Node::CategoryID> TextGenerator::Category() const
|
QVector<Node::CategoryID> TextGeneratorV2::Category() const
|
||||||
{
|
{
|
||||||
return {kCategoryGenerator};
|
return {kCategoryGenerator};
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TextGenerator::Description() const
|
QString TextGeneratorV2::Description() const
|
||||||
{
|
{
|
||||||
return tr("Generate rich text.");
|
return tr("Generate rich text.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextGenerator::Retranslate()
|
void TextGeneratorV2::Retranslate()
|
||||||
{
|
{
|
||||||
super::Retranslate();
|
super::Retranslate();
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ void TextGenerator::Retranslate()
|
|||||||
SetComboBoxStrings(kVAlignInput, {tr("Top"), tr("Center"), tr("Bottom")});
|
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;
|
GenerateJob job;
|
||||||
job.InsertValue(value);
|
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.
|
// 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
|
// 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
|
#ifndef TEXTGENERATORV2_H
|
||||||
#define TEXTGENERATOR_H
|
#define TEXTGENERATORV2_H
|
||||||
|
|
||||||
#include "node/generator/shape/shapenodebase.h"
|
#include "node/generator/shape/shapenodebase.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
class TextGenerator : public ShapeNodeBase
|
class TextGeneratorV2 : public ShapeNodeBase
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
TextGenerator();
|
TextGeneratorV2();
|
||||||
|
|
||||||
NODE_DEFAULT_DESTRUCTOR(TextGenerator)
|
NODE_DEFAULT_DESTRUCTOR(TextGeneratorV2)
|
||||||
NODE_COPY_FUNCTION(TextGenerator)
|
NODE_COPY_FUNCTION(TextGeneratorV2)
|
||||||
|
|
||||||
virtual QString Name() const override;
|
virtual QString Name() const override;
|
||||||
virtual QString id() 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
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user