Merge branch 'refs/heads/dev' into plugin

# Conflicts:
#	.gitmodules
#	CMakeLists.txt
#	ext/core
This commit is contained in:
Mike Solar
2025-12-13 21:45:46 +08:00
20 changed files with 203 additions and 142 deletions
+6 -4
View File
@@ -1,6 +1,8 @@
[submodule "ext/KDDockWidgets"]
path = ext/KDDockWidgets
url = https://github.com/olive-editor/KDDockWidgets.git
[submodule "ext/core"] [submodule "ext/core"]
path = ext/core path = ext/core
url = https://github.com/olive-editor/core url = https://github.com/OliveCommunity/core.git
branch = dev
[submodule "ext/KDDockWidgets"]
path = ext/KDDockWidgets
url = https://github.com/OliveCommunity/KDDockWidgets.git
branch = main
@@ -0,0 +1,25 @@
From 6b0ef44eb189411d36c739ccde8a081a3e62034a Mon Sep 17 00:00:00 2001
From: Mike Solar <iam@mikesolar.com>
Date: Mon, 24 Nov 2025 20:57:12 +0800
Subject: [PATCH] Fix include path for Window_p.h in qtcommon directory
---
src/qtcommon/Window_p.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/qtcommon/Window_p.h b/src/qtcommon/Window_p.h
index f29b209a5..d1d6b47ad 100644
--- a/src/qtcommon/Window_p.h
+++ b/src/qtcommon/Window_p.h
@@ -11,7 +11,7 @@
#pragma once
-#include "core/Window_p.h"
+#include "../core/Window_p.h"
#include "Screen_p.h"
#include <QPointer>
--
2.52.0
+9 -11
View File
@@ -14,16 +14,14 @@
# 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/>.
cmake_minimum_required(VERSION 3.30 FATAL_ERROR) cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
project(olive-editor VERSION 0.2.0 LANGUAGES CXX) project(olive-editor VERSION 0.2.0 LANGUAGES CXX)
if(${CMAKE_BUILD_TYPE} EQUAL Debug)
set(CMAKE_CXX_STANDARD 23) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=memory -g")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=memory -g")
set(CMAKE_CXX_SCAN_FOR_MODULES ON) set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} -fsanitize=memory)
set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD ON) endif ()
find_package(expat REQUIRED)
option(BUILD_QT6 "Build with Qt 6 over 5 (experimental)" ON) option(BUILD_QT6 "Build with Qt 6 over 5 (experimental)" ON)
option(BUILD_DOXYGEN "Build Doxygen documentation" OFF) option(BUILD_DOXYGEN "Build Doxygen documentation" OFF)
option(BUILD_TESTS "Build unit tests" OFF) option(BUILD_TESTS "Build unit tests" OFF)
@@ -37,11 +35,10 @@ set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON) set(CMAKE_AUTORCC ON)
# Generates a compile_commands.json in the build dir, link that to the repo # Generates a compile_commands.json in the build dir, link that to the repo
# root to enrich your IDE with clangd language server protocol functionalities # root to enrich your IDE with clangd language server protocol functionalities
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
add_subdirectory(third_party/openfx/HostSupport)
# Sanitizers # Sanitizers
add_library(olive-sanitizers INTERFACE) add_library(olive-sanitizers INTERFACE)
include(cmake/Sanitizers.cmake) include(cmake/Sanitizers.cmake)
@@ -110,6 +107,7 @@ list(APPEND OLIVE_INCLUDE_DIRS ${OPENEXR_INCLUDES})
list(APPEND OLIVE_LIBRARIES olivecore) list(APPEND OLIVE_LIBRARIES olivecore)
list(APPEND OLIVE_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/ext/core/include) list(APPEND OLIVE_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/ext/core/include)
# Link Qt # Link Qt
set(QT_LIBRARIES set(QT_LIBRARIES
Core Core
@@ -175,7 +173,7 @@ list(APPEND OLIVE_LIBRARIES
) )
# Link FFmpeg # Link FFmpeg
find_package(FFMPEG 7.0 REQUIRED find_package(FFMPEG REQUIRED
COMPONENTS COMPONENTS
avutil avutil
avcodec avcodec
+76 -107
View File
@@ -26,6 +26,7 @@ extern "C" {
#include <libavutil/imgutils.h> #include <libavutil/imgutils.h>
#include <libavutil/opt.h> #include <libavutil/opt.h>
#include <libavutil/pixdesc.h> #include <libavutil/pixdesc.h>
#include <libavutil/frame.h>
} }
#include <OpenImageIO/imagebuf.h> #include <OpenImageIO/imagebuf.h>
@@ -137,7 +138,7 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f,
break; break;
} }
AVFrame *hw_in = f.get(); AVFramePtr hw_in = f;
VideoParams plane_params = vp; VideoParams plane_params = vp;
plane_params.set_channel_count(1); plane_params.set_channel_count(1);
@@ -145,7 +146,7 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f,
TexturePtr y_plane = p.renderer->CreateTexture( TexturePtr y_plane = p.renderer->CreateTexture(
plane_params, hw_in->data[0], hw_in->linesize[0] / px_size); plane_params, hw_in->data[0], hw_in->linesize[0] / px_size);
y_plane->handleFrame(hw_in);
switch (f->format) { switch (f->format) {
case AV_PIX_FMT_YUV420P: case AV_PIX_FMT_YUV420P:
case AV_PIX_FMT_YUV422P: case AV_PIX_FMT_YUV422P:
@@ -167,8 +168,11 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f,
TexturePtr u_plane = p.renderer->CreateTexture( TexturePtr u_plane = p.renderer->CreateTexture(
plane_params, hw_in->data[1], hw_in->linesize[1] / px_size); plane_params, hw_in->data[1], hw_in->linesize[1] / px_size);
u_plane->handleFrame(hw_in);
TexturePtr v_plane = p.renderer->CreateTexture( TexturePtr v_plane = p.renderer->CreateTexture(
plane_params, hw_in->data[2], hw_in->linesize[2] / px_size); plane_params, hw_in->data[2], hw_in->linesize[2] / px_size);
v_plane->handleFrame(hw_in);
ShaderJob job; ShaderJob job;
job.Insert(QStringLiteral("y_channel"), job.Insert(QStringLiteral("y_channel"),
@@ -204,6 +208,7 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f,
case AV_PIX_FMT_RGBA: case AV_PIX_FMT_RGBA:
case AV_PIX_FMT_RGBA64LE: case AV_PIX_FMT_RGBA64LE:
// RGBA can be uploaded directly to the texture // RGBA can be uploaded directly to the texture
tex->handleFrame(f);
tex->Upload(f->data[0], f->linesize[0] / vp.GetBytesPerPixel()); tex->Upload(f->data[0], f->linesize[0] / vp.GetBytesPerPixel());
break; break;
} }
@@ -279,14 +284,17 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
AVCOL_RANGE_MPEG; AVCOL_RANGE_MPEG;
// Perform any CPU processing required // Perform any CPU processing required
f = PreProcessFrame(f, p); AVFramePtr ptr = PreProcessFrame(f, p);
f=std::move(ptr);
if (!f) { if (!f) {
// Error occurred while software scaling // Error occurred while software scaling
return nullptr; return nullptr;
} }
// Finally, perform any GPU processing required // Finally, perform any GPU processing required
return ProcessFrameIntoTexture(f, p, original); TexturePtr texture = ProcessFrameIntoTexture(f, p, original);
return texture;
} }
return nullptr; return nullptr;
@@ -380,116 +388,77 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename,
VideoParams::kInterlaceNone; VideoParams::kInterlaceNone;
{ {
// Read at least two frames to get more information about this video stream // Read at least two frames to get more information about this video stream
AVPacket *pkt = av_packet_alloc(); AVPacket *pkt = av_packet_alloc();
AVFrame *frame = av_frame_alloc(); AVFrame *frame = av_frame_alloc();
{ VideoParams::Interlacing interlacing = VideoParams::kInterlaceNone;
Instance instance; AVRational pixel_aspect_ratio = {1, 1};
instance.Open(filename_c, avstream->index); AVRational frame_rate = avstream->avg_frame_rate;
AVPixelFormat compatible_pix_fmt =
FFmpegUtils::GetCompatiblePixelFormat(
static_cast<AVPixelFormat>(avstream->codecpar->format));
bool image_is_still = false;
// Read first frame and retrieve some metadata {
if (instance.GetFrame(pkt, frame) >= 0) { Instance instance;
// Check if video is interlaced and what field dominance it has if so if (instance.Open(filename_c, avstream->index) != 0)
if (frame->interlaced_frame) { goto cleanup;
if (frame->top_field_first) {
interlacing =
VideoParams::kInterlacedTopFirst;
} else {
interlacing =
VideoParams::kInterlacedBottomFirst;
}
}
pixel_aspect_ratio = AVCodecContext *avctx = instance.codec_ctx();
av_guess_sample_aspect_ratio( interlacing = FFmpegFieldOrderToOlive(avctx->field_order);
instance.fmt_ctx(), instance.avstream(),
frame);
frame_rate = av_guess_frame_rate( if (instance.GetFrame(pkt, frame) >= 0) {
instance.fmt_ctx(), instance.avstream(), pixel_aspect_ratio =
frame); av_guess_sample_aspect_ratio(instance.fmt_ctx(),
instance.avstream(), frame);
frame_rate =
av_guess_frame_rate(instance.fmt_ctx(),
instance.avstream(), frame);
}
compatible_pix_fmt = int ret = instance.GetFrame(pkt, frame);
FFmpegUtils::GetCompatiblePixelFormat( if (ret == AVERROR_EOF) {
static_cast<AVPixelFormat>( image_is_still = true;
avstream->codecpar->format)); } else if (avstream->duration == AV_NOPTS_VALUE ||
} duration_guessed_from_bitrate) {
int64_t last_ts = frame->best_effort_timestamp;
while (instance.GetFrame(pkt, frame) >= 0 &&
(!cancelled || !cancelled->IsCancelled()))
last_ts = frame->best_effort_timestamp;
avstream->duration = last_ts;
}
// Read second frame instance.Close();
int ret = instance.GetFrame(pkt, frame); }
if (ret >= 0) { cleanup:
// Check if we need a manual duration av_frame_free(&frame);
if (avstream->duration == AV_NOPTS_VALUE || av_packet_free(&pkt);
duration_guessed_from_bitrate) {
if (footage_duration == AV_NOPTS_VALUE ||
duration_guessed_from_bitrate) {
// Manually read through file for duration
int64_t new_dur;
do { VideoParams stream;
new_dur = stream.set_stream_index(i);
frame->best_effort_timestamp; stream.set_width(avstream->codecpar->width);
} while (instance.GetFrame( stream.set_height(avstream->codecpar->height);
pkt, frame) >= 0 && stream.set_video_type(image_is_still ? VideoParams::kVideoTypeStill
(!cancelled || : VideoParams::kVideoTypeVideo);
!cancelled->IsCancelled())); stream.set_format(GetNativePixelFormat(compatible_pix_fmt));
stream.set_channel_count(GetNativeChannelCount(compatible_pix_fmt));
stream.set_interlacing(interlacing); // <-- 已正确填充
stream.set_pixel_aspect_ratio(pixel_aspect_ratio);
stream.set_frame_rate(frame_rate);
stream.set_start_time(avstream->start_time);
stream.set_time_base(avstream->time_base);
stream.set_duration(avstream->duration);
stream.set_color_range(avstream->codecpar->color_range == AVCOL_RANGE_JPEG
? VideoParams::kColorRangeFull
: VideoParams::kColorRangeLimited);
stream.set_premultiplied_alpha(false);
avstream->duration = new_dur; desc.AddVideoStream(stream);
image_is_still ? still_streams++ : video_streams++;
} else {
// Fallback to footage duration
avstream->duration =
Timecode::rescale_timestamp_ceil(
footage_duration,
rational(1, AV_TIME_BASE),
avstream->time_base);
}
}
} else if (ret == AVERROR_EOF) {
// Video has only one frame in it, treat it like a still image
image_is_still = true;
}
instance.Close();
}
av_frame_free(&frame);
av_packet_free(&pkt);
} }
VideoParams stream;
stream.set_stream_index(i);
stream.set_width(avstream->codecpar->width);
stream.set_height(avstream->codecpar->height);
stream.set_video_type((image_is_still) ?
VideoParams::kVideoTypeStill :
VideoParams::kVideoTypeVideo);
stream.set_format(GetNativePixelFormat(compatible_pix_fmt));
stream.set_channel_count(
GetNativeChannelCount(compatible_pix_fmt));
stream.set_interlacing(interlacing);
stream.set_pixel_aspect_ratio(pixel_aspect_ratio);
stream.set_frame_rate(frame_rate);
stream.set_start_time(avstream->start_time);
stream.set_time_base(avstream->time_base);
stream.set_duration(avstream->duration);
stream.set_color_range(avstream->codecpar->color_range ==
AVCOL_RANGE_JPEG ?
VideoParams::kColorRangeFull :
VideoParams::kColorRangeLimited);
// Defaults to false, requires user intervention if incorrect
stream.set_premultiplied_alpha(false);
desc.AddVideoStream(stream);
if (image_is_still) {
still_streams++;
} else {
video_streams++;
}
} else if (avstream->codecpar->codec_type == } else if (avstream->codecpar->codec_type ==
AVMEDIA_TYPE_AUDIO) { AVMEDIA_TYPE_AUDIO) {
@@ -627,7 +596,7 @@ bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames,
} }
// Create resampling context // Create resampling context
AVChannelLayout layout = params.channel_layout(); AVChannelLayout layout = params.channel_layout();
SwrContext *resampler; SwrContext *resampler=NULL;
swr_alloc_set_opts2( swr_alloc_set_opts2(
&resampler, &layout, &resampler, &layout,
FFmpegUtils::GetFFmpegSampleFormat(params.format()), FFmpegUtils::GetFFmpegSampleFormat(params.format()),
@@ -835,7 +804,7 @@ AVFramePtr FFmpegDecoder::PreProcessFrame(AVFramePtr f,
dest->format = f->format; dest->format = f->format;
dest->color_range = f->color_range; dest->color_range = f->color_range;
dest->colorspace = f->colorspace; dest->colorspace = f->colorspace;
dest->hw_frames_ctx = nullptr;
if (p.divider > 1) { if (p.divider > 1) {
dest->width = VideoParams::GetScaledDimension(dest->width, p.divider); dest->width = VideoParams::GetScaledDimension(dest->width, p.divider);
dest->height = VideoParams::GetScaledDimension(dest->height, p.divider); dest->height = VideoParams::GetScaledDimension(dest->height, p.divider);
@@ -888,8 +857,8 @@ AVFramePtr FFmpegDecoder::PreProcessFrame(AVFramePtr f,
dest->color_range == AVCOL_RANGE_JPEG ? 1 : 0, 0, 0x10000, 0x10000); dest->color_range == AVCOL_RANGE_JPEG ? 1 : 0, 0, 0x10000, 0x10000);
} }
r = sws_scale(sws_ctx_, f->data, f->linesize, 0, f->height, dest->data, r = sws_scale_frame(sws_ctx_, dest.get(), f.get());
dest->linesize);
if (r < 0) { if (r < 0) {
FFmpegError(r); FFmpegError(r);
return nullptr; return nullptr;
+18
View File
@@ -40,6 +40,20 @@ extern "C" {
namespace olive namespace olive
{ {
// 头文件里先备好枚举映射
static VideoParams::Interlacing FFmpegFieldOrderToOlive(AVFieldOrder fo)
{
switch (fo) {
case AV_FIELD_TT: // 隔行,顶场在前
return VideoParams::kInterlacedTopFirst;
case AV_FIELD_BB: // 隔行,底场在前
return VideoParams::kInterlacedBottomFirst;
case AV_FIELD_PROGRESSIVE:
default:
return VideoParams::kInterlaceNone;
}
}
/** /**
* @brief A Decoder derivative that wraps FFmpeg functions as on Olive decoder * @brief A Decoder derivative that wraps FFmpeg functions as on Olive decoder
*/ */
@@ -122,6 +136,10 @@ private:
{ {
return avstream_; return avstream_;
} }
AVCodecContext *codec_ctx()
{
return codec_ctx_;
}
private: private:
AVFormatContext *fmt_ctx_; AVFormatContext *fmt_ctx_;
+2 -1
View File
@@ -22,6 +22,7 @@ extern "C" {
#include <libavfilter/buffersink.h> #include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h> #include <libavfilter/buffersrc.h>
#include <libavutil/pixdesc.h> #include <libavutil/pixdesc.h>
#include <libavformat/avformat.h>
} }
#include <QFile> #include <QFile>
@@ -866,7 +867,7 @@ void FFmpegEncoder::FlushEncoders()
} }
if (fmt_ctx_) { if (fmt_ctx_) {
if (fmt_ctx_->oformat->flags & AVFMT_ALLOW_FLUSH) { if (fmt_ctx_->oformat->flags) {
int r = av_interleaved_write_frame(fmt_ctx_, nullptr); int r = av_interleaved_write_frame(fmt_ctx_, nullptr);
if (r < 0) { if (r < 0) {
FFmpegError(tr("Failed to write interleaved packet"), r); FFmpegError(tr("Failed to write interleaved packet"), r);
+1 -1
View File
@@ -85,7 +85,7 @@ public:
using AVFramePtr = std::shared_ptr<AVFrame>; using AVFramePtr = std::shared_ptr<AVFrame>;
inline AVFramePtr CreateAVFramePtr(AVFrame *f) inline AVFramePtr CreateAVFramePtr(AVFrame *f)
{ {
return std::shared_ptr<AVFrame>(f, [](AVFrame *g) { av_frame_free(&g); }); return std::shared_ptr<AVFrame>(f, [](AVFrame *g) { av_frame_free(&g); });
} }
inline AVFramePtr CreateAVFramePtr() inline AVFramePtr CreateAVFramePtr()
{ {
+1
View File
@@ -774,6 +774,7 @@ void Core::StartGUI(bool full_screen)
connect(qApp, &QApplication::focusChanged, PanelManager::instance(), connect(qApp, &QApplication::focusChanged, PanelManager::instance(),
&PanelManager::FocusChanged); &PanelManager::FocusChanged);
KDDockWidgets::initFrontend(KDDockWidgets::FrontendType::QtWidgets);
// Set KDDockWidgets flags // Set KDDockWidgets flags
auto &config = KDDockWidgets::Config::self(); auto &config = KDDockWidgets::Config::self();
auto flags = config.flags(); auto flags = config.flags();
+1
View File
@@ -143,6 +143,7 @@ int decompress_project(const QString &project)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
// Set up debug handler // Set up debug handler
qInstallMessageHandler(olive::DebugHandler); qInstallMessageHandler(olive::DebugHandler);
+15 -3
View File
@@ -25,23 +25,26 @@
#include <QStyle> #include <QStyle>
#include <QStyleOption> #include <QStyleOption>
#include <QVariant> #include <QVariant>
#include "Window_p.h"
#include "panel/panelmanager.h" #include "panel/panelmanager.h"
#include <OpenImageIO/hash.h>
namespace olive namespace olive
{ {
#define super KDDockWidgets::DockWidget #define super KDDockWidgets::QtWidgets::DockWidget
PanelWidget::PanelWidget(const QString &object_name) PanelWidget::PanelWidget(const QString &object_name)
: super(object_name) : super(object_name)
, border_visible_(false) , border_visible_(false)
, signal_instead_of_close_(false) , signal_instead_of_close_(false)
{ {
setFocusPolicy(Qt::ClickFocus); View<QWidget>::setFocusPolicy(Qt::ClickFocus);
connect(this, &PanelWidget::shown, this, connect(this, &PanelWidget::shown, this,
static_cast<void (PanelWidget::*)()>(&PanelWidget::setFocus)); reinterpret_cast<void (PanelWidget::*)()>(&PanelWidget::setFocus));
PanelManager::instance()->RegisterPanel(this); PanelManager::instance()->RegisterPanel(this);
} }
@@ -123,6 +126,15 @@ void PanelWidget::changeEvent(QEvent *e)
if (e->type() == QEvent::LanguageChange) { if (e->type() == QEvent::LanguageChange) {
Retranslate(); Retranslate();
} }
if (e->type() == QEvent::WindowStateChange) {
if (isVisible() && !isMinimized()) {
emit shown(Qt::OtherFocusReason);
}
else {
emit hidden();
}
}
super::changeEvent(e); super::changeEvent(e);
} }
+13 -3
View File
@@ -19,18 +19,23 @@
#ifndef PANEL_WIDGET_H #ifndef PANEL_WIDGET_H
#define PANEL_WIDGET_H #define PANEL_WIDGET_H
#include "KDDockWidgets/src/core/Window_p.h"
#include "KDDockWidgets/src/qtwidgets/views/TabBar.h"
#include <kddockwidgets/DockWidget.h> #include <kddockwidgets/DockWidget.h>
#include <QEvent> #include <QEvent>
#include "common/define.h" #include "common/define.h"
#include <QTabWidget>
namespace olive namespace olive
{ {
/** /**
* @brief A widget that is always dockable within the MainWindow. * @brief A widget that is always dockable within the MainWindow.
*/ */
class PanelWidget : public KDDockWidgets::DockWidget { class PanelWidget : public KDDockWidgets::QtWidgets::DockWidget {
Q_OBJECT Q_OBJECT
public: public:
/** /**
@@ -277,7 +282,8 @@ public:
signals: signals:
void CloseRequested(); void CloseRequested();
void shown(Qt::FocusReason reason);
void hidden();
protected: protected:
/** /**
* @brief paintEvent * @brief paintEvent
@@ -321,7 +327,7 @@ protected slots:
* String to set the subtitle to * String to set the subtitle to
*/ */
void SetSubtitle(const QString &t); void SetSubtitle(const QString &t);
protected slots:
private: private:
/** /**
* @brief Internal function that sets the QDockWidget's window title whenever the title/subtitle change. * @brief Internal function that sets the QDockWidget's window title whenever the title/subtitle change.
@@ -337,6 +343,10 @@ private:
bool border_visible_; bool border_visible_;
bool signal_instead_of_close_; bool signal_instead_of_close_;
QMetaObject::Connection m_tabBarConnection;
QMetaObject::Connection m_windowConnection;
bool m_lastVisibleState = false;
}; };
} }
+1 -1
View File
@@ -178,7 +178,7 @@ void ProjectPanel::ItemDoubleClickSlot(Node *item)
PanelManager::instance()->MostRecentlyFocused<FootageViewerPanel>(); PanelManager::instance()->MostRecentlyFocused<FootageViewerPanel>();
panel->ConnectViewerNode(static_cast<Footage *>(item)); panel->ConnectViewerNode(static_cast<Footage *>(item));
panel->raise(); panel->raise();
panel->setFocus(); panel->setFocus(Qt::FocusReason::MouseFocusReason);
} else if (dynamic_cast<Sequence *>(item)) { } else if (dynamic_cast<Sequence *>(item)) {
// Open this sequence in the Timeline // Open this sequence in the Timeline
Core::instance()->main_window()->OpenSequence( Core::instance()->main_window()->OpenSequence(
+8 -1
View File
@@ -19,6 +19,8 @@
#ifndef RENDERTEXTURE_H #ifndef RENDERTEXTURE_H
#define RENDERTEXTURE_H #define RENDERTEXTURE_H
#include "common/ffmpegutils.h"
#include <memory> #include <memory>
#include <QVariant> #include <QVariant>
@@ -148,7 +150,10 @@ public:
{ {
return job_; return job_;
} }
void handleFrame(AVFramePtr ptr)
{
frame_=ptr;
}
private: private:
Renderer *renderer_; Renderer *renderer_;
@@ -157,6 +162,8 @@ private:
QVariant id_; QVariant id_;
AcceleratedJob *job_; AcceleratedJob *job_;
AVFramePtr frame_;
}; };
} }
+12
View File
@@ -56,6 +56,9 @@ const double &TimeScaledObject::timebase_dbl() const
rational TimeScaledObject::SceneToTime(const double &x, const double &x_scale, rational TimeScaledObject::SceneToTime(const double &x, const double &x_scale,
const rational &timebase, bool round) const rational &timebase, bool round)
{ {
if (timebase.isNull()) {
return rational();
}
double unscaled_time = x / x_scale / timebase.toDouble(); double unscaled_time = x / x_scale / timebase.toDouble();
// Adjust screen point by scale and timebase // Adjust screen point by scale and timebase
@@ -85,16 +88,25 @@ rational TimeScaledObject::SceneToTimeNoGrid(const double &x,
double TimeScaledObject::TimeToScene(const rational &time) const double TimeScaledObject::TimeToScene(const rational &time) const
{ {
if (timebase_.isNull()) {
return 0.0;
}
return time.toDouble() * scale_; return time.toDouble() * scale_;
} }
rational TimeScaledObject::SceneToTime(const double &x, bool round) const rational TimeScaledObject::SceneToTime(const double &x, bool round) const
{ {
if (timebase_.isNull()) {
return rational();
}
return SceneToTime(x, scale_, timebase_, round); return SceneToTime(x, scale_, timebase_, round);
} }
rational TimeScaledObject::SceneToTimeNoGrid(const double &x) const rational TimeScaledObject::SceneToTimeNoGrid(const double &x) const
{ {
if (timebase_.isNull()) {
return rational::fromDouble(x / scale_);
}
return SceneToTimeNoGrid(x, scale_); return SceneToTimeNoGrid(x, scale_);
} }
+5 -2
View File
@@ -1201,8 +1201,11 @@ void ViewerWidget::UpdateMinimumScale()
// Avoids divide by zero // Avoids divide by zero
SetMinimumScale(0); SetMinimumScale(0);
} else { } else {
SetMinimumScale(static_cast<double>(ruler()->width()) / double min_scale = static_cast<double>(ruler()->width()) /
GetConnectedNode()->GetLength().toDouble()); GetConnectedNode()->GetLength().toDouble();
// Ensure min_scale doesn't exceed max_scale to prevent crash
min_scale = qMin(min_scale, GetMaximumScale());
SetMinimumScale(min_scale);
} }
} }
+1 -1
View File
@@ -372,7 +372,7 @@ bool ViewerDisplayWidget::eventFilter(QObject *o, QEvent *e)
void ViewerDisplayWidget::OnPaint() void ViewerDisplayWidget::OnPaint()
{ {
// Clear background to empty // Clear background to empty
QColor bg_color = show_widget_background_ ? palette().window().color() : QColor bg_color = show_widget_background_ ? palette().window().color() :
Qt::black; Qt::black;
renderer()->ClearDestination(nullptr, bg_color.redF(), bg_color.greenF(), renderer()->ClearDestination(nullptr, bg_color.redF(), bg_color.greenF(),
bg_color.blueF()); bg_color.blueF());
+6 -4
View File
@@ -27,15 +27,17 @@
#include <QOffscreenSurface> #include <QOffscreenSurface>
#endif #endif
#include "KDDockWidgets/src/qtwidgets/Window_p.h"
#include "dialog/about/about.h" #include "dialog/about/about.h"
#include "mainmenu.h" #include "mainmenu.h"
#include "mainstatusbar.h" #include "mainstatusbar.h"
#include "KDDockWidgets/src/LayoutSaver.h"
#include "timeline/timelineundoworkarea.h" #include "timeline/timelineundoworkarea.h"
namespace olive namespace olive
{ {
#define super KDDockWidgets::MainWindow #define super KDDockWidgets::QtWidgets::MainWindow
MainWindow::MainWindow(QWidget *parent) MainWindow::MainWindow(QWidget *parent)
: super(QStringLiteral("OliveMain"), KDDockWidgets::MainWindowOption_None, : super(QStringLiteral("OliveMain"), KDDockWidgets::MainWindowOption_None,
@@ -93,7 +95,7 @@ MainWindow::MainWindow(QWidget *parent)
// emit the "shown" signal before emitting the "hidden" signals, resulting // emit the "shown" signal before emitting the "hidden" signals, resulting
// in Core thinking there are -1 pixel samplers open. To mitigate that, // in Core thinking there are -1 pixel samplers open. To mitigate that,
// we force "shown" to emit ourselves here. // we force "shown" to emit ourselves here.
emit pixel_sampler_panel_->shown(); emit pixel_sampler_panel_->shown(Qt::OtherFocusReason);
// Make node-related connections // Make node-related connections
connect(node_panel_, &NodePanel::NodeSelectionChangedWithContexts, connect(node_panel_, &NodePanel::NodeSelectionChangedWithContexts,
@@ -380,7 +382,7 @@ void MainWindow::ToggleMaximizedPanel()
premaximized_state_.clear(); premaximized_state_.clear();
currently_focused_panel->raise(); currently_focused_panel->raise();
currently_focused_panel->setFocus(); currently_focused_panel->setFocus(Qt::ActiveWindowFocusReason);
PanelManager::instance()->SetSuppressChangedSignal(false); PanelManager::instance()->SetSuppressChangedSignal(false);
} }
@@ -428,7 +430,7 @@ void MainWindow::SetProject(Project *p)
project_panel_->set_project(p); project_panel_->set_project(p);
if (project_) { if (project_) {
project_panel_->setFocus(); project_panel_->setFocus(Qt::OtherFocusReason);
} }
} }
+1 -1
View File
@@ -51,7 +51,7 @@ namespace olive
/** /**
* @brief Olive's main window responsible for docking widgets and the main menu bar. * @brief Olive's main window responsible for docking widgets and the main menu bar.
*/ */
class MainWindow : public KDDockWidgets::MainWindow { class MainWindow : public KDDockWidgets::QtWidgets::MainWindow {
Q_OBJECT Q_OBJECT
public: public:
MainWindow(QWidget *parent = nullptr); MainWindow(QWidget *parent = nullptr);