/*** Olive - Non-Linear Video Editor Copyright (C) 2022 Olive Team Modifications Copyright (C) 2025 mikesolar 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 . ***/ #include "scopebase.h" #include "config/config.h" #include "render/job/colortransformjob.h" namespace olive { #define super ManagedDisplayWidget ScopeBase::ScopeBase(QWidget *parent) : super(parent) , texture_(nullptr) , managed_tex_up_to_date_(false) , software_image_up_to_date_(false) { EnableDefaultContextMenu(); } void ScopeBase::SetBuffer(TexturePtr frame) { texture_ = frame; managed_tex_up_to_date_ = false; software_image_up_to_date_ = false; update(); } void ScopeBase::showEvent(QShowEvent *e) { super::showEvent(e); } void ScopeBase::DrawScope(TexturePtr managed_tex, QVariant pipeline) { ShaderJob job; job.Insert(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(managed_tex))); renderer()->Blit(pipeline, job, GetViewportParams()); } void ScopeBase::UpdateSoftwareImage() { if (!texture_ || texture_->IsDummy() || !renderer()) { software_image_ = QImage(); software_image_up_to_date_ = true; return; } // Backend-neutral widgets each have their own renderer instance (e.g. a // separate Vulkan device). The reference texture emitted by the viewer lives // in the viewer's renderer, so we must download it to the CPU and re-upload // it into this scope's renderer before we can sample it. TexturePtr source_tex = texture_; if (texture_->renderer() && texture_->renderer() != renderer()) { FramePtr temp_frame = Frame::Create(); temp_frame->set_video_params(texture_->params()); temp_frame->allocate(); texture_->Download(temp_frame->data(), temp_frame->linesize_pixels()); local_texture_ = renderer()->CreateTexture( temp_frame->video_params(), temp_frame->data(), temp_frame->linesize_pixels()); source_tex = local_texture_; } if (!source_tex || source_tex->IsDummy()) { software_image_ = QImage(); software_image_up_to_date_ = true; return; } const int texture_width = static_cast(width() * devicePixelRatioF()); const int texture_height = static_cast(height() * devicePixelRatioF()); const VideoParams offscreen_params( texture_width, texture_height, PixelFormat::U8, VideoParams::kRGBAChannelCount); if (!software_tex_ || software_tex_->params() != offscreen_params) { software_tex_ = renderer()->CreateTexture(offscreen_params); software_buffer_.resize( texture_width * texture_height * VideoParams::GetBytesPerPixel(PixelFormat::U8, VideoParams::kRGBAChannelCount)); } if (!software_tex_ || software_tex_->IsDummy()) { software_image_ = QImage(); software_image_up_to_date_ = true; return; } ColorTransformJob job; job.SetColorProcessor(color_service()); job.SetInputTexture(source_tex); job.SetInputAlphaAssociation(kAlphaNone); job.SetClearDestinationEnabled(true); job.SetForceOpaque(true); renderer()->BlitColorManaged(job, software_tex_.get()); renderer()->DownloadFromTexture(software_tex_->id(), software_tex_->params(), software_buffer_.data(), 0); software_image_ = QImage( reinterpret_cast(software_buffer_.constData()), texture_width, texture_height, texture_width * VideoParams::GetBytesPerPixel(PixelFormat::U8, VideoParams::kRGBAChannelCount), QImage::Format_RGBA8888_Premultiplied); software_image_.setDevicePixelRatio(devicePixelRatioF()); software_image_up_to_date_ = true; } void ScopeBase::OnInit() { super::OnInit(); if (!IsBackendNeutral()) { pipeline_ = renderer()->CreateNativeShader(GenerateShaderCode()); } } void ScopeBase::OnPaint() { if (IsBackendNeutral()) { if (!software_image_up_to_date_) { UpdateSoftwareImage(); } QPainter p(paint_device()); p.fillRect(rect(), Qt::black); if (!software_image_.isNull()) { DrawScopeSoftware(p, software_image_); } return; } // Clear display surface renderer()->ClearDestination(); if (texture_) { // Convert reference frame to display space if (!managed_tex_ || !managed_tex_up_to_date_ || managed_tex_->params() != texture_->params()) { managed_tex_ = renderer()->CreateTexture(texture_->params()); ColorTransformJob job; job.SetColorProcessor(color_service()); job.SetInputTexture(texture_); job.SetInputAlphaAssociation(kAlphaNone); renderer()->BlitColorManaged(job, managed_tex_.get()); managed_tex_up_to_date_ = true; } DrawScope(managed_tex_, pipeline_); } } void ScopeBase::OnDestroy() { local_texture_ = nullptr; software_tex_ = nullptr; software_buffer_.clear(); software_image_ = QImage(); managed_tex_ = nullptr; texture_ = nullptr; pipeline_.clear(); super::OnDestroy(); } }