diff --git a/.travis/script.sh b/.travis/script.sh index d32a7fdd9..33feb95fd 100644 --- a/.travis/script.sh +++ b/.travis/script.sh @@ -17,19 +17,19 @@ if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then exit 1 fi - BUNDLE_PATH=$(find . -name "Olive.app") + BUNDLE_NAME=Olive.app - echo Found app at: $BUNDLE_PATH + mv app/$BUNDLE_NAME . # Move Qt deps into bundle - macdeployqt $BUNDLE_PATH + macdeployqt $BUNDLE_NAME # Fix other deps that macdeployqt missed wget -c -nv https://github.com/arl/macdeployqtfix/raw/master/macdeployqtfix.py - python2 macdeployqtfix.py $BUNDLE_PATH/Contents/MacOS/Olive /usr/local/Cellar/qt5/5.*/ + python2 macdeployqtfix.py $BUNDLE_NAME/Contents/MacOS/Olive /usr/local/Cellar/qt5/5.*/ # Distribute in zip - zip -r Olive-$VERSION-macOS.zip $BUNDLE_PATH + zip -r Olive-$VERSION-macOS.zip $BUNDLE_NAME elif [[ "$TRAVIS_OS_NAME" == "linux" ]]; then diff --git a/CMakeLists.txt b/CMakeLists.txt index 498a83fea..ccb6e9478 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,8 @@ find_package(OpenColorIO REQUIRED) find_package(OpenImageIO REQUIRED) +find_package(OpenEXR REQUIRED) + find_package(Qt5 5.6 REQUIRED COMPONENTS Core diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 334bed5a9..a6b6bd9d3 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -106,9 +106,11 @@ target_include_directories( ${FFMPEG_INCLUDE_DIRS} ${OCIO_INCLUDE_DIRS} ${OIIO_INCLUDE_DIRS} + ${OPENEXR_INCLUDE_DIRS} ) -target_link_libraries(${OLIVE_TARGET} +target_link_libraries( + ${OLIVE_TARGET} PRIVATE Qt5::Core Qt5::Gui @@ -125,8 +127,17 @@ target_link_libraries(${OLIVE_TARGET} FFMPEG::swresample ${OCIO_LIBRARIES} ${OIIO_LIBRARIES} + ${OPENEXR_LIBRARIES} ) +if (WIN32) + target_link_libraries( + ${OLIVE_TARGET} + PRIVATE + DbgHelp + ) +endif() + set(OLIVE_TS_FILES # FIXME: Empty variable ) @@ -151,3 +162,32 @@ if(DOXYGEN_FOUND) set(DOXYGEN_EXTRACT_PRIVATE "YES") doxygen_add_docs(docs ALL ${OLIVE_SOURCES}) endif() + +set(OLIVE_CRASH_TARGET "crashhandler") + +set(OLIVE_CRASH_SOURCES + dialog/crashhandler/crashhandler.h + dialog/crashhandler/crashhandler.cpp + dialog/crashhandler/crashhandlermain.cpp +) + +if (WIN32) + add_executable( + ${OLIVE_CRASH_TARGET} + WIN32 + ${OLIVE_CRASH_SOURCES} + ) +else() + add_executable( + ${OLIVE_CRASH_TARGET} + ${OLIVE_CRASH_SOURCES} + ) +endif() + +target_link_libraries( + ${OLIVE_CRASH_TARGET} + PRIVATE + Qt5::Core + Qt5::Gui + Qt5::Widgets +) diff --git a/app/codec/decoder.cpp b/app/codec/decoder.cpp index ed0dea143..224818912 100644 --- a/app/codec/decoder.cpp +++ b/app/codec/decoder.cpp @@ -61,7 +61,7 @@ void Decoder::set_stream(StreamPtr fs) stream_ = fs; } -FramePtr Decoder::RetrieveVideo(const rational &/*timecode*/) +FramePtr Decoder::RetrieveVideo(const rational &/*timecode*/, const int &/*divider*/) { return nullptr; } diff --git a/app/codec/decoder.h b/app/codec/decoder.h index 86ffc2710..af3507e33 100644 --- a/app/codec/decoder.h +++ b/app/codec/decoder.h @@ -142,7 +142,7 @@ public: * A FramePtr of valid data at this timecode or nullptr if there was nothing to retrieve at the provided timecode or * the media could not be opened. */ - virtual FramePtr RetrieveVideo(const rational& timecode); + virtual FramePtr RetrieveVideo(const rational& timecode, const int& divider); /** * @brief Retrieve video frame diff --git a/app/codec/ffmpeg/ffmpegcommon.cpp b/app/codec/ffmpeg/ffmpegcommon.cpp index d1236816a..7d560d28c 100644 --- a/app/codec/ffmpeg/ffmpegcommon.cpp +++ b/app/codec/ffmpeg/ffmpegcommon.cpp @@ -3,7 +3,9 @@ AVPixelFormat FFmpegCommon::GetCompatiblePixelFormat(const AVPixelFormat &pix_fmt) { AVPixelFormat possible_pix_fmts[] = { + AV_PIX_FMT_RGB24, AV_PIX_FMT_RGBA, + AV_PIX_FMT_RGB48, AV_PIX_FMT_RGBA64, AV_PIX_FMT_NONE }; @@ -73,8 +75,14 @@ AVPixelFormat FFmpegCommon::GetFFmpegPixelFormat(const PixelFormat::Format &pix_ return AV_PIX_FMT_RGBA; case PixelFormat::PIX_FMT_RGBA16U: return AV_PIX_FMT_RGBA64; + case PixelFormat::PIX_FMT_RGB8: + return AV_PIX_FMT_RGB24; + case PixelFormat::PIX_FMT_RGB16U: + return AV_PIX_FMT_RGB48; case PixelFormat::PIX_FMT_RGBA16F: case PixelFormat::PIX_FMT_RGBA32F: + case PixelFormat::PIX_FMT_RGB16F: + case PixelFormat::PIX_FMT_RGB32F: case PixelFormat::PIX_FMT_INVALID: case PixelFormat::PIX_FMT_COUNT: break; @@ -86,8 +94,14 @@ AVPixelFormat FFmpegCommon::GetFFmpegPixelFormat(const PixelFormat::Format &pix_ PixelFormat::Format FFmpegCommon::GetCompatiblePixelFormat(const PixelFormat::Format &pix_fmt) { switch (pix_fmt) { + case PixelFormat::PIX_FMT_RGB8: + return PixelFormat::PIX_FMT_RGB8; case PixelFormat::PIX_FMT_RGBA8: return PixelFormat::PIX_FMT_RGBA8; + case PixelFormat::PIX_FMT_RGB16U: + case PixelFormat::PIX_FMT_RGB16F: + case PixelFormat::PIX_FMT_RGB32F: + return PixelFormat::PIX_FMT_RGB16U; case PixelFormat::PIX_FMT_RGBA16U: case PixelFormat::PIX_FMT_RGBA16F: case PixelFormat::PIX_FMT_RGBA32F: diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index 48f0f9649..104c8b24e 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -36,15 +36,17 @@ extern "C" { #include "codec/waveinput.h" #include "common/define.h" #include "common/filefunctions.h" +#include "common/functiontimer.h" #include "common/timecodefunctions.h" #include "ffmpegcommon.h" #include "render/diskmanager.h" -#include "render/pixelservice.h" +#include "render/pixelformat.h" FFmpegDecoder::FFmpegDecoder() : fmt_ctx_(nullptr), codec_ctx_(nullptr), scale_ctx_(nullptr), + scale_divider_(-1), cache_at_zero_(false), cache_at_eof_(false), opts_(nullptr) @@ -153,31 +155,24 @@ bool FFmpegDecoder::Open() // Determine which Olive native pixel format we retrieved // Note that FFmpeg doesn't support float formats - if (ideal_pix_fmt_ == AV_PIX_FMT_RGBA) { + switch (ideal_pix_fmt_) { + case AV_PIX_FMT_RGB24: + native_pix_fmt_ = PixelFormat::PIX_FMT_RGB8; + break; + case AV_PIX_FMT_RGBA: native_pix_fmt_ = PixelFormat::PIX_FMT_RGBA8; - } else if (ideal_pix_fmt_ == AV_PIX_FMT_RGBA64) { + break; + case AV_PIX_FMT_RGB48: + native_pix_fmt_ = PixelFormat::PIX_FMT_RGB16U; + break; + case AV_PIX_FMT_RGBA64: native_pix_fmt_ = PixelFormat::PIX_FMT_RGBA16U; - } else { + break; + default: // We should never get here, but just in case... qFatal("Invalid output format"); } - scale_ctx_ = sws_getContext(avstream_->codecpar->width, - avstream_->codecpar->height, - static_cast(avstream_->codecpar->format), - avstream_->codecpar->width, - avstream_->codecpar->height, - ideal_pix_fmt_, - 0, - nullptr, - nullptr, - nullptr); - - if (!scale_ctx_) { - Error(QStringLiteral("Failed to allocate SwsContext")); - return false; - } - second_ts_ = qRound64(av_q2d(av_inv_q(avstream_->time_base))); QMetaObject::invokeMethod(&clear_timer_, "start"); @@ -210,7 +205,7 @@ Decoder::RetrieveState FFmpegDecoder::GetRetrieveState(const rational& time) return kReady; } -FramePtr FFmpegDecoder::RetrieveVideo(const rational &timecode) +FramePtr FFmpegDecoder::RetrieveVideo(const rational &timecode, const int ÷r) { QMutexLocker locker(&mutex_); @@ -227,21 +222,30 @@ FramePtr FFmpegDecoder::RetrieveVideo(const rational &timecode) Frame* return_frame = nullptr; + if (divider != scale_divider_) { + ClearFrameCache(); + FreeScaler(); + SetupScaler(divider); + } + // See if our RAM cache already has a frame that matches this timestamp if (!cached_frames_.isEmpty()) { - if (cache_at_zero_ && target_ts < cached_frames_.first()->native_timestamp()) { + if (target_ts < cached_frames_.first()->native_timestamp()) { - return_frame = cached_frames_.first(); - cached_frames_.accessedFirst(); + if (cache_at_zero_) { + return_frame = cached_frames_.first(); + cached_frames_.accessedFirst(); + } - } else if (cache_at_eof_ && target_ts > cached_frames_.last()->native_timestamp()) { + } else if (target_ts > cached_frames_.last()->native_timestamp()) { - return_frame = cached_frames_.last(); - cached_frames_.accessedLast(); + if (cache_at_eof_) { + return_frame = cached_frames_.last(); + cached_frames_.accessedLast(); + } - } else if (target_ts >= cached_frames_.first()->native_timestamp() - && target_ts <= cached_frames_.last()->native_timestamp()) { + } else { // We already have this frame in the cache, find it for (int i=0;icodecpar->width, - avstream_->codecpar->height, + Frame* working_frame_converted = cached_frames_.append(VideoRenderingParams(avstream_->codecpar->width / divider, + avstream_->codecpar->height / divider, avstream_->time_base, native_pix_fmt_, RenderMode::kOffline)); @@ -375,7 +378,7 @@ FramePtr FFmpegDecoder::RetrieveVideo(const rational &timecode) // Convert frame to RGBA for the rest of the pipeline uint8_t* output_data = reinterpret_cast(working_frame_converted->data()); - int output_linesize = working_frame_converted->width() * kRGBAChannels * PixelService::BytesPerChannel(native_pix_fmt_); + int output_linesize = working_frame_converted->width() * PixelFormat::ChannelCount(native_pix_fmt_) * PixelFormat::BytesPerChannel(native_pix_fmt_); sws_scale(scale_ctx_, working_frame->data, @@ -389,10 +392,10 @@ FramePtr FFmpegDecoder::RetrieveVideo(const rational &timecode) // We found the frame we want return_frame = working_frame_converted; } + } - if (return_frame) { - break; - } + if (return_frame) { + break; } } @@ -937,10 +940,7 @@ void FFmpegDecoder::ClearResources() ClearFrameCache(); - if (scale_ctx_) { - sws_freeContext(scale_ctx_); - scale_ctx_ = nullptr; - } + FreeScaler(); if (codec_ctx_) { avcodec_free_context(&codec_ctx_); @@ -955,6 +955,35 @@ void FFmpegDecoder::ClearResources() open_ = false; } +void FFmpegDecoder::SetupScaler(const int ÷r) +{ + scale_ctx_ = sws_getContext(avstream_->codecpar->width, + avstream_->codecpar->height, + static_cast(avstream_->codecpar->format), + avstream_->codecpar->width / divider, + avstream_->codecpar->height / divider, + ideal_pix_fmt_, + SWS_FAST_BILINEAR, + nullptr, + nullptr, + nullptr); + + if (!scale_ctx_) { + Error(QStringLiteral("Failed to allocate SwsContext")); + } else { + scale_divider_ = divider; + } +} + +void FFmpegDecoder::FreeScaler() +{ + if (scale_ctx_) { + sws_freeContext(scale_ctx_); + scale_ctx_ = nullptr; + scale_divider_ = -1; + } +} + void FFmpegDecoder::ClearTimerEvent() { QMutexLocker locker(&mutex_); diff --git a/app/codec/ffmpeg/ffmpegdecoder.h b/app/codec/ffmpeg/ffmpegdecoder.h index e80c70593..4ceb50184 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.h +++ b/app/codec/ffmpeg/ffmpegdecoder.h @@ -54,7 +54,7 @@ public: virtual bool Open() override; virtual RetrieveState GetRetrieveState(const rational &time) override; - virtual FramePtr RetrieveVideo(const rational &timecode) override; + virtual FramePtr RetrieveVideo(const rational &timecode, const int& divider) override; virtual FramePtr RetrieveAudio(const rational &timecode, const rational &length, const AudioRenderingParams& params) override; virtual void Close() override; @@ -102,12 +102,13 @@ private: void CacheFrameToDisk(AVFrame* f); - //void RemoveFirstFromFrameCache(); - //void RemoveLastFromFrameCache(); void ClearFrameCache(); void ClearResources(); + void SetupScaler(const int& divider); + void FreeScaler(); + AVFormatContext* fmt_ctx_; AVCodecContext* codec_ctx_; AVStream* avstream_; @@ -116,6 +117,7 @@ private: PixelFormat::Format native_pix_fmt_; SwsContext* scale_ctx_; + int scale_divider_; FFmpegFrameCache::Client cached_frames_; bool cache_at_zero_; diff --git a/app/codec/ffmpeg/ffmpegencoder.cpp b/app/codec/ffmpeg/ffmpegencoder.cpp index 6c0d6410d..ac3556f03 100644 --- a/app/codec/ffmpeg/ffmpegencoder.cpp +++ b/app/codec/ffmpeg/ffmpegencoder.cpp @@ -3,7 +3,7 @@ #include #include "ffmpegcommon.h" -#include "render/pixelservice.h" +#include "render/pixelformat.h" FFmpegEncoder::FFmpegEncoder(const EncodingParams ¶ms) : Encoder(params), @@ -188,12 +188,12 @@ void FFmpegEncoder::WriteInternal(FramePtr frame) // We may need to convert this frame to a frame that swscale will understand if (frame->format() != video_conversion_fmt_) { - frame = PixelService::ConvertPixelFormat(frame, video_conversion_fmt_); + frame = PixelFormat::ConvertPixelFormat(frame, video_conversion_fmt_); } // Use swscale context to convert formats/linesizes input_data = frame->const_data(); - input_linesize = frame->width() * PixelService::BytesPerPixel(video_conversion_fmt_); + input_linesize = frame->width() * PixelFormat::BytesPerPixel(video_conversion_fmt_); error_code = sws_scale(video_scale_ctx_, reinterpret_cast(&input_data), &input_linesize, diff --git a/app/codec/frame.cpp b/app/codec/frame.cpp index 0cdf5f144..07fc08012 100644 --- a/app/codec/frame.cpp +++ b/app/codec/frame.cpp @@ -23,8 +23,6 @@ #include #include -#include "render/pixelservice.h" - Frame::Frame() : width_(0), height_(0), @@ -139,7 +137,7 @@ void Frame::allocate() { // Assume this frame is intended to be a video frame if (width_ > 0 && height_ > 0) { - data_.resize(PixelService::GetBufferSize(static_cast(format_), width_, height_)); + data_.resize(PixelFormat::GetBufferSize(static_cast(format_), width_, height_)); } else if (sample_count_ > 0) { data_.resize(audio_params_.samples_to_bytes(sample_count_)); } diff --git a/app/codec/oiio/oiiodecoder.cpp b/app/codec/oiio/oiiodecoder.cpp index 837946af0..1e0e5562b 100644 --- a/app/codec/oiio/oiiodecoder.cpp +++ b/app/codec/oiio/oiiodecoder.cpp @@ -20,6 +20,7 @@ #include "oiiodecoder.h" +#include #include #include @@ -29,7 +30,7 @@ QStringList OIIODecoder::supported_formats_; OIIODecoder::OIIODecoder() : image_(nullptr), - frame_(nullptr) + buffer_(nullptr) { } @@ -83,6 +84,9 @@ bool OIIODecoder::Probe(Footage *f, const QAtomicInt *cancelled) image_stream->set_width(spec.width); image_stream->set_height(spec.height); + // Images will always have just one stream + image_stream->set_index(0); + // OIIO automatically premultiplies alpha // FIXME: We usually disassociate the alpha for the color management later, for 8-bit images this likely reduces the // fidelity? @@ -112,25 +116,31 @@ bool OIIODecoder::Open() width_ = spec.width; height_ = spec.height; + is_rgba_ = (spec.nchannels == kRGBAChannels); + // Weirdly, switch statement doesn't work correctly here if (spec.format == OIIO::TypeDesc::UINT8) { - pix_fmt_ = PixelFormat::PIX_FMT_RGBA8; + pix_fmt_ = is_rgba_ ? PixelFormat::PIX_FMT_RGBA8 : PixelFormat::PIX_FMT_RGB8; } else if (spec.format == OIIO::TypeDesc::UINT16) { - pix_fmt_ = PixelFormat::PIX_FMT_RGBA16U; + pix_fmt_ = is_rgba_ ? PixelFormat::PIX_FMT_RGBA16U : PixelFormat::PIX_FMT_RGB16U; } else if (spec.format == OIIO::TypeDesc::HALF) { - pix_fmt_ = PixelFormat::PIX_FMT_RGBA16F; + pix_fmt_ = is_rgba_ ? PixelFormat::PIX_FMT_RGBA16F : PixelFormat::PIX_FMT_RGB16F; } else if (spec.format == OIIO::TypeDesc::FLOAT) { - pix_fmt_ = PixelFormat::PIX_FMT_RGBA32F; + pix_fmt_ = is_rgba_ ? PixelFormat::PIX_FMT_RGBA32F : PixelFormat::PIX_FMT_RGB32F; } else { qWarning() << "Failed to convert OIIO::ImageDesc to native pixel format"; return false; } // FIXME: Many OIIO pixel formats are not handled here + type_ = PixelFormat::GetOIIOTypeDesc(pix_fmt_); - is_rgba_ = (spec.nchannels == kRGBAChannels); - - pix_fmt_info_ = PixelService::GetPixelFormatInfo(static_cast(pix_fmt_)); +#if OIIO_VERSION < 20100 + buffer_ = new OIIO::ImageBuf(OIIO::ImageSpec(spec.width, spec.height, spec.nchannels, type_)); +#else + buffer_ = new OIIO::ImageBuf(OIIO::ImageSpec(spec.width, spec.height, spec.nchannels, type_), OIIO::InitializePixels::No); +#endif + image_->read_image(type_, buffer_->localpixels()); open_ = true; @@ -148,7 +158,7 @@ Decoder::RetrieveState OIIODecoder::GetRetrieveState(const rational &time) return kReady; } -FramePtr OIIODecoder::RetrieveVideo(const rational &timecode) +FramePtr OIIODecoder::RetrieveVideo(const rational &timecode, const int& divider) { QMutexLocker locker(&mutex_); @@ -156,26 +166,20 @@ FramePtr OIIODecoder::RetrieveVideo(const rational &timecode) return nullptr; } - Q_UNUSED(timecode) + FramePtr frame = Frame::Create(); - if (!frame_) { - frame_ = Frame::Create(); + frame->set_width(width_ / divider); + frame->set_height(height_ / divider); + frame->set_format(pix_fmt_); + frame->allocate(); - frame_->set_width(width_); - frame_->set_height(height_); - frame_->set_format(pix_fmt_); - frame_->allocate(); + OIIO::ImageBuf dst(OIIO::ImageSpec(frame->width(), frame->height(), buffer_->spec().nchannels, buffer_->spec().format), frame->data()); - // Use the native format to determine what format OIIO should return - // FIXME: Behavior of RGB images as opposed to RGBA? - image_->read_image(pix_fmt_info_.oiio_desc, frame_->data()); - - if (!is_rgba_) { - PixelService::ConvertRGBtoRGBA(frame_); - } + if (!OIIO::ImageBufAlgo::resize(dst, *buffer_)) { + qWarning() << "OIIO resize failed"; } - return frame_; + return frame; } void OIIODecoder::Close() @@ -190,7 +194,8 @@ void OIIODecoder::Close() image_ = nullptr; } - frame_ = nullptr; + delete buffer_; + buffer_ = nullptr; } bool OIIODecoder::SupportsVideo() diff --git a/app/codec/oiio/oiiodecoder.h b/app/codec/oiio/oiiodecoder.h index fbbc7e8b5..be6e5f21e 100644 --- a/app/codec/oiio/oiiodecoder.h +++ b/app/codec/oiio/oiiodecoder.h @@ -22,9 +22,10 @@ #define OIIODECODER_H #include +#include #include "codec/decoder.h" -#include "render/pixelservice.h" +#include "render/pixelformat.h" class OIIODecoder : public Decoder { @@ -37,7 +38,7 @@ public: virtual bool Open() override; virtual RetrieveState GetRetrieveState(const rational &time) override; - virtual FramePtr RetrieveVideo(const rational &timecode) override; + virtual FramePtr RetrieveVideo(const rational &timecode, const int& divider) override; virtual void Close() override; virtual bool SupportsVideo() override; @@ -57,11 +58,11 @@ private: PixelFormat::Format pix_fmt_; - PixelFormat::Info pix_fmt_info_; + OIIO::TypeDesc type_; bool is_rgba_; - FramePtr frame_; + OIIO::ImageBuf* buffer_; static QStringList supported_formats_; diff --git a/app/common/CMakeLists.txt b/app/common/CMakeLists.txt index 743df6778..c02c8acf2 100644 --- a/app/common/CMakeLists.txt +++ b/app/common/CMakeLists.txt @@ -22,6 +22,8 @@ set(OLIVE_SOURCES common/channellayout.h common/clamp.h common/constructors.h + common/crashhandler.h + common/crashhandler.cpp common/debug.h common/debug.cpp common/define.h diff --git a/app/common/crashhandler.cpp b/app/common/crashhandler.cpp new file mode 100644 index 000000000..450046f08 --- /dev/null +++ b/app/common/crashhandler.cpp @@ -0,0 +1,122 @@ +#include "crashhandler.h" + +#include +#include +#include +#include +#include +#include +#include + +#if defined(Q_OS_WINDOWS) +#include +#include +#include +#include +#elif defined(Q_OS_LINUX) +#include +#endif + +void crash_handler(int sig) { + QString log_path = QDir(QStandardPaths::writableLocation(QStandardPaths::TempLocation)).filePath(QStringLiteral("olive_crash")); + QFile output(log_path); + + output.open(QFile::WriteOnly); + QTextStream ostream(&output); + + ostream << "Signal: " << sig << "\n\n"; + +#if defined(Q_OS_WINDOWS) + // Use Windows stackwalk API + HANDLE process = GetCurrentProcess(); + HANDLE thread = GetCurrentThread(); + + CONTEXT context; + memset(&context, 0, sizeof(CONTEXT)); + context.ContextFlags = CONTEXT_FULL; + RtlCaptureContext(&context); + + SymInitialize(process, NULL, TRUE); + + DWORD image; + STACKFRAME64 stackframe; + ZeroMemory(&stackframe, sizeof(STACKFRAME64)); + +#ifdef _M_IX86 + image = IMAGE_FILE_MACHINE_I386; + stackframe.AddrPC.Offset = context.Eip; + stackframe.AddrPC.Mode = AddrModeFlat; + stackframe.AddrFrame.Offset = context.Ebp; + stackframe.AddrFrame.Mode = AddrModeFlat; + stackframe.AddrStack.Offset = context.Esp; + stackframe.AddrStack.Mode = AddrModeFlat; +#elif _M_X64 + image = IMAGE_FILE_MACHINE_AMD64; + stackframe.AddrPC.Offset = context.Rip; + stackframe.AddrPC.Mode = AddrModeFlat; + stackframe.AddrFrame.Offset = context.Rsp; + stackframe.AddrFrame.Mode = AddrModeFlat; + stackframe.AddrStack.Offset = context.Rsp; + stackframe.AddrStack.Mode = AddrModeFlat; +#elif _M_IA64 + image = IMAGE_FILE_MACHINE_IA64; + stackframe.AddrPC.Offset = context.StIIP; + stackframe.AddrPC.Mode = AddrModeFlat; + stackframe.AddrFrame.Offset = context.IntSp; + stackframe.AddrFrame.Mode = AddrModeFlat; + stackframe.AddrBStore.Offset = context.RsBSP; + stackframe.AddrBStore.Mode = AddrModeFlat; + stackframe.AddrStack.Offset = context.IntSp; + stackframe.AddrStack.Mode = AddrModeFlat; +#endif + + for (int i = 0; i < 50; i++) { + + BOOL result = StackWalk64( + image, process, thread, + &stackframe, &context, NULL, + SymFunctionTableAccess64, SymGetModuleBase64, NULL); + + if (!result) { break; } + + char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; + PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer; + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + symbol->MaxNameLen = MAX_SYM_NAME; + + DWORD64 displacement = 0; + + ostream << "[" << i << "] "; + + if (SymFromAddr(process, stackframe.AddrPC.Offset, &displacement, symbol)) { + ostream << symbol->Name; + //printf("[%i] %s\n", i, symbol->Name); + } else { + ostream << "???"; + //printf("[%i] ???\n", i); + } + + ostream << "\n"; + } + + SymCleanup(process); +#elif defined(Q_OS_MAC) + // FIXME: No Mac backtrace support yet +#elif defined(Q_OS_LINUX) + void *array[10]; + size_t size; + + // get void*'s for all entries on the stack + size = backtrace(array, 10); + + // print out all the frames to stderr + backtrace_symbols_fd(array, size, output.handle()); +#endif + + output.close(); + + QString crash_handler_exe = QDir(qApp->applicationDirPath()).filePath(QStringLiteral("crashhandler")); + QProcess::startDetached(crash_handler_exe, {log_path}); + + exit(1); +} diff --git a/app/common/crashhandler.h b/app/common/crashhandler.h new file mode 100644 index 000000000..4044cdc5e --- /dev/null +++ b/app/common/crashhandler.h @@ -0,0 +1,6 @@ +#ifndef CRASHHANDLER_H +#define CRASHHANDLER_H + +void crash_handler(int sig); + +#endif // CRASHHANDLER_H diff --git a/app/core.cpp b/app/core.cpp index df478bb3e..ac88b58a1 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -48,7 +48,7 @@ #include "render/backend/opengl/opengltexturecache.h" #include "render/colormanager.h" #include "render/diskmanager.h" -#include "render/pixelservice.h" +#include "render/pixelformat.h" #include "task/taskmanager.h" #include "ui/style/style.h" #include "undo/undostack.h" @@ -145,7 +145,7 @@ void Core::Stop() DiskManager::DestroyInstance(); - PixelService::DestroyInstance(); + PixelFormat::DestroyInstance(); NodeFactory::Destroy(); @@ -385,7 +385,7 @@ void Core::StartGUI(bool full_screen) TaskManager::CreateInstance(); // Initialize pixel service - PixelService::CreateInstance(); + PixelFormat::CreateInstance(); // Connect the PanelFocusManager to the application's focus change signal connect(qApp, diff --git a/app/dialog/crashhandler/crashhandler.cpp b/app/dialog/crashhandler/crashhandler.cpp new file mode 100644 index 000000000..ac3867795 --- /dev/null +++ b/app/dialog/crashhandler/crashhandler.cpp @@ -0,0 +1,61 @@ +#include "crashhandler.h" + +#include +#include +#include +#include +#include +#include + +CrashHandlerDialog::CrashHandlerDialog(const char *log_file) +{ + setWindowTitle(tr("Olive")); + + QVBoxLayout* layout = new QVBoxLayout(this); + + layout->addWidget(new QLabel(tr("We're sorry, Olive has crashed. Please send the following log to the developers to " + "help resolve this."))); + + QTextEdit* edit = new QTextEdit(); + edit->setReadOnly(true); + layout->addWidget(edit); + + edit->append(QStringLiteral("Build Environment: %1 (%2)").arg(QSysInfo::buildCpuArchitecture(), QSysInfo::buildAbi())); + edit->append(QStringLiteral("Run Environment: %1").arg(QSysInfo::currentCpuArchitecture())); + edit->append(QStringLiteral("Kernel: %1 %2").arg(QSysInfo::kernelType(), QSysInfo::kernelVersion())); + edit->append(QStringLiteral("System: %1 (%2 %3)").arg(QSysInfo::prettyProductName(), QSysInfo::productType(), QSysInfo::productVersion())); + edit->append(QString()); + + QDialogButtonBox* buttons = new QDialogButtonBox(); + + // FIXME: Implement auto-reporting + //buttons->addButton(tr("Send Error Report"), QDialogButtonBox::AcceptRole); + //buttons->addButton(tr("Don't Send"), QDialogButtonBox::RejectRole); + buttons->addButton(QDialogButtonBox::Ok); + + connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); + layout->addWidget(buttons); + + QFile log(log_file); + if (log.open(QFile::ReadOnly | QFile::Text)) { + edit->append(log.readAll()); + + QMetaObject::invokeMethod(edit->verticalScrollBar(), + "setValue", + Qt::QueuedConnection, + Q_ARG(int, 0)); + + log.close(); + } +} + +void CrashHandlerDialog::accept() +{ + QDialog::accept(); +} + +void CrashHandlerDialog::reject() +{ + QDialog::reject(); +} diff --git a/app/dialog/crashhandler/crashhandler.h b/app/dialog/crashhandler/crashhandler.h new file mode 100644 index 000000000..80aed6143 --- /dev/null +++ b/app/dialog/crashhandler/crashhandler.h @@ -0,0 +1,19 @@ +#ifndef CRASHHANDLERDIALOG_H +#define CRASHHANDLERDIALOG_H + +#include + +class CrashHandlerDialog : public QDialog +{ + Q_OBJECT +public: + CrashHandlerDialog(const char* log_file); + +public slots: + virtual void accept() override; + + virtual void reject() override; + +}; + +#endif // CRASHHANDLERDIALOG_H diff --git a/app/dialog/crashhandler/crashhandlermain.cpp b/app/dialog/crashhandler/crashhandlermain.cpp new file mode 100644 index 000000000..945835d6d --- /dev/null +++ b/app/dialog/crashhandler/crashhandlermain.cpp @@ -0,0 +1,17 @@ +#include "crashhandler.h" + +#include + +int main(int argc, char *argv[]) +{ + if (argc < 2) { + return 1; + } + + QApplication a(argc, argv); + + CrashHandlerDialog chd(argv[1]); + chd.open(); + + return a.exec(); +} diff --git a/app/dialog/export/export.cpp b/app/dialog/export/export.cpp index c6b8a4582..1c8f62a8a 100644 --- a/app/dialog/export/export.cpp +++ b/app/dialog/export/export.cpp @@ -12,7 +12,7 @@ #include "project/item/sequence/sequence.h" #include "project/project.h" #include "render/backend/exporter.h" -#include "render/pixelservice.h" +#include "render/pixelformat.h" #include "ui/icons/icons.h" ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) : @@ -210,7 +210,7 @@ void ExportDialog::accept() VideoRenderingParams video_render_params(dest_width, dest_height, video_tab_->frame_rate().flipped(), - PixelService::instance()->GetConfiguredFormatForMode(render_mode), + PixelFormat::instance()->GetConfiguredFormatForMode(render_mode), render_mode); AudioRenderingParams audio_render_params(audio_tab_->sample_rate_combobox()->currentData().toInt(), @@ -414,8 +414,18 @@ void ExportDialog::LoadPresets() void ExportDialog::SetDefaultFilename() { - QString doc_location = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); - QString file_location = QDir(doc_location).filePath("export"); + Sequence* s = static_cast(viewer_node_->parent()); + Project* p = s->project(); + + QDir doc_location; + + if (p->filename().isEmpty()) { + doc_location.setPath(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); + } else { + doc_location = QFileInfo(p->filename()).dir(); + } + + QString file_location = doc_location.filePath(s->name()); filename_edit_->setText(file_location); } diff --git a/app/dialog/preferences/tabs/preferencesqualitytab.cpp b/app/dialog/preferences/tabs/preferencesqualitytab.cpp index 1f0432f3c..357444335 100644 --- a/app/dialog/preferences/tabs/preferencesqualitytab.cpp +++ b/app/dialog/preferences/tabs/preferencesqualitytab.cpp @@ -5,7 +5,7 @@ #include #include "render/colormanager.h" -#include "render/pixelservice.h" +#include "render/pixelformat.h" PreferencesQualityTab::PreferencesQualityTab() { @@ -26,13 +26,13 @@ PreferencesQualityTab::PreferencesQualityTab() quality_stack_ = new QStackedWidget(); offline_group_ = new PreferencesQualityGroup(tr("Offline Quality")); - offline_group_->bit_depth_combobox()->setCurrentIndex(PixelService::instance()->GetConfiguredFormatForMode(RenderMode::kOffline)); + offline_group_->bit_depth_combobox()->setCurrentIndex(PixelFormat::instance()->GetConfiguredFormatForMode(RenderMode::kOffline)); offline_group_->sample_fmt_combobox()->setCurrentIndex(SampleFormat::GetConfiguredFormatForMode(RenderMode::kOffline)); offline_group_->ocio_method()->setCurrentIndex(ColorManager::GetOCIOMethodForMode(RenderMode::kOffline)); quality_stack_->addWidget(offline_group_); online_group_ = new PreferencesQualityGroup(tr("Online Quality")); - online_group_->bit_depth_combobox()->setCurrentIndex(PixelService::instance()->GetConfiguredFormatForMode(RenderMode::kOnline)); + online_group_->bit_depth_combobox()->setCurrentIndex(PixelFormat::instance()->GetConfiguredFormatForMode(RenderMode::kOnline)); online_group_->sample_fmt_combobox()->setCurrentIndex(SampleFormat::GetConfiguredFormatForMode(RenderMode::kOnline)); online_group_->ocio_method()->setCurrentIndex(ColorManager::GetOCIOMethodForMode(RenderMode::kOnline)); quality_stack_->addWidget(online_group_); @@ -46,8 +46,8 @@ void PreferencesQualityTab::Accept() { ColorManager::SetOCIOMethodForMode(RenderMode::kOffline, static_cast(offline_group_->ocio_method()->currentIndex())); ColorManager::SetOCIOMethodForMode(RenderMode::kOnline, static_cast(online_group_->ocio_method()->currentIndex())); - PixelService::instance()->SetConfiguredFormatForMode(RenderMode::kOffline, static_cast(offline_group_->bit_depth_combobox()->currentData().toInt())); - PixelService::instance()->SetConfiguredFormatForMode(RenderMode::kOnline, static_cast(online_group_->bit_depth_combobox()->currentData().toInt())); + PixelFormat::instance()->SetConfiguredFormatForMode(RenderMode::kOffline, static_cast(offline_group_->bit_depth_combobox()->currentData().toInt())); + PixelFormat::instance()->SetConfiguredFormatForMode(RenderMode::kOnline, static_cast(online_group_->bit_depth_combobox()->currentData().toInt())); SampleFormat::SetConfiguredFormatForMode(RenderMode::kOffline, static_cast(offline_group_->sample_fmt_combobox()->currentData().toInt())); SampleFormat::SetConfiguredFormatForMode(RenderMode::kOnline, static_cast(online_group_->sample_fmt_combobox()->currentData().toInt())); } @@ -70,8 +70,13 @@ PreferencesQualityGroup::PreferencesQualityGroup(const QString &title, QWidget * // Populate with bit depths for (int i=0;iaddItem(PixelService::GetPixelFormatInfo(static_cast(i)).name, - i); + PixelFormat::Format pix_fmt = static_cast(i); + + // We always render with an alpha channel internally + if (PixelFormat::FormatHasAlphaChannel(pix_fmt)) { + bit_depth_combobox_->addItem(PixelFormat::GetName(pix_fmt), + i); + } } video_layout->addWidget(bit_depth_combobox_, row, 1); diff --git a/app/main.cpp b/app/main.cpp index e4e7491d6..e8252bbbf 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -31,15 +31,20 @@ extern "C" { #include } +#include #include #include #include "core.h" +#include "common/crashhandler.h" #include "common/debug.h" int main(int argc, char *argv[]) { av_log_set_level(AV_LOG_QUIET); + signal(SIGSEGV, crash_handler); + signal(SIGABRT, crash_handler); + // Set OpenGL display profile (3.2 Core) QSurfaceFormat format; format.setVersion(3, 2); diff --git a/app/node/input/media/video/video.cpp b/app/node/input/media/video/video.cpp index abc5bf9bd..9135a216d 100644 --- a/app/node/input/media/video/video.cpp +++ b/app/node/input/media/video/video.cpp @@ -7,7 +7,7 @@ #include "codec/ffmpeg/ffmpegdecoder.h" #include "core.h" #include "project/item/footage/footage.h" -#include "render/pixelservice.h" +#include "render/pixelformat.h" VideoInput::VideoInput() { diff --git a/app/render/CMakeLists.txt b/app/render/CMakeLists.txt index 99225da03..600d8a1e3 100644 --- a/app/render/CMakeLists.txt +++ b/app/render/CMakeLists.txt @@ -28,8 +28,6 @@ set(OLIVE_SOURCES render/diskmanager.cpp render/pixelformat.h render/pixelformat.cpp - render/pixelservice.h - render/pixelservice.cpp render/rendermodes.h render/videoparams.h render/videoparams.cpp diff --git a/app/render/backend/exporter.cpp b/app/render/backend/exporter.cpp index cabaca780..8a22e65d3 100644 --- a/app/render/backend/exporter.cpp +++ b/app/render/backend/exporter.cpp @@ -3,7 +3,7 @@ #include "render/backend/audio/audiobackend.h" #include "render/backend/opengl/openglbackend.h" #include "render/colormanager.h" -#include "render/pixelservice.h" +#include "render/pixelformat.h" Exporter::Exporter(ViewerOutput* viewer, Encoder *encoder, @@ -121,7 +121,7 @@ void Exporter::EncodeFrame() // OCIO conversion requires a frame in 32F format if (frame->format() != PixelFormat::PIX_FMT_RGBA32F) { - frame = PixelService::ConvertPixelFormat(frame, PixelFormat::PIX_FMT_RGBA32F); + frame = PixelFormat::ConvertPixelFormat(frame, PixelFormat::PIX_FMT_RGBA32F); } // Color conversion must be done with unassociated alpha, and the pipeline is always associated @@ -236,32 +236,6 @@ void Exporter::VideoHashesComplete() TimeRangeList ranges; ranges.append(TimeRange(0, viewer_node_->Length())); - QMap::const_iterator i; - QMap::iterator j; - - // Copy time hash map - QMap time_hash_map = video_backend_->frame_cache()->time_hash_map(); - - // Check for any times that share duplicate hashes - for (i=time_hash_map.begin();i!=time_hash_map.end();i++) { - j = time_hash_map.begin(); - - while (j != time_hash_map.end()) { - if (i != j && i.value() == j.value()) { - // Remove the time range from this and - ranges.RemoveTimeRange(TimeRange(j.key(), j.key() + video_backend_->params().time_base())); - - QList times_with_this_hash = matched_frames_.value(i.value()); - times_with_this_hash.append(j.key()); - matched_frames_.insert(i.value(), times_with_this_hash); - - j = time_hash_map.erase(j); - } else { - j++; - } - } - } - // Set video backend to render mode but NOT hash or download video_backend_->SetOperatingMode(VideoRenderWorker::kRenderOnly); video_backend_->SetOnlySignalLastFrameRequested(false); diff --git a/app/render/backend/exporter.h b/app/render/backend/exporter.h index f5b111372..490a775f0 100644 --- a/app/render/backend/exporter.h +++ b/app/render/backend/exporter.h @@ -73,8 +73,6 @@ private: QHash cached_frames_; - QHash< QByteArray, QList > matched_frames_; - private slots: void FrameRendered(const rational &time, FramePtr value); diff --git a/app/render/backend/opengl/openglproxy.cpp b/app/render/backend/opengl/openglproxy.cpp index de72d1ee8..c1e6cf72d 100644 --- a/app/render/backend/opengl/openglproxy.cpp +++ b/app/render/backend/opengl/openglproxy.cpp @@ -9,7 +9,7 @@ #include "openglcolorprocessor.h" #include "openglrenderfunctions.h" #include "render/colormanager.h" -#include "render/pixelservice.h" +#include "render/pixelformat.h" OpenGLProxy::OpenGLProxy(QObject *parent) : QObject(parent), @@ -60,7 +60,9 @@ void OpenGLProxy::FrameToValue(DecoderPtr decoder, StreamPtr stream, const TimeR if (stream->type() == Stream::kImage && still_image_cache_.Has(stream.get())) { CachedStill cs = still_image_cache_.Get(stream.get()); - if (cs.colorspace == colorspace_match && cs.alpha_is_associated == video_stream->premultiplied_alpha()) { + if (cs.colorspace == colorspace_match + && cs.alpha_is_associated == video_stream->premultiplied_alpha() + && cs.divider == video_params_.divider()) { footage_tex_ref = cs.texture; } else { still_image_cache_.Remove(stream.get()); @@ -80,26 +82,30 @@ void OpenGLProxy::FrameToValue(DecoderPtr decoder, StreamPtr stream, const TimeR ColorManager::OCIOMethod ocio_method = ColorManager::GetOCIOMethodForMode(video_params_.mode()); - FramePtr frame = decoder->RetrieveVideo(range.in());; + FramePtr frame = decoder->RetrieveVideo(range.in(), video_params_.divider()); // OCIO's CPU conversion is more accurate, so for online we render on CPU but offline we render GPU if (ocio_method == ColorManager::kOCIOAccurate) { + bool has_alpha = PixelFormat::FormatHasAlphaChannel(frame->format()); + // If alpha is associated, disassociate for the color transform - if (video_stream->premultiplied_alpha()) { + if (has_alpha && video_stream->premultiplied_alpha()) { ColorManager::DisassociateAlpha(frame); } // Convert frame to float for OCIO - frame = PixelService::ConvertPixelFormat(frame, PixelFormat::PIX_FMT_RGBA32F); + frame = PixelFormat::ConvertPixelFormat(frame, has_alpha ? PixelFormat::PIX_FMT_RGBA32F : PixelFormat::PIX_FMT_RGB32F); // Perform color transform color_processor->ConvertFrame(frame); // Associate alpha - if (video_stream->premultiplied_alpha()) { - ColorManager::ReassociateAlpha(frame); - } else { - ColorManager::AssociateAlpha(frame); + if (has_alpha) { + if (video_stream->premultiplied_alpha()) { + ColorManager::ReassociateAlpha(frame); + } else { + ColorManager::AssociateAlpha(frame); + } } } @@ -154,7 +160,7 @@ void OpenGLProxy::FrameToValue(DecoderPtr decoder, StreamPtr stream, const TimeR } if (stream->type() == Stream::kImage) { - still_image_cache_.Add(stream.get(), {footage_tex_ref, colorspace_match, video_stream->premultiplied_alpha()}); + still_image_cache_.Add(stream.get(), {footage_tex_ref, colorspace_match, video_stream->premultiplied_alpha(), video_params_.divider()}); } } @@ -281,8 +287,8 @@ void OpenGLProxy::RunNodeAccelerated(const Node *node, const TimeRange &range, c int res_param_location = shader->uniformLocation(QStringLiteral("%1_resolution").arg(input->id())); if (res_param_location > -1) { shader->setUniformValue(res_param_location, - static_cast(texture->texture()->width()), - static_cast(texture->texture()->height())); + static_cast(texture->texture()->width() * video_params_.divider()), + static_cast(texture->texture()->height() * video_params_.divider())); } } @@ -384,8 +390,6 @@ void OpenGLProxy::TextureToBuffer(const QVariant &tex_in, void *buffer) { OpenGLTextureCache::ReferencePtr texture = tex_in.value(); - PixelFormat::Info format_info = PixelService::GetPixelFormatInfo(video_params_.format()); - QOpenGLFunctions* f = QOpenGLContext::currentContext()->functions(); buffer_.Attach(texture->texture()); buffer_.Bind(); @@ -394,8 +398,8 @@ void OpenGLProxy::TextureToBuffer(const QVariant &tex_in, void *buffer) 0, video_params_.effective_width(), video_params_.effective_height(), - format_info.pixel_format, - format_info.gl_pixel_type, + OpenGLRenderFunctions::GetPixelFormat(video_params_.format()), + OpenGLRenderFunctions::GetPixelType(video_params_.format()), buffer); buffer_.Release(); diff --git a/app/render/backend/opengl/openglproxy.h b/app/render/backend/opengl/openglproxy.h index d1b397706..9f7f6e362 100644 --- a/app/render/backend/opengl/openglproxy.h +++ b/app/render/backend/opengl/openglproxy.h @@ -69,6 +69,7 @@ private: OpenGLTextureCache::ReferencePtr texture; QString colorspace; bool alpha_is_associated; + int divider; }; RenderCache still_image_cache_; diff --git a/app/render/backend/opengl/openglrenderfunctions.cpp b/app/render/backend/opengl/openglrenderfunctions.cpp index 00c8a32af..c315eb8e9 100644 --- a/app/render/backend/opengl/openglrenderfunctions.cpp +++ b/app/render/backend/opengl/openglrenderfunctions.cpp @@ -71,6 +71,67 @@ void OpenGLRenderFunctions::PrepareToDraw(QOpenGLFunctions* f) { f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } +GLint OpenGLRenderFunctions::GetInternalFormat(const PixelFormat::Format &format) +{ + switch (format) { + case PixelFormat::PIX_FMT_RGB8: + return GL_RGB8; + case PixelFormat::PIX_FMT_RGBA8: + return GL_RGBA8; + case PixelFormat::PIX_FMT_RGB16U: + return GL_RGB16; + case PixelFormat::PIX_FMT_RGBA16U: + return GL_RGBA16; + case PixelFormat::PIX_FMT_RGB16F: + return GL_RGB16F; + case PixelFormat::PIX_FMT_RGBA16F: + return GL_RGBA16F; + case PixelFormat::PIX_FMT_RGB32F: + return GL_RGB32F; + case PixelFormat::PIX_FMT_RGBA32F: + return GL_RGBA32F; + + case PixelFormat::PIX_FMT_INVALID: + case PixelFormat::PIX_FMT_COUNT: + break; + } + + return GL_INVALID_VALUE; +} + +GLenum OpenGLRenderFunctions::GetPixelFormat(const PixelFormat::Format &format) +{ + if (PixelFormat::FormatHasAlphaChannel(format)) { + return GL_RGBA; + } else { + return GL_RGB; + } +} + +GLenum OpenGLRenderFunctions::GetPixelType(const PixelFormat::Format &format) +{ + switch (format) { + case PixelFormat::PIX_FMT_RGB8: + case PixelFormat::PIX_FMT_RGBA8: + return GL_UNSIGNED_BYTE; + case PixelFormat::PIX_FMT_RGB16U: + case PixelFormat::PIX_FMT_RGBA16U: + return GL_UNSIGNED_SHORT; + case PixelFormat::PIX_FMT_RGB16F: + case PixelFormat::PIX_FMT_RGBA16F: + return GL_HALF_FLOAT; + case PixelFormat::PIX_FMT_RGB32F: + case PixelFormat::PIX_FMT_RGBA32F: + return GL_FLOAT; + + case PixelFormat::PIX_FMT_INVALID: + case PixelFormat::PIX_FMT_COUNT: + break; + } + + return GL_INVALID_VALUE; +} + void OpenGLRenderFunctions::Blit(OpenGLShaderPtr pipeline, bool flipped, QMatrix4x4 matrix) { // FIXME: is currentContext() reliable here? QOpenGLFunctions* func = QOpenGLContext::currentContext()->functions(); diff --git a/app/render/backend/opengl/openglrenderfunctions.h b/app/render/backend/opengl/openglrenderfunctions.h index 7540836da..22ff0c1ca 100644 --- a/app/render/backend/opengl/openglrenderfunctions.h +++ b/app/render/backend/opengl/openglrenderfunctions.h @@ -26,6 +26,7 @@ #include #include "openglshader.h" +#include "render/pixelformat.h" class OpenGLRenderFunctions { public: @@ -50,6 +51,12 @@ public: static void PrepareToDraw(QOpenGLFunctions* f); + static GLint GetInternalFormat(const PixelFormat::Format& format); + + static GLenum GetPixelFormat(const PixelFormat::Format& format); + + static GLenum GetPixelType(const PixelFormat::Format& format); + }; #endif // OPENGLFUNCTIONS_H diff --git a/app/render/backend/opengl/opengltexture.cpp b/app/render/backend/opengl/opengltexture.cpp index 03ebbfdb0..6088153c9 100644 --- a/app/render/backend/opengl/opengltexture.cpp +++ b/app/render/backend/opengl/opengltexture.cpp @@ -23,7 +23,8 @@ #include #include -#include "render/pixelservice.h" +#include "openglrenderfunctions.h" +#include "render/pixelformat.h" OpenGLTexture::OpenGLTexture() : created_ctx_(nullptr), @@ -120,16 +121,14 @@ void OpenGLTexture::Upload(const void *data) Bind(); - PixelFormat::Info info = PixelService::GetPixelFormatInfo(format_); - created_ctx_->functions()->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_, - info.pixel_format, - info.gl_pixel_type, + OpenGLRenderFunctions::GetPixelFormat(format_), + OpenGLRenderFunctions::GetPixelType(format_), data); Release(); @@ -152,16 +151,14 @@ void OpenGLTexture::CreateInternal(QOpenGLContext* create_ctx, GLuint* tex, cons f->glBindTexture(GL_TEXTURE_2D, *tex); // Allocate storage for texture - const PixelFormat::Info& bit_depth = PixelService::GetPixelFormatInfo(format_); - f->glTexImage2D(GL_TEXTURE_2D, 0, - bit_depth.internal_format, + OpenGLRenderFunctions::GetInternalFormat(format_), width_, height_, 0, - bit_depth.pixel_format, - bit_depth.gl_pixel_type, + OpenGLRenderFunctions::GetPixelFormat(format_), + OpenGLRenderFunctions::GetPixelType(format_), data); // Set texture filtering to bilinear diff --git a/app/render/backend/opengl/openglworker.cpp b/app/render/backend/opengl/openglworker.cpp index b3d364628..8d5bed4b1 100644 --- a/app/render/backend/opengl/openglworker.cpp +++ b/app/render/backend/opengl/openglworker.cpp @@ -7,7 +7,7 @@ #include "openglcolorprocessor.h" #include "openglrenderfunctions.h" #include "render/colormanager.h" -#include "render/pixelservice.h" +#include "render/pixelformat.h" OpenGLWorker::OpenGLWorker(VideoRenderFrameCache *frame_cache, DecoderCache* decoder_cache, QObject *parent) : VideoRenderWorker(frame_cache, decoder_cache, parent) diff --git a/app/render/backend/videorenderbackend.cpp b/app/render/backend/videorenderbackend.cpp index 0ebf86f22..964243e91 100644 --- a/app/render/backend/videorenderbackend.cpp +++ b/app/render/backend/videorenderbackend.cpp @@ -31,7 +31,7 @@ #include "config/config.h" #include "render/diskmanager.h" #include "render/diskmanager.h" -#include "render/pixelservice.h" +#include "render/pixelformat.h" #include "videorenderworker.h" VideoRenderBackend::VideoRenderBackend(QObject *parent) : @@ -140,7 +140,6 @@ void VideoRenderBackend::ConnectWorkerToThis(RenderWorker *processor) video_processor->SetOperatingMode(operating_mode_); - connect(video_processor, &VideoRenderWorker::CompletedFrame, this, &VideoRenderBackend::ThreadCompletedFrame, Qt::QueuedConnection); connect(video_processor, &VideoRenderWorker::HashAlreadyBeingCached, this, &VideoRenderBackend::ThreadSkippedFrame, Qt::QueuedConnection); connect(video_processor, &VideoRenderWorker::CompletedDownload, this, &VideoRenderBackend::ThreadCompletedDownload, Qt::QueuedConnection); connect(video_processor, &VideoRenderWorker::HashAlreadyExists, this, &VideoRenderBackend::ThreadHashAlreadyExists, Qt::QueuedConnection); @@ -262,22 +261,6 @@ TimeRange VideoRenderBackend::PopNextFrameFromQueue() return TimeRange(frame_range.in(), frame_range.in()); } -void VideoRenderBackend::ThreadCompletedFrame(NodeDependency path, qint64 job_time, QByteArray hash, QVariant value) -{ - if (!only_signal_last_frame_requested_ || last_time_requested_ == path.in() || frame_cache_.TimeToHash(last_time_requested_) == hash) { - Q_UNUSED(job_time) - Q_UNUSED(value) - //EmitCachedFrameReady(path.in(), value, job_time); - } - - if (!(operating_mode_ & VideoRenderWorker::kDownloadOnly)) { - // If we're not downloading, the worker is done here - SetWorkerBusyState(static_cast(sender()), false); - - CacheNext(); - } -} - void VideoRenderBackend::ThreadCompletedDownload(NodeDependency dep, qint64 job_time, QByteArray hash, bool texture_existed) { SetWorkerBusyState(static_cast(sender()), false); @@ -343,7 +326,7 @@ void VideoRenderBackend::TruncateFrameCacheLength(const rational &length) void VideoRenderBackend::FrameRemovedFromDiskCache(const QByteArray &hash) { - QList deleted_frames = frame_cache()->FramesWithHash(hash); + QList deleted_frames = frame_cache()->TakeFramesWithHash(hash); foreach (const rational& frame, deleted_frames) { TimeRange invalidated(frame, frame+params_.time_base()); diff --git a/app/render/backend/videorenderbackend.h b/app/render/backend/videorenderbackend.h index 517232782..bd2b823c1 100644 --- a/app/render/backend/videorenderbackend.h +++ b/app/render/backend/videorenderbackend.h @@ -128,7 +128,6 @@ private: bool limit_caching_; private slots: - void ThreadCompletedFrame(NodeDependency path, qint64 job_time, QByteArray hash, QVariant value); void ThreadCompletedDownload(NodeDependency dep, qint64 job_time, QByteArray hash, bool texture_existed); void ThreadSkippedFrame(NodeDependency dep, qint64 job_time, QByteArray hash); void ThreadHashAlreadyExists(NodeDependency dep, qint64 job_time, QByteArray hash); diff --git a/app/render/backend/videorenderframecache.cpp b/app/render/backend/videorenderframecache.cpp index a45591414..deebefff4 100644 --- a/app/render/backend/videorenderframecache.cpp +++ b/app/render/backend/videorenderframecache.cpp @@ -83,7 +83,7 @@ void VideoRenderFrameCache::RemoveHashFromCurrentlyCaching(const QByteArray &has currently_caching_list_.removeOne(hash); } -QList VideoRenderFrameCache::FramesWithHash(const QByteArray &hash) +QList VideoRenderFrameCache::FramesWithHash(const QByteArray &hash) const { QList times; @@ -98,6 +98,25 @@ QList VideoRenderFrameCache::FramesWithHash(const QByteArray &hash) return times; } +QList VideoRenderFrameCache::TakeFramesWithHash(const QByteArray &hash) +{ + QList times; + + QMap::iterator iterator = time_hash_map_.begin(); + + while (iterator != time_hash_map_.end()) { + if (iterator.value() == hash) { + times.append(iterator.key()); + + iterator = time_hash_map_.erase(iterator); + } else { + iterator++; + } + } + + return times; +} + const QMap &VideoRenderFrameCache::time_hash_map() const { return time_hash_map_; @@ -107,9 +126,11 @@ QString VideoRenderFrameCache::CachePathName(const QByteArray& hash, const Pixel { QString ext; - if (pix_fmt == PixelFormat::PIX_FMT_RGBA8 || pix_fmt == PixelFormat::PIX_FMT_RGBA16U) { - // For some reason, integer EXRs are extremely slow to load, so we use TIFF instead. - ext = QStringLiteral("tiff"); + if (pix_fmt == PixelFormat::PIX_FMT_RGB8 + || pix_fmt == PixelFormat::PIX_FMT_RGBA8 + || pix_fmt == PixelFormat::PIX_FMT_RGB16U + || pix_fmt == PixelFormat::PIX_FMT_RGBA16U) { + ext = QStringLiteral("jpg"); } else { ext = QStringLiteral("exr"); } diff --git a/app/render/backend/videorenderframecache.h b/app/render/backend/videorenderframecache.h index 6c5af9447..cd9902e40 100644 --- a/app/render/backend/videorenderframecache.h +++ b/app/render/backend/videorenderframecache.h @@ -43,7 +43,15 @@ public: void RemoveHashFromCurrentlyCaching(const QByteArray& hash); - QList FramesWithHash(const QByteArray& hash); + /** + * @brief Returns a list of frames that use a particular hash + */ + QList FramesWithHash(const QByteArray& hash) const; + + /** + * @brief Same as FramesWithHash() but also removes these frames from the map + */ + QList TakeFramesWithHash(const QByteArray& hash); const QMap& time_hash_map() const; diff --git a/app/render/backend/videorenderworker.cpp b/app/render/backend/videorenderworker.cpp index 951f2926f..4c4221426 100644 --- a/app/render/backend/videorenderworker.cpp +++ b/app/render/backend/videorenderworker.cpp @@ -1,17 +1,22 @@ #include "videorenderworker.h" +#include +#include +#include +#include + #include "common/define.h" +#include "common/functiontimer.h" #include "node/block/transition/transition.h" #include "node/node.h" #include "project/project.h" -#include "render/pixelservice.h" +#include "render/pixelformat.h" VideoRenderWorker::VideoRenderWorker(VideoRenderFrameCache *frame_cache, DecoderCache* decoder_cache, QObject *parent) : RenderWorker(decoder_cache, parent), frame_cache_(frame_cache), operating_mode_(kHashRenderCache) { - } const VideoRenderingParams &VideoRenderWorker::video_params() @@ -62,9 +67,6 @@ NodeValueTable VideoRenderWorker::RenderInternal(const NodeDependency& path, con // Find texture in hash QVariant texture = value.Get(NodeParam::kTexture); - // Signal that we have a frame in memory that could be shown right now - emit CompletedFrame(path, job_time, hash, texture); - // If we actually have a texture, download it into the disk cache if (!texture.isNull()) { Download(path.in(), texture, frame_cache_->CachePathName(hash, video_params_.format())); @@ -225,38 +227,89 @@ void VideoRenderWorker::CloseInternal() void VideoRenderWorker::Download(const rational& time, QVariant texture, QString filename) { - PixelFormat::Info format_info = PixelService::GetPixelFormatInfo(video_params().format()); - - // Set up OIIO::ImageSpec for compressing cached images on disk - OIIO::ImageSpec spec(video_params().effective_width(), video_params().effective_height(), kRGBAChannels, format_info.oiio_desc); - - if (video_params_.format() != PixelFormat::PIX_FMT_RGBA8 - && video_params_.format() != PixelFormat::PIX_FMT_RGBA16U) { - // Integer types don't use EXR (they use TIFF instead) because EXR is very slow with integer formats - spec.attribute("compression", "dwaa:200"); - } - if (operating_mode_ & kDownloadOnly) { TextureToBuffer(texture, download_buffer_.data()); - std::string working_fn_std = filename.toStdString(); + switch (video_params().format()) { + case PixelFormat::PIX_FMT_RGB8: + case PixelFormat::PIX_FMT_RGBA8: + case PixelFormat::PIX_FMT_RGB16U: + case PixelFormat::PIX_FMT_RGBA16U: + { + // Integer types are stored in JPEG which we run through OIIO - auto out = OIIO::ImageOutput::create(working_fn_std); + std::string fn_std = filename.toStdString(); - // Keep export to this thread only - out->threads(1); + auto out = OIIO::ImageOutput::create(fn_std); - if (out) { - out->open(working_fn_std, spec); - out->write_image(format_info.oiio_desc, download_buffer_.data()); - out->close(); + if (out) { + // Attempt to keep this write to one thread + out->threads(1); + + out->open(fn_std, OIIO::ImageSpec(video_params().effective_width(), + video_params().effective_height(), + PixelFormat::ChannelCount(video_params().format()), + PixelFormat::GetOIIOTypeDesc(video_params().format()))); + + out->write_image(PixelFormat::GetOIIOTypeDesc(video_params().format()), download_buffer_.data()); + + out->close(); #if OIIO_VERSION < 10903 - OIIO::ImageOutput::destroy(out); + OIIO::ImageOutput::destroy(out); #endif - } else { - qWarning() << "Failed to open output file:" << filename; + } else { + qCritical() << "Failed to write JPEG file:" << OIIO::geterror().c_str(); + } + break; + } + case PixelFormat::PIX_FMT_RGB16F: + case PixelFormat::PIX_FMT_RGBA16F: + case PixelFormat::PIX_FMT_RGB32F: + case PixelFormat::PIX_FMT_RGBA32F: + { + // Floating point types are stored in EXR + Imf::PixelType pix_type; + + if (video_params().format() == PixelFormat::PIX_FMT_RGB16F + || video_params().format() == PixelFormat::PIX_FMT_RGBA16F) { + pix_type = Imf::HALF; + } else { + pix_type = Imf::FLOAT; + } + + Imf::Header header(video_params().effective_width(), + video_params().effective_height()); + header.channels().insert("R", Imf::Channel(pix_type)); + header.channels().insert("G", Imf::Channel(pix_type)); + header.channels().insert("B", Imf::Channel(pix_type)); + header.channels().insert("A", Imf::Channel(pix_type)); + + header.compression() = Imf::DWAA_COMPRESSION; + header.insert("dwaCompressionLevel", Imf::FloatAttribute(200.0f)); + + Imf::OutputFile out(filename.toUtf8(), header, 0); + + int bpc = PixelFormat::BytesPerChannel(video_params().format()); + + size_t xs = kRGBAChannels * bpc; + size_t ys = video_params().effective_width() * kRGBAChannels * bpc; + + Imf::FrameBuffer framebuffer; + framebuffer.insert("R", Imf::Slice(pix_type, download_buffer_.data(), xs, ys)); + framebuffer.insert("G", Imf::Slice(pix_type, download_buffer_.data() + bpc, xs, ys)); + framebuffer.insert("B", Imf::Slice(pix_type, download_buffer_.data() + 2*bpc, xs, ys)); + framebuffer.insert("A", Imf::Slice(pix_type, download_buffer_.data() + 3*bpc, xs, ys)); + out.setFrameBuffer(framebuffer); + + out.writePixels(video_params().effective_height()); + break; + } + case PixelFormat::PIX_FMT_INVALID: + case PixelFormat::PIX_FMT_COUNT: + qCritical() << "Unable to cache invalid pixel format" << video_params().format(); + break; } } else { @@ -276,7 +329,7 @@ void VideoRenderWorker::Download(const rational& time, QVariant texture, QString void VideoRenderWorker::ResizeDownloadBuffer() { - download_buffer_.resize(PixelService::GetBufferSize(video_params_.format(), video_params_.effective_width(), video_params_.effective_height())); + download_buffer_.resize(PixelFormat::GetBufferSize(video_params_.format(), video_params_.effective_width(), video_params_.effective_height())); } NodeValueTable VideoRenderWorker::RenderBlock(const TrackOutput *track, const TimeRange &range) diff --git a/app/render/backend/videorenderworker.h b/app/render/backend/videorenderworker.h index 0520f8412..b9ec19169 100644 --- a/app/render/backend/videorenderworker.h +++ b/app/render/backend/videorenderworker.h @@ -48,13 +48,11 @@ public: void SetOperatingMode(const OperatingMode& mode); signals: - void CompletedFrame(NodeDependency CurrentPath, qint64 job_time, QByteArray hash, QVariant value); + void CompletedDownload(NodeDependency path, qint64 job_time, QByteArray hash, bool texture_existed); - void CompletedDownload(NodeDependency CurrentPath, qint64 job_time, QByteArray hash, bool texture_existed); + void HashAlreadyBeingCached(NodeDependency path, qint64 job_time, QByteArray hash); - void HashAlreadyBeingCached(NodeDependency CurrentPath, qint64 job_time, QByteArray hash); - - void HashAlreadyExists(NodeDependency CurrentPath, qint64 job_time, QByteArray hash); + void HashAlreadyExists(NodeDependency path, qint64 job_time, QByteArray hash); void GeneratedFrame(const rational &time, FramePtr frame); diff --git a/app/render/colormanager.cpp b/app/render/colormanager.cpp index 1ed688b9c..b9f66ba0d 100644 --- a/app/render/colormanager.cpp +++ b/app/render/colormanager.cpp @@ -123,6 +123,11 @@ void ColorManager::SetOCIOMethodForMode(RenderMode::Mode mode, ColorManager::OCI void ColorManager::AssociateAlphaPixFmtFilter(ColorManager::AlphaAction action, FramePtr f) { + if (!PixelFormat::FormatHasAlphaChannel(f->format())) { + // This frame has no alpha channel, do nothing + return; + } + int pixel_count = f->width() * f->height() * kRGBAChannels; switch (static_cast(f->format())) { @@ -130,15 +135,19 @@ void ColorManager::AssociateAlphaPixFmtFilter(ColorManager::AlphaAction action, case PixelFormat::PIX_FMT_COUNT: qWarning() << "Alpha association functions received an invalid pixel format"; break; + case PixelFormat::PIX_FMT_RGB8: case PixelFormat::PIX_FMT_RGBA8: + case PixelFormat::PIX_FMT_RGB16U: case PixelFormat::PIX_FMT_RGBA16U: qWarning() << "Alpha association functions only works on float-based pixel formats at this time"; break; + case PixelFormat::PIX_FMT_RGB16F: case PixelFormat::PIX_FMT_RGBA16F: { AssociateAlphaInternal(action, reinterpret_cast(f->data()), pixel_count); break; } + case PixelFormat::PIX_FMT_RGB32F: case PixelFormat::PIX_FMT_RGBA32F: { AssociateAlphaInternal(action, reinterpret_cast(f->data()), pixel_count); diff --git a/app/render/colorprocessor.cpp b/app/render/colorprocessor.cpp index d311b17b8..98bac702e 100644 --- a/app/render/colorprocessor.cpp +++ b/app/render/colorprocessor.cpp @@ -38,7 +38,7 @@ ColorProcessor::ColorProcessor(OCIO::ConstConfigRcPtr config, void ColorProcessor::ConvertFrame(FramePtr f) { - OCIO::PackedImageDesc img(reinterpret_cast(f->data()), f->width(), f->height(), kRGBAChannels); + OCIO::PackedImageDesc img(reinterpret_cast(f->data()), f->width(), f->height(), PixelFormat::ChannelCount(f->format())); processor->apply(img); } diff --git a/app/render/diskmanager.cpp b/app/render/diskmanager.cpp index 10bf36817..053f7935a 100644 --- a/app/render/diskmanager.cpp +++ b/app/render/diskmanager.cpp @@ -159,7 +159,7 @@ bool DiskManager::ClearDiskCache(bool quick_delete) const HashTime& ht = disk_data_.at(i); // We return a false result if any of the files fail to delete, but still try to delete as many as we can - if (QFile::remove(ht.file_name)) { + if (QFile::remove(ht.file_name) || !QFileInfo::exists(ht.file_name)) { emit DeletedFrame(ht.hash); disk_data_.removeAt(i); i--; diff --git a/app/render/pixelformat.cpp b/app/render/pixelformat.cpp index 4076ae21f..9fa451c3b 100644 --- a/app/render/pixelformat.cpp +++ b/app/render/pixelformat.cpp @@ -19,3 +19,237 @@ ***/ #include "pixelformat.h" + +#include "OpenImageIO/imagebuf.h" +#include +#include +#include + +#include "common/define.h" +#include "core.h" + +bool PixelFormat::FormatHasAlphaChannel(const PixelFormat::Format &format) +{ + switch (format) { + case PixelFormat::PIX_FMT_RGBA8: + case PixelFormat::PIX_FMT_RGBA16U: + case PixelFormat::PIX_FMT_RGBA16F: + case PixelFormat::PIX_FMT_RGBA32F: + return true; + case PixelFormat::PIX_FMT_RGB8: + case PixelFormat::PIX_FMT_RGB16U: + case PixelFormat::PIX_FMT_RGB16F: + case PixelFormat::PIX_FMT_RGB32F: + case PixelFormat::PIX_FMT_INVALID: + case PixelFormat::PIX_FMT_COUNT: + break; + } + + return false; +} + +OIIO::TypeDesc::BASETYPE PixelFormat::GetOIIOTypeDesc(const PixelFormat::Format &format) +{ + switch (format) { + case PixelFormat::PIX_FMT_RGB8: + case PixelFormat::PIX_FMT_RGBA8: + return OIIO::TypeDesc::UINT8; + case PixelFormat::PIX_FMT_RGB16U: + case PixelFormat::PIX_FMT_RGBA16U: + return OIIO::TypeDesc::UINT16; + case PixelFormat::PIX_FMT_RGB16F: + case PixelFormat::PIX_FMT_RGBA16F: + return OIIO::TypeDesc::HALF; + case PixelFormat::PIX_FMT_RGB32F: + case PixelFormat::PIX_FMT_RGBA32F: + return OIIO::TypeDesc::FLOAT; + case PixelFormat::PIX_FMT_INVALID: + case PixelFormat::PIX_FMT_COUNT: + break; + } + + return OIIO::TypeDesc::UNKNOWN; +} + +QString PixelFormat::GetName(const PixelFormat::Format &format) +{ + switch (format) { + case PixelFormat::PIX_FMT_RGB8: + case PixelFormat::PIX_FMT_RGBA8: + return tr("8-bit"); + case PixelFormat::PIX_FMT_RGB16U: + case PixelFormat::PIX_FMT_RGBA16U: + return tr("16-bit Integer"); + case PixelFormat::PIX_FMT_RGB16F: + case PixelFormat::PIX_FMT_RGBA16F: + return tr("Half-Float (16-bit)"); + case PixelFormat::PIX_FMT_RGB32F: + case PixelFormat::PIX_FMT_RGBA32F: + return tr("Full-Float (32-bit)"); + case PixelFormat::PIX_FMT_INVALID: + case PixelFormat::PIX_FMT_COUNT: + break; + } + + return tr("Unknown (%1)").arg(format); +} + +PixelFormat* PixelFormat::instance_ = nullptr; + +void PixelFormat::CreateInstance() +{ + instance_ = new PixelFormat(); +} + +void PixelFormat::DestroyInstance() +{ + delete instance_; +} + +PixelFormat *PixelFormat::instance() +{ + return instance_; +} + +PixelFormat::Format PixelFormat::GetConfiguredFormatForMode(RenderMode::Mode mode) +{ + return static_cast(Core::GetPreferenceForRenderMode(mode, QStringLiteral("PixelFormat")).toInt()); +} + +void PixelFormat::SetConfiguredFormatForMode(RenderMode::Mode mode, PixelFormat::Format format) +{ + if (format != GetConfiguredFormatForMode(mode)) { + Core::SetPreferenceForRenderMode(mode, QStringLiteral("PixelFormat"), format); + + emit FormatChanged(); + } +} + +PixelFormat::Format PixelFormat::OIIOFormatToOliveFormat(OIIO::TypeDesc desc, bool has_alpha) +{ + if (desc == OIIO::TypeDesc::UINT8) { + return has_alpha ? PixelFormat::PIX_FMT_RGBA8 : PixelFormat::PIX_FMT_RGB8; + } else if (desc == OIIO::TypeDesc::UINT16) { + return has_alpha ? PixelFormat::PIX_FMT_RGBA16U : PixelFormat::PIX_FMT_RGB16U; + } else if (desc == OIIO::TypeDesc::HALF) { + return has_alpha ? PixelFormat::PIX_FMT_RGBA16F : PixelFormat::PIX_FMT_RGB16F; + } else if (desc == OIIO::TypeDesc::FLOAT) { + return has_alpha ? PixelFormat::PIX_FMT_RGBA32F : PixelFormat::PIX_FMT_RGB32F; + } + + return PixelFormat::PIX_FMT_INVALID; +} + +/*PixelFormat::Info PixelFormat::GetPixelFormatInfo(const PixelFormat::Format &format) +{ + PixelFormat::Info info; + + switch (format) { + case PixelFormat::PIX_FMT_RGB8: + case PixelFormat::PIX_FMT_RGBA8: + info.name = tr("8-bit"); + info.internal_format = (format == PixelFormat::PIX_FMT_RGB8) ? GL_RGB8 : GL_RGBA8; + info.gl_pixel_type = GL_UNSIGNED_BYTE; + info.oiio_desc = OIIO::TypeDesc::UINT8; + break; + case PixelFormat::PIX_FMT_RGB16U: + case PixelFormat::PIX_FMT_RGBA16U: + info.name = tr("16-bit Integer"); + info.internal_format = (format == PixelFormat::PIX_FMT_RGB16U) ? GL_RGB8 : GL_RGBA16; + info.gl_pixel_type = GL_UNSIGNED_SHORT; + info.oiio_desc = OIIO::TypeDesc::UINT16; + break; + case PixelFormat::PIX_FMT_RGB16F: + case PixelFormat::PIX_FMT_RGBA16F: + info.name = tr("Half-Float (16-bit)"); + info.internal_format = (format == PixelFormat::PIX_FMT_RGB8) ? GL_RGB8 : GL_RGBA16F; + info.gl_pixel_type = GL_HALF_FLOAT; + info.oiio_desc = OIIO::TypeDesc::HALF; + break; + case PixelFormat::PIX_FMT_RGB32F: + case PixelFormat::PIX_FMT_RGBA32F: + info.name = tr("Full-Float (32-bit)"); + info.internal_format = (format == PixelFormat::PIX_FMT_RGB8) ? GL_RGB8 : GL_RGBA32F; + info.gl_pixel_type = GL_FLOAT; + info.oiio_desc = OIIO::TypeDesc::FLOAT; + break; + case PixelFormat::PIX_FMT_INVALID: + case PixelFormat::PIX_FMT_COUNT: + qFatal("Invalid pixel format requested"); + } + + info.pixel_format = GL_RGBA; + info.bytes_per_pixel = BytesPerPixel(format); + + return info; +}*/ + +int PixelFormat::GetBufferSize(const PixelFormat::Format &format, const int &width, const int &height) +{ + return BytesPerPixel(format) * width * height; +} + +int PixelFormat::BytesPerPixel(const PixelFormat::Format &format) +{ + return BytesPerChannel(format) * ChannelCount(format); +} + +int PixelFormat::BytesPerChannel(const PixelFormat::Format &format) +{ + switch (format) { + case PixelFormat::PIX_FMT_RGB8: + case PixelFormat::PIX_FMT_RGBA8: + return 1; + case PixelFormat::PIX_FMT_RGB16U: + case PixelFormat::PIX_FMT_RGB16F: + case PixelFormat::PIX_FMT_RGBA16U: + case PixelFormat::PIX_FMT_RGBA16F: + return 2; + case PixelFormat::PIX_FMT_RGB32F: + case PixelFormat::PIX_FMT_RGBA32F: + return 4; + case PixelFormat::PIX_FMT_INVALID: + case PixelFormat::PIX_FMT_COUNT: + break; + } + + qFatal("Invalid pixel format requested"); + + // qFatal will abort so we won't get here, but this suppresses compiler warnings + return 0; +} + +int PixelFormat::ChannelCount(const PixelFormat::Format &format) +{ + if (PixelFormat::FormatHasAlphaChannel(format)) { + return kRGBAChannels; + } else { + return kRGBChannels; + } +} + +FramePtr PixelFormat::ConvertPixelFormat(FramePtr frame, const PixelFormat::Format &dest_format) +{ + if (frame->format() == dest_format) { + return frame; + } + + FramePtr converted = Frame::Create(); + + // Copy parameters + converted->set_width(frame->width()); + converted->set_height(frame->height()); + converted->set_timestamp(frame->timestamp()); + converted->set_format(dest_format); + converted->allocate(); + + OIIO::ImageBuf src(OIIO::ImageSpec(frame->width(), frame->height(), ChannelCount(frame->format()), GetOIIOTypeDesc(frame->format())), frame->data()); + OIIO::ImageBuf dst(OIIO::ImageSpec(converted->width(), converted->height(), ChannelCount(converted->format()), GetOIIOTypeDesc(converted->format())), converted->data()); + + if (dst.copy_pixels(src)) { + return converted; + } else { + return nullptr; + } +} + diff --git a/app/render/pixelformat.h b/app/render/pixelformat.h index 918d65ad8..7f9a59555 100644 --- a/app/render/pixelformat.h +++ b/app/render/pixelformat.h @@ -21,11 +21,19 @@ #ifndef BITDEPTHS_H #define BITDEPTHS_H +#include #include #include #include -class PixelFormat { +#include "render/rendermodes.h" + +class Frame; +using FramePtr = std::shared_ptr; + +class PixelFormat : public QObject +{ + Q_OBJECT public: /** * @brief Olive's internal supported pixel formats. @@ -38,27 +46,92 @@ public: PIX_FMT_RGBA16F, PIX_FMT_RGBA32F, + PIX_FMT_RGB8, + PIX_FMT_RGB16U, + PIX_FMT_RGB16F, + PIX_FMT_RGB32F, + PIX_FMT_COUNT }; + static void CreateInstance(); + static void DestroyInstance(); + static PixelFormat* instance(); + /** - * @brief A struct of information pertaining to each enum PixelFormat. - * - * Primarily this is a means of retrieving OpenGL texture information for different pixel formats/bit depths. Both - * RAM and VRAM buffers will need a PixelFormat. To keep consistency between the OpenGL code and CPU code when using - * a given PixelFormat, the PixelFormatInfo struct contains all necessary variables that you'll need to plug into - * OpenGL. - * - * Use the static function PixelService::GetPixelFormatInfo to generate a PixelFormatInfo object. + * @brief Returns the configured pixel format for a given mode */ - struct Info { - QString name; - GLint internal_format; - GLenum pixel_format; - GLenum gl_pixel_type; - int bytes_per_pixel; - OIIO::TypeDesc oiio_desc; - }; + Format GetConfiguredFormatForMode(RenderMode::Mode mode); + void SetConfiguredFormatForMode(RenderMode::Mode mode, PixelFormat::Format format); + + static Format OIIOFormatToOliveFormat(OIIO::TypeDesc desc, bool has_alpha); + + /** + * @brief Returns the minimum buffer size (in bytes) necessary for a given format, width, and height. + * + * @param format + * + * The format of the data the buffer should contain. Must be a member of the olive::PixelFormat enum. + * + * @param width + * + * The width (in pixels) of the buffer. + * + * @param height + * + * The height (in pixels) of the buffer. + */ + static int GetBufferSize(const Format &format, const int& width, const int& height); + + /** + * @brief Returns the number of bytes per pixel for a certain format + * + * Different formats use different sizes of data for pixels. Use this function to determine how many bytes a pixel + * requires for a certain format. The number of bytes will always be a multiple of 4 since all formats use RGBA and + * are at least 1 bpc. + */ + static int BytesPerPixel(const Format &format); + + /** + * @brief Returns the number of bytes per channel for a certain format + */ + static int BytesPerChannel(const Format& format); + + /** + * @brief Return the number of channels in this format + */ + static int ChannelCount(const Format& format); + + /** + * @brief Convert a frame to a pixel format + * + * If the frame's pixel format == the destination format, this just returns `frame`. + */ + static FramePtr ConvertPixelFormat(FramePtr frame, const Format &dest_format); + + /** + * @brief Simple convenience function returning whether a pixel format has an alpha channel or not + */ + static bool FormatHasAlphaChannel(const Format& format); + + /** + * @brief Get corresponding OpenImageIO TypeDesc for a given pixel format + */ + static OIIO::TypeDesc::BASETYPE GetOIIOTypeDesc(const Format& format); + + /** + * @brief Get format name + */ + static QString GetName(const Format& format); + +signals: + void FormatChanged(); + +private: + PixelFormat() = default; + + static PixelFormat* instance_; + }; #endif // BITDEPTHS_H diff --git a/app/render/pixelservice.cpp b/app/render/pixelservice.cpp deleted file mode 100644 index 53e58437b..000000000 --- a/app/render/pixelservice.cpp +++ /dev/null @@ -1,366 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#include "pixelservice.h" - -#include -#include -#include - -#include "common/define.h" -#include "core.h" - -PixelService* PixelService::instance_ = nullptr; - -void PixelService::CreateInstance() -{ - instance_ = new PixelService(); -} - -void PixelService::DestroyInstance() -{ - delete instance_; -} - -PixelService *PixelService::instance() -{ - return instance_; -} - -PixelFormat::Format PixelService::GetConfiguredFormatForMode(RenderMode::Mode mode) -{ - return static_cast(Core::GetPreferenceForRenderMode(mode, QStringLiteral("PixelFormat")).toInt()); -} - -void PixelService::SetConfiguredFormatForMode(RenderMode::Mode mode, PixelFormat::Format format) -{ - if (format != GetConfiguredFormatForMode(mode)) { - Core::SetPreferenceForRenderMode(mode, QStringLiteral("PixelFormat"), format); - - emit FormatChanged(); - } -} - -PixelFormat::Format PixelService::OIIOFormatToOliveFormat(OIIO::TypeDesc desc) -{ - if (desc == OIIO::TypeDesc::UINT8) { - return PixelFormat::PIX_FMT_RGBA8; - } else if (desc == OIIO::TypeDesc::UINT16) { - return PixelFormat::PIX_FMT_RGBA16U; - } else if (desc == OIIO::TypeDesc::HALF) { - return PixelFormat::PIX_FMT_RGBA16F; - } else if (desc == OIIO::TypeDesc::FLOAT) { - return PixelFormat::PIX_FMT_RGBA32F; - } - - return PixelFormat::PIX_FMT_INVALID; -} - -PixelFormat::Info PixelService::GetPixelFormatInfo(const PixelFormat::Format &format) -{ - PixelFormat::Info info; - - switch (format) { - case PixelFormat::PIX_FMT_RGBA8: - info.name = tr("8-bit"); - info.internal_format = GL_RGBA8; - info.gl_pixel_type = GL_UNSIGNED_BYTE; - info.oiio_desc = OIIO::TypeDesc::UINT8; - break; - case PixelFormat::PIX_FMT_RGBA16U: - info.name = tr("16-bit Integer"); - info.internal_format = GL_RGBA16; - info.gl_pixel_type = GL_UNSIGNED_SHORT; - info.oiio_desc = OIIO::TypeDesc::UINT16; - break; - case PixelFormat::PIX_FMT_RGBA16F: - info.name = tr("Half-Float (16-bit)"); - info.internal_format = GL_RGBA16F; - info.gl_pixel_type = GL_HALF_FLOAT; - info.oiio_desc = OIIO::TypeDesc::HALF; - break; - case PixelFormat::PIX_FMT_RGBA32F: - info.name = tr("Full-Float (32-bit)"); - info.internal_format = GL_RGBA32F; - info.gl_pixel_type = GL_FLOAT; - info.oiio_desc = OIIO::TypeDesc::FLOAT; - break; - case PixelFormat::PIX_FMT_INVALID: - case PixelFormat::PIX_FMT_COUNT: - qFatal("Invalid pixel format requested"); - } - - info.pixel_format = GL_RGBA; - info.bytes_per_pixel = BytesPerPixel(format); - - return info; -} - -int PixelService::GetBufferSize(const PixelFormat::Format &format, const int &width, const int &height) -{ - return BytesPerPixel(format) * width * height; -} - -int PixelService::BytesPerPixel(const PixelFormat::Format &format) -{ - return BytesPerChannel(format) * kRGBAChannels; -} - -int PixelService::BytesPerChannel(const PixelFormat::Format &format) -{ - switch (format) { - case PixelFormat::PIX_FMT_RGBA8: - return 1; - case PixelFormat::PIX_FMT_RGBA16U: - case PixelFormat::PIX_FMT_RGBA16F: - return 2; - case PixelFormat::PIX_FMT_RGBA32F: - return 4; - case PixelFormat::PIX_FMT_INVALID: - case PixelFormat::PIX_FMT_COUNT: - break; - } - - qFatal("Invalid pixel format requested"); - - // qFatal will abort so we won't get here, but this suppresses compiler warnings - return 0; -} - -FramePtr PixelService::ConvertPixelFormat(FramePtr frame, const PixelFormat::Format &dest_format) -{ - if (frame->format() == dest_format) { - return frame; - } - - // FIXME: It'd be nice if this was multithreaded soon - - FramePtr converted = Frame::Create(); - - // Copy parameters - converted->set_width(frame->width()); - converted->set_height(frame->height()); - converted->set_timestamp(frame->timestamp()); - converted->set_format(dest_format); - converted->allocate(); - - int pix_count = frame->width() * frame->height() * kRGBAChannels; - - bool valid = true; - - switch (static_cast(frame->format())) { - case PixelFormat::PIX_FMT_RGBA8: - { - uint8_t* source = reinterpret_cast(frame->data()); - - switch (dest_format) { - case PixelFormat::PIX_FMT_RGBA16U: // 8-bit Integer -> 16-bit Integer - { - uint16_t* destination = reinterpret_cast(converted->data()); - for (int i=0;i(source[i] * 257); - } - break; - } - case PixelFormat::PIX_FMT_RGBA16F: // 8-bit Integer -> 16-bit Float - { - qfloat16* destination = reinterpret_cast(converted->data()); - for (int i=0;i 32-bit Float - { - float* destination = reinterpret_cast(converted->data()); - for (int i=0;i(frame->data()); - - switch (dest_format) { - case PixelFormat::PIX_FMT_RGBA8: // 16-bit Integer -> 8-bit Integer - { - uint8_t* destination = reinterpret_cast(converted->data()); - for (int i=0;i(source[i] / 257); - } - break; - } - case PixelFormat::PIX_FMT_RGBA16F: // 16-bit Integer -> 16-bit Float - { - qfloat16* destination = reinterpret_cast(converted->data()); - for (int i=0;i 32-bit Float - { - float* destination = reinterpret_cast(converted->data()); - for (int i=0;i(frame->data()); - - switch (dest_format) { - case PixelFormat::PIX_FMT_RGBA8: // 16-bit Float -> 8-bit Integer - { - uint8_t* destination = reinterpret_cast(converted->data()); - for (int i=0;i(source[i] * 255.0f); - } - break; - } - case PixelFormat::PIX_FMT_RGBA16U: // 16-bit Float -> 16-bit Integer - { - uint16_t* destination = reinterpret_cast(converted->data()); - for (int i=0;i(source[i] * 65535.0f); - } - break; - } - case PixelFormat::PIX_FMT_RGBA32F: // 16-bit Float -> 32-bit Float - { - float* destination = reinterpret_cast(converted->data()); - for (int i=0;i(frame->data()); - - switch (dest_format) { - case PixelFormat::PIX_FMT_RGBA8: // 32-bit Float -> 8-bit Integer - { - uint8_t* destination = reinterpret_cast(converted->data()); - for (int i=0;i(source[i] * 255.0f); - } - break; - } - case PixelFormat::PIX_FMT_RGBA16U: // 32-bit Float -> 16-bit Integer - { - uint16_t* destination = reinterpret_cast(converted->data()); - for (int i=0;i(source[i] * 65535.0f); - } - break; - } - case PixelFormat::PIX_FMT_RGBA16F: // 32-bit Float -> 16-bit Float - { - qfloat16* destination = reinterpret_cast(converted->data()); - for (int i=0;i(frame->format()); - - int rgb_pixel_size = BytesPerChannel(dest_format) * kRGBChannels; - int rgb_frame_size = frame->width() * frame->height() * rgb_pixel_size; - int rgb_iter = rgb_frame_size - rgb_pixel_size; - - int rgba_pixel_size = BytesPerChannel(dest_format) * kRGBAChannels; - int rgba_frame_size = frame->width() * frame->height() * rgba_pixel_size; - int rgba_iter = rgba_frame_size - rgba_pixel_size; - - // Work backwards to save time - while (rgb_iter >= 0) { - memcpy(&frame->data()[rgba_iter], &frame->data()[rgb_iter], static_cast(rgb_pixel_size)); - - uint8_t* alpha_ptr = reinterpret_cast(frame->data()) + rgba_iter + rgb_pixel_size; - - // Write a full alpha value according to the format - switch (dest_format) { - case PixelFormat::PIX_FMT_RGBA8: - *alpha_ptr = UINT8_MAX; - break; - case PixelFormat::PIX_FMT_RGBA16U: - *reinterpret_cast(alpha_ptr) = UINT16_MAX; - break; - case PixelFormat::PIX_FMT_RGBA16F: - *reinterpret_cast(alpha_ptr) = 1.0f; - break; - case PixelFormat::PIX_FMT_RGBA32F: - *reinterpret_cast(alpha_ptr) = 1.0f; - break; - case PixelFormat::PIX_FMT_INVALID: - case PixelFormat::PIX_FMT_COUNT: - qFatal("Invalid pixel format requested"); - } - - rgb_iter -= rgb_pixel_size; - rgba_iter -= rgba_pixel_size; - } -} diff --git a/app/render/pixelservice.h b/app/render/pixelservice.h deleted file mode 100644 index 0a874f444..000000000 --- a/app/render/pixelservice.h +++ /dev/null @@ -1,106 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#ifndef PIXELSERVICE_H -#define PIXELSERVICE_H - -#include - -#include "codec/frame.h" -#include "pixelformat.h" -#include "render/rendermodes.h" - -class PixelService : public QObject -{ - Q_OBJECT -public: - static void CreateInstance(); - static void DestroyInstance(); - static PixelService* instance(); - - /** - * @brief Returns the configured pixel format for a given mode - */ - PixelFormat::Format GetConfiguredFormatForMode(RenderMode::Mode mode); - void SetConfiguredFormatForMode(RenderMode::Mode mode, PixelFormat::Format format); - - static PixelFormat::Format OIIOFormatToOliveFormat(OIIO::TypeDesc desc); - - /** - * @brief Return a PixelFormatInfo containing information for a certain format - * - * \see PixelFormatInfo - */ - static PixelFormat::Info GetPixelFormatInfo(const PixelFormat::Format& format); - - /** - * @brief Returns the minimum buffer size (in bytes) necessary for a given format, width, and height. - * - * @param format - * - * The format of the data the buffer should contain. Must be a member of the olive::PixelFormat enum. - * - * @param width - * - * The width (in pixels) of the buffer. - * - * @param height - * - * The height (in pixels) of the buffer. - */ - static int GetBufferSize(const PixelFormat::Format &format, const int& width, const int& height); - - /** - * @brief Returns the number of bytes per pixel for a certain format - * - * Different formats use different sizes of data for pixels. Use this function to determine how many bytes a pixel - * requires for a certain format. The number of bytes will always be a multiple of 4 since all formats use RGBA and - * are at least 1 bpc. - */ - static int BytesPerPixel(const PixelFormat::Format &format); - - /** - * @brief Returns the number of bytes per channel for a certain format - */ - static int BytesPerChannel(const PixelFormat::Format& format); - - /** - * @brief Convert a frame to a pixel format - * - * If the frame's pixel format == the destination format, this just returns `frame`. - */ - static FramePtr ConvertPixelFormat(FramePtr frame, const PixelFormat::Format &dest_format); - - /** - * @brief Convert an RGB image to an RGBA image - */ - static void ConvertRGBtoRGBA(FramePtr frame); - -signals: - void FormatChanged(); - -private: - PixelService() = default; - - static PixelService* instance_; - -}; - -#endif // PIXELSERVICE_H diff --git a/app/widget/projectexplorer/projectexplorer.cpp b/app/widget/projectexplorer/projectexplorer.cpp index 1d070dfb2..d252491b9 100644 --- a/app/widget/projectexplorer/projectexplorer.cpp +++ b/app/widget/projectexplorer/projectexplorer.cpp @@ -21,7 +21,11 @@ #include "projectexplorer.h" #include +#include +#include #include +#include +#include #include #include "common/define.h" @@ -242,9 +246,25 @@ void ProjectExplorer::ShowContextMenu() QAction* project_properties = menu.addAction(tr("&Project Properties...")); connect(project_properties, &QAction::triggered, Core::instance(), &Core::DialogProjectPropertiesShow); } else { + if (selected_items.first()->type() == Item::kFootage) { + QString reveal_text; + +#if defined(Q_OS_WINDOWS) + reveal_text = tr("Reveal in Explorer"); +#elif defined(Q_OS_MAC) + reveal_text = tr("Reveal in Finder"); +#else + reveal_text = tr("Reveal in File Manager"); + #endif + + QAction* reveal_action = menu.addAction(reveal_text); + connect(reveal_action, &QAction::triggered, this, &ProjectExplorer::RevealSelectedFootage); + + menu.addSeparator(); + } + QAction* properties_action = menu.addAction(tr("P&roperties")); - // FIXME: Support for multiple items if (selected_items.first()->type() == Item::kFootage) { connect(properties_action, &QAction::triggered, this, &ProjectExplorer::ShowFootagePropertiesDialog); } else if (selected_items.first()->type() == Item::kSequence) { @@ -269,6 +289,31 @@ void ProjectExplorer::ShowSequencePropertiesDialog() sd.exec(); } +void ProjectExplorer::RevealSelectedFootage() +{ + Footage* footage = static_cast(SelectedItems().first()); + +#if defined(Q_OS_WINDOWS) + // Explorer + QStringList args; + args << "/select," << QDir::toNativeSeparators(footage->filename()); + QProcess::startDetached("explorer", args); +#elif defined(Q_OS_MAC) + QStringList args; + args << "-e"; + args << "tell application \"Finder\""; + args << "-e"; + args << "activate"; + args << "-e"; + args << "select POSIX file \""+footage->filename()+"\""; + args << "-e"; + args << "end tell"; + QProcess::startDetached("osascript", args); +#else + QDesktopServices::openUrl(QUrl::fromLocalFile(QFileInfo(footage->filename()).dir().absolutePath())); +#endif +} + Project *ProjectExplorer::project() { return model_.project(); diff --git a/app/widget/projectexplorer/projectexplorer.h b/app/widget/projectexplorer/projectexplorer.h index 65cf68335..910ebfd19 100644 --- a/app/widget/projectexplorer/projectexplorer.h +++ b/app/widget/projectexplorer/projectexplorer.h @@ -155,6 +155,8 @@ private slots: void ShowSequencePropertiesDialog(); + void RevealSelectedFootage(); + }; #endif // PROJECTEXPLORER_H diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index fface9274..f8cb8c398 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -122,6 +122,9 @@ TimelineWidget::TimelineWidget(QWidget *parent) : TimelineWidget::~TimelineWidget() { + // Ensure no blocks are selected before any child widgets are destroyed (prevents corrupt ViewSelectionChanged() signal) + Clear(); + qDeleteAll(tools_); } @@ -129,14 +132,14 @@ void TimelineWidget::Clear() { SetTimebase(0); - QMapIterator iterator(block_items_); + QMap::iterator iterator = block_items_.begin(); - while (iterator.hasNext()) { - iterator.next(); + while (iterator != block_items_.end()) { + TimelineViewBlockItem* item = iterator.value(); - if (iterator.value() != nullptr) { - delete iterator.value(); - } + iterator = block_items_.erase(iterator); + + delete item; } block_items_.clear(); diff --git a/app/widget/timeruler/timeruler.cpp b/app/widget/timeruler/timeruler.cpp index 4b831d836..a5ef88a9a 100644 --- a/app/widget/timeruler/timeruler.cpp +++ b/app/widget/timeruler/timeruler.cpp @@ -88,11 +88,13 @@ const int64_t &TimeRuler::GetTime() void TimeRuler::SetCacheStatusLength(const rational &length) { - cache_length_ = length; + if (show_cache_status_) { + cache_length_ = length; - dirty_cache_ranges_.RemoveTimeRange(TimeRange(length, RATIONAL_MAX)); + dirty_cache_ranges_.RemoveTimeRange(TimeRange(length, RATIONAL_MAX)); - update(); + update(); + } } void TimeRuler::SetTime(const int64_t &r) @@ -111,16 +113,20 @@ void TimeRuler::SetScroll(int s) void TimeRuler::CacheInvalidatedRange(const TimeRange& range) { - dirty_cache_ranges_.InsertTimeRange(range); + if (show_cache_status_) { + dirty_cache_ranges_.InsertTimeRange(range); - update(); + update(); + } } void TimeRuler::CacheTimeReady(const rational &time) { - dirty_cache_ranges_.RemoveTimeRange(TimeRange(time, time + timebase_)); + if (show_cache_status_) { + dirty_cache_ranges_.RemoveTimeRange(TimeRange(time, time + timebase_)); - update(); + update(); + } } void TimeRuler::paintEvent(QPaintEvent *) diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 81add9f61..f14080eed 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -31,7 +31,7 @@ #include "config/config.h" #include "project/item/sequence/sequence.h" #include "project/project.h" -#include "render/pixelservice.h" +#include "render/pixelformat.h" #include "widget/menu/menu.h" ViewerWidget::ViewerWidget(QWidget *parent) : @@ -85,7 +85,7 @@ ViewerWidget::ViewerWidget(QWidget *parent) : connect(video_renderer_, &VideoRenderBackend::RangeInvalidated, ruler(), &TimeRuler::CacheInvalidatedRange); audio_renderer_ = new AudioBackend(this); - connect(PixelService::instance(), &PixelService::FormatChanged, this, &ViewerWidget::UpdateRendererParameters); + connect(PixelFormat::instance(), &PixelFormat::FormatChanged, this, &ViewerWidget::UpdateRendererParameters); SetAutoMaxScrollBar(true); } @@ -296,7 +296,7 @@ void ViewerWidget::UpdateRendererParameters() RenderMode::Mode render_mode = RenderMode::kOffline; VideoRenderingParams vparam(GetConnectedNode()->video_params(), - PixelService::instance()->GetConfiguredFormatForMode(render_mode), + PixelFormat::instance()->GetConfiguredFormatForMode(render_mode), render_mode, divider_); diff --git a/app/widget/viewer/viewerglwidget.cpp b/app/widget/viewer/viewerglwidget.cpp index 9deac2bdd..76b2f9efc 100644 --- a/app/widget/viewer/viewerglwidget.cpp +++ b/app/widget/viewer/viewerglwidget.cpp @@ -27,9 +27,10 @@ #include #include +#include "common/define.h" #include "render/backend/opengl/openglrenderfunctions.h" #include "render/backend/opengl/openglshader.h" -#include "render/pixelservice.h" +#include "render/pixelformat.h" #ifdef Q_OS_LINUX bool ViewerGLWidget::nouveau_check_done_ = false; @@ -83,7 +84,8 @@ void ViewerGLWidget::SetImage(const QString &fn) if (input) { - PixelFormat::Format image_format = PixelService::OIIOFormatToOliveFormat(input->spec().format); + PixelFormat::Format image_format = PixelFormat::OIIOFormatToOliveFormat(input->spec().format, + input->spec().nchannels == kRGBAChannels); // Ensure the following texture operations are done in our context (in case we're in a separate window for instance) makeCurrent(); diff --git a/cmake/FindOpenEXR.cmake b/cmake/FindOpenEXR.cmake index f036c742e..2d71428ed 100644 --- a/cmake/FindOpenEXR.cmake +++ b/cmake/FindOpenEXR.cmake @@ -1,94 +1,99 @@ +# +# Copyright 2016 Pixar +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# + +find_path(OPENEXR_INCLUDE_DIR + OpenEXR/half.h +HINTS + "${OPENEXR_LOCATION}" + "$ENV{OPENEXR_LOCATION}" +PATH_SUFFIXES + include/ +DOC + "OpenEXR headers path" +) + +if(OPENEXR_INCLUDE_DIR) + set(openexr_config_file "${OPENEXR_INCLUDE_DIR}/OpenEXR/OpenEXRConfig.h") + if(EXISTS ${openexr_config_file}) + file(STRINGS + ${openexr_config_file} + TMP + REGEX "#define OPENEXR_VERSION_STRING.*$") + string(REGEX MATCHALL "[0-9.]+" OPENEXR_VERSION ${TMP}) + + file(STRINGS + ${openexr_config_file} + TMP + REGEX "#define OPENEXR_VERSION_MAJOR.*$") + string(REGEX MATCHALL "[0-9]" OPENEXR_MAJOR_VERSION ${TMP}) + + file(STRINGS + ${openexr_config_file} + TMP + REGEX "#define OPENEXR_VERSION_MINOR.*$") + string(REGEX MATCHALL "[0-9]" OPENEXR_MINOR_VERSION ${TMP}) + endif() +endif() + +foreach(OPENEXR_LIB + Half + Iex + Imath + IlmImf + IlmThread + ) + + # OpenEXR libraries may be suffixed with the version number, so we search + # using both versioned and unversioned names. + find_library(OPENEXR_${OPENEXR_LIB}_LIBRARY + NAMES + ${OPENEXR_LIB}-${OPENEXR_MAJOR_VERSION}_${OPENEXR_MINOR_VERSION} + ${OPENEXR_LIB} + HINTS + "${OPENEXR_LOCATION}" + "$ENV{OPENEXR_LOCATION}" + PATH_SUFFIXES + lib/ + DOC + "OPENEXR's ${OPENEXR_LIB} library path" + ) + + if(OPENEXR_${OPENEXR_LIB}_LIBRARY) + list(APPEND OPENEXR_LIBRARIES ${OPENEXR_${OPENEXR_LIB}_LIBRARY}) + endif() +endforeach(OPENEXR_LIB) + +# So #include works +list(APPEND OPENEXR_INCLUDE_DIRS ${OPENEXR_INCLUDE_DIR}) +list(APPEND OPENEXR_INCLUDE_DIRS ${OPENEXR_INCLUDE_DIR}/OpenEXR) + include(FindPackageHandleStandardArgs) - -find_path(OpenEXR_INCLUDE_DIRS OpenEXR/OpenEXRConfig.h) -find_path(OPENEXR_INCLUDE_PATHS NAMES ImfRgbaFile.h PATH_SUFFIXES OpenEXR) - -file(STRINGS "${OpenEXR_INCLUDE_DIRS}/OpenEXR/OpenEXRConfig.h" OPENEXR_CONFIG_H) - -string(REGEX REPLACE "^.*define OPENEXR_VERSION_MAJOR ([0-9]+).*$" "\\1" OpenEXR_VERSION_MAJOR "${OPENEXR_CONFIG_H}") -string(REGEX REPLACE "^.*define OPENEXR_VERSION_MINOR ([0-9]+).*$" "\\1" OpenEXR_VERSION_MINOR "${OPENEXR_CONFIG_H}") -set(OpenEXR_LIB_SUFFIX "${OpenEXR_VERSION_MAJOR}_${OpenEXR_VERSION_MINOR}") - -include(SelectLibraryConfigurations) - -if(NOT OpenEXR_BASE_LIBRARY) - find_library(OpenEXR_BASE_LIBRARY_RELEASE NAMES IlmImf-${OpenEXR_LIB_SUFFIX}) - find_library(OpenEXR_BASE_LIBRARY_DEBUG NAMES IlmImf-${OpenEXR_LIB_SUFFIX}_d) - select_library_configurations(OpenEXR_BASE) -endif() - -if(NOT OpenEXR_UTIL_LIBRARY) - find_library(OpenEXR_UTIL_LIBRARY_RELEASE NAMES IlmImfUtil-${OpenEXR_LIB_SUFFIX}) - find_library(OpenEXR_UTIL_LIBRARY_DEBUG NAMES IlmImfUtil-${OpenEXR_LIB_SUFFIX}_d) - select_library_configurations(OpenEXR_UTIL) -endif() - -if(NOT OpenEXR_HALF_LIBRARY) - find_library(OpenEXR_HALF_LIBRARY_RELEASE NAMES Half-${OpenEXR_LIB_SUFFIX}) - find_library(OpenEXR_HALF_LIBRARY_DEBUG NAMES Half-${OpenEXR_LIB_SUFFIX}_d) - select_library_configurations(OpenEXR_HALF) -endif() - -if(NOT OpenEXR_IEX_LIBRARY) - find_library(OpenEXR_IEX_LIBRARY_RELEASE NAMES Iex-${OpenEXR_LIB_SUFFIX}) - find_library(OpenEXR_IEX_LIBRARY_DEBUG NAMES Iex-${OpenEXR_LIB_SUFFIX}_d) - select_library_configurations(OpenEXR_IEX) -endif() - -if(NOT OpenEXR_MATH_LIBRARY) - find_library(OpenEXR_MATH_LIBRARY_RELEASE NAMES Imath-${OpenEXR_LIB_SUFFIX}) - find_library(OpenEXR_MATH_LIBRARY_DEBUG NAMES Imath-${OpenEXR_LIB_SUFFIX}_d) - select_library_configurations(OpenEXR_MATH) -endif() - -if(NOT OpenEXR_THREAD_LIBRARY) - find_library(OpenEXR_THREAD_LIBRARY_RELEASE NAMES IlmThread-${OpenEXR_LIB_SUFFIX}) - find_library(OpenEXR_THREAD_LIBRARY_DEBUG NAMES IlmThread-${OpenEXR_LIB_SUFFIX}_d) - select_library_configurations(OpenEXR_THREAD) -endif() - -if(NOT OpenEXR_IEXMATH_LIBRARY) - find_library(OpenEXR_IEXMATH_LIBRARY_RELEASE NAMES IexMath-${OpenEXR_LIB_SUFFIX}) - find_library(OpenEXR_IEXMATH_LIBRARY_DEBUG NAMES IexMath-${OpenEXR_LIB_SUFFIX}_d) - select_library_configurations(OpenEXR_IEXMATH) -endif() - -set(OPENEXR_HALF_LIBRARY "${OpenEXR_HALF_LIBRARY}") -set(OPENEXR_Half_LIBRARY "${OpenEXR_HALF_LIBRARY}") -set(OPENEXR_IEX_LIBRARY "${OpenEXR_IEX_LIBRARY}") -set(OPENEXR_Iex_LIBRARY "${OpenEXR_IEX_LIBRARY}") -set(OPENEXR_IMATH_LIBRARY "${OpenEXR_MATH_LIBRARY}") -set(OPENEXR_ILMIMF_LIBRARY "${OpenEXR_BASE_LIBRARY}") -set(OPENEXR_IlmImf_LIBRARY "${OpenEXR_BASE_LIBRARY}") -set(OPENEXR_ILMIMFUTIL_LIBRARY "${OpenEXR_UTIL_LIBRARY}") -set(OPENEXR_ILMTHREAD_LIBRARY "${OpenEXR_THREAD_LIBRARY}") - -set(OpenEXR_LIBRARY "${OpenEXR_BASE_LIBRARY}") - -set(OpenEXR_LIBRARIES - ${OpenEXR_LIBRARY} - ${OpenEXR_MATH_LIBRARY} - ${OpenEXR_IEXMATH_LIBRARY} - ${OpenEXR_UTIL_LIBRARY} - ${OpenEXR_HALF_LIBRARY} - ${OpenEXR_IEX_LIBRARY} - ${OpenEXR_THREAD_LIBRARY} +find_package_handle_standard_args(OpenEXR + REQUIRED_VARS + OPENEXR_INCLUDE_DIRS + OPENEXR_LIBRARIES + VERSION_VAR + OPENEXR_VERSION ) -set(OPENEXR_LIBRARIES - ${OPENEXR_HALF_LIBRARY} - ${OPENEXR_IEX_LIBRARY} - ${OPENEXR_IMATH_LIBRARY} - ${OPENEXR_ILMIMF_LIBRARY} - ${OPENEXR_ILMTHREAD_LIBRARY} -) - -set(OpenEXR_INCLUDE_DIR ${OpenEXR_INCLUDE_DIRS}) -set(OPENEXR_INCLUDE_DIRS ${OpenEXR_INCLUDE_DIRS}) -set(OPENEXR_INCLUDE_DIR ${OPENEXR_INCLUDE_PATHS}) - -FIND_PACKAGE_HANDLE_STANDARD_ARGS(OpenEXR REQUIRED_VARS OpenEXR_LIBRARIES OpenEXR_INCLUDE_DIRS) - -if(OpenEXR_FOUND) - set(OPENEXR_FOUND 1) -endif()