diff --git a/Outputs/ScanTargets/BufferingScanTarget.cpp b/Outputs/ScanTargets/BufferingScanTarget.cpp index b178e5713..14f5f6d9d 100644 --- a/Outputs/ScanTargets/BufferingScanTarget.cpp +++ b/Outputs/ScanTargets/BufferingScanTarget.cpp @@ -234,6 +234,29 @@ void BufferingScanTarget::announce( ++read; frame_read_.store(read, std::memory_order_relaxed); } + + // Look for an even-odd pattern in start-of-frame line placement as an indication that + // incoming video is interlaced. That might be helpful information in deciding exactly + // how to display scans pleasantly, depending on the display target. + start_history_[start_history_pointer_] = location; + ++start_history_pointer_; + + static constexpr int CoordinateEpsilon = 100; + const auto similar_enough = [&](const size_t start, const size_t stride) { + for(size_t index = start + stride; index < start_history_.size(); index += stride) { + if( + abs(start_history_[index - stride].x - start_history_[index].x) > CoordinateEpsilon || + abs(start_history_[index - stride].y - start_history_[index].y) > CoordinateEpsilon + ) { + return false; + } + } + return true; + }; + is_interlaced_.store( + !similar_enough(0, 1) && similar_enough(0, 2) && similar_enough(1, 2), + std::memory_order_relaxed + ); } // Proceed from here only if a change in visibility has occurred. @@ -416,3 +439,7 @@ const Outputs::Display::ScanTarget::Modals &BufferingScanTarget::modals() const bool BufferingScanTarget::has_new_modals() const { return modals_are_dirty_.load(std::memory_order_relaxed); } + +bool BufferingScanTarget::is_interlaced() const { + return is_interlaced_.load(std::memory_order_relaxed); +} diff --git a/Outputs/ScanTargets/BufferingScanTarget.hpp b/Outputs/ScanTargets/BufferingScanTarget.hpp index 1354596bf..22e92384b 100644 --- a/Outputs/ScanTargets/BufferingScanTarget.hpp +++ b/Outputs/ScanTargets/BufferingScanTarget.hpp @@ -164,6 +164,13 @@ public: /// @returns the current @c Modals. const Modals &modals() const; + /// @returns whether the current output being received appears to describe an interlaced signal. + /// This is a hint only, potentially to provide for better deinterlacing of output, being derived locally + /// from line positioning. Specifically: if a scan target pays no heed to this whatsoever it's likely to + /// end up doing the equivalent of a bob. If it so desires, it might prefer to do something closer to + /// a weave if and only if interlaced video is detected. + bool is_interlaced() const; + /// @returns @c true if new modals are available; @c false otherwise. /// /// Safe to call from any thread. @@ -317,6 +324,12 @@ private: std::atomic> frame_read_; std::atomic> frame_write_; + // A recent history of field start positions, to allow detection of interlaced video. + // That can be used as a hint for display purposes. + static constexpr size_t StartHistoryLength = 16; + std::array start_history_; + Numeric::CircularCounter start_history_pointer_; + std::atomic is_interlaced_ = false; // By convention everything in the PointerSet points to the next instance // of whatever it is that will be used. So a client should start with whatever