From ba18334b612efca6abb996659d77a78adac0c313 Mon Sep 17 00:00:00 2001 From: Darby Johnston Date: Sun, 7 Sep 2025 14:21:15 -0700 Subject: [PATCH 01/24] Refactoring Signed-off-by: Darby Johnston --- bin/toucan-filmstrip/App.cpp | 2 +- bin/toucan-render/App.cpp | 4 +- lib/toucanRender/Comp.cpp | 16 +- lib/toucanRender/Comp.h | 2 +- lib/toucanRender/ImageEffect.cpp | 19 +- lib/toucanRender/ImageEffect.h | 2 +- lib/toucanRender/ImageGraph.cpp | 152 +++--------- lib/toucanRender/ImageGraph.h | 4 - lib/toucanRender/ImageNode.cpp | 34 +-- lib/toucanRender/ImageNode.h | 22 +- lib/toucanRender/Read.cpp | 282 +++++++++++----------- lib/toucanRender/Read.h | 115 +++++---- lib/toucanRender/TimeWarp.cpp | 24 +- lib/toucanRender/TimeWarp.h | 4 +- lib/toucanRender/TimelineWrapper.cpp | 70 +++--- lib/toucanRender/TimelineWrapper.h | 10 + lib/toucanView/ExportTool.cpp | 2 +- lib/toucanView/File.cpp | 3 +- lib/toucanView/ThumbnailGenerator.cpp | 28 +-- lib/toucanView/ThumbnailsWidget.h | 2 + tests/toucanRenderTest/CompTest.cpp | 6 +- tests/toucanRenderTest/ImageGraphTest.cpp | 2 +- tests/toucanRenderTest/ReadTest.cpp | 4 +- 23 files changed, 327 insertions(+), 482 deletions(-) diff --git a/bin/toucan-filmstrip/App.cpp b/bin/toucan-filmstrip/App.cpp index 1b9b37e..af37df0 100644 --- a/bin/toucan-filmstrip/App.cpp +++ b/bin/toucan-filmstrip/App.cpp @@ -102,7 +102,7 @@ namespace toucan if (auto node = _graph->exec(_host, time)) { // Execute the graph. - const auto buf = node->exec(); + const auto buf = node->exec(time); // Append the image. const auto thumbnailBuf = OIIO::ImageBufAlgo::resize( diff --git a/bin/toucan-render/App.cpp b/bin/toucan-render/App.cpp index 5d975c9..93bcd9a 100644 --- a/bin/toucan-render/App.cpp +++ b/bin/toucan-render/App.cpp @@ -200,7 +200,7 @@ namespace toucan // Open the movie file. std::shared_ptr ffWrite; - if (MovieReadNode::hasExtension(outputPath.extension().string())) + if (hasExtension(outputPath.extension().string(), MovieReadNode::getExtensions())) { ffmpeg::VideoCodec videoCodec = ffmpeg::VideoCodec::MJPEG; if (_cmdLine.videoCodec->hasValue()) @@ -232,7 +232,7 @@ namespace toucan if (auto node = _graph->exec(_host, time)) { // Execute the graph. - const auto buf = node->exec(); + const auto buf = node->exec(time); // Save the image. if (!_cmdLine.outputRaw) diff --git a/lib/toucanRender/Comp.cpp b/lib/toucanRender/Comp.cpp index 49242e4..c631c50 100644 --- a/lib/toucanRender/Comp.cpp +++ b/lib/toucanRender/Comp.cpp @@ -26,20 +26,13 @@ namespace toucan _resize = resize; } - OIIO::ImageBuf CompNode::exec() + OIIO::ImageBuf CompNode::exec(const OTIO_NS::RationalTime& t) { OIIO::ImageBuf buf; - OTIO_NS::RationalTime offsetTime = _time; - if (!_timeOffset.is_invalid_time()) - { - offsetTime -= _timeOffset; - } if (_inputs.size() > 1 && _inputs[0] && _inputs[1]) { - _inputs[0]->setTime(offsetTime); - auto fgBuf = _inputs[0]->exec(); - _inputs[1]->setTime(offsetTime); - buf = _inputs[1]->exec(); + auto fgBuf = _inputs[0]->exec(t); + buf = _inputs[1]->exec(t); const auto fgSpec = fgBuf.spec(); if (_premult && fgSpec.width > 0 && @@ -81,8 +74,7 @@ namespace toucan } else if (1 == _inputs.size() && _inputs[0]) { - _inputs[0]->setTime(offsetTime); - buf = _inputs[0]->exec(); + buf = _inputs[0]->exec(t); if (_premult) { buf = OIIO::ImageBufAlgo::premult(buf); diff --git a/lib/toucanRender/Comp.h b/lib/toucanRender/Comp.h index bb0137a..cca5f07 100644 --- a/lib/toucanRender/Comp.h +++ b/lib/toucanRender/Comp.h @@ -21,7 +21,7 @@ namespace toucan //! Set whether images are resized before compositing. void setResize(bool); - OIIO::ImageBuf exec() override; + OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; private: bool _premult = false; diff --git a/lib/toucanRender/ImageEffect.cpp b/lib/toucanRender/ImageEffect.cpp index 6ef593b..bb7ba30 100644 --- a/lib/toucanRender/ImageEffect.cpp +++ b/lib/toucanRender/ImageEffect.cpp @@ -74,16 +74,10 @@ namespace toucan nullptr); } - OIIO::ImageBuf ImageEffectNode::exec() + OIIO::ImageBuf ImageEffectNode::exec(const OTIO_NS::RationalTime& t) { OIIO::ImageBuf out; - OTIO_NS::RationalTime offsetTime = _time; - if (!_timeOffset.is_invalid_time()) - { - offsetTime -= _timeOffset; - } - // Initialize the images. std::vector inputs; IMATH_NAMESPACE::V2i size = IMATH_NAMESPACE::V2i(0, 0); @@ -104,8 +98,7 @@ namespace toucan !_inputs.empty() && _inputs[0]) { - _inputs[0]->setTime(offsetTime); - inputs.push_back(_inputs[0]->exec()); + inputs.push_back(_inputs[0]->exec(t)); auto spec = inputs[0].spec(); if (size.x > 0 && size.y > 0) { @@ -122,10 +115,8 @@ namespace toucan _inputs[0] && _inputs[1]) { - _inputs[0]->setTime(offsetTime); - inputs.push_back(_inputs[0]->exec()); - _inputs[1]->setTime(offsetTime); - inputs.push_back(_inputs[1]->exec()); + inputs.push_back(_inputs[0]->exec(t)); + inputs.push_back(_inputs[1]->exec(t)); auto spec = inputs[0].spec(); if (size.x > 0 && size.y > 0) { @@ -143,7 +134,7 @@ namespace toucan if (spec.width > 0 && spec.height > 0) { PropertySet args; - args.setDouble(kOfxPropTime, 0, offsetTime.value()); + args.setDouble(kOfxPropTime, 0, t.value()); OfxRectI bounds; bounds.x1 = 0; bounds.x2 = spec.width; diff --git a/lib/toucanRender/ImageEffect.h b/lib/toucanRender/ImageEffect.h index ed564c6..6c038fe 100644 --- a/lib/toucanRender/ImageEffect.h +++ b/lib/toucanRender/ImageEffect.h @@ -50,7 +50,7 @@ namespace toucan virtual ~ImageEffectNode(); - OIIO::ImageBuf exec() override; + OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; private: ImageEffectPlugin& _plugin; diff --git a/lib/toucanRender/ImageGraph.cpp b/lib/toucanRender/ImageGraph.cpp index e6646b5..5b7c13d 100644 --- a/lib/toucanRender/ImageGraph.cpp +++ b/lib/toucanRender/ImageGraph.cpp @@ -48,8 +48,6 @@ namespace toucan _timelineWrapper(timelineWrapper), _timeRange(timelineWrapper->getTimeRange()) { - _loadCache.setMax(10); - // Get the image information from the first video clip. for (auto clip : getVideoClips(_timelineWrapper->getTimeline())) { @@ -57,21 +55,7 @@ namespace toucan { try { - const std::filesystem::path path = _timelineWrapper->getMediaPath(externalRef->target_url()); - const MemoryReference mem = _timelineWrapper->getMemoryReference(externalRef->target_url()); - std::shared_ptr read; - if (MovieReadNode::hasExtension(path.extension().string())) - { - read = std::make_shared(path, nullptr, mem); - } - else if (SVGReadNode::hasExtension(path.extension().string())) - { - read = std::make_shared(path, nullptr, mem); - } - else - { - read = std::make_shared(path, nullptr, mem); - } + auto read = _timelineWrapper->getReadNode(externalRef); const auto& spec = read->getSpec(); if (spec.width > 0) { @@ -94,16 +78,7 @@ namespace toucan { try { - auto read = std::make_shared( - _timelineWrapper->getMediaPath(sequenceRef->target_url_base()), - sequenceRef->name_prefix(), - sequenceRef->name_suffix(), - sequenceRef->start_frame(), - sequenceRef->frame_step(), - sequenceRef->rate(), - sequenceRef->frame_zero_padding(), - nullptr, - _timelineWrapper->getMemoryReferences()); + auto read = _timelineWrapper->getReadNode(sequenceRef); const auto& spec = read->getSpec(); if (spec.width > 0) { @@ -207,9 +182,6 @@ namespace toucan node = _effects(host, stack->trimmed_range(), effects, node); } - // Set the time. - node->setTime(time - _timeRange.start_time()); - return node; } @@ -220,7 +192,8 @@ namespace toucan { std::shared_ptr out; - // Find the items for the given time. + // Find the item for the given time. The previous and next items are + // also tracked for handling transitions. OTIO_NS::SerializableObject::Retainer item; OTIO_NS::SerializableObject::Retainer prev; OTIO_NS::SerializableObject::Retainer prev2; @@ -274,27 +247,26 @@ namespace toucan (time - trimmedRangeInParent.value().start_time()).value() / trimmedRangeInParent.value().duration().value(); + auto a = _item( + host, + prevItem->trimmed_range_in_parent().value(), + track->transformed_time(time, prevItem), + prevItem); + auto metaData = prevTransition->metadata(); metaData["value"] = value; auto node = host->createNode( metaData, - prevTransition->transition_type()); + prevTransition->transition_type(), + { a, out }); if (!node) { node = host->createNode( metaData, - "toucan:Dissolve"); - } - if (node) - { - auto a = _item( - host, - prevItem->trimmed_range_in_parent().value(), - track->transformed_time(time, prevItem), - prevItem); - node->setInputs({ a, out }); - out = node; + "toucan:Dissolve", + { a, out }); } + out = node; } } } @@ -309,27 +281,26 @@ namespace toucan (time - trimmedRangeInParent.value().start_time()).value() / trimmedRangeInParent.value().duration().value(); + auto b = _item( + host, + nextItem->trimmed_range_in_parent().value(), + track->transformed_time(time, nextItem), + nextItem); + auto metaData = nextTransition->metadata(); metaData["value"] = value; auto node = host->createNode( metaData, - nextTransition->transition_type()); + nextTransition->transition_type(), + { out, b }); if (!node) { node = host->createNode( metaData, - "toucan:Dissolve"); - } - if (node) - { - auto b = _item( - host, - nextItem->trimmed_range_in_parent().value(), - track->transformed_time(time, nextItem), - nextItem); - node->setInputs({ out, b }); - out = node; + "toucan:Dissolve", + { out, b }); } + out = node; } } } @@ -352,67 +323,25 @@ namespace toucan if (auto externalRef = dynamic_cast(clip->media_reference())) { std::shared_ptr read; - if (!_loadCache.get(externalRef, read)) + try { - try - { - const std::filesystem::path path = _timelineWrapper->getMediaPath(externalRef->target_url()); - const MemoryReference mem = _timelineWrapper->getMemoryReference(externalRef->target_url()); - if (MovieReadNode::hasExtension(path.extension().string())) - { - read = std::make_shared(path, externalRef, mem); - } - else if (SVGReadNode::hasExtension(path.extension().string())) - { - read = std::make_shared(path, externalRef, mem); - } - else - { - read = std::make_shared(path, externalRef, mem); - } - } - catch (const std::exception& e) - { - _context.lock()->getSystem()->print( - logPrefix, - e.what(), - ftk::LogType::Error); - } - _loadCache.add(externalRef, read); + read = _timelineWrapper->getReadNode(externalRef); } - if (read) + catch (const std::exception& e) { - OTIO_NS::RationalTime timeOffset = -item->trimmed_range().start_time(); - - //! \bug Workaround for when the available range does not match - //! the range in the media. - const OTIO_NS::TimeRange& timeRange = read->getTimeRange(); - const auto availableOpt = externalRef->available_range(); - if (availableOpt.has_value() && - !availableOpt.value().start_time().strictly_equal(timeRange.start_time())) - { - timeOffset += availableOpt.value().start_time() - timeRange.start_time(); - } - - read->setTimeOffset(timeOffset); + _context.lock()->getSystem()->print( + logPrefix, + e.what(), + ftk::LogType::Error); } out = read; } else if (auto sequenceRef = dynamic_cast(clip->media_reference())) { - std::shared_ptr read; + std::shared_ptr read; try { - read = std::make_shared( - _timelineWrapper->getMediaPath(sequenceRef->target_url_base()), - sequenceRef->name_prefix(), - sequenceRef->name_suffix(), - sequenceRef->start_frame(), - sequenceRef->frame_step(), - sequenceRef->rate(), - sequenceRef->frame_zero_padding(), - sequenceRef, - _timelineWrapper->getMemoryReferences()); + read = _timelineWrapper->getReadNode(sequenceRef); } catch (const std::exception& e) { @@ -421,10 +350,6 @@ namespace toucan e.what(), ftk::LogType::Error); } - if (read) - { - read->setTimeOffset(-item->trimmed_range().start_time()); - } out = read; } else if (auto generatorRef = dynamic_cast(clip->media_reference())) @@ -448,12 +373,6 @@ namespace toucan out = _effects(host, item->trimmed_range(), effects, out); } - // Set the time offset. - if (out) - { - out->setTimeOffset(out->getTimeOffset() + trimmedRangeInParent.start_time()); - } - return out; } @@ -470,7 +389,6 @@ namespace toucan { auto linearTimeWarpNode = std::make_shared( static_cast(linearTimeWarp->time_scalar()), - trimmedRange, std::vector >{ out }); out = linearTimeWarpNode; } diff --git a/lib/toucanRender/ImageGraph.h b/lib/toucanRender/ImageGraph.h index 820c028..3ecea85 100644 --- a/lib/toucanRender/ImageGraph.h +++ b/lib/toucanRender/ImageGraph.h @@ -7,7 +7,6 @@ #include #include -#include #include #include @@ -18,8 +17,6 @@ namespace toucan { - class IReadNode; - //! Create image graphs from a timeline. class ImageGraph : public std::enable_shared_from_this { @@ -70,6 +67,5 @@ namespace toucan IMATH_NAMESPACE::V2i _imageSize = IMATH_NAMESPACE::V2i(0, 0); int _imageChannels = 0; std::string _imageDataType; - ftk::LRUCache > _loadCache; }; } diff --git a/lib/toucanRender/ImageNode.cpp b/lib/toucanRender/ImageNode.cpp index 7c0b54f..65ccb3c 100644 --- a/lib/toucanRender/ImageNode.cpp +++ b/lib/toucanRender/ImageNode.cpp @@ -30,31 +30,6 @@ namespace toucan return _inputs; } - void IImageNode::setInputs(const std::vector >& value) - { - _inputs = value; - } - - const OTIO_NS::RationalTime& IImageNode::getTimeOffset() const - { - return _timeOffset; - } - - void IImageNode::setTimeOffset(const OTIO_NS::RationalTime& timeOffset) - { - _timeOffset = timeOffset; - } - - const OTIO_NS::RationalTime& IImageNode::getTime() const - { - return _time; - } - - void IImageNode::setTime(const OTIO_NS::RationalTime& time) - { - _time = time; - } - std::vector IImageNode::graph(const std::string& name) { std::vector out; @@ -62,20 +37,15 @@ namespace toucan ss << "digraph " << name << " {"; out.push_back(ss.str()); out.push_back(" node [shape=box, fontsize=12, margin=0.05, width=0, height=0];"); - _graph(_time, shared_from_this(), out); + _graph(shared_from_this(), out); out.push_back("}"); return out; } void IImageNode::_graph( - OTIO_NS::RationalTime time, const std::shared_ptr& node, std::vector& out) { - if (!node->_timeOffset.is_invalid_time()) - { - time -= node->_timeOffset; - } std::stringstream ss; const std::string graphName = node->_getGraphName(); ss << " " << graphName << " [label=\"" << node->getLabel() << "\"]"; @@ -87,7 +57,7 @@ namespace toucan std::stringstream ss; ss << " " << input->_getGraphName() << " -> " << graphName; out.push_back(ss.str()); - _graph(time, input, out); + _graph(input, out); } } } diff --git a/lib/toucanRender/ImageNode.h b/lib/toucanRender/ImageNode.h index 239c163..510beb2 100644 --- a/lib/toucanRender/ImageNode.h +++ b/lib/toucanRender/ImageNode.h @@ -33,39 +33,19 @@ namespace toucan //! Get the inputs. const std::vector >& getInputs() const; - //! Set the inputs. - void setInputs(const std::vector >&); - - //! Get the time offset. - const OTIO_NS::RationalTime& getTimeOffset() const; - - //! Set the time offset. - //! - //! \todo How should time transforms be handled? - void setTimeOffset(const OTIO_NS::RationalTime&); - - //! Get the time. - const OTIO_NS::RationalTime& getTime() const; - - //! Set the time. - void setTime(const OTIO_NS::RationalTime&); - //! Execute the image operation. - virtual OIIO::ImageBuf exec() = 0; + virtual OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) = 0; //! Generate a Grapviz graph std::vector graph(const std::string& name); protected: void _graph( - OTIO_NS::RationalTime, const std::shared_ptr&, std::vector&); std::string _getGraphName() const; std::string _name; - OTIO_NS::RationalTime _timeOffset; - OTIO_NS::RationalTime _time; std::vector > _inputs; }; } diff --git a/lib/toucanRender/Read.cpp b/lib/toucanRender/Read.cpp index c0b1ba5..7020a2e 100644 --- a/lib/toucanRender/Read.cpp +++ b/lib/toucanRender/Read.cpp @@ -3,6 +3,7 @@ #include "Read.h" +#include "TimelineWrapper.h" #include "Util.h" #include @@ -11,22 +12,13 @@ namespace toucan { - IReadNode::IReadNode( - const OTIO_NS::MediaReference* ref, - const std::string& name, - const std::vector >& inputs) : - IImageNode(name, inputs), - _ref(ref) + IReadNode::IReadNode(const std::string& name) : + IImageNode(name) {} IReadNode::~IReadNode() {} - const OTIO_NS::MediaReference* IReadNode::getRef() const - { - return _ref; - } - const OIIO::ImageSpec& IReadNode::getSpec() const { return _spec; @@ -39,10 +31,8 @@ namespace toucan ImageReadNode::ImageReadNode( const std::filesystem::path& path, - const OTIO_NS::MediaReference* ref, - const MemoryReference& memoryReference, - const std::vector >& inputs) : - IReadNode(ref, "ImageRead", inputs), + const MemoryReference& memoryReference) : + IReadNode("ImageRead"), _path(path), _memoryReader(getMemoryReader(memoryReference)) { @@ -71,7 +61,7 @@ namespace toucan return ss.str(); } - OIIO::ImageBuf ImageReadNode::exec() + OIIO::ImageBuf ImageReadNode::exec(const OTIO_NS::RationalTime&) { OIIO::ImageBuf out; @@ -112,94 +102,7 @@ namespace toucan std::vector ImageReadNode::getExtensions() { - return std::vector({ ".exr", ".tif", ".tiff", ".jpg", ".jpeg", ".png" }); - } - - bool ImageReadNode::hasExtension(const std::string& value) - { - const std::vector extensions = getExtensions(); - const auto i = std::find(extensions.begin(), extensions.end(), toLower(value)); - return i != extensions.end(); - } - - SVGReadNode::SVGReadNode( - const std::filesystem::path& path, - const OTIO_NS::MediaReference* ref, - const MemoryReference& memoryReference, - const std::vector >& inputs) : - IReadNode(ref, "SVGRead", inputs), - _path(path) - { - if (memoryReference.isValid()) - { - _svg = lunasvg::Document::loadFromData( - reinterpret_cast(memoryReference.getData()), - memoryReference.getSize()); - } - else - { - _svg = lunasvg::Document::loadFromFile(path.u8string()); - } - if (!_svg) - { - std::stringstream ss; - ss << "Cannot open file: " << _path.string(); - throw std::runtime_error(ss.str()); - } - _spec = OIIO::ImageSpec( - _svg->width(), - _svg->height(), - 4, - OIIO::TypeDesc::BASETYPE::UINT8); - } - - SVGReadNode::~SVGReadNode() - {} - - std::string SVGReadNode::getLabel() const - { - std::stringstream ss; - ss << "Read: " << _path.filename().string(); - return ss.str(); - } - - OIIO::ImageBuf SVGReadNode::exec() - { - OIIO::ImageBuf out; - - const int w = _svg->width(); - const int h = _svg->height(); - auto bitmap = _svg->renderToBitmap(w, h, 0x00000000); - if (!bitmap.isNull()) - { - out = OIIO::ImageBuf(_spec); - for (int y = 0; y < h; ++y) - { - uint8_t* imageP = reinterpret_cast(out.localpixels()) + y * w * 4; - const uint8_t* bitmapP = bitmap.data() + y * w * 4; - for (int x = 0; x < w; ++x, imageP += 4, bitmapP += 4) - { - imageP[0] = bitmapP[2]; - imageP[1] = bitmapP[1]; - imageP[2] = bitmapP[0]; - imageP[3] = bitmapP[3]; - } - } - } - - return out; - } - - std::vector SVGReadNode::getExtensions() - { - return std::vector({ ".svg" }); - } - - bool SVGReadNode::hasExtension(const std::string& value) - { - const std::vector extensions = getExtensions(); - const auto i = std::find(extensions.begin(), extensions.end(), toLower(value)); - return i != extensions.end(); + return { ".exr", ".tif", ".tiff", ".jpg", ".jpeg", ".png" }; } SequenceReadNode::SequenceReadNode( @@ -210,10 +113,8 @@ namespace toucan int frameStep, double rate, int frameZeroPadding, - const OTIO_NS::MediaReference* ref, - const MemoryReferences& memoryReferences, - const std::vector >& inputs) : - IReadNode(ref, "SequenceRead", inputs), + const MemoryReferences& memoryReferences) : + IReadNode("SequenceRead"), _base(base), _namePrefix(namePrefix), _nameSuffix(nameSuffix), @@ -260,22 +161,15 @@ namespace toucan return ss.str(); } - OIIO::ImageBuf SequenceReadNode::exec() + OIIO::ImageBuf SequenceReadNode::exec(const OTIO_NS::RationalTime& t) { OIIO::ImageBuf out; - // Get the time. - OTIO_NS::RationalTime offsetTime = _time; - if (!_timeOffset.is_invalid_time()) - { - offsetTime -= _timeOffset; - } - // Open the sequence file. const std::string url = getSequenceFrame( _base, _namePrefix, - offsetTime.floor().to_frames(), + t.to_frames(), _frameZeroPadding, _nameSuffix); std::unique_ptr memoryReader; @@ -323,22 +217,84 @@ namespace toucan std::vector SequenceReadNode::getExtensions() { - return std::vector({ ".exr", ".tif", ".png" }); + return { ".exr", ".tif", ".tiff", ".jpg", ".jpeg", ".png" }; } - bool SequenceReadNode::hasExtension(const std::string& value) + SVGReadNode::SVGReadNode( + const std::filesystem::path& path, + const MemoryReference& memoryReference) : + IReadNode("SVGRead"), + _path(path) { - const std::vector extensions = getExtensions(); - const auto i = std::find(extensions.begin(), extensions.end(), toLower(value)); - return i != extensions.end(); + if (memoryReference.isValid()) + { + _svg = lunasvg::Document::loadFromData( + reinterpret_cast(memoryReference.getData()), + memoryReference.getSize()); + } + else + { + _svg = lunasvg::Document::loadFromFile(path.u8string()); + } + if (!_svg) + { + std::stringstream ss; + ss << "Cannot open file: " << _path.string(); + throw std::runtime_error(ss.str()); + } + _spec = OIIO::ImageSpec( + _svg->width(), + _svg->height(), + 4, + OIIO::TypeDesc::BASETYPE::UINT8); + } + + SVGReadNode::~SVGReadNode() + {} + + std::string SVGReadNode::getLabel() const + { + std::stringstream ss; + ss << "Read: " << _path.filename().string(); + return ss.str(); + } + + OIIO::ImageBuf SVGReadNode::exec(const OTIO_NS::RationalTime&) + { + OIIO::ImageBuf out; + + const int w = _svg->width(); + const int h = _svg->height(); + auto bitmap = _svg->renderToBitmap(w, h, 0x00000000); + if (!bitmap.isNull()) + { + out = OIIO::ImageBuf(_spec); + for (int y = 0; y < h; ++y) + { + uint8_t* imageP = reinterpret_cast(out.localpixels()) + y * w * 4; + const uint8_t* bitmapP = bitmap.data() + y * w * 4; + for (int x = 0; x < w; ++x, imageP += 4, bitmapP += 4) + { + imageP[0] = bitmapP[2]; + imageP[1] = bitmapP[1]; + imageP[2] = bitmapP[0]; + imageP[3] = bitmapP[3]; + } + } + } + + return out; + } + + std::vector SVGReadNode::getExtensions() + { + return { ".svg" }; } MovieReadNode::MovieReadNode( const std::filesystem::path& path, - const OTIO_NS::MediaReference* ref, - const MemoryReference& memoryReference, - const std::vector >& inputs) : - IReadNode(ref, "MovieReadNode", inputs), + const MemoryReference& memoryReference) : + IReadNode("MovieReadNode"), _path(path), _memoryReader(getMemoryReader(memoryReference)) { @@ -357,19 +313,12 @@ namespace toucan return ss.str(); } - OIIO::ImageBuf MovieReadNode::exec() + OIIO::ImageBuf MovieReadNode::exec(const OTIO_NS::RationalTime& t) { OIIO::ImageBuf out; - // Get the time. - OTIO_NS::RationalTime offsetTime = _time; - if (!_timeOffset.is_invalid_time()) - { - offsetTime -= _timeOffset; - } - // Read the image. - out = _ffRead->getImage(offsetTime); + out = _ffRead->getImage(t); const auto& spec = out.spec(); if (3 == spec.nchannels) @@ -386,13 +335,68 @@ namespace toucan std::vector MovieReadNode::getExtensions() { - return std::vector({ ".mov", ".mp4", ".m4v", ".y4m" }); + return { ".mov", ".mp4", ".m4v", ".y4m" }; + } + + ReadFactory::ReadFactory() + { + _cache.setMax(10); + } + + std::shared_ptr ReadFactory::getReadNode( + OTIO_NS::MediaReference* ref, + const std::filesystem::path& path, + const MemoryReference& mem) + { + std::shared_ptr out; + if (hasExtension(path.extension().string(), MovieReadNode::getExtensions())) + { + out = std::make_shared(path, mem); + } + else if (hasExtension(path.extension().string(), ImageReadNode::getExtensions())) + { + out = std::make_shared(path, mem); + } + else if (hasExtension(path.extension().string(), SVGReadNode::getExtensions())) + { + out = std::make_shared(path, mem); + } + return out; + } + + std::shared_ptr ReadFactory::getReadNode( + OTIO_NS::MediaReference*, + const std::string& base, + const std::string& namePrefix, + const std::string& nameSuffix, + int startFrame, + int frameStep, + double rate, + int frameZeroPadding, + const MemoryReferences& mem) + { + std::shared_ptr out; + if (hasExtension(nameSuffix, SequenceReadNode::getExtensions())) + { + out = std::make_shared( + base, + namePrefix, + nameSuffix, + startFrame, + frameStep, + rate, + frameZeroPadding, + mem); + } + return out; + } - bool MovieReadNode::hasExtension(const std::string& value) + bool hasExtension( + const std::string& extension, + const std::vector& extensions) { - const std::vector extensions = getExtensions(); - const auto i = std::find(extensions.begin(), extensions.end(), toLower(value)); + const auto i = std::find(extensions.begin(), extensions.end(), toLower(extension)); return i != extensions.end(); } } diff --git a/lib/toucanRender/Read.h b/lib/toucanRender/Read.h index 47e224c..f8eda77 100644 --- a/lib/toucanRender/Read.h +++ b/lib/toucanRender/Read.h @@ -7,6 +7,8 @@ #include #include +#include + #include #include @@ -21,21 +23,15 @@ namespace toucan class IReadNode : public IImageNode { public: - IReadNode( - const OTIO_NS::MediaReference*, - const std::string& name, - const std::vector >& = {}); + IReadNode(const std::string& name); virtual ~IReadNode() = 0; - const OTIO_NS::MediaReference* getRef() const; - const OIIO::ImageSpec& getSpec() const; const OTIO_NS::TimeRange& getTimeRange() const; protected: - const OTIO_NS::MediaReference* _ref = nullptr; OIIO::ImageSpec _spec; OTIO_NS::TimeRange _timeRange; }; @@ -46,51 +42,22 @@ namespace toucan public: ImageReadNode( const std::filesystem::path&, - const OTIO_NS::MediaReference*, - const MemoryReference & = {}, - const std::vector > & = {}); + const MemoryReference& = {}); virtual ~ImageReadNode(); std::string getLabel() const override; - OIIO::ImageBuf exec() override; + OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; static std::vector getExtensions(); - static bool hasExtension(const std::string&); - private: std::filesystem::path _path; std::shared_ptr _memoryReader; std::unique_ptr _input; }; - //! SVG read node. - class SVGReadNode : public IReadNode - { - public: - SVGReadNode( - const std::filesystem::path&, - const OTIO_NS::MediaReference*, - const MemoryReference& = {}, - const std::vector >& = {}); - - virtual ~SVGReadNode(); - - std::string getLabel() const override; - - OIIO::ImageBuf exec() override; - - static std::vector getExtensions(); - - static bool hasExtension(const std::string&); - - private: - std::filesystem::path _path; - std::unique_ptr _svg; - }; - //! Image sequence read node. class SequenceReadNode : public IReadNode { @@ -102,21 +69,17 @@ namespace toucan int startFrame, int frameStep, double rate, - int frameZerPadding, - const OTIO_NS::MediaReference*, - const MemoryReferences& = {}, - const std::vector >& = {}); + int frameZeroPadding, + const MemoryReferences& = {}); virtual ~SequenceReadNode(); std::string getLabel() const override; - OIIO::ImageBuf exec() override; + OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; static std::vector getExtensions(); - static bool hasExtension(const std::string&); - private: std::string _base; std::string _namePrefix; @@ -128,29 +91,79 @@ namespace toucan MemoryReferences _memoryReferences; }; + //! SVG read node. + class SVGReadNode : public IReadNode + { + public: + SVGReadNode( + const std::filesystem::path&, + const MemoryReference& = {}); + + virtual ~SVGReadNode(); + + std::string getLabel() const override; + + OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; + + static std::vector getExtensions(); + + private: + std::filesystem::path _path; + std::unique_ptr _svg; + }; + //! Movie read node. class MovieReadNode : public IReadNode { public: MovieReadNode( const std::filesystem::path&, - const OTIO_NS::MediaReference*, - const MemoryReference& = {}, - const std::vector >& = {}); + const MemoryReference& = {}); virtual ~MovieReadNode(); std::string getLabel() const override; - OIIO::ImageBuf exec() override; + OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; static std::vector getExtensions(); - static bool hasExtension(const std::string&); - private: std::filesystem::path _path; std::shared_ptr _memoryReader; std::unique_ptr _ffRead; }; + + //! Read factory. + class ReadFactory : public std::enable_shared_from_this + { + public: + ReadFactory(); + + //! Get a read node. + std::shared_ptr getReadNode( + OTIO_NS::MediaReference*, + const std::filesystem::path&, + const MemoryReference& = {}); + + //! Get a read node. + std::shared_ptr getReadNode( + OTIO_NS::MediaReference*, + const std::string& base, + const std::string& namePrefix, + const std::string& nameSuffix, + int startFrame, + int frameStep, + double rate, + int frameZerPadding, + const MemoryReferences& = {}); + + private: + ftk::LRUCache > _cache; + }; + + //! Is the extension in the list? + bool hasExtension( + const std::string& extension, + const std::vector& extensions); } diff --git a/lib/toucanRender/TimeWarp.cpp b/lib/toucanRender/TimeWarp.cpp index f64878c..6076d53 100644 --- a/lib/toucanRender/TimeWarp.cpp +++ b/lib/toucanRender/TimeWarp.cpp @@ -7,37 +7,21 @@ namespace toucan { LinearTimeWarpNode::LinearTimeWarpNode( double timeScalar, - const OTIO_NS::TimeRange& timeRange, const std::vector >& inputs) : IImageNode("LinearTimeWarp", inputs), - _timeScalar(timeScalar), - _timeRange(timeRange) + _timeScalar(timeScalar) {} LinearTimeWarpNode::~LinearTimeWarpNode() {} - OIIO::ImageBuf LinearTimeWarpNode::exec() + OIIO::ImageBuf LinearTimeWarpNode::exec(const OTIO_NS::RationalTime& t) { OIIO::ImageBuf buf; if (!_inputs.empty()) { - OTIO_NS::RationalTime offsetTime = _time; - if (!_timeOffset.is_invalid_time()) - { - offsetTime -= _timeOffset; - } - double timeScalar = _timeScalar; - if (timeScalar < 0.0) - { - timeScalar *= -1.0; - offsetTime = _timeRange.duration() - - OTIO_NS::RationalTime(1.0, _timeRange.duration().rate()) - - offsetTime; - } - const OTIO_NS::RationalTime scaledTime = OTIO_NS::RationalTime(offsetTime.value() * timeScalar, _time.rate()).floor(); - _inputs[0]->setTime(scaledTime); - buf = _inputs[0]->exec(); + const OTIO_NS::RationalTime scaledTime = OTIO_NS::RationalTime(t.value() * _timeScalar, t.rate()).floor(); + buf = _inputs[0]->exec(scaledTime); } return buf; } diff --git a/lib/toucanRender/TimeWarp.h b/lib/toucanRender/TimeWarp.h index 9562710..791c432 100644 --- a/lib/toucanRender/TimeWarp.h +++ b/lib/toucanRender/TimeWarp.h @@ -13,15 +13,13 @@ namespace toucan public: LinearTimeWarpNode( double timeScalar, - const OTIO_NS::TimeRange&, const std::vector >& = {}); virtual ~LinearTimeWarpNode(); - OIIO::ImageBuf exec() override; + OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; private: double _timeScalar = 1.F; - OTIO_NS::TimeRange _timeRange; }; } diff --git a/lib/toucanRender/TimelineWrapper.cpp b/lib/toucanRender/TimelineWrapper.cpp index f44a96a..a9a3044 100644 --- a/lib/toucanRender/TimelineWrapper.cpp +++ b/lib/toucanRender/TimelineWrapper.cpp @@ -241,9 +241,9 @@ namespace toucan } } } - else if (MovieReadNode::hasExtension(path.extension().string())) + else if (hasExtension(extension, MovieReadNode::getExtensions())) { - auto read = std::make_shared(path, nullptr); + auto read = std::make_shared(path); _timeline = OTIO_NS::SerializableObject::Retainer(new OTIO_NS::Timeline); OTIO_NS::SerializableObject::Retainer track(new OTIO_NS::Track("Video")); _timeline->tracks()->append_child(track); @@ -254,37 +254,25 @@ namespace toucan clip->set_media_reference(ref); clip->set_source_range(read->getTimeRange()); } - else if (ImageReadNode::hasExtension(path.extension().string()) || - SVGReadNode::hasExtension(path.extension().string()) || - SequenceReadNode::hasExtension(path.extension().string())) + else if (hasExtension(extension, ImageReadNode::getExtensions()) || + hasExtension(extension, SVGReadNode::getExtensions()) || + hasExtension(extension, SequenceReadNode::getExtensions())) { const auto sequence = getSequence(path); const auto split = splitFileNameNumber(sequence.front().stem().string()); if (split.second.empty()) { - std::shared_ptr read; - if (ImageReadNode::hasExtension(path.extension().u8string())) - { - read = std::make_shared(path, nullptr); - } - else if (SVGReadNode::hasExtension(path.extension().u8string())) - { - read = std::make_shared(path, nullptr); - } - if (read) - { - _timeline = OTIO_NS::SerializableObject::Retainer(new OTIO_NS::Timeline); - OTIO_NS::SerializableObject::Retainer track(new OTIO_NS::Track("Video")); - _timeline->tracks()->append_child(track); - OTIO_NS::SerializableObject::Retainer clip(new OTIO_NS::Clip); - track->append_child(clip); - OTIO_NS::SerializableObject::Retainer ref( - new OTIO_NS::ExternalReference(path.string())); - clip->set_media_reference(ref); - clip->set_source_range(OTIO_NS::TimeRange( - OTIO_NS::RationalTime(0.0, 24.0), - OTIO_NS::RationalTime(1.0, 24.0))); - } + _timeline = OTIO_NS::SerializableObject::Retainer(new OTIO_NS::Timeline); + OTIO_NS::SerializableObject::Retainer track(new OTIO_NS::Track("Video")); + _timeline->tracks()->append_child(track); + OTIO_NS::SerializableObject::Retainer clip(new OTIO_NS::Clip); + track->append_child(clip); + OTIO_NS::SerializableObject::Retainer ref( + new OTIO_NS::ExternalReference(path.string())); + clip->set_media_reference(ref); + clip->set_source_range(OTIO_NS::TimeRange( + OTIO_NS::RationalTime(0.0, 24.0), + OTIO_NS::RationalTime(1.0, 24.0))); } else { @@ -295,7 +283,6 @@ namespace toucan const int step = 1; const double rate = 24.0; const int padding = getNumberPadding(split.second); - auto read = std::make_shared(base, prefix, suffix, start, 1, rate, padding, nullptr); _timeline = OTIO_NS::SerializableObject::Retainer(new OTIO_NS::Timeline); OTIO_NS::SerializableObject::Retainer track(new OTIO_NS::Track("Video")); _timeline->tracks()->append_child(track); @@ -320,6 +307,8 @@ namespace toucan globalStartTime.value() : OTIO_NS::RationalTime(0.0, _timeline->duration().rate()), _timeline->duration()); + + _readFactory = std::make_shared(); } TimelineWrapper::~TimelineWrapper() @@ -372,4 +361,27 @@ namespace toucan const auto i = _memoryReferences.find(url); return i != _memoryReferences.end() ? i->second : MemoryReference(); } + + std::shared_ptr TimelineWrapper::getReadNode(OTIO_NS::ExternalReference* ref) + { + const std::string path = getMediaPath(ref->target_url()); + const MemoryReference mem = getMemoryReference(ref->target_url()); + return _readFactory->getReadNode(ref, path, mem); + } + + std::shared_ptr TimelineWrapper::getReadNode(OTIO_NS::ImageSequenceReference* ref) + { + const std::string path = getMediaPath(ref->target_url_base()); + const MemoryReferences mem = getMemoryReferences(); + return _readFactory->getReadNode( + ref, + path, + ref->name_prefix(), + ref->name_suffix(), + ref->start_frame(), + ref->frame_step(), + ref->rate(), + ref->frame_zero_padding(), + mem); + } } diff --git a/lib/toucanRender/TimelineWrapper.h b/lib/toucanRender/TimelineWrapper.h index be60348..b6228c6 100644 --- a/lib/toucanRender/TimelineWrapper.h +++ b/lib/toucanRender/TimelineWrapper.h @@ -5,6 +5,8 @@ #include +#include +#include #include #include @@ -13,6 +15,9 @@ namespace toucan { + class IReadNode; + class ReadFactory; + //! Timeline wrapper that supports .otiod and .otioz files. class TimelineWrapper : public std::enable_shared_from_this { @@ -30,6 +35,10 @@ namespace toucan const MemoryReferences& getMemoryReferences() const; MemoryReference getMemoryReference(const std::string& url) const; + std::shared_ptr getReadNode(OTIO_NS::ExternalReference*); + + std::shared_ptr getReadNode(OTIO_NS::ImageSequenceReference*); + private: std::filesystem::path _path; //std::filesystem::path _tmpPath; @@ -37,5 +46,6 @@ namespace toucan MemoryReferences _memoryReferences; OTIO_NS::SerializableObject::Retainer _timeline; OTIO_NS::TimeRange _timeRange; + std::shared_ptr _readFactory; }; } diff --git a/lib/toucanView/ExportTool.cpp b/lib/toucanView/ExportTool.cpp index 921a397..bccd71f 100644 --- a/lib/toucanView/ExportTool.cpp +++ b/lib/toucanView/ExportTool.cpp @@ -439,7 +439,7 @@ namespace toucan { if (auto node = _graph->exec(_host, _time)) { - auto buf = node->exec(); + auto buf = node->exec(_time); if (_outputSize != _imageSize) { buf = OIIO::ImageBufAlgo::resize( diff --git a/lib/toucanView/File.cpp b/lib/toucanView/File.cpp index 800b23b..20f3330 100644 --- a/lib/toucanView/File.cpp +++ b/lib/toucanView/File.cpp @@ -142,8 +142,7 @@ namespace toucan if (_currentNode->get()) { const OTIO_NS::TimeRange& timeRange = _playbackModel->getTimeRange(); - _currentNode->get()->setTime(_currentTime - timeRange.start_time()); - _imageBuf = _currentNode->get()->exec(); + _imageBuf = _currentNode->get()->exec(_currentTime - timeRange.start_time()); const auto& spec = _imageBuf.spec(); ftk::ImageType imageType = ftk::ImageType::None; diff --git a/lib/toucanView/ThumbnailGenerator.cpp b/lib/toucanView/ThumbnailGenerator.cpp index 3cf6b17..89eee85 100644 --- a/lib/toucanView/ThumbnailGenerator.cpp +++ b/lib/toucanView/ThumbnailGenerator.cpp @@ -214,8 +214,7 @@ namespace toucan { try { - node->setTime(aspectRequest->time - _timelineWrapper->getTimeRange().start_time()); - const OIIO::ImageBuf buf = node->exec(); + const OIIO::ImageBuf buf = node->exec(aspectRequest->time - _timelineWrapper->getTimeRange().start_time()); const OIIO::ImageSpec& spec = buf.spec(); if (spec.height > 0) { @@ -241,14 +240,10 @@ namespace toucan if (request->ref) { node = _findNode(node, request->ref); - if (node) - { - node->setTime(request->time - _timelineWrapper->getTimeRange().start_time()); - } } if (node) { - buf = node->exec(); + buf = node->exec(request->time - _timelineWrapper->getTimeRange().start_time()); } } catch (const std::exception& e) @@ -348,25 +343,6 @@ namespace toucan const OTIO_NS::MediaReference* ref) { std::shared_ptr out; - if (node) - { - auto read = std::dynamic_pointer_cast(node); - if (read && read->getRef() == ref) - { - out = node; - } - else - { - for (const auto& input : node->getInputs()) - { - out = _findNode(input, ref); - if (out) - { - break; - } - } - } - } return out; } } diff --git a/lib/toucanView/ThumbnailsWidget.h b/lib/toucanView/ThumbnailsWidget.h index 356133e..532dc95 100644 --- a/lib/toucanView/ThumbnailsWidget.h +++ b/lib/toucanView/ThumbnailsWidget.h @@ -6,6 +6,8 @@ #include #include +#include + #include namespace toucan diff --git a/tests/toucanRenderTest/CompTest.cpp b/tests/toucanRenderTest/CompTest.cpp index a62bec3..d6ea16a 100644 --- a/tests/toucanRenderTest/CompTest.cpp +++ b/tests/toucanRenderTest/CompTest.cpp @@ -11,12 +11,12 @@ namespace toucan void compTest(const std::filesystem::path& path) { std::cout << "compTest" << std::endl; - auto fg = std::make_shared(path / "Letter_A.png", nullptr); - auto bg = std::make_shared(path / "Gradient.png", nullptr); + auto fg = std::make_shared(path / "Letter_A.png"); + auto bg = std::make_shared(path / "Gradient.png"); auto comp = std::make_shared( std::vector >{ fg, bg }); comp->setPremult(true); - auto buf = comp->exec(); + auto buf = comp->exec(OTIO_NS::RationalTime(0.0, 24.0)); buf.write("compTest.png"); } } diff --git a/tests/toucanRenderTest/ImageGraphTest.cpp b/tests/toucanRenderTest/ImageGraphTest.cpp index dbf9d71..af88c6b 100644 --- a/tests/toucanRenderTest/ImageGraphTest.cpp +++ b/tests/toucanRenderTest/ImageGraphTest.cpp @@ -62,7 +62,7 @@ namespace toucan if (auto op = graph->exec(host, time)) { // Execute the image operation graph. - const auto buf = op->exec(); + const auto buf = op->exec(time); // Save the image. const std::string fileName = getSequenceFrame( diff --git a/tests/toucanRenderTest/ReadTest.cpp b/tests/toucanRenderTest/ReadTest.cpp index 6722125..d3d6948 100644 --- a/tests/toucanRenderTest/ReadTest.cpp +++ b/tests/toucanRenderTest/ReadTest.cpp @@ -12,8 +12,8 @@ namespace toucan void readTest(const std::filesystem::path& path) { std::cout << "readTest" << std::endl; - auto read = std::make_shared(path / "Letter_A.png", nullptr); - auto buf = read->exec(); + auto read = std::make_shared(path / "Letter_A.png"); + auto buf = read->exec(OTIO_NS::RationalTime(0.0, 24.0)); const auto& spec = buf.spec(); assert(spec.width > 0); } From c5e6a28576f6d2794a4ebb17814cc66c3fcfaece Mon Sep 17 00:00:00 2001 From: Darby Johnston Date: Mon, 15 Sep 2025 10:30:19 -0700 Subject: [PATCH 02/24] Refactoring Signed-off-by: Darby Johnston --- bin/toucan-filmstrip/App.cpp | 2 +- bin/toucan-render/App.cpp | 2 +- lib/toucanRender/Comp.cpp | 8 +- lib/toucanRender/Comp.h | 2 +- lib/toucanRender/ImageEffect.cpp | 10 +- lib/toucanRender/ImageEffect.h | 2 +- lib/toucanRender/ImageGraph.cpp | 66 ++++++----- lib/toucanRender/ImageGraph.h | 2 - lib/toucanRender/ImageNode.cpp | 5 + lib/toucanRender/ImageNode.h | 6 +- lib/toucanRender/Read.cpp | 23 ++-- lib/toucanRender/Read.h | 54 ++++----- lib/toucanRender/TimeWarp.cpp | 4 +- lib/toucanRender/TimeWarp.h | 4 +- lib/toucanRender/TimelineWrapper.cpp | 63 +++++----- lib/toucanRender/TimelineWrapper.h | 13 +- lib/toucanView/AudioClipItem.cpp | 36 +++--- lib/toucanView/AudioClipItem.h | 9 +- lib/toucanView/ExportTool.cpp | 2 +- lib/toucanView/File.cpp | 15 +-- lib/toucanView/File.h | 5 - lib/toucanView/GapItem.cpp | 38 +++--- lib/toucanView/GapItem.h | 9 +- lib/toucanView/IItem.cpp | 29 ++--- lib/toucanView/IItem.h | 8 +- lib/toucanView/MarkerItem.cpp | 6 +- lib/toucanView/MarkerItem.h | 7 +- lib/toucanView/StackItem.cpp | 35 +++--- lib/toucanView/StackItem.h | 9 +- lib/toucanView/ThumbnailGenerator.cpp | 138 ++++++++-------------- lib/toucanView/ThumbnailGenerator.h | 22 +--- lib/toucanView/ThumbnailsWidget.cpp | 42 ++++++- lib/toucanView/ThumbnailsWidget.h | 16 ++- lib/toucanView/TimelineItem.cpp | 128 +------------------- lib/toucanView/TimelineItem.h | 15 +-- lib/toucanView/TimelineWidget.cpp | 8 +- lib/toucanView/TimelineWidget.h | 2 + lib/toucanView/TrackItem.cpp | 41 +++---- lib/toucanView/TrackItem.h | 8 +- lib/toucanView/VideoClipItem.cpp | 41 +++---- lib/toucanView/VideoClipItem.h | 8 +- tests/toucanRenderTest/CompTest.cpp | 2 +- tests/toucanRenderTest/ImageGraphTest.cpp | 2 +- tests/toucanRenderTest/ReadTest.cpp | 3 +- 44 files changed, 370 insertions(+), 580 deletions(-) diff --git a/bin/toucan-filmstrip/App.cpp b/bin/toucan-filmstrip/App.cpp index af37df0..1b9b37e 100644 --- a/bin/toucan-filmstrip/App.cpp +++ b/bin/toucan-filmstrip/App.cpp @@ -102,7 +102,7 @@ namespace toucan if (auto node = _graph->exec(_host, time)) { // Execute the graph. - const auto buf = node->exec(time); + const auto buf = node->exec(); // Append the image. const auto thumbnailBuf = OIIO::ImageBufAlgo::resize( diff --git a/bin/toucan-render/App.cpp b/bin/toucan-render/App.cpp index 93bcd9a..dc7ff2f 100644 --- a/bin/toucan-render/App.cpp +++ b/bin/toucan-render/App.cpp @@ -232,7 +232,7 @@ namespace toucan if (auto node = _graph->exec(_host, time)) { // Execute the graph. - const auto buf = node->exec(time); + const auto buf = node->exec(); // Save the image. if (!_cmdLine.outputRaw) diff --git a/lib/toucanRender/Comp.cpp b/lib/toucanRender/Comp.cpp index c631c50..6d364e1 100644 --- a/lib/toucanRender/Comp.cpp +++ b/lib/toucanRender/Comp.cpp @@ -26,13 +26,13 @@ namespace toucan _resize = resize; } - OIIO::ImageBuf CompNode::exec(const OTIO_NS::RationalTime& t) + OIIO::ImageBuf CompNode::exec() { OIIO::ImageBuf buf; if (_inputs.size() > 1 && _inputs[0] && _inputs[1]) { - auto fgBuf = _inputs[0]->exec(t); - buf = _inputs[1]->exec(t); + auto fgBuf = _inputs[0]->exec(); + buf = _inputs[1]->exec(); const auto fgSpec = fgBuf.spec(); if (_premult && fgSpec.width > 0 && @@ -74,7 +74,7 @@ namespace toucan } else if (1 == _inputs.size() && _inputs[0]) { - buf = _inputs[0]->exec(t); + buf = _inputs[0]->exec(); if (_premult) { buf = OIIO::ImageBufAlgo::premult(buf); diff --git a/lib/toucanRender/Comp.h b/lib/toucanRender/Comp.h index cca5f07..bb0137a 100644 --- a/lib/toucanRender/Comp.h +++ b/lib/toucanRender/Comp.h @@ -21,7 +21,7 @@ namespace toucan //! Set whether images are resized before compositing. void setResize(bool); - OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; + OIIO::ImageBuf exec() override; private: bool _premult = false; diff --git a/lib/toucanRender/ImageEffect.cpp b/lib/toucanRender/ImageEffect.cpp index bb7ba30..1aefe01 100644 --- a/lib/toucanRender/ImageEffect.cpp +++ b/lib/toucanRender/ImageEffect.cpp @@ -74,7 +74,7 @@ namespace toucan nullptr); } - OIIO::ImageBuf ImageEffectNode::exec(const OTIO_NS::RationalTime& t) + OIIO::ImageBuf ImageEffectNode::exec() { OIIO::ImageBuf out; @@ -98,7 +98,7 @@ namespace toucan !_inputs.empty() && _inputs[0]) { - inputs.push_back(_inputs[0]->exec(t)); + inputs.push_back(_inputs[0]->exec()); auto spec = inputs[0].spec(); if (size.x > 0 && size.y > 0) { @@ -115,8 +115,8 @@ namespace toucan _inputs[0] && _inputs[1]) { - inputs.push_back(_inputs[0]->exec(t)); - inputs.push_back(_inputs[1]->exec(t)); + inputs.push_back(_inputs[0]->exec()); + inputs.push_back(_inputs[1]->exec()); auto spec = inputs[0].spec(); if (size.x > 0 && size.y > 0) { @@ -134,7 +134,7 @@ namespace toucan if (spec.width > 0 && spec.height > 0) { PropertySet args; - args.setDouble(kOfxPropTime, 0, t.value()); + args.setDouble(kOfxPropTime, 0, _time.value()); OfxRectI bounds; bounds.x1 = 0; bounds.x2 = spec.width; diff --git a/lib/toucanRender/ImageEffect.h b/lib/toucanRender/ImageEffect.h index 6c038fe..ed564c6 100644 --- a/lib/toucanRender/ImageEffect.h +++ b/lib/toucanRender/ImageEffect.h @@ -50,7 +50,7 @@ namespace toucan virtual ~ImageEffectNode(); - OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; + OIIO::ImageBuf exec() override; private: ImageEffectPlugin& _plugin; diff --git a/lib/toucanRender/ImageGraph.cpp b/lib/toucanRender/ImageGraph.cpp index 5b7c13d..2a31d76 100644 --- a/lib/toucanRender/ImageGraph.cpp +++ b/lib/toucanRender/ImageGraph.cpp @@ -53,9 +53,20 @@ namespace toucan { if (auto externalRef = dynamic_cast(clip->media_reference())) { + std::shared_ptr read; try { - auto read = _timelineWrapper->getReadNode(externalRef); + read = _timelineWrapper->createReadNode(externalRef); + } + catch (const std::exception& e) + { + _context.lock()->getSystem()->print( + logPrefix, + e.what(), + ftk::LogType::Error); + } + if (read) + { const auto& spec = read->getSpec(); if (spec.width > 0) { @@ -66,6 +77,14 @@ namespace toucan break; } } + } + else if (auto sequenceRef = dynamic_cast(clip->media_reference())) + { + std::shared_ptr read; + try + { + read = _timelineWrapper->createReadNode(sequenceRef); + } catch (const std::exception& e) { _context.lock()->getSystem()->print( @@ -73,12 +92,8 @@ namespace toucan e.what(), ftk::LogType::Error); } - } - else if (auto sequenceRef = dynamic_cast(clip->media_reference())) - { - try + if (read) { - auto read = _timelineWrapper->getReadNode(sequenceRef); const auto& spec = read->getSpec(); if (spec.width > 0) { @@ -89,13 +104,6 @@ namespace toucan break; } } - catch (const std::exception& e) - { - _context.lock()->getSystem()->print( - logPrefix, - e.what(), - ftk::LogType::Error); - } } else if (auto generatorRef = dynamic_cast(clip->media_reference())) { @@ -107,6 +115,7 @@ namespace toucan //! \bug Hard coded: _imageChannels = 4; _imageDataType = toImageDataType(OIIO::TypeDesc::UINT8); + break; } } } @@ -155,7 +164,7 @@ namespace toucan const auto& effects = track->effects(); if (trackNode && !effects.empty()) { - trackNode = _effects(host, track->trimmed_range(), effects, trackNode); + trackNode = _effects(host, effects, trackNode); } // Composite this track over the previous track. @@ -179,7 +188,7 @@ namespace toucan const auto& effects = stack->effects(); if (!effects.empty()) { - node = _effects(host, stack->trimmed_range(), effects, node); + node = _effects(host, effects, node); } return node; @@ -209,7 +218,6 @@ namespace toucan { out = _item( host, - trimmedRangeInParent.value(), track->transformed_time(time, item), item); if (i > 0) @@ -249,7 +257,6 @@ namespace toucan auto a = _item( host, - prevItem->trimmed_range_in_parent().value(), track->transformed_time(time, prevItem), prevItem); @@ -283,7 +290,6 @@ namespace toucan auto b = _item( host, - nextItem->trimmed_range_in_parent().value(), track->transformed_time(time, nextItem), nextItem); @@ -311,7 +317,6 @@ namespace toucan std::shared_ptr ImageGraph::_item( const std::shared_ptr& host, - const OTIO_NS::TimeRange& trimmedRangeInParent, const OTIO_NS::RationalTime& time, const OTIO_NS::SerializableObject::Retainer& item) { @@ -322,10 +327,9 @@ namespace toucan // Get the media reference. if (auto externalRef = dynamic_cast(clip->media_reference())) { - std::shared_ptr read; try { - read = _timelineWrapper->getReadNode(externalRef); + out = _timelineWrapper->createReadNode(externalRef); } catch (const std::exception& e) { @@ -334,14 +338,12 @@ namespace toucan e.what(), ftk::LogType::Error); } - out = read; } else if (auto sequenceRef = dynamic_cast(clip->media_reference())) { - std::shared_ptr read; try { - read = _timelineWrapper->getReadNode(sequenceRef); + out = _timelineWrapper->createReadNode(sequenceRef); } catch (const std::exception& e) { @@ -350,7 +352,6 @@ namespace toucan e.what(), ftk::LogType::Error); } - out = read; } else if (auto generatorRef = dynamic_cast(clip->media_reference())) { @@ -365,12 +366,16 @@ namespace toucan metaData["size"] = vecToAny(_imageSize); out = host->createNode(metaData, "toucan:Fill"); } + if (out) + { + out->setTime(time); + } // Get the effects. const auto& effects = item->effects(); if (out && !effects.empty()) { - out = _effects(host, item->trimmed_range(), effects, out); + out = _effects(host, effects, out); } return out; @@ -378,7 +383,6 @@ namespace toucan std::shared_ptr ImageGraph::_effects( const std::shared_ptr& host, - const OTIO_NS::TimeRange& trimmedRange, const std::vector >& effects, const std::shared_ptr& input) { @@ -387,10 +391,10 @@ namespace toucan { if (auto linearTimeWarp = dynamic_cast(effect.value)) { - auto linearTimeWarpNode = std::make_shared( - static_cast(linearTimeWarp->time_scalar()), - std::vector >{ out }); - out = linearTimeWarpNode; + //auto linearTimeWarpNode = std::make_shared( + // static_cast(linearTimeWarp->time_scalar()), + // std::vector >{ out }); + //out = linearTimeWarpNode; } else { diff --git a/lib/toucanRender/ImageGraph.h b/lib/toucanRender/ImageGraph.h index 3ecea85..e671057 100644 --- a/lib/toucanRender/ImageGraph.h +++ b/lib/toucanRender/ImageGraph.h @@ -50,13 +50,11 @@ namespace toucan std::shared_ptr _item( const std::shared_ptr&, - const OTIO_NS::TimeRange& trimmedRangeInParent, const OTIO_NS::RationalTime&, const OTIO_NS::SerializableObject::Retainer&); std::shared_ptr _effects( const std::shared_ptr&, - const OTIO_NS::TimeRange& trimmedRange, const std::vector >&, const std::shared_ptr&); diff --git a/lib/toucanRender/ImageNode.cpp b/lib/toucanRender/ImageNode.cpp index 65ccb3c..a9170bc 100644 --- a/lib/toucanRender/ImageNode.cpp +++ b/lib/toucanRender/ImageNode.cpp @@ -30,6 +30,11 @@ namespace toucan return _inputs; } + void IImageNode::setTime(const OTIO_NS::RationalTime& value) + { + _time = value; + } + std::vector IImageNode::graph(const std::string& name) { std::vector out; diff --git a/lib/toucanRender/ImageNode.h b/lib/toucanRender/ImageNode.h index 510beb2..c66aa44 100644 --- a/lib/toucanRender/ImageNode.h +++ b/lib/toucanRender/ImageNode.h @@ -33,8 +33,11 @@ namespace toucan //! Get the inputs. const std::vector >& getInputs() const; + //! Set the time. + void setTime(const OTIO_NS::RationalTime&); + //! Execute the image operation. - virtual OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) = 0; + virtual OIIO::ImageBuf exec() = 0; //! Generate a Grapviz graph std::vector graph(const std::string& name); @@ -47,5 +50,6 @@ namespace toucan std::string _name; std::vector > _inputs; + OTIO_NS::RationalTime _time; }; } diff --git a/lib/toucanRender/Read.cpp b/lib/toucanRender/Read.cpp index 7020a2e..37f7cd1 100644 --- a/lib/toucanRender/Read.cpp +++ b/lib/toucanRender/Read.cpp @@ -61,7 +61,7 @@ namespace toucan return ss.str(); } - OIIO::ImageBuf ImageReadNode::exec(const OTIO_NS::RationalTime&) + OIIO::ImageBuf ImageReadNode::exec() { OIIO::ImageBuf out; @@ -161,7 +161,7 @@ namespace toucan return ss.str(); } - OIIO::ImageBuf SequenceReadNode::exec(const OTIO_NS::RationalTime& t) + OIIO::ImageBuf SequenceReadNode::exec() { OIIO::ImageBuf out; @@ -169,7 +169,7 @@ namespace toucan const std::string url = getSequenceFrame( _base, _namePrefix, - t.to_frames(), + _time.to_frames(), _frameZeroPadding, _nameSuffix); std::unique_ptr memoryReader; @@ -259,7 +259,7 @@ namespace toucan return ss.str(); } - OIIO::ImageBuf SVGReadNode::exec(const OTIO_NS::RationalTime&) + OIIO::ImageBuf SVGReadNode::exec() { OIIO::ImageBuf out; @@ -313,12 +313,12 @@ namespace toucan return ss.str(); } - OIIO::ImageBuf MovieReadNode::exec(const OTIO_NS::RationalTime& t) + OIIO::ImageBuf MovieReadNode::exec() { OIIO::ImageBuf out; // Read the image. - out = _ffRead->getImage(t); + out = _ffRead->getImage(_time); const auto& spec = out.spec(); if (3 == spec.nchannels) @@ -338,13 +338,7 @@ namespace toucan return { ".mov", ".mp4", ".m4v", ".y4m" }; } - ReadFactory::ReadFactory() - { - _cache.setMax(10); - } - - std::shared_ptr ReadFactory::getReadNode( - OTIO_NS::MediaReference* ref, + std::shared_ptr createReadNode( const std::filesystem::path& path, const MemoryReference& mem) { @@ -364,8 +358,7 @@ namespace toucan return out; } - std::shared_ptr ReadFactory::getReadNode( - OTIO_NS::MediaReference*, + std::shared_ptr createReadNode( const std::string& base, const std::string& namePrefix, const std::string& nameSuffix, diff --git a/lib/toucanRender/Read.h b/lib/toucanRender/Read.h index f8eda77..88190d5 100644 --- a/lib/toucanRender/Read.h +++ b/lib/toucanRender/Read.h @@ -7,12 +7,10 @@ #include #include -#include +#include #include -#include - #include #include @@ -48,7 +46,7 @@ namespace toucan std::string getLabel() const override; - OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; + OIIO::ImageBuf exec() override; static std::vector getExtensions(); @@ -76,7 +74,7 @@ namespace toucan std::string getLabel() const override; - OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; + OIIO::ImageBuf exec() override; static std::vector getExtensions(); @@ -103,7 +101,7 @@ namespace toucan std::string getLabel() const override; - OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; + OIIO::ImageBuf exec() override; static std::vector getExtensions(); @@ -124,7 +122,7 @@ namespace toucan std::string getLabel() const override; - OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; + OIIO::ImageBuf exec() override; static std::vector getExtensions(); @@ -134,33 +132,21 @@ namespace toucan std::unique_ptr _ffRead; }; - //! Read factory. - class ReadFactory : public std::enable_shared_from_this - { - public: - ReadFactory(); - - //! Get a read node. - std::shared_ptr getReadNode( - OTIO_NS::MediaReference*, - const std::filesystem::path&, - const MemoryReference& = {}); - - //! Get a read node. - std::shared_ptr getReadNode( - OTIO_NS::MediaReference*, - const std::string& base, - const std::string& namePrefix, - const std::string& nameSuffix, - int startFrame, - int frameStep, - double rate, - int frameZerPadding, - const MemoryReferences& = {}); - - private: - ftk::LRUCache > _cache; - }; + //! Create a read node. + std::shared_ptr createReadNode( + const std::filesystem::path&, + const MemoryReference& = {}); + + //! Create a read node. + std::shared_ptr createReadNode( + const std::string& base, + const std::string& namePrefix, + const std::string& nameSuffix, + int startFrame, + int frameStep, + double rate, + int frameZerPadding, + const MemoryReferences& = {}); //! Is the extension in the list? bool hasExtension( diff --git a/lib/toucanRender/TimeWarp.cpp b/lib/toucanRender/TimeWarp.cpp index 6076d53..c19b9b5 100644 --- a/lib/toucanRender/TimeWarp.cpp +++ b/lib/toucanRender/TimeWarp.cpp @@ -5,7 +5,7 @@ namespace toucan { - LinearTimeWarpNode::LinearTimeWarpNode( + /*LinearTimeWarpNode::LinearTimeWarpNode( double timeScalar, const std::vector >& inputs) : IImageNode("LinearTimeWarp", inputs), @@ -24,5 +24,5 @@ namespace toucan buf = _inputs[0]->exec(scaledTime); } return buf; - } + }*/ } diff --git a/lib/toucanRender/TimeWarp.h b/lib/toucanRender/TimeWarp.h index 791c432..c702e73 100644 --- a/lib/toucanRender/TimeWarp.h +++ b/lib/toucanRender/TimeWarp.h @@ -8,7 +8,7 @@ namespace toucan { //! Linear time warp node. - class LinearTimeWarpNode : public IImageNode + /*class LinearTimeWarpNode : public IImageNode { public: LinearTimeWarpNode( @@ -21,5 +21,5 @@ namespace toucan private: double _timeScalar = 1.F; - }; + };*/ } diff --git a/lib/toucanRender/TimelineWrapper.cpp b/lib/toucanRender/TimelineWrapper.cpp index a9a3044..5ed2c5d 100644 --- a/lib/toucanRender/TimelineWrapper.cpp +++ b/lib/toucanRender/TimelineWrapper.cpp @@ -72,6 +72,7 @@ namespace toucan const std::string extension = ftk::toLower(_path.extension().string()); if (".otio" == extension) { + // Open an .otio file. OTIO_NS::ErrorStatus errorStatus; _timeline = OTIO_NS::SerializableObject::Retainer( dynamic_cast(OTIO_NS::Timeline::from_json_file(_path.string(), &errorStatus))); @@ -82,6 +83,7 @@ namespace toucan } else if (".otiod" == extension) { + // Open an otiod archive. _path = _path / "content.otio"; OTIO_NS::ErrorStatus errorStatus; _timeline = OTIO_NS::SerializableObject::Retainer( @@ -93,6 +95,7 @@ namespace toucan } else if (".otioz" == extension) { + // Open an otioz archive. /* // Create a temp directory. _tmpPath = makeUniqueTemp(); @@ -243,8 +246,10 @@ namespace toucan } else if (hasExtension(extension, MovieReadNode::getExtensions())) { + // Open a movie file and create a timeline for it. auto read = std::make_shared(path); _timeline = OTIO_NS::SerializableObject::Retainer(new OTIO_NS::Timeline); + _timeline->set_global_start_time(read->getTimeRange().start_time()); OTIO_NS::SerializableObject::Retainer track(new OTIO_NS::Track("Video")); _timeline->tracks()->append_child(track); OTIO_NS::SerializableObject::Retainer clip(new OTIO_NS::Clip); @@ -262,6 +267,7 @@ namespace toucan const auto split = splitFileNameNumber(sequence.front().stem().string()); if (split.second.empty()) { + // Open an image file and create a timeline for it. _timeline = OTIO_NS::SerializableObject::Retainer(new OTIO_NS::Timeline); OTIO_NS::SerializableObject::Retainer track(new OTIO_NS::Track("Video")); _timeline->tracks()->append_child(track); @@ -276,6 +282,7 @@ namespace toucan } else { + // Open an image sequence and create a timeline for it. const std::string base = sequence.front().parent_path().string(); const std::string prefix = split.first; const std::string suffix = sequence.front().extension().string(); @@ -307,8 +314,6 @@ namespace toucan globalStartTime.value() : OTIO_NS::RationalTime(0.0, _timeline->duration().rate()), _timeline->duration()); - - _readFactory = std::make_shared(); } TimelineWrapper::~TimelineWrapper() @@ -319,6 +324,11 @@ namespace toucan //} } + const std::filesystem::path& TimelineWrapper::getPath() const + { + return _path; + } + const OTIO_NS::SerializableObject::Retainer& TimelineWrapper::getTimeline() const { return _timeline; @@ -351,37 +361,34 @@ namespace toucan return out; } - const MemoryReferences& TimelineWrapper::getMemoryReferences() const + std::shared_ptr TimelineWrapper::createReadNode(const OTIO_NS::MediaReference* ref) { - return _memoryReferences; + std::shared_ptr out; + if (auto externalRef = dynamic_cast(ref)) + { + const std::string path = getMediaPath(externalRef->target_url()); + const MemoryReference mem = _getMemoryReference(externalRef->target_url()); + out = toucan::createReadNode(path, mem); + } + else if (auto seqRef = dynamic_cast(ref)) + { + const std::string path = getMediaPath(seqRef->target_url_base()); + out = toucan::createReadNode( + path, + seqRef->name_prefix(), + seqRef->name_suffix(), + seqRef->start_frame(), + seqRef->frame_step(), + seqRef->rate(), + seqRef->frame_zero_padding(), + _memoryReferences); + } + return out; } - MemoryReference TimelineWrapper::getMemoryReference(const std::string& url) const + MemoryReference TimelineWrapper::_getMemoryReference(const std::string& url) const { const auto i = _memoryReferences.find(url); return i != _memoryReferences.end() ? i->second : MemoryReference(); } - - std::shared_ptr TimelineWrapper::getReadNode(OTIO_NS::ExternalReference* ref) - { - const std::string path = getMediaPath(ref->target_url()); - const MemoryReference mem = getMemoryReference(ref->target_url()); - return _readFactory->getReadNode(ref, path, mem); - } - - std::shared_ptr TimelineWrapper::getReadNode(OTIO_NS::ImageSequenceReference* ref) - { - const std::string path = getMediaPath(ref->target_url_base()); - const MemoryReferences mem = getMemoryReferences(); - return _readFactory->getReadNode( - ref, - path, - ref->name_prefix(), - ref->name_suffix(), - ref->start_frame(), - ref->frame_step(), - ref->rate(), - ref->frame_zero_padding(), - mem); - } } diff --git a/lib/toucanRender/TimelineWrapper.h b/lib/toucanRender/TimelineWrapper.h index b6228c6..5b69743 100644 --- a/lib/toucanRender/TimelineWrapper.h +++ b/lib/toucanRender/TimelineWrapper.h @@ -16,7 +16,6 @@ namespace toucan { class IReadNode; - class ReadFactory; //! Timeline wrapper that supports .otiod and .otioz files. class TimelineWrapper : public std::enable_shared_from_this @@ -26,26 +25,24 @@ namespace toucan ~TimelineWrapper(); + const std::filesystem::path& getPath() const; + const OTIO_NS::SerializableObject::Retainer& getTimeline() const; const OTIO_NS::TimeRange& getTimeRange() const; std::string getMediaPath(const std::string& url) const; - const MemoryReferences& getMemoryReferences() const; - MemoryReference getMemoryReference(const std::string& url) const; - - std::shared_ptr getReadNode(OTIO_NS::ExternalReference*); - - std::shared_ptr getReadNode(OTIO_NS::ImageSequenceReference*); + std::shared_ptr createReadNode(const OTIO_NS::MediaReference*); private: + MemoryReference _getMemoryReference(const std::string& url) const; + std::filesystem::path _path; //std::filesystem::path _tmpPath; std::unique_ptr _memoryMap; MemoryReferences _memoryReferences; OTIO_NS::SerializableObject::Retainer _timeline; OTIO_NS::TimeRange _timeRange; - std::shared_ptr _readFactory; }; } diff --git a/lib/toucanView/AudioClipItem.cpp b/lib/toucanView/AudioClipItem.cpp index 6efab0f..8831c58 100644 --- a/lib/toucanView/AudioClipItem.cpp +++ b/lib/toucanView/AudioClipItem.cpp @@ -17,27 +17,24 @@ namespace toucan void AudioClipItem::_init( const std::shared_ptr& context, const ItemData& data, - const OTIO_NS::SerializableObject::Retainer& clip, - const OTIO_NS::SerializableObject::Retainer& timeline, + const OTIO_NS::Clip* clip, const ftk::Color4F& color, const std::shared_ptr& parent) { + auto timelineWrapper = data.file->getTimelineWrapper(); OTIO_NS::TimeRange timeRange = clip->transformed_time_range( clip->trimmed_range(), - timeline->tracks()); - if (timeline->global_start_time().has_value()) - { - timeRange = OTIO_NS::TimeRange( - timeline->global_start_time().value() + timeRange.start_time(), - timeRange.duration()); - } + timelineWrapper->getTimeline()->tracks()); + timeRange = OTIO_NS::TimeRange( + timelineWrapper->getTimeRange().start_time() + timeRange.start_time(), + timeRange.duration()); timeRange = OTIO_NS::TimeRange( timeRange.start_time().round(), timeRange.duration().round()); IItem::_init( context, data, - OTIO_NS::dynamic_retainer_cast(clip), + clip, timeRange, "toucan::AudioClipItem", parent); @@ -64,13 +61,10 @@ namespace toucan OTIO_NS::TimeRange markerRange( marker->marked_range().start_time() + trimmedRange.start_time(), marker->marked_range().duration()); - markerRange = clip->transformed_time_range(markerRange, timeline->tracks()); - if (timeline->global_start_time().has_value()) - { - markerRange = OTIO_NS::TimeRange( - timeline->global_start_time().value() + markerRange.start_time(), - markerRange.duration()); - } + markerRange = clip->transformed_time_range(markerRange, timelineWrapper->getTimeline()->tracks()); + markerRange = OTIO_NS::TimeRange( + timelineWrapper->getTimeRange().start_time() + markerRange.start_time(), + markerRange.duration()); auto markerItem = MarkerItem::create( context, data, @@ -90,13 +84,12 @@ namespace toucan std::shared_ptr AudioClipItem::create( const std::shared_ptr& context, const ItemData& data, - const OTIO_NS::SerializableObject::Retainer& clip, - const OTIO_NS::SerializableObject::Retainer& timeline, + const OTIO_NS::Clip* clip, const ftk::Color4F& color, const std::shared_ptr& parent) { auto out = std::make_shared(); - out->_init(context, data, clip, timeline, color, parent); + out->_init(context, data, clip, color, parent); return out; } @@ -159,8 +152,7 @@ namespace toucan "Open Media", [this, externalReference] { - auto file = _file.lock(); - const std::filesystem::path path = file->getTimelineWrapper()->getMediaPath(externalReference->target_url()); + const std::filesystem::path path = _file->getTimelineWrapper()->getMediaPath(externalReference->target_url()); auto app = _app.lock(); app->open(path); }); diff --git a/lib/toucanView/AudioClipItem.h b/lib/toucanView/AudioClipItem.h index 505aa2e..762b972 100644 --- a/lib/toucanView/AudioClipItem.h +++ b/lib/toucanView/AudioClipItem.h @@ -20,8 +20,7 @@ namespace toucan void _init( const std::shared_ptr&, const ItemData&, - const OTIO_NS::SerializableObject::Retainer&, - const OTIO_NS::SerializableObject::Retainer&, + const OTIO_NS::Clip*, const ftk::Color4F&, const std::shared_ptr& parent); @@ -32,8 +31,7 @@ namespace toucan static std::shared_ptr create( const std::shared_ptr&, const ItemData&, - const OTIO_NS::SerializableObject::Retainer&, - const OTIO_NS::SerializableObject::Retainer&, + const OTIO_NS::Clip*, const ftk::Color4F&, const std::shared_ptr& parent = nullptr); @@ -51,7 +49,8 @@ namespace toucan private: void _textUpdate(); - OTIO_NS::SerializableObject::Retainer _clip; + const OTIO_NS::Timeline* _timeline = nullptr; + const OTIO_NS::Clip* _clip = nullptr; std::string _text; ftk::Color4F _color; diff --git a/lib/toucanView/ExportTool.cpp b/lib/toucanView/ExportTool.cpp index bccd71f..921a397 100644 --- a/lib/toucanView/ExportTool.cpp +++ b/lib/toucanView/ExportTool.cpp @@ -439,7 +439,7 @@ namespace toucan { if (auto node = _graph->exec(_host, _time)) { - auto buf = node->exec(_time); + auto buf = node->exec(); if (_outputSize != _imageSize) { buf = OIIO::ImageBufAlgo::resize( diff --git a/lib/toucanView/File.cpp b/lib/toucanView/File.cpp index 20f3330..c8d53e9 100644 --- a/lib/toucanView/File.cpp +++ b/lib/toucanView/File.cpp @@ -5,7 +5,6 @@ #include "PlaybackModel.h" #include "SelectionModel.h" -#include "ThumbnailGenerator.h" #include "ViewModel.h" #include @@ -32,12 +31,6 @@ namespace toucan _selectionModel = std::make_shared(); - _thumbnailGenerator = std::make_shared( - context, - _path.parent_path(), - _timelineWrapper, - _host); - _currentImage = ftk::ObservableValue >::create(); _rootNode = ftk::ObservableValue >::create(); @@ -93,11 +86,6 @@ namespace toucan return _selectionModel; } - const std::shared_ptr& File::getThumbnailGenerator() const - { - return _thumbnailGenerator; - } - const IMATH_NAMESPACE::V2i& File::getImageSize() const { return _graph->getImageSize(); @@ -142,7 +130,8 @@ namespace toucan if (_currentNode->get()) { const OTIO_NS::TimeRange& timeRange = _playbackModel->getTimeRange(); - _imageBuf = _currentNode->get()->exec(_currentTime - timeRange.start_time()); + _currentNode->get()->setTime(_currentTime - timeRange.start_time()); + _imageBuf = _currentNode->get()->exec(); const auto& spec = _imageBuf.spec(); ftk::ImageType imageType = ftk::ImageType::None; diff --git a/lib/toucanView/File.h b/lib/toucanView/File.h index 46738c3..9dd210d 100644 --- a/lib/toucanView/File.h +++ b/lib/toucanView/File.h @@ -17,7 +17,6 @@ namespace toucan { class PlaybackModel; class SelectionModel; - class ThumbnailGenerator; class ViewModel; //! Timeline file. @@ -49,9 +48,6 @@ namespace toucan //! Get the selection model. const std::shared_ptr& getSelectionModel() const; - //! Get the thumbnail generator. - const std::shared_ptr& getThumbnailGenerator() const; - //! Get the image size. const IMATH_NAMESPACE::V2i& getImageSize() const; @@ -82,7 +78,6 @@ namespace toucan std::shared_ptr _playbackModel; std::shared_ptr _viewModel; std::shared_ptr _selectionModel; - std::shared_ptr _thumbnailGenerator; std::shared_ptr > > _currentImage; OTIO_NS::RationalTime _currentTime; diff --git a/lib/toucanView/GapItem.cpp b/lib/toucanView/GapItem.cpp index d42de46..765d01a 100644 --- a/lib/toucanView/GapItem.cpp +++ b/lib/toucanView/GapItem.cpp @@ -3,6 +3,8 @@ #include "GapItem.h" +#include "File.h" + #include namespace toucan @@ -10,23 +12,23 @@ namespace toucan void GapItem::_init( const std::shared_ptr& context, const ItemData& data, - const OTIO_NS::SerializableObject::Retainer& gap, - const OTIO_NS::SerializableObject::Retainer& timeline, + const OTIO_NS::Gap* gap, const std::shared_ptr& parent) { + auto timelineWrapper = data.file->getTimelineWrapper(); OTIO_NS::TimeRange timeRange = gap->transformed_time_range( gap->trimmed_range(), - timeline->tracks()); - if (timeline->global_start_time().has_value()) - { - timeRange = OTIO_NS::TimeRange( - timeline->global_start_time().value() + timeRange.start_time(), - timeRange.duration()); - } + timelineWrapper->getTimeline()->tracks()); + timeRange = OTIO_NS::TimeRange( + timelineWrapper->getTimeRange().start_time() + timeRange.start_time(), + timeRange.duration()); + timeRange = OTIO_NS::TimeRange( + timeRange.start_time().round(), + timeRange.duration().round()); IItem::_init( context, data, - OTIO_NS::dynamic_retainer_cast(gap), + gap, timeRange, "toucan::ClipItem", parent); @@ -51,13 +53,10 @@ namespace toucan { OTIO_NS::TimeRange markerTimeRange = gap->transformed_time_range( marker->marked_range(), - timeline->tracks()); - if (timeline->global_start_time().has_value()) - { - markerTimeRange = OTIO_NS::TimeRange( - timeline->global_start_time().value() + markerTimeRange.start_time(), - markerTimeRange.duration()); - } + timelineWrapper->getTimeline()->tracks()); + markerTimeRange = OTIO_NS::TimeRange( + timelineWrapper->getTimeRange().start_time() + markerTimeRange.start_time(), + markerTimeRange.duration()); auto markerItem = MarkerItem::create( context, data, @@ -77,12 +76,11 @@ namespace toucan std::shared_ptr GapItem::create( const std::shared_ptr& context, const ItemData& data, - const OTIO_NS::SerializableObject::Retainer& gap, - const OTIO_NS::SerializableObject::Retainer& timeline, + const OTIO_NS::Gap* gap, const std::shared_ptr& parent) { auto out = std::make_shared(); - out->_init(context, data, gap, timeline, parent); + out->_init(context, data, gap, parent); return out; } diff --git a/lib/toucanView/GapItem.h b/lib/toucanView/GapItem.h index 8c38eab..65c333e 100644 --- a/lib/toucanView/GapItem.h +++ b/lib/toucanView/GapItem.h @@ -18,8 +18,7 @@ namespace toucan void _init( const std::shared_ptr&, const ItemData&, - const OTIO_NS::SerializableObject::Retainer&, - const OTIO_NS::SerializableObject::Retainer&, + const OTIO_NS::Gap*, const std::shared_ptr& parent); public: @@ -29,8 +28,7 @@ namespace toucan static std::shared_ptr create( const std::shared_ptr&, const ItemData&, - const OTIO_NS::SerializableObject::Retainer&, - const OTIO_NS::SerializableObject::Retainer&, + const OTIO_NS::Gap*, const std::shared_ptr& parent = nullptr); void setScale(double) override; @@ -46,7 +44,8 @@ namespace toucan private: void _textUpdate(); - OTIO_NS::SerializableObject::Retainer _gap; + const OTIO_NS::Timeline* _timeline = nullptr; + const OTIO_NS::Gap* _gap = nullptr; std::string _text; ftk::Color4F _color; diff --git a/lib/toucanView/IItem.cpp b/lib/toucanView/IItem.cpp index 017e087..27855d3 100644 --- a/lib/toucanView/IItem.cpp +++ b/lib/toucanView/IItem.cpp @@ -12,7 +12,7 @@ namespace toucan void IItem::_init( const std::shared_ptr& context, const ItemData& data, - const OTIO_NS::SerializableObject::Retainer& object, + const OTIO_NS::SerializableObjectWithMetadata* object, const OTIO_NS::TimeRange& timeRange, const std::string& objectName, const std::shared_ptr& parent) @@ -35,7 +35,7 @@ namespace toucan IItem::~IItem() {} - const OTIO_NS::SerializableObject::Retainer& IItem::getObject() const + const OTIO_NS::SerializableObjectWithMetadata* IItem::getObject() const { return _object; } @@ -102,10 +102,7 @@ namespace toucan "Go To Start", [this] { - if (auto file = _file.lock()) - { - file->getPlaybackModel()->setCurrentTime(_timeRange.start_time()); - } + _file->getPlaybackModel()->setCurrentTime(_timeRange.start_time()); }); menu->addAction(action); @@ -117,10 +114,7 @@ namespace toucan "Set In/Out Points", [this] { - if (auto file = _file.lock()) - { - file->getPlaybackModel()->setInOutRange(_timeRange); - } + _file->getPlaybackModel()->setInOutRange(_timeRange); }); menu->addAction(action); } @@ -129,19 +123,12 @@ namespace toucan "Reset In/Out Points", [this] { - if (auto file = _file.lock()) - { - file->getPlaybackModel()->resetInOutPoints(); - } + _file->getPlaybackModel()->resetInOutPoints(); }); menu->addAction(action); - bool enabled = true; - if (auto file = _file.lock()) - { - enabled = - file->getPlaybackModel()->getInOutRange() != - file->getPlaybackModel()->getTimeRange(); - } + const bool enabled = + _file->getPlaybackModel()->getInOutRange() != + _file->getPlaybackModel()->getTimeRange(); menu->setEnabled(action, enabled); } } diff --git a/lib/toucanView/IItem.h b/lib/toucanView/IItem.h index b4adb03..884cdfc 100644 --- a/lib/toucanView/IItem.h +++ b/lib/toucanView/IItem.h @@ -34,7 +34,7 @@ namespace toucan void _init( const std::shared_ptr&, const ItemData&, - const OTIO_NS::SerializableObject::Retainer&, + const OTIO_NS::SerializableObjectWithMetadata*, const OTIO_NS::TimeRange&, const std::string& objectName, const std::shared_ptr& parent); @@ -43,7 +43,7 @@ namespace toucan virtual ~IItem() = 0; //! Get the OTIO object. - const OTIO_NS::SerializableObject::Retainer& getObject() const; + const OTIO_NS::SerializableObjectWithMetadata* getObject() const; //! Get the item selection rectangle. const ftk::Box2I& getSelectionRect() const; @@ -63,8 +63,8 @@ namespace toucan virtual void _buildMenu(const std::shared_ptr&); std::weak_ptr _app; - std::weak_ptr _file; - OTIO_NS::SerializableObject::Retainer _object; + std::shared_ptr _file; + const OTIO_NS::SerializableObjectWithMetadata* _object = nullptr; TimeUnits _timeUnits = TimeUnits::First; ftk::Box2I _selectionRect; bool _selected = false; diff --git a/lib/toucanView/MarkerItem.cpp b/lib/toucanView/MarkerItem.cpp index 9c242e9..2653e81 100644 --- a/lib/toucanView/MarkerItem.cpp +++ b/lib/toucanView/MarkerItem.cpp @@ -60,14 +60,14 @@ namespace toucan void MarkerItem::_init( const std::shared_ptr& context, const ItemData& data, - const OTIO_NS::SerializableObject::Retainer& marker, + const OTIO_NS::Marker* marker, const OTIO_NS::TimeRange& timeRange, const std::shared_ptr& parent) { IItem::_init( context, data, - OTIO_NS::dynamic_retainer_cast(marker), + marker, timeRange, "toucan::MarkerItem", parent); @@ -90,7 +90,7 @@ namespace toucan std::shared_ptr MarkerItem::create( const std::shared_ptr& context, const ItemData& data, - const OTIO_NS::SerializableObject::Retainer& marker, + const OTIO_NS::Marker* marker, const OTIO_NS::TimeRange& timeRange, const std::shared_ptr& parent) { diff --git a/lib/toucanView/MarkerItem.h b/lib/toucanView/MarkerItem.h index 4ba9984..fb3fe05 100644 --- a/lib/toucanView/MarkerItem.h +++ b/lib/toucanView/MarkerItem.h @@ -22,7 +22,7 @@ namespace toucan void _init( const std::shared_ptr&, const ItemData&, - const OTIO_NS::SerializableObject::Retainer&, + const OTIO_NS::Marker*, const OTIO_NS::TimeRange&, const std::shared_ptr& parent); @@ -33,7 +33,7 @@ namespace toucan static std::shared_ptr create( const std::shared_ptr&, const ItemData&, - const OTIO_NS::SerializableObject::Retainer&, + const OTIO_NS::Marker*, const OTIO_NS::TimeRange&, const std::shared_ptr& parent = nullptr); @@ -47,7 +47,8 @@ namespace toucan private: void _textUpdate(); - OTIO_NS::SerializableObject::Retainer _marker; + const OTIO_NS::Timeline* _timeline = nullptr; + const OTIO_NS::Marker* _marker = nullptr; std::string _text; ftk::Color4F _color; diff --git a/lib/toucanView/StackItem.cpp b/lib/toucanView/StackItem.cpp index ea98d72..9431d29 100644 --- a/lib/toucanView/StackItem.cpp +++ b/lib/toucanView/StackItem.cpp @@ -3,6 +3,7 @@ #include "StackItem.h" +#include "File.h" #include "TrackItem.h" #include @@ -13,24 +14,21 @@ namespace toucan void StackItem::_init( const std::shared_ptr& context, const ItemData& data, - const OTIO_NS::SerializableObject::Retainer& stack, - const OTIO_NS::SerializableObject::Retainer& timeline , + const OTIO_NS::Stack* stack, const std::shared_ptr& parent) { + auto timelineWrapper = data.file->getTimelineWrapper(); OTIO_NS::TimeRange timeRange = stack->trimmed_range(); - if (timeline->global_start_time().has_value()) - { - timeRange = OTIO_NS::TimeRange( - timeline->global_start_time().value() + timeRange.start_time(), - timeRange.duration()); - } + timeRange = OTIO_NS::TimeRange( + timelineWrapper->getTimeRange().start_time() + timeRange.start_time(), + timeRange.duration()); timeRange = OTIO_NS::TimeRange( timeRange.start_time().round(), timeRange.duration().round()); IItem::_init( context, data, - OTIO_NS::dynamic_retainer_cast(stack), + stack, timeRange, "toucan::StackItem", parent); @@ -53,18 +51,15 @@ namespace toucan _markerLayout = TimeLayout::create(context, timeRange, _layout); for (const auto& marker : markers) { - OTIO_NS::TimeRange markerTimeRange = marker->marked_range(); - if (timeline->global_start_time().has_value()) - { - markerTimeRange = OTIO_NS::TimeRange( - timeline->global_start_time().value() + markerTimeRange.start_time(), - markerTimeRange.duration()); - } + OTIO_NS::TimeRange markerRange = marker->marked_range(); + markerRange = OTIO_NS::TimeRange( + timelineWrapper->getTimeRange().start_time() + markerRange.start_time(), + markerRange.duration()); auto markerItem = MarkerItem::create( context, data, marker, - markerTimeRange, + markerRange, _markerLayout); _markerItems.push_back(markerItem); } @@ -79,7 +74,6 @@ namespace toucan context, data, track, - timeline, _timeLayout); } } @@ -93,12 +87,11 @@ namespace toucan std::shared_ptr StackItem::create( const std::shared_ptr& context, const ItemData& data, - const OTIO_NS::SerializableObject::Retainer& stack, - const OTIO_NS::SerializableObject::Retainer& timeline, + const OTIO_NS::Stack* stack, const std::shared_ptr& parent) { auto out = std::make_shared(); - out->_init(context, data, stack, timeline, parent); + out->_init(context, data, stack, parent); return out; } diff --git a/lib/toucanView/StackItem.h b/lib/toucanView/StackItem.h index 86246e9..7a27df2 100644 --- a/lib/toucanView/StackItem.h +++ b/lib/toucanView/StackItem.h @@ -18,8 +18,7 @@ namespace toucan void _init( const std::shared_ptr&, const ItemData&, - const OTIO_NS::SerializableObject::Retainer&, - const OTIO_NS::SerializableObject::Retainer&, + const OTIO_NS::Stack*, const std::shared_ptr& parent); public: @@ -29,8 +28,7 @@ namespace toucan static std::shared_ptr create( const std::shared_ptr&, const ItemData&, - const OTIO_NS::SerializableObject::Retainer&, - const OTIO_NS::SerializableObject::Retainer&, + const OTIO_NS::Stack*, const std::shared_ptr& parent = nullptr); void setScale(double) override; @@ -46,7 +44,8 @@ namespace toucan private: void _textUpdate(); - OTIO_NS::SerializableObject::Retainer _stack; + const OTIO_NS::Timeline* _timeline = nullptr; + const OTIO_NS::Stack* _stack = nullptr; std::string _text; ftk::Color4F _color; diff --git a/lib/toucanView/ThumbnailGenerator.cpp b/lib/toucanView/ThumbnailGenerator.cpp index 89eee85..cfd9003 100644 --- a/lib/toucanView/ThumbnailGenerator.cpp +++ b/lib/toucanView/ThumbnailGenerator.cpp @@ -7,6 +7,7 @@ #include +#include #include #include @@ -30,36 +31,15 @@ namespace toucan ThumbnailGenerator::ThumbnailGenerator( const std::shared_ptr& context, - const std::filesystem::path& path, - const std::shared_ptr& timelineWrapper, - const std::shared_ptr& host) : - _path(path), - _timelineWrapper(timelineWrapper), - _host(host) + const std::shared_ptr& timelineWrapper) : + _timelineWrapper(timelineWrapper) { _logSystem = context->getSystem(); - _graph = std::make_shared(context, _path, _timelineWrapper); - _thread.running = true; _thread.thread = std::thread( [this] { - try - { - const IMATH_NAMESPACE::V2i& imageSize = _graph->getImageSize(); - _aspect = imageSize.y > 0 ? - (imageSize.x / static_cast(imageSize.y)) : - 0.F; - } - catch (const std::exception& e) - { - _logSystem->print( - "toucan::ThumbnailGenerator", - e.what(), - ftk::LogType::Error); - } - while (_thread.running) { _run(); @@ -81,41 +61,21 @@ namespace toucan } } - float ThumbnailGenerator::getAspect() const - { - return _aspect; - } - - ThumbnailRequest ThumbnailGenerator::getThumbnail( - const OTIO_NS::RationalTime& time, - int height) - { - return getThumbnail(nullptr, time, height); - } - - ThumbnailRequest ThumbnailGenerator::getThumbnail( + std::future ThumbnailGenerator::getAspect( const OTIO_NS::MediaReference* ref, - const OTIO_NS::RationalTime& time, - int height) + const OTIO_NS::RationalTime& time) { - _requestId++; - auto request = std::make_shared(); - request->id = _requestId; + auto request = std::make_shared(); request->ref = ref; request->time = time; - request->height = height; - ThumbnailRequest out; - out.id = _requestId; - out.height = height; - out.time = time; - out.future = request->promise.get_future(); + auto out = request->promise.get_future(); bool valid = false; { std::unique_lock lock(_mutex.mutex); if (!_mutex.stopped) { valid = true; - _mutex.requests.push_back(request); + _mutex.aspectRequests.push_back(request); } } if (valid) @@ -124,26 +84,34 @@ namespace toucan } else { - request->promise.set_value(nullptr); + request->promise.set_value(0.F); } return out; } - std::future ThumbnailGenerator::getAspect( + ThumbnailRequest ThumbnailGenerator::getThumbnail( const OTIO_NS::MediaReference* ref, - const OTIO_NS::RationalTime& time) + const OTIO_NS::RationalTime& time, + int height) { - auto request = std::make_shared(); + _requestId++; + auto request = std::make_shared(); + request->id = _requestId; request->ref = ref; request->time = time; - auto out = request->promise.get_future(); + request->height = height; + ThumbnailRequest out; + out.id = _requestId; + out.height = height; + out.time = time; + out.future = request->promise.get_future(); bool valid = false; { std::unique_lock lock(_mutex.mutex); if (!_mutex.stopped) { valid = true; - _mutex.aspectRequests.push_back(request); + _mutex.requests.push_back(request); } } if (valid) @@ -152,7 +120,7 @@ namespace toucan } else { - request->promise.set_value(0.F); + request->promise.set_value(nullptr); } return out; } @@ -208,43 +176,37 @@ namespace toucan } if (aspectRequest) { + std::shared_ptr read; + try + { + read = _timelineWrapper->createReadNode(aspectRequest->ref); + } + catch (const std::exception& e) + { + _logSystem->print( + "toucan::ThumbnailGenerator", + e.what(), + ftk::LogType::Error); + } float aspect = 0.F; - auto node = _graph->exec(_host, aspectRequest->time); - if (node = _findNode(node, aspectRequest->ref)) + if (read) { - try - { - const OIIO::ImageBuf buf = node->exec(aspectRequest->time - _timelineWrapper->getTimeRange().start_time()); - const OIIO::ImageSpec& spec = buf.spec(); - if (spec.height > 0) - { - aspect = spec.width / static_cast(spec.height); - } - } - catch (const std::exception& e) + read->setTime(aspectRequest->time); + const OIIO::ImageBuf buf = read->exec(); + const OIIO::ImageSpec& spec = buf.spec(); + if (spec.height > 0) { - _logSystem->print( - "toucan::ThumbnailGenerator", - e.what(), - ftk::LogType::Error); + aspect = spec.width / static_cast(spec.height); } } aspectRequest->promise.set_value(aspect); } if (request) { - OIIO::ImageBuf buf; + std::shared_ptr read; try { - auto node = _graph->exec(_host, request->time); - if (request->ref) - { - node = _findNode(node, request->ref); - } - if (node) - { - buf = node->exec(request->time - _timelineWrapper->getTimeRange().start_time()); - } + read = _timelineWrapper->createReadNode(request->ref); } catch (const std::exception& e) { @@ -253,6 +215,12 @@ namespace toucan e.what(), ftk::LogType::Error); } + OIIO::ImageBuf buf; + if (read) + { + read->setTime(request->time); + buf = read->exec(); + } std::shared_ptr thumbnail; const auto& spec = buf.spec(); @@ -337,12 +305,4 @@ namespace toucan request->promise.set_value(nullptr); } } - - std::shared_ptr ThumbnailGenerator::_findNode( - const std::shared_ptr& node, - const OTIO_NS::MediaReference* ref) - { - std::shared_ptr out; - return out; - } } diff --git a/lib/toucanView/ThumbnailGenerator.h b/lib/toucanView/ThumbnailGenerator.h index 35ed584..e568874 100644 --- a/lib/toucanView/ThumbnailGenerator.h +++ b/lib/toucanView/ThumbnailGenerator.h @@ -3,8 +3,6 @@ #pragma once -#include -#include #include #include @@ -40,20 +38,10 @@ namespace toucan public: ThumbnailGenerator( const std::shared_ptr&, - const std::filesystem::path&, - const std::shared_ptr&, - const std::shared_ptr&); + const std::shared_ptr&); ~ThumbnailGenerator(); - //! Get the timeline aspect ratio. - float getAspect() const; - - //! Get a timeline thumbnail. - ThumbnailRequest getThumbnail( - const OTIO_NS::RationalTime&, - int height); - //! Get a media aspect ratio. std::future getAspect( const OTIO_NS::MediaReference*, @@ -72,16 +60,8 @@ namespace toucan void _run(); void _cancel(); - std::shared_ptr _findNode( - const std::shared_ptr&, - const OTIO_NS::MediaReference*); - std::shared_ptr _logSystem; - std::filesystem::path _path; std::shared_ptr _timelineWrapper; - std::shared_ptr _host; - std::shared_ptr _graph; - float _aspect = 1.F; struct AspectRequest { diff --git a/lib/toucanView/ThumbnailsWidget.cpp b/lib/toucanView/ThumbnailsWidget.cpp index 5d88051..fc9919e 100644 --- a/lib/toucanView/ThumbnailsWidget.cpp +++ b/lib/toucanView/ThumbnailsWidget.cpp @@ -12,17 +12,26 @@ namespace toucan { void ThumbnailsWidget::_init( const std::shared_ptr& context, - const OTIO_NS::SerializableObject::Retainer& ref, + const std::shared_ptr& timelineWrapper, + const OTIO_NS::Clip* clip, + const OTIO_NS::MediaReference* ref, const std::shared_ptr& thumbnailGenerator, const std::shared_ptr > >& thumbnailCache, const OTIO_NS::TimeRange& timeRange, const std::shared_ptr& parent) { ITimeWidget::_init(context, timeRange, "toucan::ThumbnailsWidget", parent); + + _timelineWrapper = timelineWrapper; + _clip = clip; _ref = ref; _thumbnailGenerator = thumbnailGenerator; _thumbnailCache = thumbnailCache; - _thumbnailAspectRequest = _thumbnailGenerator->getAspect(ref, timeRange.start_time()); + + const OTIO_NS::RationalTime t = _timelineWrapper->getTimeline()->tracks()->transformed_time( + timeRange.start_time(), + _clip) - _timelineWrapper->getTimeRange().start_time(); + _thumbnailAspectRequest = _thumbnailGenerator->getAspect(ref, t); } ThumbnailsWidget::~ThumbnailsWidget() @@ -30,17 +39,34 @@ namespace toucan std::shared_ptr ThumbnailsWidget::create( const std::shared_ptr& context, - const OTIO_NS::SerializableObject::Retainer& ref, + const std::shared_ptr& timelineWrapper, + const OTIO_NS::Clip* clip, + const OTIO_NS::MediaReference* ref, const std::shared_ptr& thumbnailGenerator, const std::shared_ptr > >& thumbnailCache, const OTIO_NS::TimeRange& timeRange, const std::shared_ptr& parent) { auto out = std::make_shared(); - out->_init(context, ref, thumbnailGenerator, thumbnailCache, timeRange, parent); + out->_init(context, timelineWrapper, clip, ref, thumbnailGenerator, thumbnailCache, timeRange, parent); return out; } + void ThumbnailsWidget::setScale(double value) + { + const bool changed = value != _scale; + ITimeWidget::setScale(value); + if (changed) + { + std::vector cancel; + for (const auto& request : _thumbnailRequests) + { + cancel.push_back(request.id); + } + _thumbnailGenerator->cancelThumbnails(cancel); + } + } + void ThumbnailsWidget::tickEvent( bool parentsVisible, bool parentsEnabled, @@ -105,7 +131,9 @@ namespace toucan const ftk::Box2I g2(x, y, thumbnailWidth, _size.thumbnailHeight); if (ftk::intersects(g2, drawRect)) { - const OTIO_NS::RationalTime t = posToTime(x); + const OTIO_NS::RationalTime t = _timelineWrapper->getTimeline()->tracks()->transformed_time( + posToTime(x), + _clip) - _timelineWrapper->getTimeRange().start_time(); std::shared_ptr image; const std::string cacheKey = getThumbnailCacheKey(_ref, t, _size.thumbnailHeight); if (_thumbnailCache->get(cacheKey, image)) @@ -141,7 +169,9 @@ namespace toucan auto i = _thumbnailRequests.begin(); while (i != _thumbnailRequests.end()) { - const int x = timeToPos(i->time); + const int x = timeToPos( + _timelineWrapper->getTimeRange().start_time() + + _clip->transformed_time(i->time, _timelineWrapper->getTimeline()->tracks())); const ftk::Box2I g2(x, y, thumbnailWidth, _size.thumbnailHeight); if (!ftk::intersects(g2, drawRect)) { diff --git a/lib/toucanView/ThumbnailsWidget.h b/lib/toucanView/ThumbnailsWidget.h index 532dc95..931ea05 100644 --- a/lib/toucanView/ThumbnailsWidget.h +++ b/lib/toucanView/ThumbnailsWidget.h @@ -12,13 +12,17 @@ namespace toucan { + class TimelineWrapper; + //! Timeline thumbnails widget. class ThumbnailsWidget : public ITimeWidget { protected: void _init( const std::shared_ptr&, - const OTIO_NS::SerializableObject::Retainer&, + const std::shared_ptr&, + const OTIO_NS::Clip*, + const OTIO_NS::MediaReference*, const std::shared_ptr&, const std::shared_ptr > >&, const OTIO_NS::TimeRange&, @@ -30,11 +34,15 @@ namespace toucan //! Create a new widget. static std::shared_ptr create( const std::shared_ptr&, - const OTIO_NS::SerializableObject::Retainer&, + const std::shared_ptr&, + const OTIO_NS::Clip*, + const OTIO_NS::MediaReference*, const std::shared_ptr&, const std::shared_ptr > >&, const OTIO_NS::TimeRange&, const std::shared_ptr& parent = nullptr); + + void setScale(double) override; void tickEvent( bool parentsVisible, @@ -44,7 +52,9 @@ namespace toucan void drawEvent(const ftk::Box2I&, const ftk::DrawEvent&) override; private: - OTIO_NS::SerializableObject::Retainer _ref; + std::shared_ptr _timelineWrapper; + const OTIO_NS::Clip* _clip = nullptr; + const OTIO_NS::MediaReference* _ref = nullptr; float _thumbnailAspect = 0.F; std::shared_ptr _thumbnailGenerator; std::shared_ptr > > _thumbnailCache; diff --git a/lib/toucanView/TimelineItem.cpp b/lib/toucanView/TimelineItem.cpp index 06edb57..f5b935e 100644 --- a/lib/toucanView/TimelineItem.cpp +++ b/lib/toucanView/TimelineItem.cpp @@ -20,7 +20,7 @@ namespace toucan IItem::_init( context, data, - nullptr, + data.file->getTimelineWrapper()->getTimeline(), data.file->getTimelineWrapper()->getTimeRange(), "toucan::TimelineItem", parent); @@ -34,30 +34,18 @@ namespace toucan _timeline = data.file->getTimeline(); _timeRange = data.file->getTimelineWrapper()->getTimeRange(); _selectionModel = data.file->getSelectionModel(); - _thumbnailGenerator = data.thumbnailGenerator; - _thumbnailCache = data.thumbnailCache; - StackItem::create( + _stackItem = StackItem::create( context, data, _timeline->tracks(), - _timeline, shared_from_this()); _selectionObserver = ftk::ListObserver::create( _selectionModel->observeSelection(), [this](const std::vector& selection) { - _select(shared_from_this(), selection); - }); - - _thumbnailsObserver = ftk::ValueObserver::create( - data.app->getWindowModel()->observeThumbnails(), - [this](bool value) - { - _thumbnails = value; - _setSizeUpdate(); - _setDrawUpdate(); + _select(_stackItem, selection); }); } @@ -108,14 +96,9 @@ namespace toucan for (const auto& child : getChildren()) { const ftk::Size2I& sizeHint = child->getSizeHint(); - int h = timeHeight; - if (_thumbnails) - { - h += _size.thumbnailHeight; - } child->setGeometry(ftk::Box2I( g.min.x, - g.min.y + h, + g.min.y + timeHeight, sizeHint.w, sizeHint.h)); } @@ -125,30 +108,6 @@ namespace toucan } } - void TimelineItem::tickEvent( - bool parentsVisible, - bool parentsEnabled, - const ftk::TickEvent& event) - { - IItem::tickEvent(parentsVisible, parentsEnabled, event); - auto i = _thumbnailRequests.begin(); - while (i != _thumbnailRequests.end()) - { - if (i->future.valid() && - i->future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) - { - const auto image = i->future.get(); - _thumbnailCache->add(getThumbnailCacheKey(nullptr, i->time, _size.thumbnailHeight), image); - _setDrawUpdate(); - i = _thumbnailRequests.erase(i); - } - else - { - ++i; - } - } - } - void TimelineItem::sizeHintEvent(const ftk::SizeHintEvent& event) { IItem::sizeHintEvent(event); @@ -163,14 +122,6 @@ namespace toucan _size.thumbnailHeight = 2 * event.style->getSizeRole(ftk::SizeRole::SwatchLarge, event.displayScale); _size.fontInfo = event.style->getFontRole(ftk::FontRole::Mono, event.displayScale); _size.fontMetrics = event.fontSystem->getMetrics(_size.fontInfo); - std::vector ids; - for (const auto& request : _thumbnailRequests) - { - ids.push_back(request.id); - } - _thumbnailRequests.clear(); - _thumbnailGenerator->cancelThumbnails(ids); - _thumbnailCache->clear(); } int childSizeHint = 0; for (const auto& child : getChildren()) @@ -180,77 +131,10 @@ namespace toucan ftk::Size2I sizeHint( _timeRange.duration().rescaled_to(1.0).value() * _scale, _size.fontMetrics.lineHeight + _size.margin * 2); - if (_thumbnails) - { - sizeHint.h += _size.thumbnailHeight; - } sizeHint.h += childSizeHint; _setSizeHint(sizeHint); } - void TimelineItem::drawEvent(const ftk::Box2I& drawRect, const ftk::DrawEvent& event) - { - IItem::drawEvent(drawRect, event); - - const ftk::Box2I& g = getGeometry(); - const int thumbnailWidth = _size.thumbnailHeight * _thumbnailGenerator->getAspect(); - const int y = g.min.y + _size.fontMetrics.lineHeight + _size.margin * 2; - if (_thumbnails) - { - for (int x = g.min.x; x < g.max.x && thumbnailWidth > 0; x += thumbnailWidth) - { - const ftk::Box2I g2(x, y, thumbnailWidth, _size.thumbnailHeight); - if (ftk::intersects(g2, drawRect)) - { - const OTIO_NS::RationalTime t = posToTime(x); - std::shared_ptr image; - if (_thumbnailCache->get(getThumbnailCacheKey(nullptr, t, _size.thumbnailHeight), image)) - { - if (image) - { - event.render->drawImage( - image, - ftk::Box2I(x, y, image->getWidth(), image->getHeight())); - } - } - else - { - const auto j = std::find_if( - _thumbnailRequests.begin(), - _thumbnailRequests.end(), - [this, t](const ThumbnailRequest& request) - { - return t == request.time && _size.thumbnailHeight == request.height; - }); - if (j == _thumbnailRequests.end()) - { - _thumbnailRequests.push_back( - _thumbnailGenerator->getThumbnail(t, _size.thumbnailHeight)); - } - } - } - } - } - - std::vector cancel; - auto i = _thumbnailRequests.begin(); - while (i != _thumbnailRequests.end()) - { - const int x = timeToPos(i->time); - const ftk::Box2I g2(x, y, thumbnailWidth, _size.thumbnailHeight); - if (!ftk::intersects(g2, drawRect)) - { - cancel.push_back(i->id); - i = _thumbnailRequests.erase(i); - } - else - { - ++i; - } - } - _thumbnailGenerator->cancelThumbnails(cancel); - } - void TimelineItem::drawOverlayEvent(const ftk::Box2I& drawRect, const ftk::DrawEvent& event) { IItem::drawOverlayEvent(drawRect, event); @@ -328,7 +212,7 @@ namespace toucan static_cast(ftk::commandKeyModifier) == event.modifiers)) { std::shared_ptr selection; - _select(shared_from_this(), event.pos, selection); + _select(_stackItem, event.pos, selection); SelectionItem item; if (selection) { @@ -604,7 +488,7 @@ namespace toucan selection.end(), [object](const SelectionItem& item) { - return object.value == item.object.value; + return object == item.object; }); iitem->setSelected(i != selection.end()); } diff --git a/lib/toucanView/TimelineItem.h b/lib/toucanView/TimelineItem.h index ebca580..aae655f 100644 --- a/lib/toucanView/TimelineItem.h +++ b/lib/toucanView/TimelineItem.h @@ -10,7 +10,7 @@ namespace toucan { - class SelectionModel; + class StackItem; //! Timeline item. class TimelineItem : public IItem @@ -43,12 +43,7 @@ namespace toucan void setInOutRange(const OTIO_NS::TimeRange&); void setGeometry(const ftk::Box2I&) override; - void tickEvent( - bool parentsVisible, - bool parentsEnabled, - const ftk::TickEvent&) override; void sizeHintEvent(const ftk::SizeHintEvent&) override; - void drawEvent(const ftk::Box2I&, const ftk::DrawEvent&) override; void drawOverlayEvent(const ftk::Box2I&, const ftk::DrawEvent&) override; void mouseMoveEvent(ftk::MouseMoveEvent&) override; void mousePressEvent(ftk::MouseClickEvent&) override; @@ -79,16 +74,13 @@ namespace toucan const std::shared_ptr&, const std::vector&); - OTIO_NS::SerializableObject::Retainer _timeline; + const OTIO_NS::Timeline* _timeline = nullptr; OTIO_NS::TimeRange _timeRange; OTIO_NS::RationalTime _currentTime = OTIO_NS::RationalTime(-1.0, -1.0); std::function _currentTimeCallback; OTIO_NS::TimeRange _inOutRange; std::shared_ptr _selectionModel; - bool _thumbnails = true; - std::shared_ptr _thumbnailGenerator; - std::list _thumbnailRequests; - std::shared_ptr > > _thumbnailCache; + std::shared_ptr _stackItem; struct SizeData { @@ -117,6 +109,5 @@ namespace toucan MouseData _mouse; std::shared_ptr > _selectionObserver; - std::shared_ptr > _thumbnailsObserver; }; } diff --git a/lib/toucanView/TimelineWidget.cpp b/lib/toucanView/TimelineWidget.cpp index 560a825..4d582d5 100644 --- a/lib/toucanView/TimelineWidget.cpp +++ b/lib/toucanView/TimelineWidget.cpp @@ -46,6 +46,7 @@ namespace toucan viewState.scale = _scale; viewState.frameView = _frameView->get(); _file->getPlaybackModel()->setViewState(viewState); + _thumbnailGenerator.reset(); } _file = file; if (file) @@ -61,10 +62,15 @@ namespace toucan _sizeInit = true; } + auto context = getContext(); + _thumbnailGenerator = std::make_shared( + context, + file->getTimelineWrapper()); + ItemData data; data.app = appWeak.lock(); data.file = file; - data.thumbnailGenerator = file->getThumbnailGenerator(); + data.thumbnailGenerator = _thumbnailGenerator; data.thumbnailCache = std::make_shared > >(); data.thumbnailCache->setMax(1000); _timelineItem = TimelineItem::create(getContext(), data); diff --git a/lib/toucanView/TimelineWidget.h b/lib/toucanView/TimelineWidget.h index 5b29a3f..9567716 100644 --- a/lib/toucanView/TimelineWidget.h +++ b/lib/toucanView/TimelineWidget.h @@ -16,6 +16,7 @@ namespace toucan class App; class File; class TimelineItem; + class ThumbnailGenerator; //! Timeline widget. class TimelineWidget : public ftk::IWidget @@ -82,6 +83,7 @@ namespace toucan std::shared_ptr > _frameView; bool _sizeInit = true; std::optional _viewState; + std::shared_ptr _thumbnailGenerator; std::shared_ptr _scrollWidget; std::shared_ptr _timelineItem; diff --git a/lib/toucanView/TrackItem.cpp b/lib/toucanView/TrackItem.cpp index 74f0615..068155a 100644 --- a/lib/toucanView/TrackItem.cpp +++ b/lib/toucanView/TrackItem.cpp @@ -4,6 +4,7 @@ #include "TrackItem.h" #include "AudioClipItem.h" +#include "File.h" #include "GapItem.h" #include "VideoClipItem.h" @@ -15,26 +16,23 @@ namespace toucan void TrackItem::_init( const std::shared_ptr& context, const ItemData& data, - const OTIO_NS::SerializableObject::Retainer& track, - const OTIO_NS::SerializableObject::Retainer& timeline, + const OTIO_NS::Track* track, const std::shared_ptr& parent) { + auto timelineWrapper = data.file->getTimelineWrapper(); OTIO_NS::TimeRange timeRange = track->transformed_time_range( track->trimmed_range(), - timeline->tracks()); - if (timeline->global_start_time().has_value()) - { - timeRange = OTIO_NS::TimeRange( - timeline->global_start_time().value() + timeRange.start_time(), - timeRange.duration()); - } + timelineWrapper->getTimeline()->tracks()); + timeRange = OTIO_NS::TimeRange( + timelineWrapper->getTimeRange().start_time() + timeRange.start_time(), + timeRange.duration()); timeRange = OTIO_NS::TimeRange( timeRange.start_time().round(), timeRange.duration().round()); IItem::_init( context, data, - OTIO_NS::dynamic_retainer_cast(track), + track, timeRange, "toucan::TrackItem", parent); @@ -59,20 +57,17 @@ namespace toucan _markerLayout = TimeLayout::create(context, timeRange, _layout); for (const auto& marker : markers) { - OTIO_NS::TimeRange markerTimeRange = track->transformed_time_range( + OTIO_NS::TimeRange markerRange = track->transformed_time_range( marker->marked_range(), - timeline->tracks()); - if (timeline->global_start_time().has_value()) - { - markerTimeRange = OTIO_NS::TimeRange( - timeline->global_start_time().value() + markerTimeRange.start_time(), - markerTimeRange.duration()); - } + timelineWrapper->getTimeline()->tracks()); + markerRange = OTIO_NS::TimeRange( + timelineWrapper->getTimeRange().start_time() + markerRange.start_time(), + markerRange.duration()); auto markerItem = MarkerItem::create( context, data, marker, - markerTimeRange, + markerRange, _markerLayout); _markerItems.push_back(markerItem); } @@ -89,7 +84,6 @@ namespace toucan context, data, clip, - timeline, ftk::Color4F(.4F, .4F, .6F), _timeLayout); } @@ -99,7 +93,6 @@ namespace toucan context, data, clip, - timeline, ftk::Color4F(.4F, .6F, .4F), _timeLayout); } @@ -110,7 +103,6 @@ namespace toucan context, data, gap, - timeline, _timeLayout); } } @@ -124,12 +116,11 @@ namespace toucan std::shared_ptr TrackItem::create( const std::shared_ptr& context, const ItemData& data, - const OTIO_NS::SerializableObject::Retainer& track, - const OTIO_NS::SerializableObject::Retainer& timeline, + const OTIO_NS::Track* track, const std::shared_ptr& parent) { auto out = std::make_shared(); - out->_init(context, data, track, timeline, parent); + out->_init(context, data, track, parent); return out; } diff --git a/lib/toucanView/TrackItem.h b/lib/toucanView/TrackItem.h index 885b516..c40df2a 100644 --- a/lib/toucanView/TrackItem.h +++ b/lib/toucanView/TrackItem.h @@ -18,8 +18,7 @@ namespace toucan void _init( const std::shared_ptr&, const ItemData&, - const OTIO_NS::SerializableObject::Retainer&, - const OTIO_NS::SerializableObject::Retainer&, + const OTIO_NS::Track*, const std::shared_ptr& parent); public: @@ -29,8 +28,7 @@ namespace toucan static std::shared_ptr create( const std::shared_ptr&, const ItemData&, - const OTIO_NS::SerializableObject::Retainer&, - const OTIO_NS::SerializableObject::Retainer&, + const OTIO_NS::Track*, const std::shared_ptr& parent = nullptr); void setScale(double) override; @@ -46,7 +44,7 @@ namespace toucan private: void _textUpdate(); - OTIO_NS::SerializableObject::Retainer _track; + const OTIO_NS::Track* _track = nullptr; std::string _text; ftk::Color4F _color; diff --git a/lib/toucanView/VideoClipItem.cpp b/lib/toucanView/VideoClipItem.cpp index e11afc3..43df2b4 100644 --- a/lib/toucanView/VideoClipItem.cpp +++ b/lib/toucanView/VideoClipItem.cpp @@ -19,27 +19,24 @@ namespace toucan void VideoClipItem::_init( const std::shared_ptr& context, const ItemData& data, - const OTIO_NS::SerializableObject::Retainer& clip, - const OTIO_NS::SerializableObject::Retainer& timeline, + const OTIO_NS::Clip* clip, const ftk::Color4F& color, const std::shared_ptr& parent) { + auto timelineWrapper = data.file->getTimelineWrapper(); OTIO_NS::TimeRange timeRange = clip->transformed_time_range( clip->trimmed_range(), - timeline->tracks()); - if (timeline->global_start_time().has_value()) - { - timeRange = OTIO_NS::TimeRange( - timeline->global_start_time().value() + timeRange.start_time(), - timeRange.duration()); - } + timelineWrapper->getTimeline()->tracks()); + timeRange = OTIO_NS::TimeRange( + timelineWrapper->getTimeRange().start_time() + timeRange.start_time(), + timeRange.duration()); timeRange = OTIO_NS::TimeRange( timeRange.start_time().round(), timeRange.duration().round()); IItem::_init( context, data, - OTIO_NS::dynamic_retainer_cast(clip), + clip, timeRange, "toucan::VideoClipItem", parent); @@ -58,6 +55,8 @@ namespace toucan _thumbnailsWidget = ThumbnailsWidget::create( context, + timelineWrapper, + _clip, _clip->media_reference(), data.thumbnailGenerator, data.thumbnailCache, @@ -74,13 +73,10 @@ namespace toucan OTIO_NS::TimeRange markerRange( marker->marked_range().start_time() + trimmedRange.start_time(), marker->marked_range().duration()); - markerRange = clip->transformed_time_range(markerRange, timeline->tracks()); - if (timeline->global_start_time().has_value()) - { - markerRange = OTIO_NS::TimeRange( - timeline->global_start_time().value() + markerRange.start_time(), - markerRange.duration()); - } + markerRange = clip->transformed_time_range(markerRange, timelineWrapper->getTimeline()->tracks()); + markerRange = OTIO_NS::TimeRange( + timelineWrapper->getTimeRange().start_time() + markerRange.start_time(), + markerRange.duration()); auto markerItem = MarkerItem::create( context, data, @@ -107,13 +103,12 @@ namespace toucan std::shared_ptr VideoClipItem::create( const std::shared_ptr& context, const ItemData& data, - const OTIO_NS::SerializableObject::Retainer& clip, - const OTIO_NS::SerializableObject::Retainer& timeline, + const OTIO_NS::Clip* clip, const ftk::Color4F& color, const std::shared_ptr& parent) { auto out = std::make_shared(); - out->_init(context, data, clip, timeline, color, parent); + out->_init(context, data, clip, color, parent); return out; } @@ -180,8 +175,7 @@ namespace toucan "Open Media", [this, externalReference] { - auto file = _file.lock(); - const std::filesystem::path path = file->getTimelineWrapper()->getMediaPath(externalReference->target_url()); + const std::filesystem::path path = _file->getTimelineWrapper()->getMediaPath(externalReference->target_url()); auto app = _app.lock(); app->open(path); }); @@ -194,9 +188,8 @@ namespace toucan "Open Image Sequence", [this, sequenceRef] { - auto file = _file.lock(); const std::string path = getSequenceFrame( - file->getTimelineWrapper()->getMediaPath(sequenceRef->target_url_base()), + _file->getTimelineWrapper()->getMediaPath(sequenceRef->target_url_base()), sequenceRef->name_prefix(), sequenceRef->start_frame(), sequenceRef->frame_zero_padding(), diff --git a/lib/toucanView/VideoClipItem.h b/lib/toucanView/VideoClipItem.h index bd116f5..28830dc 100644 --- a/lib/toucanView/VideoClipItem.h +++ b/lib/toucanView/VideoClipItem.h @@ -21,8 +21,7 @@ namespace toucan void _init( const std::shared_ptr&, const ItemData&, - const OTIO_NS::SerializableObject::Retainer&, - const OTIO_NS::SerializableObject::Retainer&, + const OTIO_NS::Clip*, const ftk::Color4F&, const std::shared_ptr& parent); @@ -33,8 +32,7 @@ namespace toucan static std::shared_ptr create( const std::shared_ptr&, const ItemData&, - const OTIO_NS::SerializableObject::Retainer&, - const OTIO_NS::SerializableObject::Retainer&, + const OTIO_NS::Clip*, const ftk::Color4F&, const std::shared_ptr& parent = nullptr); @@ -52,7 +50,7 @@ namespace toucan private: void _textUpdate(); - OTIO_NS::SerializableObject::Retainer _clip; + const OTIO_NS::Clip* _clip = nullptr; std::string _text; ftk::Color4F _color; std::string _url; diff --git a/tests/toucanRenderTest/CompTest.cpp b/tests/toucanRenderTest/CompTest.cpp index d6ea16a..de0b836 100644 --- a/tests/toucanRenderTest/CompTest.cpp +++ b/tests/toucanRenderTest/CompTest.cpp @@ -16,7 +16,7 @@ namespace toucan auto comp = std::make_shared( std::vector >{ fg, bg }); comp->setPremult(true); - auto buf = comp->exec(OTIO_NS::RationalTime(0.0, 24.0)); + auto buf = comp->exec(); buf.write("compTest.png"); } } diff --git a/tests/toucanRenderTest/ImageGraphTest.cpp b/tests/toucanRenderTest/ImageGraphTest.cpp index af88c6b..dbf9d71 100644 --- a/tests/toucanRenderTest/ImageGraphTest.cpp +++ b/tests/toucanRenderTest/ImageGraphTest.cpp @@ -62,7 +62,7 @@ namespace toucan if (auto op = graph->exec(host, time)) { // Execute the image operation graph. - const auto buf = op->exec(time); + const auto buf = op->exec(); // Save the image. const std::string fileName = getSequenceFrame( diff --git a/tests/toucanRenderTest/ReadTest.cpp b/tests/toucanRenderTest/ReadTest.cpp index d3d6948..66dd288 100644 --- a/tests/toucanRenderTest/ReadTest.cpp +++ b/tests/toucanRenderTest/ReadTest.cpp @@ -13,7 +13,8 @@ namespace toucan { std::cout << "readTest" << std::endl; auto read = std::make_shared(path / "Letter_A.png"); - auto buf = read->exec(OTIO_NS::RationalTime(0.0, 24.0)); + read->setTime(OTIO_NS::RationalTime(0.0, 24.0)); + auto buf = read->exec(); const auto& spec = buf.spec(); assert(spec.width > 0); } From ba0a0cfe118f02db9e131c71beec01ddb8bba1ef Mon Sep 17 00:00:00 2001 From: Darby Johnston Date: Mon, 15 Sep 2025 11:35:07 -0700 Subject: [PATCH 03/24] Workaround for missing timecode Signed-off-by: Darby Johnston --- lib/toucanRender/ImageGraph.cpp | 14 ++++++++++++-- lib/toucanView/ThumbnailGenerator.cpp | 17 ++++++++++++----- lib/toucanView/ThumbnailGenerator.h | 10 ++++++---- lib/toucanView/ThumbnailsWidget.cpp | 6 ++---- 4 files changed, 32 insertions(+), 15 deletions(-) diff --git a/lib/toucanRender/ImageGraph.cpp b/lib/toucanRender/ImageGraph.cpp index 2a31d76..0104d82 100644 --- a/lib/toucanRender/ImageGraph.cpp +++ b/lib/toucanRender/ImageGraph.cpp @@ -322,6 +322,7 @@ namespace toucan { std::shared_ptr out; + OTIO_NS::RationalTime t = time; if (auto clip = OTIO_NS::dynamic_retainer_cast(item)) { // Get the media reference. @@ -329,7 +330,16 @@ namespace toucan { try { - out = _timelineWrapper->createReadNode(externalRef); + auto read = _timelineWrapper->createReadNode(externalRef); + + //! \bug Workaround for files that are missing timecode. + if (t > read->getTimeRange().end_time_inclusive()) + { + const OTIO_NS::TimeRange available = clip->available_range(); + t -= available.start_time(); + } + + out = read; } catch (const std::exception& e) { @@ -368,7 +378,7 @@ namespace toucan } if (out) { - out->setTime(time); + out->setTime(t); } // Get the effects. diff --git a/lib/toucanView/ThumbnailGenerator.cpp b/lib/toucanView/ThumbnailGenerator.cpp index cfd9003..fa8b21d 100644 --- a/lib/toucanView/ThumbnailGenerator.cpp +++ b/lib/toucanView/ThumbnailGenerator.cpp @@ -62,12 +62,10 @@ namespace toucan } std::future ThumbnailGenerator::getAspect( - const OTIO_NS::MediaReference* ref, - const OTIO_NS::RationalTime& time) + const OTIO_NS::MediaReference* ref) { auto request = std::make_shared(); request->ref = ref; - request->time = time; auto out = request->promise.get_future(); bool valid = false; { @@ -92,6 +90,7 @@ namespace toucan ThumbnailRequest ThumbnailGenerator::getThumbnail( const OTIO_NS::MediaReference* ref, const OTIO_NS::RationalTime& time, + const OTIO_NS::TimeRange& availableRange, int height) { _requestId++; @@ -99,6 +98,7 @@ namespace toucan request->id = _requestId; request->ref = ref; request->time = time; + request->availableRange = availableRange; request->height = height; ThumbnailRequest out; out.id = _requestId; @@ -191,7 +191,7 @@ namespace toucan float aspect = 0.F; if (read) { - read->setTime(aspectRequest->time); + read->setTime(read->getTimeRange().start_time()); const OIIO::ImageBuf buf = read->exec(); const OIIO::ImageSpec& spec = buf.spec(); if (spec.height > 0) @@ -218,7 +218,14 @@ namespace toucan OIIO::ImageBuf buf; if (read) { - read->setTime(request->time); + //! \bug Workaround for files that are missing timecode. + OTIO_NS::RationalTime t = request->time; + if (t > read->getTimeRange().end_time_inclusive()) + { + t -= request->availableRange.start_time(); + } + + read->setTime(t); buf = read->exec(); } diff --git a/lib/toucanView/ThumbnailGenerator.h b/lib/toucanView/ThumbnailGenerator.h index e568874..f7ff8b6 100644 --- a/lib/toucanView/ThumbnailGenerator.h +++ b/lib/toucanView/ThumbnailGenerator.h @@ -43,14 +43,16 @@ namespace toucan ~ThumbnailGenerator(); //! Get a media aspect ratio. - std::future getAspect( - const OTIO_NS::MediaReference*, - const OTIO_NS::RationalTime&); + std::future getAspect(const OTIO_NS::MediaReference*); //! Get a media thumbnail. + //! + //! \bug The availableRange parameter is a workaround for files that + //! are missing timecode. ThumbnailRequest getThumbnail( const OTIO_NS::MediaReference*, const OTIO_NS::RationalTime&, + const OTIO_NS::TimeRange& availableRange, int height); //! Cancel thumbnail requests. @@ -66,7 +68,6 @@ namespace toucan struct AspectRequest { const OTIO_NS::MediaReference* ref = nullptr; - OTIO_NS::RationalTime time; std::promise promise; }; @@ -75,6 +76,7 @@ namespace toucan uint64_t id = 0; const OTIO_NS::MediaReference* ref = nullptr; OTIO_NS::RationalTime time; + OTIO_NS::TimeRange availableRange; int height = 0; std::promise > promise; }; diff --git a/lib/toucanView/ThumbnailsWidget.cpp b/lib/toucanView/ThumbnailsWidget.cpp index fc9919e..3898a41 100644 --- a/lib/toucanView/ThumbnailsWidget.cpp +++ b/lib/toucanView/ThumbnailsWidget.cpp @@ -28,10 +28,7 @@ namespace toucan _thumbnailGenerator = thumbnailGenerator; _thumbnailCache = thumbnailCache; - const OTIO_NS::RationalTime t = _timelineWrapper->getTimeline()->tracks()->transformed_time( - timeRange.start_time(), - _clip) - _timelineWrapper->getTimeRange().start_time(); - _thumbnailAspectRequest = _thumbnailGenerator->getAspect(ref, t); + _thumbnailAspectRequest = _thumbnailGenerator->getAspect(ref); } ThumbnailsWidget::~ThumbnailsWidget() @@ -159,6 +156,7 @@ namespace toucan _thumbnailRequests.push_back(_thumbnailGenerator->getThumbnail( _ref, t, + _clip->available_range(), _size.thumbnailHeight)); } } From f366e3f5db9c51b3c1067171d01e462db9fe3362 Mon Sep 17 00:00:00 2001 From: Darby Johnston Date: Mon, 15 Sep 2025 13:14:54 -0700 Subject: [PATCH 04/24] Cache read nodes Signed-off-by: Darby Johnston --- lib/toucanRender/ImageGraph.cpp | 66 ++++++++++++++++----------- lib/toucanRender/ImageGraph.h | 2 + lib/toucanRender/Read.h | 2 - lib/toucanRender/TimelineWrapper.h | 1 - lib/toucanView/ThumbnailGenerator.cpp | 24 ++++++---- lib/toucanView/ThumbnailGenerator.h | 2 + lib/toucanView/ThumbnailsWidget.cpp | 27 ++++++++--- lib/toucanView/ThumbnailsWidget.h | 3 ++ lib/toucanView/VideoClipItem.cpp | 1 + 9 files changed, 83 insertions(+), 45 deletions(-) diff --git a/lib/toucanRender/ImageGraph.cpp b/lib/toucanRender/ImageGraph.cpp index 0104d82..1d585d2 100644 --- a/lib/toucanRender/ImageGraph.cpp +++ b/lib/toucanRender/ImageGraph.cpp @@ -48,6 +48,8 @@ namespace toucan _timelineWrapper(timelineWrapper), _timeRange(timelineWrapper->getTimeRange()) { + _readCache.setMax(100); + // Get the image information from the first video clip. for (auto clip : getVideoClips(_timelineWrapper->getTimeline())) { @@ -325,45 +327,55 @@ namespace toucan OTIO_NS::RationalTime t = time; if (auto clip = OTIO_NS::dynamic_retainer_cast(item)) { - // Get the media reference. - if (auto externalRef = dynamic_cast(clip->media_reference())) + auto mediaRef = clip->media_reference(); + if (auto externalRef = dynamic_cast(mediaRef)) { - try + std::shared_ptr read; + if (!_readCache.get(externalRef, read)) { - auto read = _timelineWrapper->createReadNode(externalRef); - - //! \bug Workaround for files that are missing timecode. - if (t > read->getTimeRange().end_time_inclusive()) + try { - const OTIO_NS::TimeRange available = clip->available_range(); - t -= available.start_time(); + read = _timelineWrapper->createReadNode(externalRef); + _readCache.add(externalRef, read); + } + catch (const std::exception& e) + { + _context.lock()->getSystem()->print( + logPrefix, + e.what(), + ftk::LogType::Error); } - - out = read; } - catch (const std::exception& e) + + //! \bug Workaround for files that are missing timecode. + if (t > read->getTimeRange().end_time_inclusive()) { - _context.lock()->getSystem()->print( - logPrefix, - e.what(), - ftk::LogType::Error); + t -= clip->available_range().start_time(); } + + out = read; } - else if (auto sequenceRef = dynamic_cast(clip->media_reference())) + else if (auto sequenceRef = dynamic_cast(mediaRef)) { - try - { - out = _timelineWrapper->createReadNode(sequenceRef); - } - catch (const std::exception& e) + std::shared_ptr read; + if (!_readCache.get(clip->media_reference(), read)) { - _context.lock()->getSystem()->print( - logPrefix, - e.what(), - ftk::LogType::Error); + try + { + out = _timelineWrapper->createReadNode(sequenceRef); + _readCache.add(sequenceRef, read); + } + catch (const std::exception& e) + { + _context.lock()->getSystem()->print( + logPrefix, + e.what(), + ftk::LogType::Error); + } } + out = read; } - else if (auto generatorRef = dynamic_cast(clip->media_reference())) + else if (auto generatorRef = dynamic_cast(mediaRef)) { out = host->createNode( generatorRef->parameters(), diff --git a/lib/toucanRender/ImageGraph.h b/lib/toucanRender/ImageGraph.h index e671057..ff53c3a 100644 --- a/lib/toucanRender/ImageGraph.h +++ b/lib/toucanRender/ImageGraph.h @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -65,5 +66,6 @@ namespace toucan IMATH_NAMESPACE::V2i _imageSize = IMATH_NAMESPACE::V2i(0, 0); int _imageChannels = 0; std::string _imageDataType; + ftk::LRUCache > _readCache; }; } diff --git a/lib/toucanRender/Read.h b/lib/toucanRender/Read.h index 88190d5..5342ea0 100644 --- a/lib/toucanRender/Read.h +++ b/lib/toucanRender/Read.h @@ -9,8 +9,6 @@ #include -#include - #include #include diff --git a/lib/toucanRender/TimelineWrapper.h b/lib/toucanRender/TimelineWrapper.h index 5b69743..d3c4e0e 100644 --- a/lib/toucanRender/TimelineWrapper.h +++ b/lib/toucanRender/TimelineWrapper.h @@ -6,7 +6,6 @@ #include #include -#include #include #include diff --git a/lib/toucanView/ThumbnailGenerator.cpp b/lib/toucanView/ThumbnailGenerator.cpp index fa8b21d..934244c 100644 --- a/lib/toucanView/ThumbnailGenerator.cpp +++ b/lib/toucanView/ThumbnailGenerator.cpp @@ -37,6 +37,7 @@ namespace toucan _logSystem = context->getSystem(); _thread.running = true; + _thread.readCache.setMax(100); _thread.thread = std::thread( [this] { @@ -204,17 +205,22 @@ namespace toucan if (request) { std::shared_ptr read; - try - { - read = _timelineWrapper->createReadNode(request->ref); - } - catch (const std::exception& e) + if (!_thread.readCache.get(request->ref, read)) { - _logSystem->print( - "toucan::ThumbnailGenerator", - e.what(), - ftk::LogType::Error); + try + { + read = _timelineWrapper->createReadNode(request->ref); + _thread.readCache.add(request->ref, read); + } + catch (const std::exception& e) + { + _logSystem->print( + "toucan::ThumbnailGenerator", + e.what(), + ftk::LogType::Error); + } } + OIIO::ImageBuf buf; if (read) { diff --git a/lib/toucanView/ThumbnailGenerator.h b/lib/toucanView/ThumbnailGenerator.h index f7ff8b6..676d120 100644 --- a/lib/toucanView/ThumbnailGenerator.h +++ b/lib/toucanView/ThumbnailGenerator.h @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -96,6 +97,7 @@ namespace toucan std::condition_variable cv; std::thread thread; std::atomic running; + ftk::LRUCache > readCache; }; Thread _thread; }; diff --git a/lib/toucanView/ThumbnailsWidget.cpp b/lib/toucanView/ThumbnailsWidget.cpp index 3898a41..73ce6e7 100644 --- a/lib/toucanView/ThumbnailsWidget.cpp +++ b/lib/toucanView/ThumbnailsWidget.cpp @@ -55,12 +55,7 @@ namespace toucan ITimeWidget::setScale(value); if (changed) { - std::vector cancel; - for (const auto& request : _thumbnailRequests) - { - cancel.push_back(request.id); - } - _thumbnailGenerator->cancelThumbnails(cancel); + _cancelThumbnails(); } } @@ -113,6 +108,15 @@ namespace toucan } _setSizeHint(ftk::Size2I(0, _size.thumbnailHeight)); } + + void ThumbnailsWidget::clipEvent(const ftk::Box2I& clipRect, bool clipped) + { + ITimeWidget::clipEvent(clipRect, clipped); + if (clipped) + { + _cancelThumbnails(); + } + } void ThumbnailsWidget::drawEvent( const ftk::Box2I& drawRect, @@ -183,4 +187,15 @@ namespace toucan } _thumbnailGenerator->cancelThumbnails(cancel); } + + void ThumbnailsWidget::_cancelThumbnails() + { + std::vector cancel; + for (const auto& request : _thumbnailRequests) + { + cancel.push_back(request.id); + } + _thumbnailRequests.clear(); + _thumbnailGenerator->cancelThumbnails(cancel); + } } diff --git a/lib/toucanView/ThumbnailsWidget.h b/lib/toucanView/ThumbnailsWidget.h index 931ea05..5bc1895 100644 --- a/lib/toucanView/ThumbnailsWidget.h +++ b/lib/toucanView/ThumbnailsWidget.h @@ -49,9 +49,12 @@ namespace toucan bool parentsEnabled, const ftk::TickEvent&) override; void sizeHintEvent(const ftk::SizeHintEvent&) override; + void clipEvent(const ftk::Box2I&, bool clipped) override; void drawEvent(const ftk::Box2I&, const ftk::DrawEvent&) override; private: + void _cancelThumbnails(); + std::shared_ptr _timelineWrapper; const OTIO_NS::Clip* _clip = nullptr; const OTIO_NS::MediaReference* _ref = nullptr; diff --git a/lib/toucanView/VideoClipItem.cpp b/lib/toucanView/VideoClipItem.cpp index 43df2b4..d43e971 100644 --- a/lib/toucanView/VideoClipItem.cpp +++ b/lib/toucanView/VideoClipItem.cpp @@ -115,6 +115,7 @@ namespace toucan void VideoClipItem::setScale(double value) { IItem::setScale(value); + _thumbnailsWidget->setScale(value); if (_markerLayout) { _markerLayout->setScale(value); From b601607b576b28ad9a7df6face095e26f84f7d30 Mon Sep 17 00:00:00 2001 From: Darby Johnston Date: Mon, 15 Sep 2025 16:55:12 -0700 Subject: [PATCH 05/24] Time warp fixes Signed-off-by: Darby Johnston --- lib/toucanRender/ImageGraph.cpp | 108 +++++++++++++++++--------- lib/toucanRender/ImageGraph.h | 7 +- lib/toucanRender/ImageNode.cpp | 5 ++ lib/toucanRender/ImageNode.h | 3 + lib/toucanView/ThumbnailGenerator.cpp | 3 +- lib/toucanView/TimeWidgets.cpp | 9 ++- 6 files changed, 95 insertions(+), 40 deletions(-) diff --git a/lib/toucanRender/ImageGraph.cpp b/lib/toucanRender/ImageGraph.cpp index 1d585d2..f5adf32 100644 --- a/lib/toucanRender/ImageGraph.cpp +++ b/lib/toucanRender/ImageGraph.cpp @@ -151,22 +151,40 @@ namespace toucan metaData["color"] = vecToAny(IMATH_NAMESPACE::V4f(0.F, 0.F, 0.F, 1.F)); std::shared_ptr node = host->createNode(metaData, "toucan:Fill"); - // Loop over the tracks. + // Get the stack effects. auto stack = _timelineWrapper->getTimeline()->tracks(); + const auto& stackEffects = stack->effects(); + std::vector > stackEffectsNodes; + OTIO_NS::RationalTime t = time - _timeRange.start_time(); + if (!stackEffects.empty()) + { + stackEffectsNodes = _effects(host, t, stack->available_range(), stackEffects); + } + + // Loop over the tracks. for (const auto& i : stack->children()) { if (auto track = OTIO_NS::dynamic_retainer_cast(i)) { if (track->kind() == OTIO_NS::Track::Kind::video && !track->find_clips().empty()) { + // Get the effects. + const auto& effects = track->effects(); + std::vector > effectsNodes; + OTIO_NS::RationalTime t2 = t; + if (!effects.empty()) + { + effectsNodes = _effects(host, t2, track->available_range(), effects); + } + // Process this track. - auto trackNode = _track(host, time - _timeRange.start_time(), track); + auto trackNode = _track(host, t2, track); - // Get the track effects. - const auto& effects = track->effects(); - if (trackNode && !effects.empty()) + // Add the effects. + if (!effectsNodes.empty()) { - trackNode = _effects(host, effects, trackNode); + effectsNodes.front()->setInputs({ trackNode }); + trackNode = effectsNodes.back(); } // Composite this track over the previous track. @@ -186,11 +204,11 @@ namespace toucan } } - // Get the stack effects. - const auto& effects = stack->effects(); - if (!effects.empty()) + // Add the stack effects. + if (!stackEffectsNodes.empty()) { - node = _effects(host, effects, node); + stackEffectsNodes.front()->setInputs({ node }); + node = stackEffectsNodes.back(); } return node; @@ -325,6 +343,16 @@ namespace toucan std::shared_ptr out; OTIO_NS::RationalTime t = time; + + // Get the effects. + const auto& effects = item->effects(); + std::vector > effectsNodes; + if (!effects.empty()) + { + effectsNodes = _effects(host, t, item->available_range(), effects); + } + + // Get the media. if (auto clip = OTIO_NS::dynamic_retainer_cast(item)) { auto mediaRef = clip->media_reference(); @@ -346,23 +374,27 @@ namespace toucan ftk::LogType::Error); } } - - //! \bug Workaround for files that are missing timecode. - if (t > read->getTimeRange().end_time_inclusive()) + if (read) { - t -= clip->available_range().start_time(); - } + //! \bug Workaround for files that are missing timecode. + if (clip->available_range().start_time() != + read->getTimeRange().start_time()) + { + t -= clip->available_range().start_time(); + } + read->setTime(t); + } out = read; } else if (auto sequenceRef = dynamic_cast(mediaRef)) { std::shared_ptr read; - if (!_readCache.get(clip->media_reference(), read)) + if (!_readCache.get(sequenceRef, read)) { try { - out = _timelineWrapper->createReadNode(sequenceRef); + read = _timelineWrapper->createReadNode(sequenceRef); _readCache.add(sequenceRef, read); } catch (const std::exception& e) @@ -373,6 +405,10 @@ namespace toucan ftk::LogType::Error); } } + if (read) + { + read->setTime(t); + } out = read; } else if (auto generatorRef = dynamic_cast(mediaRef)) @@ -388,44 +424,46 @@ namespace toucan metaData["size"] = vecToAny(_imageSize); out = host->createNode(metaData, "toucan:Fill"); } - if (out) - { - out->setTime(t); - } - // Get the effects. - const auto& effects = item->effects(); - if (out && !effects.empty()) + // Add the effects. + if (!effectsNodes.empty()) { - out = _effects(host, effects, out); + effectsNodes.front()->setInputs({ out }); + out = effectsNodes.back(); } return out; } - std::shared_ptr ImageGraph::_effects( + std::vector > ImageGraph::_effects( const std::shared_ptr& host, - const std::vector >& effects, - const std::shared_ptr& input) + OTIO_NS::RationalTime& time, + const OTIO_NS::TimeRange& timeRange, + const std::vector >& effects) { - std::shared_ptr out = input; + std::vector > out; for (const auto& effect : effects) { if (auto linearTimeWarp = dynamic_cast(effect.value)) { - //auto linearTimeWarpNode = std::make_shared( - // static_cast(linearTimeWarp->time_scalar()), - // std::vector >{ out }); - //out = linearTimeWarpNode; + const double s = linearTimeWarp->time_scalar(); + time = OTIO_NS::RationalTime( + (time - timeRange.start_time()).value() * s, + time.rate()); } else { + std::vector > inputs; + if (!out.empty()) + { + inputs.push_back(out.back()); + } if (auto imageEffect = host->createNode( effect->metadata(), effect->effect_name(), - { out })) + inputs)) { - out = imageEffect; + out.push_back(imageEffect); } } } diff --git a/lib/toucanRender/ImageGraph.h b/lib/toucanRender/ImageGraph.h index ff53c3a..f474782 100644 --- a/lib/toucanRender/ImageGraph.h +++ b/lib/toucanRender/ImageGraph.h @@ -54,10 +54,11 @@ namespace toucan const OTIO_NS::RationalTime&, const OTIO_NS::SerializableObject::Retainer&); - std::shared_ptr _effects( + std::vector > _effects( const std::shared_ptr&, - const std::vector >&, - const std::shared_ptr&); + OTIO_NS::RationalTime&, + const OTIO_NS::TimeRange&, + const std::vector >&); std::weak_ptr _context; std::filesystem::path _path; diff --git a/lib/toucanRender/ImageNode.cpp b/lib/toucanRender/ImageNode.cpp index a9170bc..3a91678 100644 --- a/lib/toucanRender/ImageNode.cpp +++ b/lib/toucanRender/ImageNode.cpp @@ -30,6 +30,11 @@ namespace toucan return _inputs; } + void IImageNode::setInputs(const std::vector >& inputs) + { + _inputs = inputs; + } + void IImageNode::setTime(const OTIO_NS::RationalTime& value) { _time = value; diff --git a/lib/toucanRender/ImageNode.h b/lib/toucanRender/ImageNode.h index c66aa44..639fdb2 100644 --- a/lib/toucanRender/ImageNode.h +++ b/lib/toucanRender/ImageNode.h @@ -33,6 +33,9 @@ namespace toucan //! Get the inputs. const std::vector >& getInputs() const; + //! Set the inputs. + void setInputs(const std::vector >&); + //! Set the time. void setTime(const OTIO_NS::RationalTime&); diff --git a/lib/toucanView/ThumbnailGenerator.cpp b/lib/toucanView/ThumbnailGenerator.cpp index 934244c..9815fcf 100644 --- a/lib/toucanView/ThumbnailGenerator.cpp +++ b/lib/toucanView/ThumbnailGenerator.cpp @@ -226,7 +226,8 @@ namespace toucan { //! \bug Workaround for files that are missing timecode. OTIO_NS::RationalTime t = request->time; - if (t > read->getTimeRange().end_time_inclusive()) + if (request->availableRange.start_time() != + read->getTimeRange().start_time()) { t -= request->availableRange.start_time(); } diff --git a/lib/toucanView/TimeWidgets.cpp b/lib/toucanView/TimeWidgets.cpp index fd726de..664b572 100644 --- a/lib/toucanView/TimeWidgets.cpp +++ b/lib/toucanView/TimeWidgets.cpp @@ -47,7 +47,14 @@ namespace toucan { if (_callback) { - _callback(static_cast(index)); + TimeAction action = TimeAction::FrameStart; + switch (index) + { + case 1: action = TimeAction::FramePrev; break; + case 2: action = TimeAction::FrameNext; break; + case 3: action = TimeAction::FrameEnd; break; + } + _callback(action); } }); } From 04eb0d1829d7ec28fa28a427f8550c72f6cdee83 Mon Sep 17 00:00:00 2001 From: Darby Johnston Date: Mon, 15 Sep 2025 17:10:55 -0700 Subject: [PATCH 06/24] Round value Signed-off-by: Darby Johnston --- lib/toucanRender/ImageGraph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/toucanRender/ImageGraph.cpp b/lib/toucanRender/ImageGraph.cpp index f5adf32..c11327f 100644 --- a/lib/toucanRender/ImageGraph.cpp +++ b/lib/toucanRender/ImageGraph.cpp @@ -449,7 +449,7 @@ namespace toucan const double s = linearTimeWarp->time_scalar(); time = OTIO_NS::RationalTime( (time - timeRange.start_time()).value() * s, - time.rate()); + time.rate()).round(); } else { From cb79106833f894cc2532a9c5beb28f4a45252a4c Mon Sep 17 00:00:00 2001 From: Darby Johnston Date: Mon, 15 Sep 2025 17:11:10 -0700 Subject: [PATCH 07/24] Loop time Signed-off-by: Darby Johnston --- lib/toucanView/PlaybackModel.cpp | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/lib/toucanView/PlaybackModel.cpp b/lib/toucanView/PlaybackModel.cpp index 9e46bb2..774a127 100644 --- a/lib/toucanView/PlaybackModel.cpp +++ b/lib/toucanView/PlaybackModel.cpp @@ -99,22 +99,34 @@ namespace toucan setCurrentTime(timeRange.start_time()); break; case TimeAction::FramePrev: - setCurrentTime(currentTime - OTIO_NS::RationalTime(1.0, currentTime.rate())); + setCurrentTime( + currentTime - OTIO_NS::RationalTime(1.0, currentTime.rate()), + CurrentTime::Loop); break; case TimeAction::FramePrevX10: - setCurrentTime(currentTime - OTIO_NS::RationalTime(10.0, currentTime.rate())); + setCurrentTime( + currentTime - OTIO_NS::RationalTime(10.0, currentTime.rate()), + CurrentTime::Loop); break; case TimeAction::FramePrevX100: - setCurrentTime(currentTime - OTIO_NS::RationalTime(100.0, currentTime.rate())); + setCurrentTime( + currentTime - OTIO_NS::RationalTime(100.0, currentTime.rate()), + CurrentTime::Loop); break; case TimeAction::FrameNext: - setCurrentTime(currentTime + OTIO_NS::RationalTime(1.0, currentTime.rate())); + setCurrentTime( + currentTime + OTIO_NS::RationalTime(1.0, currentTime.rate()), + CurrentTime::Loop); break; case TimeAction::FrameNextX10: - setCurrentTime(currentTime + OTIO_NS::RationalTime(10.0, currentTime.rate())); + setCurrentTime( + currentTime + OTIO_NS::RationalTime(10.0, currentTime.rate()), + CurrentTime::Loop); break; case TimeAction::FrameNextX100: - setCurrentTime(currentTime + OTIO_NS::RationalTime(100.0, currentTime.rate())); + setCurrentTime( + currentTime + OTIO_NS::RationalTime(100.0, currentTime.rate()), + CurrentTime::Loop); break; case TimeAction::FrameEnd: setCurrentTime(timeRange.end_time_inclusive()); From 81e9038e9ebd7619865fbad6c5ec10b66da857a4 Mon Sep 17 00:00:00 2001 From: Darby Johnston Date: Mon, 15 Sep 2025 18:15:25 -0700 Subject: [PATCH 08/24] Debugging Signed-off-by: Darby Johnston --- .github/workflows/ci-workflow.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index 9031a31..0359f24 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -55,7 +55,7 @@ jobs: sudo apt-get install xorg-dev libglu1-mesa-dev mesa-common-dev mesa-utils xvfb - name: Build - run: sh toucan/sbuild-macos.sh + run: sh toucan/sbuild-linux.sh - name: Install run: cmake --build build-Release --config Release --target install @@ -93,6 +93,7 @@ jobs: run: | echo "$PWD/install-Debug/bin" >> $GITHUB_PATH echo "DYLD_LIBRARY_PATH=$PWD/install-Debug/lib:$DYLD_LIBRARY_PATH" >> $GITHUB_ENV + cmake --version - name: Build run: sh toucan/sbuild-macos.sh Debug @@ -120,6 +121,7 @@ jobs: run: | echo "$PWD/install-Debug/bin" >> $GITHUB_PATH echo "DYLD_LIBRARY_PATH=$PWD/install-Debug/lib:$DYLD_LIBRARY_PATH" >> $GITHUB_ENV + cmake --version - name: Build run: sh toucan/sbuild-macos.sh Debug From 479a5744938a20a461439155c9b6c58a7199e211 Mon Sep 17 00:00:00 2001 From: Darby Johnston Date: Tue, 16 Sep 2025 09:26:56 -0700 Subject: [PATCH 09/24] Debugging Signed-off-by: Darby Johnston --- .github/workflows/ci-workflow.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index 0359f24..9ada7ac 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -25,6 +25,7 @@ jobs: echo "LD_LIBRARY_PATH=$PWD/install-Debug/lib:$LD_LIBRARY_PATH" >> $GITHUB_ENV sudo apt-get update sudo apt-get install xorg-dev libglu1-mesa-dev mesa-common-dev mesa-utils xvfb + cmake --version - name: Build run: sh toucan/sbuild-linux.sh Debug @@ -53,6 +54,7 @@ jobs: echo "LD_LIBRARY_PATH=$PWD/install-Release/lib:$LD_LIBRARY_PATH" >> $GITHUB_ENV sudo apt-get update sudo apt-get install xorg-dev libglu1-mesa-dev mesa-common-dev mesa-utils xvfb + cmake --version - name: Build run: sh toucan/sbuild-linux.sh @@ -149,6 +151,7 @@ jobs: run: | echo "$PWD/install-Debug/bin" >> $GITHUB_PATH echo "DYLD_LIBRARY_PATH=$PWD/install-Debug/lib:$DYLD_LIBRARY_PATH" >> $GITHUB_ENV + cmake --version - name: Build run: bash toucan/sbuild-macos.sh @@ -192,6 +195,7 @@ jobs: echo %CD%\install-Debug\bin>> %GITHUB_PATH% echo %CD%\install-Debug\lib>> %GITHUB_PATH% echo CTEST_OUTPUT_ON_FAILURE=1 >> %GITHUB_ENV% + cmake --version - name: Build shell: cmd @@ -224,6 +228,7 @@ jobs: run: | echo %CD%\install-Debug\bin>> %GITHUB_PATH% echo %CD%\install-Debug\lib>> %GITHUB_PATH% + cmake --version - name: Build shell: cmd From 2dd5058a727fe39127bf44a05a1c6a4df14ffe2d Mon Sep 17 00:00:00 2001 From: Darby Johnston Date: Tue, 16 Sep 2025 09:27:47 -0700 Subject: [PATCH 10/24] Update pystring version Signed-off-by: Darby Johnston --- cmake/SuperBuild/Buildpystring.cmake | 5 +--- .../SuperBuild/pystring-patch/CMakeLists.txt | 24 ------------------- 2 files changed, 1 insertion(+), 28 deletions(-) delete mode 100644 cmake/SuperBuild/pystring-patch/CMakeLists.txt diff --git a/cmake/SuperBuild/Buildpystring.cmake b/cmake/SuperBuild/Buildpystring.cmake index c7c21a3..ac1ff7d 100644 --- a/cmake/SuperBuild/Buildpystring.cmake +++ b/cmake/SuperBuild/Buildpystring.cmake @@ -1,7 +1,7 @@ include(ExternalProject) set(pystring_GIT_REPOSITORY "https://github.com/imageworks/pystring.git") -set(pystring_GIT_TAG "v1.1.4") +set(pystring_GIT_TAG "02ef1186d6b77bc35f385bd4db2da75b4736adb7") set(pystring_ARGS ${toucan_EXTERNAL_PROJECT_ARGS}) @@ -10,8 +10,5 @@ ExternalProject_Add( PREFIX ${CMAKE_CURRENT_BINARY_DIR}/pystring GIT_REPOSITORY ${pystring_GIT_REPOSITORY} GIT_TAG ${pystring_GIT_TAG} - PATCH_COMMAND ${CMAKE_COMMAND} -E copy_if_different - ${CMAKE_CURRENT_SOURCE_DIR}/pystring-patch/CMakeLists.txt - ${CMAKE_CURRENT_BINARY_DIR}/pystring/src/pystring/CMakeLists.txt LIST_SEPARATOR | CMAKE_ARGS ${pystring_ARGS}) diff --git a/cmake/SuperBuild/pystring-patch/CMakeLists.txt b/cmake/SuperBuild/pystring-patch/CMakeLists.txt deleted file mode 100644 index 8e11add..0000000 --- a/cmake/SuperBuild/pystring-patch/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ -cmake_minimum_required(VERSION 3.2) -project(pystring CXX) - -#set(BUILD_SHARED_LIBS YES) - -add_library(pystring - pystring.cpp - pystring.h -) -set_target_properties(pystring PROPERTIES PUBLIC_HEADER pystring.h) - -add_executable (pystring_test test.cpp) -TARGET_LINK_LIBRARIES (pystring_test pystring) - -#enable_testing() -#add_test(NAME PyStringTest COMMAND pystring_test) - -include(GNUInstallDirs) - -install(TARGETS pystring - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/pystring -) - From 463da73cfa72def049f0b57e659be5460b611e26 Mon Sep 17 00:00:00 2001 From: Darby Johnston Date: Tue, 16 Sep 2025 09:27:58 -0700 Subject: [PATCH 11/24] Fix tests Signed-off-by: Darby Johnston --- tests/toucanViewTest/PlaybackModelTest.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/toucanViewTest/PlaybackModelTest.cpp b/tests/toucanViewTest/PlaybackModelTest.cpp index 7dffd3f..13238a3 100644 --- a/tests/toucanViewTest/PlaybackModelTest.cpp +++ b/tests/toucanViewTest/PlaybackModelTest.cpp @@ -100,14 +100,14 @@ namespace toucan test.model->timeAction(TimeAction::FrameNextX10, nullptr); assert(OTIO_NS::RationalTime(11.0, 24.0) == test.currentTime); test.model->timeAction(TimeAction::FrameNextX100, nullptr); - assert(OTIO_NS::RationalTime(47.0, 24.0) == test.currentTime); + assert(OTIO_NS::RationalTime(0.0, 24.0) == test.currentTime); test.model->timeAction(TimeAction::FramePrev, nullptr); - assert(OTIO_NS::RationalTime(46.0, 24.0) == test.currentTime); + assert(OTIO_NS::RationalTime(47.0, 24.0) == test.currentTime); test.model->timeAction(TimeAction::FramePrevX10, nullptr); - assert(OTIO_NS::RationalTime(36.0, 24.0) == test.currentTime); + assert(OTIO_NS::RationalTime(37.0, 24.0) == test.currentTime); test.model->timeAction(TimeAction::FramePrevX100, nullptr); - assert(OTIO_NS::RationalTime(0.0, 24.0) == test.currentTime); + assert(OTIO_NS::RationalTime(47.0, 24.0) == test.currentTime); } { Test test(context); From e2de6e9297d2755ce4d27a9a4a4946a9d387d09c Mon Sep 17 00:00:00 2001 From: Darby Johnston Date: Tue, 16 Sep 2025 09:34:28 -0700 Subject: [PATCH 12/24] Update expat version Signed-off-by: Darby Johnston --- cmake/SuperBuild/Buildexpat.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/SuperBuild/Buildexpat.cmake b/cmake/SuperBuild/Buildexpat.cmake index ecd53da..1e8aa75 100644 --- a/cmake/SuperBuild/Buildexpat.cmake +++ b/cmake/SuperBuild/Buildexpat.cmake @@ -1,7 +1,7 @@ include(ExternalProject) set(expat_GIT_REPOSITORY "https://github.com/libexpat/libexpat.git") -set(expat_GIT_TAG "R_2_5_0") +set(expat_GIT_TAG "R_2_6_0") set(expat_ARGS ${toucan_EXTERNAL_PROJECT_ARGS} From 629f98afb46a05b34f3a41481bf6cfa6981bbfc7 Mon Sep 17 00:00:00 2001 From: Darby Johnston Date: Tue, 16 Sep 2025 09:37:57 -0700 Subject: [PATCH 13/24] Update yaml-cpp version Signed-off-by: Darby Johnston --- cmake/SuperBuild/Buildyaml-cpp.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/SuperBuild/Buildyaml-cpp.cmake b/cmake/SuperBuild/Buildyaml-cpp.cmake index 5a53d1b..7d04957 100644 --- a/cmake/SuperBuild/Buildyaml-cpp.cmake +++ b/cmake/SuperBuild/Buildyaml-cpp.cmake @@ -1,7 +1,7 @@ include(ExternalProject) set(yaml-cpp_GIT_REPOSITORY "https://github.com/jbeder/yaml-cpp.git") -set(yaml-cpp_GIT_TAG "yaml-cpp-0.7.0") +set(yaml-cpp_GIT_TAG "0.8.0") set(yaml-cpp_ARGS ${toucan_EXTERNAL_PROJECT_ARGS} From 2053918189a9e39862af9f7707c10e9edc4e11e7 Mon Sep 17 00:00:00 2001 From: Darby Johnston Date: Tue, 16 Sep 2025 09:53:03 -0700 Subject: [PATCH 14/24] Revert updates and add patches for CMake Signed-off-by: Darby Johnston --- cmake/SuperBuild/Buildexpat.cmake | 5 +- cmake/SuperBuild/Buildpystring.cmake | 5 +- cmake/SuperBuild/Buildyaml-cpp.cmake | 5 +- .../expat-patch/expat/CMakeLists.txt | 921 ++++++++++++++++++ .../SuperBuild/pystring-patch/CMakeLists.txt | 22 + .../SuperBuild/yaml-cpp-patch/CMakeLists.txt | 174 ++++ 6 files changed, 1129 insertions(+), 3 deletions(-) create mode 100644 cmake/SuperBuild/expat-patch/expat/CMakeLists.txt create mode 100644 cmake/SuperBuild/pystring-patch/CMakeLists.txt create mode 100644 cmake/SuperBuild/yaml-cpp-patch/CMakeLists.txt diff --git a/cmake/SuperBuild/Buildexpat.cmake b/cmake/SuperBuild/Buildexpat.cmake index 1e8aa75..45837bc 100644 --- a/cmake/SuperBuild/Buildexpat.cmake +++ b/cmake/SuperBuild/Buildexpat.cmake @@ -1,7 +1,7 @@ include(ExternalProject) set(expat_GIT_REPOSITORY "https://github.com/libexpat/libexpat.git") -set(expat_GIT_TAG "R_2_6_0") +set(expat_GIT_TAG "R_2_5_0") set(expat_ARGS ${toucan_EXTERNAL_PROJECT_ARGS} @@ -14,6 +14,9 @@ ExternalProject_Add( PREFIX ${CMAKE_CURRENT_BINARY_DIR}/expat GIT_REPOSITORY ${expat_GIT_REPOSITORY} GIT_TAG ${expat_GIT_TAG} + PATCH_COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${CMAKE_CURRENT_SOURCE_DIR}/expat-patch/expat/CMakeLists.txt + ${CMAKE_CURRENT_BINARY_DIR}/expat/src/expat/expat/CMakeLists.txt SOURCE_SUBDIR expat LIST_SEPARATOR | CMAKE_ARGS ${expat_ARGS}) diff --git a/cmake/SuperBuild/Buildpystring.cmake b/cmake/SuperBuild/Buildpystring.cmake index ac1ff7d..c7c21a3 100644 --- a/cmake/SuperBuild/Buildpystring.cmake +++ b/cmake/SuperBuild/Buildpystring.cmake @@ -1,7 +1,7 @@ include(ExternalProject) set(pystring_GIT_REPOSITORY "https://github.com/imageworks/pystring.git") -set(pystring_GIT_TAG "02ef1186d6b77bc35f385bd4db2da75b4736adb7") +set(pystring_GIT_TAG "v1.1.4") set(pystring_ARGS ${toucan_EXTERNAL_PROJECT_ARGS}) @@ -10,5 +10,8 @@ ExternalProject_Add( PREFIX ${CMAKE_CURRENT_BINARY_DIR}/pystring GIT_REPOSITORY ${pystring_GIT_REPOSITORY} GIT_TAG ${pystring_GIT_TAG} + PATCH_COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${CMAKE_CURRENT_SOURCE_DIR}/pystring-patch/CMakeLists.txt + ${CMAKE_CURRENT_BINARY_DIR}/pystring/src/pystring/CMakeLists.txt LIST_SEPARATOR | CMAKE_ARGS ${pystring_ARGS}) diff --git a/cmake/SuperBuild/Buildyaml-cpp.cmake b/cmake/SuperBuild/Buildyaml-cpp.cmake index 7d04957..933011b 100644 --- a/cmake/SuperBuild/Buildyaml-cpp.cmake +++ b/cmake/SuperBuild/Buildyaml-cpp.cmake @@ -1,7 +1,7 @@ include(ExternalProject) set(yaml-cpp_GIT_REPOSITORY "https://github.com/jbeder/yaml-cpp.git") -set(yaml-cpp_GIT_TAG "0.8.0") +set(yaml-cpp_GIT_TAG "yaml-cpp-0.7.0") set(yaml-cpp_ARGS ${toucan_EXTERNAL_PROJECT_ARGS} @@ -14,5 +14,8 @@ ExternalProject_Add( PREFIX ${CMAKE_CURRENT_BINARY_DIR}/yaml-cpp GIT_REPOSITORY ${yaml-cpp_GIT_REPOSITORY} GIT_TAG ${yaml-cpp_GIT_TAG} + PATCH_COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${CMAKE_CURRENT_SOURCE_DIR}/yaml-cpp-patch/CMakeLists.txt + ${CMAKE_CURRENT_BINARY_DIR}/yaml-cpp/src/yaml-cpp/CMakeLists.txt LIST_SEPARATOR | CMAKE_ARGS ${yaml-cpp_ARGS}) diff --git a/cmake/SuperBuild/expat-patch/expat/CMakeLists.txt b/cmake/SuperBuild/expat-patch/expat/CMakeLists.txt new file mode 100644 index 0000000..a9b383c --- /dev/null +++ b/cmake/SuperBuild/expat-patch/expat/CMakeLists.txt @@ -0,0 +1,921 @@ +# __ __ _ +# ___\ \/ /_ __ __ _| |_ +# / _ \\ /| '_ \ / _` | __| +# | __// \| |_) | (_| | |_ +# \___/_/\_\ .__/ \__,_|\__| +# |_| XML parser +# +# Copyright (c) 2010 Patrick Spendrin +# Copyright (c) 2012 Karl Waclawek +# Copyright (c) 2016-2022 Sebastian Pipping +# Copyright (c) 2016 Sergei Nikulov +# Copyright (c) 2016 Björn Lindahl +# Copyright (c) 2016 Tobias Taschner +# Copyright (c) 2016 Ben Boeckel +# Copyright (c) 2017 Rhodri James +# Copyright (c) 2017 Rolf Eike Beer +# Copyright (c) 2017 Stephen Groat +# Copyright (c) 2017 Franek Korta +# Copyright (c) 2018 pedro-vicente +# Copyright (c) 2018 Frank Rast +# Copyright (c) 2018 userwithuid +# Copyright (c) 2018 Yury Gribov +# Copyright (c) 2019 Kishore Kunche +# Copyright (c) 2019 xantares +# Copyright (c) 2019 Mohammed Khajapasha +# Copyright (c) 2019 David Loffredo +# Copyright (c) 2019 Bhargava Shastry +# Copyright (c) 2020 Maciej Sroczyński +# Copyright (c) 2020 Gulliver +# Copyright (c) 2020 Thomas Beutlich +# Copyright (c) 2021 Alex Richardson +# Copyright (c) 2022 Johnny Jazeix +# Copyright (c) 2022 David Faure +# Unlike most of Expat, +# this file is copyrighted under the BSD-license for buildsystem files of KDE. + +cmake_minimum_required(VERSION 3.5) + +project(expat + VERSION + 2.5.0 + LANGUAGES + C +) + +set(PACKAGE_BUGREPORT "expat-bugs@libexpat.org") +set(PACKAGE_NAME "expat") +set(PACKAGE_VERSION "${PROJECT_VERSION}") +set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}") +set(PACKAGE_TARNAME "${PACKAGE_NAME}") + +include(CMakePackageConfigHelpers) +include(GNUInstallDirs) + +# +# Configuration defaults +# +if(WINCE) + set(_EXPAT_BUILD_TOOLS_DEFAULT OFF) +else() + set(_EXPAT_BUILD_TOOLS_DEFAULT ON) +endif() +if(MSVC OR NOT _EXPAT_BUILD_TOOLS_DEFAULT) + set(_EXPAT_BUILD_DOCS_DEFAULT OFF) +else() + find_program(DOCBOOK_TO_MAN NAMES docbook2x-man db2x_docbook2man docbook2man docbook-to-man) + if(DOCBOOK_TO_MAN) + set(_EXPAT_BUILD_DOCS_DEFAULT ON) + else() + set(_EXPAT_BUILD_DOCS_DEFAULT OFF) + endif() +endif() +if(MSVC) + set(_EXPAT_BUILD_PKGCONFIG_DEFAULT OFF) +else() + set(_EXPAT_BUILD_PKGCONFIG_DEFAULT ON) +endif() +if(DEFINED BUILD_SHARED_LIBS) + set(_EXPAT_SHARED_LIBS_DEFAULT ${BUILD_SHARED_LIBS}) +else() + set(_EXPAT_SHARED_LIBS_DEFAULT ON) +endif() +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE NoConfig) # so that accessing EXPAT_*_POSTFIX will be waterproof +endif() +string(TOUPPER "${CMAKE_BUILD_TYPE}" _EXPAT_BUILD_TYPE_UPPER) + +# +# Configuration +# + +macro(expat_shy_set var default cache type desc) + # Macro expat_shy_set came into life because: + # - Expat was previously using an inconsistent mix of CMake's native set() + # and option() to define public build time options. + # - option() is more friendly than set() with regard to configuring an + # external project that is pulled in by means of add_subdirectory() -- + # see comments in issue #597 -- so we wanted to get away from set(). + # - option() auto-converts non-bool values to bool when writing to the CMake + # cache, so we needed something that supports non-bool better and hence + # wanted to get away from plain option(), too. + # + # As a result, this function serves as a hybrid between CMake's regular set() + # and option(): from set() it takes support for non-bool types and the function + # name and signature whereas from option() (with policy CMP0077 mode NEW) it + # takes being shy when a value has previously been defined for that variable. + # + # So that resolves all need for set(.. FORCE) when pulling in Expat by means of + # add_subdirectory(). + # + if(NOT ${cache} STREQUAL "CACHE") + message(SEND_ERROR "Macro usage is: expat_shy_set(var default CACHE type desc)") + endif() + + if(DEFINED ${var}) + # NOTE: The idea is to (ideally) only add to the cache if + # there is no cache entry, yet. "if(DEFINED CACHE{var})" + # requires CMake >=3.14. + if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.14" AND NOT DEFINED "CACHE{${var}}") + set("${var}" "${${var}}" CACHE "${type}" "${desc}") + endif() + else() + set("${var}" "${default}" CACHE "${type}" "${desc}") + endif() +endmacro() + +expat_shy_set(EXPAT_BUILD_TOOLS ${_EXPAT_BUILD_TOOLS_DEFAULT} CACHE BOOL "Build the xmlwf tool for expat library") +expat_shy_set(EXPAT_BUILD_EXAMPLES ON CACHE BOOL "Build the examples for expat library") +expat_shy_set(EXPAT_BUILD_TESTS ON CACHE BOOL "Build the tests for expat library") +expat_shy_set(EXPAT_SHARED_LIBS ${_EXPAT_SHARED_LIBS_DEFAULT} CACHE BOOL "Build a shared expat library") +expat_shy_set(EXPAT_BUILD_DOCS ${_EXPAT_BUILD_DOCS_DEFAULT} CACHE BOOL "Build man page for xmlwf") +expat_shy_set(EXPAT_BUILD_FUZZERS OFF CACHE BOOL "Build fuzzers for the expat library") +expat_shy_set(EXPAT_BUILD_PKGCONFIG ${_EXPAT_BUILD_PKGCONFIG_DEFAULT} CACHE BOOL "Build pkg-config file") +expat_shy_set(EXPAT_OSSFUZZ_BUILD OFF CACHE BOOL "Build fuzzers via ossfuzz for the expat library") +if(UNIX OR _EXPAT_HELP) + expat_shy_set(EXPAT_WITH_LIBBSD OFF CACHE BOOL "Utilize libbsd (for arc4random_buf)") +endif() +expat_shy_set(EXPAT_ENABLE_INSTALL ON CACHE BOOL "Install expat files in cmake install target") +expat_shy_set(EXPAT_CONTEXT_BYTES 1024 CACHE STRING "Define to specify how much context to retain around the current parse point") +mark_as_advanced(EXPAT_CONTEXT_BYTES) +expat_shy_set(EXPAT_DTD ON CACHE BOOL "Define to make parameter entity parsing functionality available") +mark_as_advanced(EXPAT_DTD) +expat_shy_set(EXPAT_NS ON CACHE BOOL "Define to make XML Namespaces functionality available") +mark_as_advanced(EXPAT_NS) +expat_shy_set(EXPAT_WARNINGS_AS_ERRORS OFF CACHE BOOL "Treat all compiler warnings as errors") +if(UNIX OR _EXPAT_HELP) + expat_shy_set(EXPAT_DEV_URANDOM ON CACHE BOOL "Define to include code reading entropy from `/dev/urandom'.") + expat_shy_set(EXPAT_WITH_GETRANDOM "AUTO" CACHE STRING "Make use of getrandom function (ON|OFF|AUTO) [default=AUTO]") + expat_shy_set(EXPAT_WITH_SYS_GETRANDOM "AUTO" CACHE STRING "Make use of syscall SYS_getrandom (ON|OFF|AUTO) [default=AUTO]") + mark_as_advanced(EXPAT_DEV_URANDOM) +endif() +expat_shy_set(EXPAT_CHAR_TYPE "char" CACHE STRING "Character type to use (char|ushort|wchar_t) [default=char]") +expat_shy_set(EXPAT_ATTR_INFO OFF CACHE BOOL "Define to allow retrieving the byte offsets for attribute names and values") +mark_as_advanced(EXPAT_ATTR_INFO) +expat_shy_set(EXPAT_LARGE_SIZE OFF CACHE BOOL "Make XML_GetCurrent* functions return <(unsigned) long long> rather than <(unsigned) long>") +mark_as_advanced(EXPAT_LARGE_SIZE) +expat_shy_set(EXPAT_MIN_SIZE OFF CACHE BOOL "Get a smaller (but slower) parser (in particular avoid multiple copies of the tokenizer)") +mark_as_advanced(EXPAT_MIN_SIZE) +if(MSVC OR _EXPAT_HELP) + expat_shy_set(EXPAT_MSVC_STATIC_CRT OFF CACHE BOOL "Use /MT flag (static CRT) when compiling in MSVC") +endif() +if(NOT _EXPAT_HELP) + expat_shy_set(_EXPAT_M32 OFF CACHE BOOL "(Unofficial!) Produce 32bit code with -m32") + mark_as_advanced(_EXPAT_M32) +endif() + +if(EXPAT_BUILD_TESTS) + # We have to call enable_language() before modifying any CMAKE_CXX_* variables + enable_language(CXX) +endif() + +# +# Environment checks +# +if(EXPAT_WITH_LIBBSD) + find_library(LIB_BSD NAMES bsd) + if(NOT LIB_BSD) + message(SEND_ERROR "EXPAT_WITH_LIBBSD option is enabled, but libbsd was not found") + else() + set(HAVE_LIBBSD TRUE) + endif() +endif() + +if(MSVC) + # For the three types of MSVC version values, please see: + # - https://cmake.org/cmake/help/latest/variable/MSVC_VERSION.html + # - https://sourceforge.net/p/predef/wiki/Compilers/ + # - https://en.wikipedia.org/wiki/Microsoft_Visual_Studio#History + set(_EXPAT_MSVC_REQUIRED_INT 1800) # i.e. 12.0/2013/1800; see PR #426 + set(_EXPAT_MSVC_SUPPORTED_INT 1910) + set(_EXPAT_MSVC_SUPPORTED_DISPLAY "Visual Studio 15.0/2017/${_EXPAT_MSVC_SUPPORTED_INT}") + + if(MSVC_VERSION VERSION_LESS ${_EXPAT_MSVC_SUPPORTED_INT}) + if(MSVC_VERSION VERSION_LESS ${_EXPAT_MSVC_REQUIRED_INT}) + message(SEND_ERROR "MSVC_VERSION ${MSVC_VERSION} is TOO OLD to compile Expat without errors.") + message(SEND_ERROR "Please use officially supported ${_EXPAT_MSVC_SUPPORTED_DISPLAY} or later. Thank you!") + else() + message(WARNING "MSVC_VERSION ${MSVC_VERSION} is NOT OFFICIALLY SUPPORTED by Expat.") + message(WARNING "Please use ${_EXPAT_MSVC_SUPPORTED_DISPLAY} or later. Thank you!") + endif() + endif() +endif() + +macro(_expat_copy_bool_int source_ref dest_ref) + if(${source_ref}) + set(${dest_ref} 1) + else() + set(${dest_ref} 0) + endif() +endmacro() + +if(EXPAT_LARGE_SIZE) + add_definitions(-DXML_LARGE_SIZE) +endif() + +if(EXPAT_MIN_SIZE) + add_definitions(-DXML_MIN_SIZE) +endif() + +if(EXPAT_CHAR_TYPE STREQUAL "char") + set(_EXPAT_UNICODE OFF) + set(_EXPAT_UNICODE_WCHAR_T OFF) +elseif(EXPAT_CHAR_TYPE STREQUAL "ushort") + set(_EXPAT_UNICODE ON) + set(_EXPAT_UNICODE_WCHAR_T OFF) + if(EXPAT_BUILD_EXAMPLES) + message(SEND_ERROR "Examples can not be built with option -DEXPAT_CHAR_TYPE=ushort. Please pass -DEXPAT_CHAR_TYPE=(char|wchar_t) or -DEXPAT_BUILD_EXAMPLES=OFF.") + endif() + if(EXPAT_BUILD_TESTS) + message(SEND_ERROR "The testsuite can not be built with option -DEXPAT_CHAR_TYPE=ushort. Please pass -DEXPAT_CHAR_TYPE=(char|wchar_t) or -DEXPAT_BUILD_TESTS=OFF.") + endif() + if(EXPAT_BUILD_TOOLS) + message(SEND_ERROR "The xmlwf tool can not be built with option -DEXPAT_CHAR_TYPE=ushort. Please pass -DEXPAT_CHAR_TYPE=(char|wchar_t) or -DEXPAT_BUILD_TOOLS=OFF.") + endif() +elseif(EXPAT_CHAR_TYPE STREQUAL "wchar_t") + set(_EXPAT_UNICODE ON) + set(_EXPAT_UNICODE_WCHAR_T ON) + if(NOT WIN32) + string(FIND "${CMAKE_C_FLAGS}" "-fshort-wchar" _expat_short_wchar_found) + if(${_expat_short_wchar_found} EQUAL "-1") + message(SEND_ERROR "Configuration -DEXPAT_CHAR_TYPE=wchar_t requires -DCMAKE_{C,CXX}_FLAGS=-fshort-wchar (which was not found) and libc compiled with -fshort-wchar, too.") + endif() + if (EXPAT_BUILD_TOOLS) + message(SEND_ERROR "The xmlwf tool can not be built with option -DEXPAT_CHAR_TYPE=wchar_t outside of Windows. Please pass -DEXPAT_CHAR_TYPE=char or -DEXPAT_BUILD_TOOLS=OFF.") + endif() + endif() +else() + message(SEND_ERROR "Option -DEXPAT_CHAR_TYPE=(char|ushort|wchar_t) cannot be \"${EXPAT_CHAR_TYPE}\".") +endif() + +if(_EXPAT_UNICODE) + add_definitions(-DXML_UNICODE) # for unsigned short + if(_EXPAT_UNICODE_WCHAR_T) + add_definitions(-DXML_UNICODE_WCHAR_T) # for wchar_t + endif() +endif() + +include(${CMAKE_CURRENT_LIST_DIR}/ConfigureChecks.cmake) + +macro(evaluate_detection_results use_ref have_ref thing_lower thing_title) + if(${use_ref} AND NOT (${use_ref} STREQUAL "AUTO") AND NOT ${have_ref}) + message(SEND_ERROR + "Use of ${thing_lower} was enforced by ${use_ref}=ON but it could not be found.") + elseif(NOT ${use_ref} AND ${have_ref}) + message("${thing_title} was found but it will not be used due to ${use_ref}=OFF.") + set(${have_ref} 0) + endif() +endmacro() + +if(NOT WIN32) + evaluate_detection_results(EXPAT_WITH_GETRANDOM HAVE_GETRANDOM "function getrandom" "Function getrandom") + evaluate_detection_results(EXPAT_WITH_SYS_GETRANDOM HAVE_SYSCALL_GETRANDOM "syscall SYS_getrandom" "Syscall SYS_getrandom") +endif() + +_expat_copy_bool_int(EXPAT_ATTR_INFO XML_ATTR_INFO) +_expat_copy_bool_int(EXPAT_DTD XML_DTD) +_expat_copy_bool_int(EXPAT_LARGE_SIZE XML_LARGE_SIZE) +_expat_copy_bool_int(EXPAT_MIN_SIZE XML_MIN_SIZE) +_expat_copy_bool_int(EXPAT_NS XML_NS) +if(NOT WIN32) + _expat_copy_bool_int(EXPAT_DEV_URANDOM XML_DEV_URANDOM) +endif() +set(XML_CONTEXT_BYTES ${EXPAT_CONTEXT_BYTES}) + +macro(expat_install) + if(EXPAT_ENABLE_INSTALL) + install(${ARGN}) + endif() +endmacro() + +configure_file(expat_config.h.cmake "${CMAKE_CURRENT_BINARY_DIR}/expat_config.h") +expat_install(FILES "${CMAKE_CURRENT_BINARY_DIR}/expat_config.h" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + + +set(EXTRA_COMPILE_FLAGS) +if(FLAG_NO_STRICT_ALIASING) + set(EXTRA_COMPILE_FLAGS "${EXTRA_COMPILE_FLAGS} -fno-strict-aliasing") +endif() +if(FLAG_VISIBILITY) + if(EXPAT_SHARED_LIBS) + add_definitions(-DXML_ENABLE_VISIBILITY=1) + endif() + set(EXTRA_COMPILE_FLAGS "${EXTRA_COMPILE_FLAGS} -fvisibility=hidden") +endif() +if(MINGW) + # Without __USE_MINGW_ANSI_STDIO the compiler produces a false positive + set(EXTRA_COMPILE_FLAGS "${EXTRA_COMPILE_FLAGS} -Wno-pedantic-ms-format") +endif() +if (EXPAT_WARNINGS_AS_ERRORS) + if(MSVC) + add_definitions(/WX) + else() + set(EXTRA_COMPILE_FLAGS "${EXTRA_COMPILE_FLAGS} -Werror") + endif() +endif() +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_COMPILE_FLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_COMPILE_FLAGS}") + +if (MSVC) + if (EXPAT_MSVC_STATIC_CRT) + message("-- Using static CRT ${EXPAT_MSVC_STATIC_CRT}") + foreach(flag_var + CMAKE_CXX_FLAGS_${_EXPAT_BUILD_TYPE_UPPER} + CMAKE_CXX_FLAGS_DEBUG + CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL + CMAKE_CXX_FLAGS_RELWITHDEBINFO + CMAKE_C_FLAGS_${_EXPAT_BUILD_TYPE_UPPER} + CMAKE_C_FLAGS_DEBUG + CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_MINSIZEREL + CMAKE_C_FLAGS_RELWITHDEBINFO + ) + string(REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") + endforeach() + endif() +endif() + +if(_EXPAT_M32 AND NOT MSVC) + foreach(flag_var + CMAKE_CXX_FLAGS_${_EXPAT_BUILD_TYPE_UPPER} + CMAKE_CXX_FLAGS_DEBUG + CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL + CMAKE_CXX_FLAGS_RELWITHDEBINFO + CMAKE_C_FLAGS_${_EXPAT_BUILD_TYPE_UPPER} + CMAKE_C_FLAGS_DEBUG + CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_MINSIZEREL + CMAKE_C_FLAGS_RELWITHDEBINFO + ) + set(${flag_var} "${${flag_var}} -m32") + endforeach() +endif() + +include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/lib) +if(MSVC) + add_definitions(-D_CRT_SECURE_NO_WARNINGS -wd4996) +endif() + +# +# C library +# +set(_EXPAT_C_SOURCES + lib/xmlparse.c + lib/xmlrole.c + lib/xmltok.c +# NOTE: ISO C forbids an empty translation unit +# lib/xmltok_impl.c +# lib/xmltok_ns.c +) + +if(EXPAT_SHARED_LIBS) + set(_SHARED SHARED) + if(WIN32) + macro(_expat_def_file_toggle source_var target_var) + if(${source_var}) + set(${target_var} " ") # i.e. not commented out, a single space + else() + set(${target_var} ";") # i.e. commented out + endif() + endmacro() + + _expat_def_file_toggle(EXPAT_DTD _EXPAT_COMMENT_DTD) + _expat_def_file_toggle(EXPAT_ATTR_INFO _EXPAT_COMMENT_ATTR_INFO) + + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/lib/libexpat.def.cmake" "${CMAKE_CURRENT_BINARY_DIR}/lib/libexpat.def") + set(_EXPAT_EXTRA_SOURCES ${_EXPAT_EXTRA_SOURCES} "${CMAKE_CURRENT_BINARY_DIR}/lib/libexpat.def") + + # Add DLL version + string(REPLACE "." "," _EXPAT_DLL_VERSION ${PROJECT_VERSION}.0) + set(_EXPAT_EXTRA_SOURCES ${_EXPAT_EXTRA_SOURCES} win32/version.rc) + endif() +else() + set(_SHARED STATIC) +endif() + +add_library(expat ${_SHARED} ${_EXPAT_C_SOURCES} ${_EXPAT_EXTRA_SOURCES}) +if(_EXPAT_LIBM_FOUND) + target_link_libraries(expat m) +endif() +if(EXPAT_WITH_LIBBSD) + target_link_libraries(expat ${LIB_BSD}) +endif() + +# +# Library filename postfix +# +if(_EXPAT_UNICODE) + set(_POSTFIX_WIDE "w") +endif() + +if(MSVC AND NOT EXPAT_SHARED_LIBS) + if(EXPAT_MSVC_STATIC_CRT) + set(_POSTFIX_CRT "MT") + else() + set(_POSTFIX_CRT "MD") + endif() +endif() + +foreach(build_type_upper + ${_EXPAT_BUILD_TYPE_UPPER} + DEBUG + RELEASE + MINSIZEREL + RELWITHDEBINFO + ) + if(WIN32 AND build_type_upper STREQUAL "DEBUG") + set(_POSTFIX_DEBUG "d") + else() + set(_POSTFIX_DEBUG "") # needs a reset because of being looped + endif() + + expat_shy_set(EXPAT_${build_type_upper}_POSTFIX "${_POSTFIX_WIDE}${_POSTFIX_DEBUG}${_POSTFIX_CRT}" CACHE STRING "Library filename postfix for build type ${build_type_upper}; yields filenames libexpat.(dll|dylib|lib|so)") + mark_as_advanced(EXPAT_${build_type_upper}_POSTFIX) + set_property(TARGET expat PROPERTY ${build_type_upper}_POSTFIX ${EXPAT_${build_type_upper}_POSTFIX}) +endforeach() + +set(LIBCURRENT 9) # sync +set(LIBREVISION 10) # with +set(LIBAGE 8) # configure.ac! +math(EXPR LIBCURRENT_MINUS_AGE "${LIBCURRENT} - ${LIBAGE}") + +if(NOT WIN32) + set_property(TARGET expat PROPERTY VERSION ${LIBCURRENT_MINUS_AGE}.${LIBAGE}.${LIBREVISION}) + set_property(TARGET expat PROPERTY SOVERSION ${LIBCURRENT_MINUS_AGE}) + set_property(TARGET expat PROPERTY NO_SONAME ${NO_SONAME}) + + if(APPLE) + if(NOT CMAKE_VERSION VERSION_GREATER_EQUAL 3.17) + message(FATAL_ERROR "Expat requires CMake >=3.17 on platform \"APPLE\".") + endif() + + # NOTE: This intends to talk CMake into compatiblity with GNU Libtool + math(EXPR _EXPAT_MACHO_COMPATIBILITY_VERSION "${LIBCURRENT} + 1") + set(_EXPAT_MACHO_CURRENT_VERSION "${_EXPAT_MACHO_COMPATIBILITY_VERSION}.${LIBREVISION}") + set_property(TARGET expat PROPERTY MACHO_COMPATIBILITY_VERSION ${_EXPAT_MACHO_COMPATIBILITY_VERSION}) + set_property(TARGET expat PROPERTY MACHO_CURRENT_VERSION ${_EXPAT_MACHO_CURRENT_VERSION}) + endif() +endif() + +if(MINGW AND EXPAT_SHARED_LIBS) + set_target_properties(expat PROPERTIES SUFFIX "-${LIBCURRENT_MINUS_AGE}.dll") +endif() + +if(WIN32 AND NOT MINGW) + # NOTE: This avoids a name collision with Expat.dll of Perl's XML::Parser::Expat + # on Windows by resorting to filename libexpat.dll since Expat 1.95.3. + # Everything but MSVC is already adding prefix "lib", automatically. + # NOTE: "set_property(TARGET expat PROPERTY PREFIX lib)" would only affect *.dll + # files but not *.lib files, so we have to rely on property OUTPUT_NAME, instead. + # Target property _POSTFIX still applies. + set(_EXPAT_OUTPUT_NAME libexpat) + set_property(TARGET expat PROPERTY OUTPUT_NAME ${_EXPAT_OUTPUT_NAME}) +else() + set(_EXPAT_OUTPUT_NAME expat) +endif() + +target_include_directories(expat + INTERFACE + $ + $ + $ +) + +if(WIN32 AND EXPAT_SHARED_LIBS) + target_compile_definitions(expat PRIVATE VER_FILEVERSION=${_EXPAT_DLL_VERSION}) +endif() + +expat_install(TARGETS expat EXPORT expat + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + +expat_install(FILES lib/expat.h lib/expat_external.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + +# +# pkg-config file +# +if(EXPAT_BUILD_PKGCONFIG) + if(CMAKE_INSTALL_LIBDIR MATCHES "^/") + set(_expat_pkgconfig_libdir "${CMAKE_INSTALL_LIBDIR}") + else() + set(_expat_pkgconfig_libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}") + endif() + + if(CMAKE_INSTALL_INCLUDEDIR MATCHES "^/") + set(_expat_pkgconfig_includedir "${CMAKE_INSTALL_INCLUDEDIR}") + else() + set(_expat_pkgconfig_includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") + endif() + + set_target_properties(expat PROPERTIES + pkgconfig_prefix "${CMAKE_INSTALL_PREFIX}" + pkgconfig_exec_prefix "\${prefix}" + pkgconfig_libdir "${_expat_pkgconfig_libdir}" + pkgconfig_includedir "${_expat_pkgconfig_includedir}" + pkgconfig_version "${PACKAGE_VERSION}") + + foreach(_build_type ${CMAKE_BUILD_TYPE} Debug Release RelWithDebInfo MinSizeRel) + string(TOLOWER "${_build_type}" _build_type_lower) + string(TOUPPER "${_build_type}" _build_type_upper) + set_property(TARGET expat PROPERTY "pkgconfig_${_build_type_lower}_name" "expat${EXPAT_${_build_type_upper}_POSTFIX}") + set_property(TARGET expat PROPERTY "pkgconfig_${_build_type_lower}_output_name" "${_EXPAT_OUTPUT_NAME}${EXPAT_${_build_type_upper}_POSTFIX}") + if(_EXPAT_LIBM_FOUND) + set_property(TARGET expat PROPERTY "pkgconfig_libm" "-lm") + else() + set_property(TARGET expat PROPERTY "pkgconfig_libm" "") + endif() + endforeach() + + file(GENERATE + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/$/expat.pc + INPUT ${PROJECT_SOURCE_DIR}/expat.pc.cmake) + + expat_install(FILES ${CMAKE_CURRENT_BINARY_DIR}/$/expat.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) +endif() + +# +# C command line tool xmlwf +# +if(EXPAT_BUILD_TOOLS) + set(xmlwf_SRCS + xmlwf/xmlwf.c + xmlwf/xmlfile.c + xmlwf/codepage.c + xmlwf/readfilemap.c + ) + + add_executable(xmlwf ${xmlwf_SRCS}) + set_property(TARGET xmlwf PROPERTY RUNTIME_OUTPUT_DIRECTORY xmlwf) + target_link_libraries(xmlwf expat) + if(_EXPAT_LIBM_FOUND) + target_link_libraries(xmlwf m) + endif() + expat_install(TARGETS xmlwf DESTINATION ${CMAKE_INSTALL_BINDIR}) + + if(MINGW AND _EXPAT_UNICODE_WCHAR_T) + # https://gcc.gnu.org/onlinedocs/gcc/x86-Windows-Options.html + set_target_properties(xmlwf PROPERTIES LINK_FLAGS -municode) + endif() + + if(EXPAT_BUILD_DOCS) + file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/doc) + add_custom_target( + xmlwf-manpage + COMMAND + "${DOCBOOK_TO_MAN}" "${PROJECT_SOURCE_DIR}/doc/xmlwf.xml" && mv "XMLWF.1" "${PROJECT_BINARY_DIR}/doc/xmlwf.1" + BYPRODUCTS + doc/xmlwf.1) + add_dependencies(expat xmlwf-manpage) + expat_install(FILES "${PROJECT_BINARY_DIR}/doc/xmlwf.1" DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) + elseif(EXISTS ${PROJECT_SOURCE_DIR}/doc/xmlwf.1) + expat_install(FILES "${PROJECT_SOURCE_DIR}/doc/xmlwf.1" DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) + endif() +endif() + +# +# C code examples +# +if(EXPAT_BUILD_EXAMPLES) + add_executable(elements examples/elements.c) + add_executable(outline examples/outline.c) + + foreach(_target elements outline) + set_property(TARGET ${_target} PROPERTY RUNTIME_OUTPUT_DIRECTORY examples) + target_link_libraries(${_target} expat) + endforeach() +endif() + +# +# C/C++ test runners +# +if(EXPAT_BUILD_TESTS) + ## these are unittests that can be run on any platform + enable_testing() + + set(test_SRCS + tests/chardata.c + tests/memcheck.c + tests/minicheck.c + tests/structdata.c + ${_EXPAT_C_SOURCES} + ) + + if(NOT MSVC) + if(MINGW) + set(host whatever-mingw32) # for nothing but run.sh + endif() + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/run.sh.in run.sh @ONLY) + endif() + + function(expat_add_test _name _file) + if(MSVC) + add_test(NAME ${_name} COMMAND ${_file}) + else() + add_test(NAME ${_name} COMMAND bash run.sh ${_file}) + endif() + endfunction() + + set(_EXPAT_TEST_TARGETS runtests runtestspp) + add_executable(runtests tests/runtests.c ${test_SRCS}) + add_executable(runtestspp tests/runtestspp.cpp ${test_SRCS}) + + foreach(_target ${_EXPAT_TEST_TARGETS}) + set_property(TARGET ${_target} PROPERTY RUNTIME_OUTPUT_DIRECTORY tests) + expat_add_test(${_target} $) + + if(_EXPAT_LIBM_FOUND) + target_link_libraries(${_target} m) + endif() + + if(EXPAT_WITH_LIBBSD) + target_link_libraries(${_target} ${LIB_BSD}) + endif() + endforeach() +endif() + +# +# Fuzzers +# +if(EXPAT_BUILD_FUZZERS) + if(NOT "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") + message(SEND_ERROR + "Building fuzz targets without Clang (but ${CMAKE_C_COMPILER_ID}) " + "is not supported. Please set " + "-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++.") + endif() + + string(FIND "${CMAKE_C_FLAGS}" "-fsanitize" sanitizer_present) + if(${sanitizer_present} EQUAL "-1") + message(WARNING + "There was no sanitizer present when building the fuzz targets. " + "This is likely in error - consider adding " + "-DCMAKE_C_FLAGS='-fsanitize=' and " + "-DCMAKE_CXX_FLAGS='-fsanitize=' and " + "-DCMAKE_EXE_LINKER_FLAGS='-fsanitize=' and " + "-DCMAKE_MODULE_LINKER_FLAGS='-fsanitize=' and " + "-DCMAKE_SHARED_LINKER_FLAGS='-fsanitize=' to your cmake " + "execution.") + endif() + + if(EXPAT_OSSFUZZ_BUILD AND NOT DEFINED ENV{LIB_FUZZING_ENGINE}) + message(SEND_ERROR + "OSS-Fuzz builds require the environment variable " + "LIB_FUZZING_ENGINE to be set. If you are seeing this " + "warning, it points to a deeper problem in the ossfuzz " + "build setup.") + endif() + + set(encoding_types UTF-16 UTF-8 ISO-8859-1 US-ASCII UTF-16BE UTF-16LE) + set(fuzz_targets xml_parse_fuzzer xml_parsebuffer_fuzzer) + + add_library(fuzzpat STATIC ${_EXPAT_C_SOURCES}) + if(NOT EXPAT_OSSFUZZ_BUILD) + target_compile_options(fuzzpat PRIVATE -fsanitize=fuzzer-no-link) + endif() + + foreach(fuzz_target ${fuzz_targets}) + foreach(encoding_type ${encoding_types}) + set(target_name ${fuzz_target}_${encoding_type}) + add_executable(${target_name} fuzz/${fuzz_target}.c) + target_link_libraries(${target_name} fuzzpat) + target_compile_definitions(${target_name} + PRIVATE ENCODING_FOR_FUZZING=${encoding_type}) + if(NOT EXPAT_OSSFUZZ_BUILD) + target_compile_options(${target_name} PRIVATE -fsanitize=fuzzer-no-link) + endif() + # NOTE: Avoiding target_link_options here only because it needs CMake >=3.13 + if(EXPAT_OSSFUZZ_BUILD) + set_target_properties(${target_name} PROPERTIES LINK_FLAGS $ENV{LIB_FUZZING_ENGINE}) + set_target_properties(${target_name} PROPERTIES LINKER_LANGUAGE "CXX") + else() + set_target_properties(${target_name} PROPERTIES LINK_FLAGS -fsanitize=fuzzer) + endif() + set_property( + TARGET ${target_name} PROPERTY RUNTIME_OUTPUT_DIRECTORY fuzz) + endforeach() + endforeach() +else() + if(EXPAT_OSSFUZZ_BUILD) + message(SEND_ERROR + "Attempting to perform an ossfuzz build without turning on the fuzzer build. " + "This is likely in error - consider adding " + "-DEXPAT_BUILD_FUZZERS=ON to your cmake execution.") + endif() +endif() + +# +# C/C++ config affecting multiple targets +# +if(WIN32) + set(_EXPAT_STATIC_TARGETS ${_EXPAT_TEST_TARGETS}) + if(NOT EXPAT_SHARED_LIBS) + list(APPEND _EXPAT_STATIC_TARGETS expat) + endif() + if(EXPAT_BUILD_FUZZERS) + list(APPEND _EXPAT_STATIC_TARGETS fuzzpat) + endif() + + foreach(_target ${_EXPAT_STATIC_TARGETS}) + target_compile_definitions(${_target} PUBLIC -DXML_STATIC) + endforeach() +endif() + +# +# Custom target "run-xmltest" +# +if(EXPAT_BUILD_TOOLS AND NOT MSVC) + add_custom_target( + xmlts-zip-downloaded + COMMAND + sh -c 'test -f xmlts.zip || wget --output-document=xmlts.zip https://www.w3.org/XML/Test/xmlts20080827.zip' + BYPRODUCTS + tests/xmlts.zip + WORKING_DIRECTORY + tests/) + + add_custom_target( + xmlts-zip-extracted + COMMAND + sh -c 'test -d xmlconf || unzip -q xmlts.zip' + BYPRODUCTS + tests/xmlconf + WORKING_DIRECTORY + tests/) + add_dependencies(xmlts-zip-extracted xmlts-zip-downloaded) + + add_custom_target( + xmltest-sh-been-run + COMMAND + sh -c '${CMAKE_CURRENT_SOURCE_DIR}/tests/xmltest.sh "bash ${CMAKE_CURRENT_BINARY_DIR}/run.sh $" 2>&1 | tee tests/xmltest.log' + BYPRODUCTS + tests/xmltest.log) + add_dependencies(xmltest-sh-been-run xmlts-zip-extracted xmlwf) + + add_custom_target( + xmltest-log-fixed + COMMAND + ${CMAKE_CURRENT_SOURCE_DIR}/fix-xmltest-log.sh tests/xmltest.log + DEPENDS + tests/xmltest.log) + add_dependencies(xmltest-log-fixed xmltest-sh-been-run) + + add_custom_target( + xmltest-log-verified + COMMAND + diff -u ${CMAKE_CURRENT_SOURCE_DIR}/tests/xmltest.log.expected tests/xmltest.log) + add_dependencies(xmltest-log-verified xmltest-log-fixed) + + add_custom_target(run-xmltest) + add_dependencies(run-xmltest xmltest-log-verified) +endif() + +# +# Documentation +# +configure_file(Changes changelog COPYONLY) +expat_install( + FILES + AUTHORS + ${CMAKE_CURRENT_BINARY_DIR}/changelog + DESTINATION + ${CMAKE_INSTALL_DOCDIR}) + +# +# CMake files for find_package(expat [..] CONFIG [..]) +# +configure_package_config_file( + cmake/expat-config.cmake.in + cmake/expat-config.cmake + INSTALL_DESTINATION + ${CMAKE_INSTALL_LIBDIR}/cmake/expat-${PROJECT_VERSION}/ +) +write_basic_package_version_file( + cmake/expat-config-version.cmake + COMPATIBILITY SameMajorVersion # i.e. semver +) +export( + TARGETS + expat + FILE + cmake/expat-targets.cmake # not going to be installed +) +expat_install( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/cmake/expat-config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/cmake/expat-config-version.cmake + DESTINATION + ${CMAKE_INSTALL_LIBDIR}/cmake/expat-${PROJECT_VERSION}/ +) +expat_install( + EXPORT + expat + DESTINATION + ${CMAKE_INSTALL_LIBDIR}/cmake/expat-${PROJECT_VERSION}/ + NAMESPACE + expat:: +) + +# +# CPack +# + +# This effectively disables target "package_source". +# That is done due to CPack's unfortunate choice of an exclusion list +# rather than inclusion list. An exclusion list does not protect against +# unwanted files ending up in the resulting archive in a way that's +# safe to run from an Expat developer's machine. +set(CPACK_SOURCE_GENERATOR '') + +if(WIN32) + set(CPACK_GENERATOR ZIP) +else() + set(CPACK_GENERATOR TGZ) +endif() + +include(CPack) + +# +# Summary +# +if(EXPAT_CHAR_TYPE STREQUAL "char") + set(_EXPAT_CHAR_TYPE_SUMMARY "char (UTF-8)") +elseif(EXPAT_CHAR_TYPE STREQUAL "ushort") + set(_EXPAT_CHAR_TYPE_SUMMARY "ushort (unsigned short, UTF-16)") +elseif(EXPAT_CHAR_TYPE STREQUAL "wchar_t") + if(WIN32) + set(_EXPAT_CHAR_TYPE_SUMMARY "wchar_t (UTF-16)") + else() + set(_EXPAT_CHAR_TYPE_SUMMARY "wchar_t (UTF-32) // not implemented") + endif() +else() + set(_EXPAT_CHAR_TYPE_SUMMARY "ERROR") +endif() +# NOTE: We're not accessing global property GENERATOR_IS_MULTI_CONFIG +# because that would require CMake >=3.9 +if(CMAKE_CONFIGURATION_TYPES) + set(_EXPAT_GENERATOR_IS_MULTI_CONFIG TRUE) +else() + set(_EXPAT_GENERATOR_IS_MULTI_CONFIG FALSE) +endif() + +message(STATUS "===========================================================================") +message(STATUS "") +message(STATUS "Configuration") +message(STATUS " Generator .................. ${CMAKE_GENERATOR}") +if(_EXPAT_GENERATOR_IS_MULTI_CONFIG) + message(STATUS " Build types ................ ${CMAKE_CONFIGURATION_TYPES}") +else() + message(STATUS " Build type ................. ${CMAKE_BUILD_TYPE}") +endif() +message(STATUS " Prefix ..................... ${CMAKE_INSTALL_PREFIX}") +message(STATUS " Shared libraries ........... ${EXPAT_SHARED_LIBS}") +if(MSVC) + message(STATUS " Static CRT ................. ${EXPAT_MSVC_STATIC_CRT}") +endif() +message(STATUS " Character type ............. ${_EXPAT_CHAR_TYPE_SUMMARY}") +if(NOT _EXPAT_GENERATOR_IS_MULTI_CONFIG) + message(STATUS " Library name postfix ....... ${EXPAT_${_EXPAT_BUILD_TYPE_UPPER}_POSTFIX}") +endif() +message(STATUS "") +message(STATUS " Build documentation ........ ${EXPAT_BUILD_DOCS}") +message(STATUS " Build examples ............. ${EXPAT_BUILD_EXAMPLES}") +message(STATUS " Build fuzzers .............. ${EXPAT_BUILD_FUZZERS}") +message(STATUS " Build tests ................ ${EXPAT_BUILD_TESTS}") +message(STATUS " Build tools (xmlwf) ........ ${EXPAT_BUILD_TOOLS}") +message(STATUS " Build pkg-config file ...... ${EXPAT_BUILD_PKGCONFIG}") +message(STATUS " Install files .............. ${EXPAT_ENABLE_INSTALL}") +message(STATUS "") +message(STATUS " Features") +message(STATUS " // Advanced options, changes not advised") +message(STATUS " Attributes info .......... ${EXPAT_ATTR_INFO}") +message(STATUS " Context bytes ............ ${EXPAT_CONTEXT_BYTES}") +message(STATUS " DTD support .............. ${EXPAT_DTD}") +message(STATUS " Large size ............... ${EXPAT_LARGE_SIZE}") +message(STATUS " Minimum size ............. ${EXPAT_MIN_SIZE}") +message(STATUS " Namespace support ........ ${EXPAT_NS}") +message(STATUS "") +message(STATUS " Entropy sources") +if(WIN32) + message(STATUS " rand_s ................... ON") +else() + message(STATUS " getrandom ................ ${HAVE_GETRANDOM}") + message(STATUS " syscall SYS_getrandom .... ${HAVE_SYSCALL_GETRANDOM}") + message(STATUS " libbsd ................... ${EXPAT_WITH_LIBBSD}") + message(STATUS " /dev/random .............. ${EXPAT_DEV_URANDOM}") +endif() +message(STATUS "") +if(CMAKE_GENERATOR STREQUAL "Unix Makefiles") + message(STATUS "Continue with") + message(STATUS " make") + if(EXPAT_BUILD_TESTS) + message(STATUS " make test") + endif() + if(EXPAT_ENABLE_INSTALL) + message(STATUS " sudo make install") + endif() + message(STATUS "") +endif() +message(STATUS "===========================================================================") diff --git a/cmake/SuperBuild/pystring-patch/CMakeLists.txt b/cmake/SuperBuild/pystring-patch/CMakeLists.txt new file mode 100644 index 0000000..b2864e0 --- /dev/null +++ b/cmake/SuperBuild/pystring-patch/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.5) +project(pystring CXX) + +#set(BUILD_SHARED_LIBS YES) + +add_library(pystring + pystring.cpp + pystring.h +) + +add_executable (pystring_test test.cpp) +TARGET_LINK_LIBRARIES (pystring_test pystring) + +enable_testing() +add_test(NAME PyStringTest COMMAND pystring_test) + +include(GNUInstallDirs) + +install(TARGETS pystring + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + diff --git a/cmake/SuperBuild/yaml-cpp-patch/CMakeLists.txt b/cmake/SuperBuild/yaml-cpp-patch/CMakeLists.txt new file mode 100644 index 0000000..078e18e --- /dev/null +++ b/cmake/SuperBuild/yaml-cpp-patch/CMakeLists.txt @@ -0,0 +1,174 @@ +# 3.5 is actually available almost everywhere, but this a good minimum +cmake_minimum_required(VERSION 3.5) + +# enable MSVC_RUNTIME_LIBRARY target property +# see https://cmake.org/cmake/help/latest/policy/CMP0091.html +if(POLICY CMP0091) + cmake_policy(SET CMP0091 NEW) +endif() + +project(YAML_CPP VERSION 0.7.0 LANGUAGES CXX) + +include(CMakePackageConfigHelpers) +include(CMakeDependentOption) +include(CheckCXXCompilerFlag) +include(GNUInstallDirs) +include(CTest) + +find_program(YAML_CPP_CLANG_FORMAT_EXE NAMES clang-format) + +option(YAML_CPP_BUILD_CONTRIB "Enable yaml-cpp contrib in library" ON) +option(YAML_CPP_BUILD_TOOLS "Enable parse tools" ON) +option(YAML_BUILD_SHARED_LIBS "Build yaml-cpp shared library" ${BUILD_SHARED_LIBS}) + +cmake_dependent_option(YAML_CPP_BUILD_TESTS + "Enable yaml-cpp tests" ON + "BUILD_TESTING;CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR" OFF) +cmake_dependent_option(YAML_CPP_INSTALL + "Enable generation of yaml-cpp install targets" ON + "CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR" OFF) +cmake_dependent_option(YAML_MSVC_SHARED_RT + "MSVC: Build yaml-cpp with shared runtime libs (/MD)" ON + "MSVC" OFF) + +set(yaml-cpp-type STATIC) +set(yaml-cpp-label-postfix "static") +if (YAML_BUILD_SHARED_LIBS) + set(yaml-cpp-type SHARED) + set(yaml-cpp-label-postfix "shared") +endif() + +set(build-shared $) +set(build-windows-dll $,${build-shared}>) +set(not-msvc $>) +set(msvc-shared_rt $) + +if (NOT DEFINED CMAKE_MSVC_RUNTIME_LIBRARY) + set(CMAKE_MSVC_RUNTIME_LIBRARY + MultiThreaded$<$:Debug>$<${msvc-shared_rt}:DLL>) +endif() + +set(contrib-pattern "src/contrib/*.cpp") +set(src-pattern "src/*.cpp") +if (CMAKE_VERSION VERSION_GREATER 3.12) + list(INSERT contrib-pattern 0 CONFIGURE_DEPENDS) + list(INSERT src-pattern 0 CONFIGURE_DEPENDS) +endif() + +file(GLOB yaml-cpp-contrib-sources ${contrib-pattern}) +file(GLOB yaml-cpp-sources ${src-pattern}) + +set(msvc-rt $) + +set(msvc-rt-mtd-static $) +set(msvc-rt-mt-static $) + +set(msvc-rt-mtd-dll $) +set(msvc-rt-mt-dll $) + +set(backport-msvc-runtime $) + +add_library(yaml-cpp ${yaml-cpp-type} "") +add_library(yaml-cpp::yaml-cpp ALIAS yaml-cpp) + +set_property(TARGET yaml-cpp + PROPERTY + MSVC_RUNTIME_LIBRARY ${CMAKE_MSVC_RUNTIME_LIBRARY}) +set_property(TARGET yaml-cpp + PROPERTY + CXX_STANDARD_REQUIRED ON) + +target_include_directories(yaml-cpp + PUBLIC + $ + $ + PRIVATE + $) + +if (NOT DEFINED CMAKE_CXX_STANDARD) + set_target_properties(yaml-cpp + PROPERTIES + CXX_STANDARD 11) +endif() + +target_compile_options(yaml-cpp + PRIVATE + $<${not-msvc}:-Wall -Wextra -Wshadow -Weffc++ -Wno-long-long> + $<${not-msvc}:-pedantic -pedantic-errors> + + $<$:-MTd> + $<$:-MT> + $<$:-MDd> + $<$:-MD> + + # /wd4127 = disable warning C4127 "conditional expression is constant" + # http://msdn.microsoft.com/en-us/library/6t66728h.aspx + # /wd4355 = disable warning C4355 "'this' : used in base member initializer list + # http://msdn.microsoft.com/en-us/library/3c594ae3.aspx + $<$:/W3 /wd4127 /wd4355>) + +target_compile_definitions(yaml-cpp + PRIVATE + $<${build-windows-dll}:${PROJECT_NAME}_DLL> + $<$>:YAML_CPP_NO_CONTRIB>) + +target_sources(yaml-cpp + PRIVATE + $<$:${yaml-cpp-contrib-sources}> + ${yaml-cpp-sources}) + +if (NOT DEFINED CMAKE_DEBUG_POSTFIX) + set(CMAKE_DEBUG_POSTFIX "d") +endif() + +set_target_properties(yaml-cpp PROPERTIES + VERSION "${PROJECT_VERSION}" + SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}" + PROJECT_LABEL "yaml-cpp ${yaml-cpp-label-postfix}" + DEBUG_POSTFIX "${CMAKE_DEBUG_POSTFIX}") + +configure_package_config_file( + "${PROJECT_SOURCE_DIR}/yaml-cpp-config.cmake.in" + "${PROJECT_BINARY_DIR}/yaml-cpp-config.cmake" + INSTALL_DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/yaml-cpp") + +write_basic_package_version_file( + "${PROJECT_BINARY_DIR}/yaml-cpp-config-version.cmake" + COMPATIBILITY AnyNewerVersion) + +configure_file(yaml-cpp.pc.in yaml-cpp.pc @ONLY) + +if (YAML_CPP_INSTALL) + install(TARGETS yaml-cpp + EXPORT yaml-cpp-targets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + FILES_MATCHING PATTERN "*.h") + install(EXPORT yaml-cpp-targets + DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/yaml-cpp") + install(FILES + "${PROJECT_BINARY_DIR}/yaml-cpp-config.cmake" + "${PROJECT_BINARY_DIR}/yaml-cpp-config-version.cmake" + DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/yaml-cpp") + install(FILES "${PROJECT_BINARY_DIR}/yaml-cpp.pc" + DESTINATION ${CMAKE_INSTALL_DATADIR}/pkgconfig) +endif() + +if(YAML_CPP_BUILD_TESTS) + add_subdirectory(test) +endif() + +if(YAML_CPP_BUILD_TOOLS) + add_subdirectory(util) +endif() + +if (YAML_CPP_CLANG_FORMAT_EXE) + add_custom_target(format + COMMAND clang-format --style=file -i $ + COMMAND_EXPAND_LISTS + COMMENT "Running clang-format" + VERBATIM) +endif() From 96d9c250e4504cbaa67fb78bc8085ef76cde59de Mon Sep 17 00:00:00 2001 From: Darby Johnston Date: Tue, 16 Sep 2025 10:14:13 -0700 Subject: [PATCH 15/24] Revert update Signed-off-by: Darby Johnston --- cmake/SuperBuild/Buildpystring.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/SuperBuild/Buildpystring.cmake b/cmake/SuperBuild/Buildpystring.cmake index c7c21a3..ad48e25 100644 --- a/cmake/SuperBuild/Buildpystring.cmake +++ b/cmake/SuperBuild/Buildpystring.cmake @@ -1,7 +1,7 @@ include(ExternalProject) set(pystring_GIT_REPOSITORY "https://github.com/imageworks/pystring.git") -set(pystring_GIT_TAG "v1.1.4") +set(pystring_GIT_TAG "v1.1.3") set(pystring_ARGS ${toucan_EXTERNAL_PROJECT_ARGS}) From 36ba4988c9541d5f1b95e46a7a34f4c2435242a9 Mon Sep 17 00:00:00 2001 From: Darby Johnston Date: Tue, 16 Sep 2025 10:54:23 -0700 Subject: [PATCH 16/24] Debugging Signed-off-by: Darby Johnston --- cmake/SuperBuild/Buildexpat.cmake | 6 +- cmake/SuperBuild/Buildpystring.cmake | 6 +- cmake/SuperBuild/Buildyaml-cpp.cmake | 6 +- .../expat-patch/expat/CMakeLists.txt | 921 ------------------ .../SuperBuild/pystring-patch/CMakeLists.txt | 8 +- .../SuperBuild/yaml-cpp-patch/CMakeLists.txt | 174 ---- 6 files changed, 13 insertions(+), 1108 deletions(-) delete mode 100644 cmake/SuperBuild/expat-patch/expat/CMakeLists.txt delete mode 100644 cmake/SuperBuild/yaml-cpp-patch/CMakeLists.txt diff --git a/cmake/SuperBuild/Buildexpat.cmake b/cmake/SuperBuild/Buildexpat.cmake index 45837bc..7fdb743 100644 --- a/cmake/SuperBuild/Buildexpat.cmake +++ b/cmake/SuperBuild/Buildexpat.cmake @@ -7,16 +7,14 @@ set(expat_ARGS ${toucan_EXTERNAL_PROJECT_ARGS} -DEXPAT_BUILD_TOOLS=OFF -DEXPAT_BUILD_EXAMPLES=OFF - -DEXPAT_BUILD_TESTS=OFF) + -DEXPAT_BUILD_TESTS=OFF + -DCMAKE_POLICY_VERSION_MINIMUM=3.5) ExternalProject_Add( expat PREFIX ${CMAKE_CURRENT_BINARY_DIR}/expat GIT_REPOSITORY ${expat_GIT_REPOSITORY} GIT_TAG ${expat_GIT_TAG} - PATCH_COMMAND ${CMAKE_COMMAND} -E copy_if_different - ${CMAKE_CURRENT_SOURCE_DIR}/expat-patch/expat/CMakeLists.txt - ${CMAKE_CURRENT_BINARY_DIR}/expat/src/expat/expat/CMakeLists.txt SOURCE_SUBDIR expat LIST_SEPARATOR | CMAKE_ARGS ${expat_ARGS}) diff --git a/cmake/SuperBuild/Buildpystring.cmake b/cmake/SuperBuild/Buildpystring.cmake index ad48e25..0a2b4d0 100644 --- a/cmake/SuperBuild/Buildpystring.cmake +++ b/cmake/SuperBuild/Buildpystring.cmake @@ -1,9 +1,11 @@ include(ExternalProject) set(pystring_GIT_REPOSITORY "https://github.com/imageworks/pystring.git") -set(pystring_GIT_TAG "v1.1.3") +set(pystring_GIT_TAG "v1.1.4") -set(pystring_ARGS ${toucan_EXTERNAL_PROJECT_ARGS}) +set(pystring_ARGS + -DCMAKE_POLICY_VERSION_MINIMUM=3.5 + ${toucan_EXTERNAL_PROJECT_ARGS}) ExternalProject_Add( pystring diff --git a/cmake/SuperBuild/Buildyaml-cpp.cmake b/cmake/SuperBuild/Buildyaml-cpp.cmake index 933011b..718f8b1 100644 --- a/cmake/SuperBuild/Buildyaml-cpp.cmake +++ b/cmake/SuperBuild/Buildyaml-cpp.cmake @@ -7,15 +7,13 @@ set(yaml-cpp_ARGS ${toucan_EXTERNAL_PROJECT_ARGS} -DYAML_CPP_BUILD_CONTRIB=OFF -DYAML_CPP_BUILD_TOOLS=OFF - -DYAML_CPP_BUILD_TESTS=OFF) + -DYAML_CPP_BUILD_TESTS=OFF + -DCMAKE_POLICY_VERSION_MINIMUM=3.5) ExternalProject_Add( yaml-cpp PREFIX ${CMAKE_CURRENT_BINARY_DIR}/yaml-cpp GIT_REPOSITORY ${yaml-cpp_GIT_REPOSITORY} GIT_TAG ${yaml-cpp_GIT_TAG} - PATCH_COMMAND ${CMAKE_COMMAND} -E copy_if_different - ${CMAKE_CURRENT_SOURCE_DIR}/yaml-cpp-patch/CMakeLists.txt - ${CMAKE_CURRENT_BINARY_DIR}/yaml-cpp/src/yaml-cpp/CMakeLists.txt LIST_SEPARATOR | CMAKE_ARGS ${yaml-cpp_ARGS}) diff --git a/cmake/SuperBuild/expat-patch/expat/CMakeLists.txt b/cmake/SuperBuild/expat-patch/expat/CMakeLists.txt deleted file mode 100644 index a9b383c..0000000 --- a/cmake/SuperBuild/expat-patch/expat/CMakeLists.txt +++ /dev/null @@ -1,921 +0,0 @@ -# __ __ _ -# ___\ \/ /_ __ __ _| |_ -# / _ \\ /| '_ \ / _` | __| -# | __// \| |_) | (_| | |_ -# \___/_/\_\ .__/ \__,_|\__| -# |_| XML parser -# -# Copyright (c) 2010 Patrick Spendrin -# Copyright (c) 2012 Karl Waclawek -# Copyright (c) 2016-2022 Sebastian Pipping -# Copyright (c) 2016 Sergei Nikulov -# Copyright (c) 2016 Björn Lindahl -# Copyright (c) 2016 Tobias Taschner -# Copyright (c) 2016 Ben Boeckel -# Copyright (c) 2017 Rhodri James -# Copyright (c) 2017 Rolf Eike Beer -# Copyright (c) 2017 Stephen Groat -# Copyright (c) 2017 Franek Korta -# Copyright (c) 2018 pedro-vicente -# Copyright (c) 2018 Frank Rast -# Copyright (c) 2018 userwithuid -# Copyright (c) 2018 Yury Gribov -# Copyright (c) 2019 Kishore Kunche -# Copyright (c) 2019 xantares -# Copyright (c) 2019 Mohammed Khajapasha -# Copyright (c) 2019 David Loffredo -# Copyright (c) 2019 Bhargava Shastry -# Copyright (c) 2020 Maciej Sroczyński -# Copyright (c) 2020 Gulliver -# Copyright (c) 2020 Thomas Beutlich -# Copyright (c) 2021 Alex Richardson -# Copyright (c) 2022 Johnny Jazeix -# Copyright (c) 2022 David Faure -# Unlike most of Expat, -# this file is copyrighted under the BSD-license for buildsystem files of KDE. - -cmake_minimum_required(VERSION 3.5) - -project(expat - VERSION - 2.5.0 - LANGUAGES - C -) - -set(PACKAGE_BUGREPORT "expat-bugs@libexpat.org") -set(PACKAGE_NAME "expat") -set(PACKAGE_VERSION "${PROJECT_VERSION}") -set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}") -set(PACKAGE_TARNAME "${PACKAGE_NAME}") - -include(CMakePackageConfigHelpers) -include(GNUInstallDirs) - -# -# Configuration defaults -# -if(WINCE) - set(_EXPAT_BUILD_TOOLS_DEFAULT OFF) -else() - set(_EXPAT_BUILD_TOOLS_DEFAULT ON) -endif() -if(MSVC OR NOT _EXPAT_BUILD_TOOLS_DEFAULT) - set(_EXPAT_BUILD_DOCS_DEFAULT OFF) -else() - find_program(DOCBOOK_TO_MAN NAMES docbook2x-man db2x_docbook2man docbook2man docbook-to-man) - if(DOCBOOK_TO_MAN) - set(_EXPAT_BUILD_DOCS_DEFAULT ON) - else() - set(_EXPAT_BUILD_DOCS_DEFAULT OFF) - endif() -endif() -if(MSVC) - set(_EXPAT_BUILD_PKGCONFIG_DEFAULT OFF) -else() - set(_EXPAT_BUILD_PKGCONFIG_DEFAULT ON) -endif() -if(DEFINED BUILD_SHARED_LIBS) - set(_EXPAT_SHARED_LIBS_DEFAULT ${BUILD_SHARED_LIBS}) -else() - set(_EXPAT_SHARED_LIBS_DEFAULT ON) -endif() -if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE NoConfig) # so that accessing EXPAT_*_POSTFIX will be waterproof -endif() -string(TOUPPER "${CMAKE_BUILD_TYPE}" _EXPAT_BUILD_TYPE_UPPER) - -# -# Configuration -# - -macro(expat_shy_set var default cache type desc) - # Macro expat_shy_set came into life because: - # - Expat was previously using an inconsistent mix of CMake's native set() - # and option() to define public build time options. - # - option() is more friendly than set() with regard to configuring an - # external project that is pulled in by means of add_subdirectory() -- - # see comments in issue #597 -- so we wanted to get away from set(). - # - option() auto-converts non-bool values to bool when writing to the CMake - # cache, so we needed something that supports non-bool better and hence - # wanted to get away from plain option(), too. - # - # As a result, this function serves as a hybrid between CMake's regular set() - # and option(): from set() it takes support for non-bool types and the function - # name and signature whereas from option() (with policy CMP0077 mode NEW) it - # takes being shy when a value has previously been defined for that variable. - # - # So that resolves all need for set(.. FORCE) when pulling in Expat by means of - # add_subdirectory(). - # - if(NOT ${cache} STREQUAL "CACHE") - message(SEND_ERROR "Macro usage is: expat_shy_set(var default CACHE type desc)") - endif() - - if(DEFINED ${var}) - # NOTE: The idea is to (ideally) only add to the cache if - # there is no cache entry, yet. "if(DEFINED CACHE{var})" - # requires CMake >=3.14. - if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.14" AND NOT DEFINED "CACHE{${var}}") - set("${var}" "${${var}}" CACHE "${type}" "${desc}") - endif() - else() - set("${var}" "${default}" CACHE "${type}" "${desc}") - endif() -endmacro() - -expat_shy_set(EXPAT_BUILD_TOOLS ${_EXPAT_BUILD_TOOLS_DEFAULT} CACHE BOOL "Build the xmlwf tool for expat library") -expat_shy_set(EXPAT_BUILD_EXAMPLES ON CACHE BOOL "Build the examples for expat library") -expat_shy_set(EXPAT_BUILD_TESTS ON CACHE BOOL "Build the tests for expat library") -expat_shy_set(EXPAT_SHARED_LIBS ${_EXPAT_SHARED_LIBS_DEFAULT} CACHE BOOL "Build a shared expat library") -expat_shy_set(EXPAT_BUILD_DOCS ${_EXPAT_BUILD_DOCS_DEFAULT} CACHE BOOL "Build man page for xmlwf") -expat_shy_set(EXPAT_BUILD_FUZZERS OFF CACHE BOOL "Build fuzzers for the expat library") -expat_shy_set(EXPAT_BUILD_PKGCONFIG ${_EXPAT_BUILD_PKGCONFIG_DEFAULT} CACHE BOOL "Build pkg-config file") -expat_shy_set(EXPAT_OSSFUZZ_BUILD OFF CACHE BOOL "Build fuzzers via ossfuzz for the expat library") -if(UNIX OR _EXPAT_HELP) - expat_shy_set(EXPAT_WITH_LIBBSD OFF CACHE BOOL "Utilize libbsd (for arc4random_buf)") -endif() -expat_shy_set(EXPAT_ENABLE_INSTALL ON CACHE BOOL "Install expat files in cmake install target") -expat_shy_set(EXPAT_CONTEXT_BYTES 1024 CACHE STRING "Define to specify how much context to retain around the current parse point") -mark_as_advanced(EXPAT_CONTEXT_BYTES) -expat_shy_set(EXPAT_DTD ON CACHE BOOL "Define to make parameter entity parsing functionality available") -mark_as_advanced(EXPAT_DTD) -expat_shy_set(EXPAT_NS ON CACHE BOOL "Define to make XML Namespaces functionality available") -mark_as_advanced(EXPAT_NS) -expat_shy_set(EXPAT_WARNINGS_AS_ERRORS OFF CACHE BOOL "Treat all compiler warnings as errors") -if(UNIX OR _EXPAT_HELP) - expat_shy_set(EXPAT_DEV_URANDOM ON CACHE BOOL "Define to include code reading entropy from `/dev/urandom'.") - expat_shy_set(EXPAT_WITH_GETRANDOM "AUTO" CACHE STRING "Make use of getrandom function (ON|OFF|AUTO) [default=AUTO]") - expat_shy_set(EXPAT_WITH_SYS_GETRANDOM "AUTO" CACHE STRING "Make use of syscall SYS_getrandom (ON|OFF|AUTO) [default=AUTO]") - mark_as_advanced(EXPAT_DEV_URANDOM) -endif() -expat_shy_set(EXPAT_CHAR_TYPE "char" CACHE STRING "Character type to use (char|ushort|wchar_t) [default=char]") -expat_shy_set(EXPAT_ATTR_INFO OFF CACHE BOOL "Define to allow retrieving the byte offsets for attribute names and values") -mark_as_advanced(EXPAT_ATTR_INFO) -expat_shy_set(EXPAT_LARGE_SIZE OFF CACHE BOOL "Make XML_GetCurrent* functions return <(unsigned) long long> rather than <(unsigned) long>") -mark_as_advanced(EXPAT_LARGE_SIZE) -expat_shy_set(EXPAT_MIN_SIZE OFF CACHE BOOL "Get a smaller (but slower) parser (in particular avoid multiple copies of the tokenizer)") -mark_as_advanced(EXPAT_MIN_SIZE) -if(MSVC OR _EXPAT_HELP) - expat_shy_set(EXPAT_MSVC_STATIC_CRT OFF CACHE BOOL "Use /MT flag (static CRT) when compiling in MSVC") -endif() -if(NOT _EXPAT_HELP) - expat_shy_set(_EXPAT_M32 OFF CACHE BOOL "(Unofficial!) Produce 32bit code with -m32") - mark_as_advanced(_EXPAT_M32) -endif() - -if(EXPAT_BUILD_TESTS) - # We have to call enable_language() before modifying any CMAKE_CXX_* variables - enable_language(CXX) -endif() - -# -# Environment checks -# -if(EXPAT_WITH_LIBBSD) - find_library(LIB_BSD NAMES bsd) - if(NOT LIB_BSD) - message(SEND_ERROR "EXPAT_WITH_LIBBSD option is enabled, but libbsd was not found") - else() - set(HAVE_LIBBSD TRUE) - endif() -endif() - -if(MSVC) - # For the three types of MSVC version values, please see: - # - https://cmake.org/cmake/help/latest/variable/MSVC_VERSION.html - # - https://sourceforge.net/p/predef/wiki/Compilers/ - # - https://en.wikipedia.org/wiki/Microsoft_Visual_Studio#History - set(_EXPAT_MSVC_REQUIRED_INT 1800) # i.e. 12.0/2013/1800; see PR #426 - set(_EXPAT_MSVC_SUPPORTED_INT 1910) - set(_EXPAT_MSVC_SUPPORTED_DISPLAY "Visual Studio 15.0/2017/${_EXPAT_MSVC_SUPPORTED_INT}") - - if(MSVC_VERSION VERSION_LESS ${_EXPAT_MSVC_SUPPORTED_INT}) - if(MSVC_VERSION VERSION_LESS ${_EXPAT_MSVC_REQUIRED_INT}) - message(SEND_ERROR "MSVC_VERSION ${MSVC_VERSION} is TOO OLD to compile Expat without errors.") - message(SEND_ERROR "Please use officially supported ${_EXPAT_MSVC_SUPPORTED_DISPLAY} or later. Thank you!") - else() - message(WARNING "MSVC_VERSION ${MSVC_VERSION} is NOT OFFICIALLY SUPPORTED by Expat.") - message(WARNING "Please use ${_EXPAT_MSVC_SUPPORTED_DISPLAY} or later. Thank you!") - endif() - endif() -endif() - -macro(_expat_copy_bool_int source_ref dest_ref) - if(${source_ref}) - set(${dest_ref} 1) - else() - set(${dest_ref} 0) - endif() -endmacro() - -if(EXPAT_LARGE_SIZE) - add_definitions(-DXML_LARGE_SIZE) -endif() - -if(EXPAT_MIN_SIZE) - add_definitions(-DXML_MIN_SIZE) -endif() - -if(EXPAT_CHAR_TYPE STREQUAL "char") - set(_EXPAT_UNICODE OFF) - set(_EXPAT_UNICODE_WCHAR_T OFF) -elseif(EXPAT_CHAR_TYPE STREQUAL "ushort") - set(_EXPAT_UNICODE ON) - set(_EXPAT_UNICODE_WCHAR_T OFF) - if(EXPAT_BUILD_EXAMPLES) - message(SEND_ERROR "Examples can not be built with option -DEXPAT_CHAR_TYPE=ushort. Please pass -DEXPAT_CHAR_TYPE=(char|wchar_t) or -DEXPAT_BUILD_EXAMPLES=OFF.") - endif() - if(EXPAT_BUILD_TESTS) - message(SEND_ERROR "The testsuite can not be built with option -DEXPAT_CHAR_TYPE=ushort. Please pass -DEXPAT_CHAR_TYPE=(char|wchar_t) or -DEXPAT_BUILD_TESTS=OFF.") - endif() - if(EXPAT_BUILD_TOOLS) - message(SEND_ERROR "The xmlwf tool can not be built with option -DEXPAT_CHAR_TYPE=ushort. Please pass -DEXPAT_CHAR_TYPE=(char|wchar_t) or -DEXPAT_BUILD_TOOLS=OFF.") - endif() -elseif(EXPAT_CHAR_TYPE STREQUAL "wchar_t") - set(_EXPAT_UNICODE ON) - set(_EXPAT_UNICODE_WCHAR_T ON) - if(NOT WIN32) - string(FIND "${CMAKE_C_FLAGS}" "-fshort-wchar" _expat_short_wchar_found) - if(${_expat_short_wchar_found} EQUAL "-1") - message(SEND_ERROR "Configuration -DEXPAT_CHAR_TYPE=wchar_t requires -DCMAKE_{C,CXX}_FLAGS=-fshort-wchar (which was not found) and libc compiled with -fshort-wchar, too.") - endif() - if (EXPAT_BUILD_TOOLS) - message(SEND_ERROR "The xmlwf tool can not be built with option -DEXPAT_CHAR_TYPE=wchar_t outside of Windows. Please pass -DEXPAT_CHAR_TYPE=char or -DEXPAT_BUILD_TOOLS=OFF.") - endif() - endif() -else() - message(SEND_ERROR "Option -DEXPAT_CHAR_TYPE=(char|ushort|wchar_t) cannot be \"${EXPAT_CHAR_TYPE}\".") -endif() - -if(_EXPAT_UNICODE) - add_definitions(-DXML_UNICODE) # for unsigned short - if(_EXPAT_UNICODE_WCHAR_T) - add_definitions(-DXML_UNICODE_WCHAR_T) # for wchar_t - endif() -endif() - -include(${CMAKE_CURRENT_LIST_DIR}/ConfigureChecks.cmake) - -macro(evaluate_detection_results use_ref have_ref thing_lower thing_title) - if(${use_ref} AND NOT (${use_ref} STREQUAL "AUTO") AND NOT ${have_ref}) - message(SEND_ERROR - "Use of ${thing_lower} was enforced by ${use_ref}=ON but it could not be found.") - elseif(NOT ${use_ref} AND ${have_ref}) - message("${thing_title} was found but it will not be used due to ${use_ref}=OFF.") - set(${have_ref} 0) - endif() -endmacro() - -if(NOT WIN32) - evaluate_detection_results(EXPAT_WITH_GETRANDOM HAVE_GETRANDOM "function getrandom" "Function getrandom") - evaluate_detection_results(EXPAT_WITH_SYS_GETRANDOM HAVE_SYSCALL_GETRANDOM "syscall SYS_getrandom" "Syscall SYS_getrandom") -endif() - -_expat_copy_bool_int(EXPAT_ATTR_INFO XML_ATTR_INFO) -_expat_copy_bool_int(EXPAT_DTD XML_DTD) -_expat_copy_bool_int(EXPAT_LARGE_SIZE XML_LARGE_SIZE) -_expat_copy_bool_int(EXPAT_MIN_SIZE XML_MIN_SIZE) -_expat_copy_bool_int(EXPAT_NS XML_NS) -if(NOT WIN32) - _expat_copy_bool_int(EXPAT_DEV_URANDOM XML_DEV_URANDOM) -endif() -set(XML_CONTEXT_BYTES ${EXPAT_CONTEXT_BYTES}) - -macro(expat_install) - if(EXPAT_ENABLE_INSTALL) - install(${ARGN}) - endif() -endmacro() - -configure_file(expat_config.h.cmake "${CMAKE_CURRENT_BINARY_DIR}/expat_config.h") -expat_install(FILES "${CMAKE_CURRENT_BINARY_DIR}/expat_config.h" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - - -set(EXTRA_COMPILE_FLAGS) -if(FLAG_NO_STRICT_ALIASING) - set(EXTRA_COMPILE_FLAGS "${EXTRA_COMPILE_FLAGS} -fno-strict-aliasing") -endif() -if(FLAG_VISIBILITY) - if(EXPAT_SHARED_LIBS) - add_definitions(-DXML_ENABLE_VISIBILITY=1) - endif() - set(EXTRA_COMPILE_FLAGS "${EXTRA_COMPILE_FLAGS} -fvisibility=hidden") -endif() -if(MINGW) - # Without __USE_MINGW_ANSI_STDIO the compiler produces a false positive - set(EXTRA_COMPILE_FLAGS "${EXTRA_COMPILE_FLAGS} -Wno-pedantic-ms-format") -endif() -if (EXPAT_WARNINGS_AS_ERRORS) - if(MSVC) - add_definitions(/WX) - else() - set(EXTRA_COMPILE_FLAGS "${EXTRA_COMPILE_FLAGS} -Werror") - endif() -endif() -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_COMPILE_FLAGS}") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_COMPILE_FLAGS}") - -if (MSVC) - if (EXPAT_MSVC_STATIC_CRT) - message("-- Using static CRT ${EXPAT_MSVC_STATIC_CRT}") - foreach(flag_var - CMAKE_CXX_FLAGS_${_EXPAT_BUILD_TYPE_UPPER} - CMAKE_CXX_FLAGS_DEBUG - CMAKE_CXX_FLAGS_RELEASE - CMAKE_CXX_FLAGS_MINSIZEREL - CMAKE_CXX_FLAGS_RELWITHDEBINFO - CMAKE_C_FLAGS_${_EXPAT_BUILD_TYPE_UPPER} - CMAKE_C_FLAGS_DEBUG - CMAKE_C_FLAGS_RELEASE - CMAKE_C_FLAGS_MINSIZEREL - CMAKE_C_FLAGS_RELWITHDEBINFO - ) - string(REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") - endforeach() - endif() -endif() - -if(_EXPAT_M32 AND NOT MSVC) - foreach(flag_var - CMAKE_CXX_FLAGS_${_EXPAT_BUILD_TYPE_UPPER} - CMAKE_CXX_FLAGS_DEBUG - CMAKE_CXX_FLAGS_RELEASE - CMAKE_CXX_FLAGS_MINSIZEREL - CMAKE_CXX_FLAGS_RELWITHDEBINFO - CMAKE_C_FLAGS_${_EXPAT_BUILD_TYPE_UPPER} - CMAKE_C_FLAGS_DEBUG - CMAKE_C_FLAGS_RELEASE - CMAKE_C_FLAGS_MINSIZEREL - CMAKE_C_FLAGS_RELWITHDEBINFO - ) - set(${flag_var} "${${flag_var}} -m32") - endforeach() -endif() - -include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/lib) -if(MSVC) - add_definitions(-D_CRT_SECURE_NO_WARNINGS -wd4996) -endif() - -# -# C library -# -set(_EXPAT_C_SOURCES - lib/xmlparse.c - lib/xmlrole.c - lib/xmltok.c -# NOTE: ISO C forbids an empty translation unit -# lib/xmltok_impl.c -# lib/xmltok_ns.c -) - -if(EXPAT_SHARED_LIBS) - set(_SHARED SHARED) - if(WIN32) - macro(_expat_def_file_toggle source_var target_var) - if(${source_var}) - set(${target_var} " ") # i.e. not commented out, a single space - else() - set(${target_var} ";") # i.e. commented out - endif() - endmacro() - - _expat_def_file_toggle(EXPAT_DTD _EXPAT_COMMENT_DTD) - _expat_def_file_toggle(EXPAT_ATTR_INFO _EXPAT_COMMENT_ATTR_INFO) - - configure_file("${CMAKE_CURRENT_SOURCE_DIR}/lib/libexpat.def.cmake" "${CMAKE_CURRENT_BINARY_DIR}/lib/libexpat.def") - set(_EXPAT_EXTRA_SOURCES ${_EXPAT_EXTRA_SOURCES} "${CMAKE_CURRENT_BINARY_DIR}/lib/libexpat.def") - - # Add DLL version - string(REPLACE "." "," _EXPAT_DLL_VERSION ${PROJECT_VERSION}.0) - set(_EXPAT_EXTRA_SOURCES ${_EXPAT_EXTRA_SOURCES} win32/version.rc) - endif() -else() - set(_SHARED STATIC) -endif() - -add_library(expat ${_SHARED} ${_EXPAT_C_SOURCES} ${_EXPAT_EXTRA_SOURCES}) -if(_EXPAT_LIBM_FOUND) - target_link_libraries(expat m) -endif() -if(EXPAT_WITH_LIBBSD) - target_link_libraries(expat ${LIB_BSD}) -endif() - -# -# Library filename postfix -# -if(_EXPAT_UNICODE) - set(_POSTFIX_WIDE "w") -endif() - -if(MSVC AND NOT EXPAT_SHARED_LIBS) - if(EXPAT_MSVC_STATIC_CRT) - set(_POSTFIX_CRT "MT") - else() - set(_POSTFIX_CRT "MD") - endif() -endif() - -foreach(build_type_upper - ${_EXPAT_BUILD_TYPE_UPPER} - DEBUG - RELEASE - MINSIZEREL - RELWITHDEBINFO - ) - if(WIN32 AND build_type_upper STREQUAL "DEBUG") - set(_POSTFIX_DEBUG "d") - else() - set(_POSTFIX_DEBUG "") # needs a reset because of being looped - endif() - - expat_shy_set(EXPAT_${build_type_upper}_POSTFIX "${_POSTFIX_WIDE}${_POSTFIX_DEBUG}${_POSTFIX_CRT}" CACHE STRING "Library filename postfix for build type ${build_type_upper}; yields filenames libexpat.(dll|dylib|lib|so)") - mark_as_advanced(EXPAT_${build_type_upper}_POSTFIX) - set_property(TARGET expat PROPERTY ${build_type_upper}_POSTFIX ${EXPAT_${build_type_upper}_POSTFIX}) -endforeach() - -set(LIBCURRENT 9) # sync -set(LIBREVISION 10) # with -set(LIBAGE 8) # configure.ac! -math(EXPR LIBCURRENT_MINUS_AGE "${LIBCURRENT} - ${LIBAGE}") - -if(NOT WIN32) - set_property(TARGET expat PROPERTY VERSION ${LIBCURRENT_MINUS_AGE}.${LIBAGE}.${LIBREVISION}) - set_property(TARGET expat PROPERTY SOVERSION ${LIBCURRENT_MINUS_AGE}) - set_property(TARGET expat PROPERTY NO_SONAME ${NO_SONAME}) - - if(APPLE) - if(NOT CMAKE_VERSION VERSION_GREATER_EQUAL 3.17) - message(FATAL_ERROR "Expat requires CMake >=3.17 on platform \"APPLE\".") - endif() - - # NOTE: This intends to talk CMake into compatiblity with GNU Libtool - math(EXPR _EXPAT_MACHO_COMPATIBILITY_VERSION "${LIBCURRENT} + 1") - set(_EXPAT_MACHO_CURRENT_VERSION "${_EXPAT_MACHO_COMPATIBILITY_VERSION}.${LIBREVISION}") - set_property(TARGET expat PROPERTY MACHO_COMPATIBILITY_VERSION ${_EXPAT_MACHO_COMPATIBILITY_VERSION}) - set_property(TARGET expat PROPERTY MACHO_CURRENT_VERSION ${_EXPAT_MACHO_CURRENT_VERSION}) - endif() -endif() - -if(MINGW AND EXPAT_SHARED_LIBS) - set_target_properties(expat PROPERTIES SUFFIX "-${LIBCURRENT_MINUS_AGE}.dll") -endif() - -if(WIN32 AND NOT MINGW) - # NOTE: This avoids a name collision with Expat.dll of Perl's XML::Parser::Expat - # on Windows by resorting to filename libexpat.dll since Expat 1.95.3. - # Everything but MSVC is already adding prefix "lib", automatically. - # NOTE: "set_property(TARGET expat PROPERTY PREFIX lib)" would only affect *.dll - # files but not *.lib files, so we have to rely on property OUTPUT_NAME, instead. - # Target property _POSTFIX still applies. - set(_EXPAT_OUTPUT_NAME libexpat) - set_property(TARGET expat PROPERTY OUTPUT_NAME ${_EXPAT_OUTPUT_NAME}) -else() - set(_EXPAT_OUTPUT_NAME expat) -endif() - -target_include_directories(expat - INTERFACE - $ - $ - $ -) - -if(WIN32 AND EXPAT_SHARED_LIBS) - target_compile_definitions(expat PRIVATE VER_FILEVERSION=${_EXPAT_DLL_VERSION}) -endif() - -expat_install(TARGETS expat EXPORT expat - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) - -expat_install(FILES lib/expat.h lib/expat_external.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - -# -# pkg-config file -# -if(EXPAT_BUILD_PKGCONFIG) - if(CMAKE_INSTALL_LIBDIR MATCHES "^/") - set(_expat_pkgconfig_libdir "${CMAKE_INSTALL_LIBDIR}") - else() - set(_expat_pkgconfig_libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}") - endif() - - if(CMAKE_INSTALL_INCLUDEDIR MATCHES "^/") - set(_expat_pkgconfig_includedir "${CMAKE_INSTALL_INCLUDEDIR}") - else() - set(_expat_pkgconfig_includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") - endif() - - set_target_properties(expat PROPERTIES - pkgconfig_prefix "${CMAKE_INSTALL_PREFIX}" - pkgconfig_exec_prefix "\${prefix}" - pkgconfig_libdir "${_expat_pkgconfig_libdir}" - pkgconfig_includedir "${_expat_pkgconfig_includedir}" - pkgconfig_version "${PACKAGE_VERSION}") - - foreach(_build_type ${CMAKE_BUILD_TYPE} Debug Release RelWithDebInfo MinSizeRel) - string(TOLOWER "${_build_type}" _build_type_lower) - string(TOUPPER "${_build_type}" _build_type_upper) - set_property(TARGET expat PROPERTY "pkgconfig_${_build_type_lower}_name" "expat${EXPAT_${_build_type_upper}_POSTFIX}") - set_property(TARGET expat PROPERTY "pkgconfig_${_build_type_lower}_output_name" "${_EXPAT_OUTPUT_NAME}${EXPAT_${_build_type_upper}_POSTFIX}") - if(_EXPAT_LIBM_FOUND) - set_property(TARGET expat PROPERTY "pkgconfig_libm" "-lm") - else() - set_property(TARGET expat PROPERTY "pkgconfig_libm" "") - endif() - endforeach() - - file(GENERATE - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/$/expat.pc - INPUT ${PROJECT_SOURCE_DIR}/expat.pc.cmake) - - expat_install(FILES ${CMAKE_CURRENT_BINARY_DIR}/$/expat.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) -endif() - -# -# C command line tool xmlwf -# -if(EXPAT_BUILD_TOOLS) - set(xmlwf_SRCS - xmlwf/xmlwf.c - xmlwf/xmlfile.c - xmlwf/codepage.c - xmlwf/readfilemap.c - ) - - add_executable(xmlwf ${xmlwf_SRCS}) - set_property(TARGET xmlwf PROPERTY RUNTIME_OUTPUT_DIRECTORY xmlwf) - target_link_libraries(xmlwf expat) - if(_EXPAT_LIBM_FOUND) - target_link_libraries(xmlwf m) - endif() - expat_install(TARGETS xmlwf DESTINATION ${CMAKE_INSTALL_BINDIR}) - - if(MINGW AND _EXPAT_UNICODE_WCHAR_T) - # https://gcc.gnu.org/onlinedocs/gcc/x86-Windows-Options.html - set_target_properties(xmlwf PROPERTIES LINK_FLAGS -municode) - endif() - - if(EXPAT_BUILD_DOCS) - file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/doc) - add_custom_target( - xmlwf-manpage - COMMAND - "${DOCBOOK_TO_MAN}" "${PROJECT_SOURCE_DIR}/doc/xmlwf.xml" && mv "XMLWF.1" "${PROJECT_BINARY_DIR}/doc/xmlwf.1" - BYPRODUCTS - doc/xmlwf.1) - add_dependencies(expat xmlwf-manpage) - expat_install(FILES "${PROJECT_BINARY_DIR}/doc/xmlwf.1" DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) - elseif(EXISTS ${PROJECT_SOURCE_DIR}/doc/xmlwf.1) - expat_install(FILES "${PROJECT_SOURCE_DIR}/doc/xmlwf.1" DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) - endif() -endif() - -# -# C code examples -# -if(EXPAT_BUILD_EXAMPLES) - add_executable(elements examples/elements.c) - add_executable(outline examples/outline.c) - - foreach(_target elements outline) - set_property(TARGET ${_target} PROPERTY RUNTIME_OUTPUT_DIRECTORY examples) - target_link_libraries(${_target} expat) - endforeach() -endif() - -# -# C/C++ test runners -# -if(EXPAT_BUILD_TESTS) - ## these are unittests that can be run on any platform - enable_testing() - - set(test_SRCS - tests/chardata.c - tests/memcheck.c - tests/minicheck.c - tests/structdata.c - ${_EXPAT_C_SOURCES} - ) - - if(NOT MSVC) - if(MINGW) - set(host whatever-mingw32) # for nothing but run.sh - endif() - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/run.sh.in run.sh @ONLY) - endif() - - function(expat_add_test _name _file) - if(MSVC) - add_test(NAME ${_name} COMMAND ${_file}) - else() - add_test(NAME ${_name} COMMAND bash run.sh ${_file}) - endif() - endfunction() - - set(_EXPAT_TEST_TARGETS runtests runtestspp) - add_executable(runtests tests/runtests.c ${test_SRCS}) - add_executable(runtestspp tests/runtestspp.cpp ${test_SRCS}) - - foreach(_target ${_EXPAT_TEST_TARGETS}) - set_property(TARGET ${_target} PROPERTY RUNTIME_OUTPUT_DIRECTORY tests) - expat_add_test(${_target} $) - - if(_EXPAT_LIBM_FOUND) - target_link_libraries(${_target} m) - endif() - - if(EXPAT_WITH_LIBBSD) - target_link_libraries(${_target} ${LIB_BSD}) - endif() - endforeach() -endif() - -# -# Fuzzers -# -if(EXPAT_BUILD_FUZZERS) - if(NOT "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") - message(SEND_ERROR - "Building fuzz targets without Clang (but ${CMAKE_C_COMPILER_ID}) " - "is not supported. Please set " - "-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++.") - endif() - - string(FIND "${CMAKE_C_FLAGS}" "-fsanitize" sanitizer_present) - if(${sanitizer_present} EQUAL "-1") - message(WARNING - "There was no sanitizer present when building the fuzz targets. " - "This is likely in error - consider adding " - "-DCMAKE_C_FLAGS='-fsanitize=' and " - "-DCMAKE_CXX_FLAGS='-fsanitize=' and " - "-DCMAKE_EXE_LINKER_FLAGS='-fsanitize=' and " - "-DCMAKE_MODULE_LINKER_FLAGS='-fsanitize=' and " - "-DCMAKE_SHARED_LINKER_FLAGS='-fsanitize=' to your cmake " - "execution.") - endif() - - if(EXPAT_OSSFUZZ_BUILD AND NOT DEFINED ENV{LIB_FUZZING_ENGINE}) - message(SEND_ERROR - "OSS-Fuzz builds require the environment variable " - "LIB_FUZZING_ENGINE to be set. If you are seeing this " - "warning, it points to a deeper problem in the ossfuzz " - "build setup.") - endif() - - set(encoding_types UTF-16 UTF-8 ISO-8859-1 US-ASCII UTF-16BE UTF-16LE) - set(fuzz_targets xml_parse_fuzzer xml_parsebuffer_fuzzer) - - add_library(fuzzpat STATIC ${_EXPAT_C_SOURCES}) - if(NOT EXPAT_OSSFUZZ_BUILD) - target_compile_options(fuzzpat PRIVATE -fsanitize=fuzzer-no-link) - endif() - - foreach(fuzz_target ${fuzz_targets}) - foreach(encoding_type ${encoding_types}) - set(target_name ${fuzz_target}_${encoding_type}) - add_executable(${target_name} fuzz/${fuzz_target}.c) - target_link_libraries(${target_name} fuzzpat) - target_compile_definitions(${target_name} - PRIVATE ENCODING_FOR_FUZZING=${encoding_type}) - if(NOT EXPAT_OSSFUZZ_BUILD) - target_compile_options(${target_name} PRIVATE -fsanitize=fuzzer-no-link) - endif() - # NOTE: Avoiding target_link_options here only because it needs CMake >=3.13 - if(EXPAT_OSSFUZZ_BUILD) - set_target_properties(${target_name} PROPERTIES LINK_FLAGS $ENV{LIB_FUZZING_ENGINE}) - set_target_properties(${target_name} PROPERTIES LINKER_LANGUAGE "CXX") - else() - set_target_properties(${target_name} PROPERTIES LINK_FLAGS -fsanitize=fuzzer) - endif() - set_property( - TARGET ${target_name} PROPERTY RUNTIME_OUTPUT_DIRECTORY fuzz) - endforeach() - endforeach() -else() - if(EXPAT_OSSFUZZ_BUILD) - message(SEND_ERROR - "Attempting to perform an ossfuzz build without turning on the fuzzer build. " - "This is likely in error - consider adding " - "-DEXPAT_BUILD_FUZZERS=ON to your cmake execution.") - endif() -endif() - -# -# C/C++ config affecting multiple targets -# -if(WIN32) - set(_EXPAT_STATIC_TARGETS ${_EXPAT_TEST_TARGETS}) - if(NOT EXPAT_SHARED_LIBS) - list(APPEND _EXPAT_STATIC_TARGETS expat) - endif() - if(EXPAT_BUILD_FUZZERS) - list(APPEND _EXPAT_STATIC_TARGETS fuzzpat) - endif() - - foreach(_target ${_EXPAT_STATIC_TARGETS}) - target_compile_definitions(${_target} PUBLIC -DXML_STATIC) - endforeach() -endif() - -# -# Custom target "run-xmltest" -# -if(EXPAT_BUILD_TOOLS AND NOT MSVC) - add_custom_target( - xmlts-zip-downloaded - COMMAND - sh -c 'test -f xmlts.zip || wget --output-document=xmlts.zip https://www.w3.org/XML/Test/xmlts20080827.zip' - BYPRODUCTS - tests/xmlts.zip - WORKING_DIRECTORY - tests/) - - add_custom_target( - xmlts-zip-extracted - COMMAND - sh -c 'test -d xmlconf || unzip -q xmlts.zip' - BYPRODUCTS - tests/xmlconf - WORKING_DIRECTORY - tests/) - add_dependencies(xmlts-zip-extracted xmlts-zip-downloaded) - - add_custom_target( - xmltest-sh-been-run - COMMAND - sh -c '${CMAKE_CURRENT_SOURCE_DIR}/tests/xmltest.sh "bash ${CMAKE_CURRENT_BINARY_DIR}/run.sh $" 2>&1 | tee tests/xmltest.log' - BYPRODUCTS - tests/xmltest.log) - add_dependencies(xmltest-sh-been-run xmlts-zip-extracted xmlwf) - - add_custom_target( - xmltest-log-fixed - COMMAND - ${CMAKE_CURRENT_SOURCE_DIR}/fix-xmltest-log.sh tests/xmltest.log - DEPENDS - tests/xmltest.log) - add_dependencies(xmltest-log-fixed xmltest-sh-been-run) - - add_custom_target( - xmltest-log-verified - COMMAND - diff -u ${CMAKE_CURRENT_SOURCE_DIR}/tests/xmltest.log.expected tests/xmltest.log) - add_dependencies(xmltest-log-verified xmltest-log-fixed) - - add_custom_target(run-xmltest) - add_dependencies(run-xmltest xmltest-log-verified) -endif() - -# -# Documentation -# -configure_file(Changes changelog COPYONLY) -expat_install( - FILES - AUTHORS - ${CMAKE_CURRENT_BINARY_DIR}/changelog - DESTINATION - ${CMAKE_INSTALL_DOCDIR}) - -# -# CMake files for find_package(expat [..] CONFIG [..]) -# -configure_package_config_file( - cmake/expat-config.cmake.in - cmake/expat-config.cmake - INSTALL_DESTINATION - ${CMAKE_INSTALL_LIBDIR}/cmake/expat-${PROJECT_VERSION}/ -) -write_basic_package_version_file( - cmake/expat-config-version.cmake - COMPATIBILITY SameMajorVersion # i.e. semver -) -export( - TARGETS - expat - FILE - cmake/expat-targets.cmake # not going to be installed -) -expat_install( - FILES - ${CMAKE_CURRENT_BINARY_DIR}/cmake/expat-config.cmake - ${CMAKE_CURRENT_BINARY_DIR}/cmake/expat-config-version.cmake - DESTINATION - ${CMAKE_INSTALL_LIBDIR}/cmake/expat-${PROJECT_VERSION}/ -) -expat_install( - EXPORT - expat - DESTINATION - ${CMAKE_INSTALL_LIBDIR}/cmake/expat-${PROJECT_VERSION}/ - NAMESPACE - expat:: -) - -# -# CPack -# - -# This effectively disables target "package_source". -# That is done due to CPack's unfortunate choice of an exclusion list -# rather than inclusion list. An exclusion list does not protect against -# unwanted files ending up in the resulting archive in a way that's -# safe to run from an Expat developer's machine. -set(CPACK_SOURCE_GENERATOR '') - -if(WIN32) - set(CPACK_GENERATOR ZIP) -else() - set(CPACK_GENERATOR TGZ) -endif() - -include(CPack) - -# -# Summary -# -if(EXPAT_CHAR_TYPE STREQUAL "char") - set(_EXPAT_CHAR_TYPE_SUMMARY "char (UTF-8)") -elseif(EXPAT_CHAR_TYPE STREQUAL "ushort") - set(_EXPAT_CHAR_TYPE_SUMMARY "ushort (unsigned short, UTF-16)") -elseif(EXPAT_CHAR_TYPE STREQUAL "wchar_t") - if(WIN32) - set(_EXPAT_CHAR_TYPE_SUMMARY "wchar_t (UTF-16)") - else() - set(_EXPAT_CHAR_TYPE_SUMMARY "wchar_t (UTF-32) // not implemented") - endif() -else() - set(_EXPAT_CHAR_TYPE_SUMMARY "ERROR") -endif() -# NOTE: We're not accessing global property GENERATOR_IS_MULTI_CONFIG -# because that would require CMake >=3.9 -if(CMAKE_CONFIGURATION_TYPES) - set(_EXPAT_GENERATOR_IS_MULTI_CONFIG TRUE) -else() - set(_EXPAT_GENERATOR_IS_MULTI_CONFIG FALSE) -endif() - -message(STATUS "===========================================================================") -message(STATUS "") -message(STATUS "Configuration") -message(STATUS " Generator .................. ${CMAKE_GENERATOR}") -if(_EXPAT_GENERATOR_IS_MULTI_CONFIG) - message(STATUS " Build types ................ ${CMAKE_CONFIGURATION_TYPES}") -else() - message(STATUS " Build type ................. ${CMAKE_BUILD_TYPE}") -endif() -message(STATUS " Prefix ..................... ${CMAKE_INSTALL_PREFIX}") -message(STATUS " Shared libraries ........... ${EXPAT_SHARED_LIBS}") -if(MSVC) - message(STATUS " Static CRT ................. ${EXPAT_MSVC_STATIC_CRT}") -endif() -message(STATUS " Character type ............. ${_EXPAT_CHAR_TYPE_SUMMARY}") -if(NOT _EXPAT_GENERATOR_IS_MULTI_CONFIG) - message(STATUS " Library name postfix ....... ${EXPAT_${_EXPAT_BUILD_TYPE_UPPER}_POSTFIX}") -endif() -message(STATUS "") -message(STATUS " Build documentation ........ ${EXPAT_BUILD_DOCS}") -message(STATUS " Build examples ............. ${EXPAT_BUILD_EXAMPLES}") -message(STATUS " Build fuzzers .............. ${EXPAT_BUILD_FUZZERS}") -message(STATUS " Build tests ................ ${EXPAT_BUILD_TESTS}") -message(STATUS " Build tools (xmlwf) ........ ${EXPAT_BUILD_TOOLS}") -message(STATUS " Build pkg-config file ...... ${EXPAT_BUILD_PKGCONFIG}") -message(STATUS " Install files .............. ${EXPAT_ENABLE_INSTALL}") -message(STATUS "") -message(STATUS " Features") -message(STATUS " // Advanced options, changes not advised") -message(STATUS " Attributes info .......... ${EXPAT_ATTR_INFO}") -message(STATUS " Context bytes ............ ${EXPAT_CONTEXT_BYTES}") -message(STATUS " DTD support .............. ${EXPAT_DTD}") -message(STATUS " Large size ............... ${EXPAT_LARGE_SIZE}") -message(STATUS " Minimum size ............. ${EXPAT_MIN_SIZE}") -message(STATUS " Namespace support ........ ${EXPAT_NS}") -message(STATUS "") -message(STATUS " Entropy sources") -if(WIN32) - message(STATUS " rand_s ................... ON") -else() - message(STATUS " getrandom ................ ${HAVE_GETRANDOM}") - message(STATUS " syscall SYS_getrandom .... ${HAVE_SYSCALL_GETRANDOM}") - message(STATUS " libbsd ................... ${EXPAT_WITH_LIBBSD}") - message(STATUS " /dev/random .............. ${EXPAT_DEV_URANDOM}") -endif() -message(STATUS "") -if(CMAKE_GENERATOR STREQUAL "Unix Makefiles") - message(STATUS "Continue with") - message(STATUS " make") - if(EXPAT_BUILD_TESTS) - message(STATUS " make test") - endif() - if(EXPAT_ENABLE_INSTALL) - message(STATUS " sudo make install") - endif() - message(STATUS "") -endif() -message(STATUS "===========================================================================") diff --git a/cmake/SuperBuild/pystring-patch/CMakeLists.txt b/cmake/SuperBuild/pystring-patch/CMakeLists.txt index b2864e0..8e11add 100644 --- a/cmake/SuperBuild/pystring-patch/CMakeLists.txt +++ b/cmake/SuperBuild/pystring-patch/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.2) project(pystring CXX) #set(BUILD_SHARED_LIBS YES) @@ -7,16 +7,18 @@ add_library(pystring pystring.cpp pystring.h ) +set_target_properties(pystring PROPERTIES PUBLIC_HEADER pystring.h) add_executable (pystring_test test.cpp) TARGET_LINK_LIBRARIES (pystring_test pystring) -enable_testing() -add_test(NAME PyStringTest COMMAND pystring_test) +#enable_testing() +#add_test(NAME PyStringTest COMMAND pystring_test) include(GNUInstallDirs) install(TARGETS pystring LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/pystring ) diff --git a/cmake/SuperBuild/yaml-cpp-patch/CMakeLists.txt b/cmake/SuperBuild/yaml-cpp-patch/CMakeLists.txt deleted file mode 100644 index 078e18e..0000000 --- a/cmake/SuperBuild/yaml-cpp-patch/CMakeLists.txt +++ /dev/null @@ -1,174 +0,0 @@ -# 3.5 is actually available almost everywhere, but this a good minimum -cmake_minimum_required(VERSION 3.5) - -# enable MSVC_RUNTIME_LIBRARY target property -# see https://cmake.org/cmake/help/latest/policy/CMP0091.html -if(POLICY CMP0091) - cmake_policy(SET CMP0091 NEW) -endif() - -project(YAML_CPP VERSION 0.7.0 LANGUAGES CXX) - -include(CMakePackageConfigHelpers) -include(CMakeDependentOption) -include(CheckCXXCompilerFlag) -include(GNUInstallDirs) -include(CTest) - -find_program(YAML_CPP_CLANG_FORMAT_EXE NAMES clang-format) - -option(YAML_CPP_BUILD_CONTRIB "Enable yaml-cpp contrib in library" ON) -option(YAML_CPP_BUILD_TOOLS "Enable parse tools" ON) -option(YAML_BUILD_SHARED_LIBS "Build yaml-cpp shared library" ${BUILD_SHARED_LIBS}) - -cmake_dependent_option(YAML_CPP_BUILD_TESTS - "Enable yaml-cpp tests" ON - "BUILD_TESTING;CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR" OFF) -cmake_dependent_option(YAML_CPP_INSTALL - "Enable generation of yaml-cpp install targets" ON - "CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR" OFF) -cmake_dependent_option(YAML_MSVC_SHARED_RT - "MSVC: Build yaml-cpp with shared runtime libs (/MD)" ON - "MSVC" OFF) - -set(yaml-cpp-type STATIC) -set(yaml-cpp-label-postfix "static") -if (YAML_BUILD_SHARED_LIBS) - set(yaml-cpp-type SHARED) - set(yaml-cpp-label-postfix "shared") -endif() - -set(build-shared $) -set(build-windows-dll $,${build-shared}>) -set(not-msvc $>) -set(msvc-shared_rt $) - -if (NOT DEFINED CMAKE_MSVC_RUNTIME_LIBRARY) - set(CMAKE_MSVC_RUNTIME_LIBRARY - MultiThreaded$<$:Debug>$<${msvc-shared_rt}:DLL>) -endif() - -set(contrib-pattern "src/contrib/*.cpp") -set(src-pattern "src/*.cpp") -if (CMAKE_VERSION VERSION_GREATER 3.12) - list(INSERT contrib-pattern 0 CONFIGURE_DEPENDS) - list(INSERT src-pattern 0 CONFIGURE_DEPENDS) -endif() - -file(GLOB yaml-cpp-contrib-sources ${contrib-pattern}) -file(GLOB yaml-cpp-sources ${src-pattern}) - -set(msvc-rt $) - -set(msvc-rt-mtd-static $) -set(msvc-rt-mt-static $) - -set(msvc-rt-mtd-dll $) -set(msvc-rt-mt-dll $) - -set(backport-msvc-runtime $) - -add_library(yaml-cpp ${yaml-cpp-type} "") -add_library(yaml-cpp::yaml-cpp ALIAS yaml-cpp) - -set_property(TARGET yaml-cpp - PROPERTY - MSVC_RUNTIME_LIBRARY ${CMAKE_MSVC_RUNTIME_LIBRARY}) -set_property(TARGET yaml-cpp - PROPERTY - CXX_STANDARD_REQUIRED ON) - -target_include_directories(yaml-cpp - PUBLIC - $ - $ - PRIVATE - $) - -if (NOT DEFINED CMAKE_CXX_STANDARD) - set_target_properties(yaml-cpp - PROPERTIES - CXX_STANDARD 11) -endif() - -target_compile_options(yaml-cpp - PRIVATE - $<${not-msvc}:-Wall -Wextra -Wshadow -Weffc++ -Wno-long-long> - $<${not-msvc}:-pedantic -pedantic-errors> - - $<$:-MTd> - $<$:-MT> - $<$:-MDd> - $<$:-MD> - - # /wd4127 = disable warning C4127 "conditional expression is constant" - # http://msdn.microsoft.com/en-us/library/6t66728h.aspx - # /wd4355 = disable warning C4355 "'this' : used in base member initializer list - # http://msdn.microsoft.com/en-us/library/3c594ae3.aspx - $<$:/W3 /wd4127 /wd4355>) - -target_compile_definitions(yaml-cpp - PRIVATE - $<${build-windows-dll}:${PROJECT_NAME}_DLL> - $<$>:YAML_CPP_NO_CONTRIB>) - -target_sources(yaml-cpp - PRIVATE - $<$:${yaml-cpp-contrib-sources}> - ${yaml-cpp-sources}) - -if (NOT DEFINED CMAKE_DEBUG_POSTFIX) - set(CMAKE_DEBUG_POSTFIX "d") -endif() - -set_target_properties(yaml-cpp PROPERTIES - VERSION "${PROJECT_VERSION}" - SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}" - PROJECT_LABEL "yaml-cpp ${yaml-cpp-label-postfix}" - DEBUG_POSTFIX "${CMAKE_DEBUG_POSTFIX}") - -configure_package_config_file( - "${PROJECT_SOURCE_DIR}/yaml-cpp-config.cmake.in" - "${PROJECT_BINARY_DIR}/yaml-cpp-config.cmake" - INSTALL_DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/yaml-cpp") - -write_basic_package_version_file( - "${PROJECT_BINARY_DIR}/yaml-cpp-config-version.cmake" - COMPATIBILITY AnyNewerVersion) - -configure_file(yaml-cpp.pc.in yaml-cpp.pc @ONLY) - -if (YAML_CPP_INSTALL) - install(TARGETS yaml-cpp - EXPORT yaml-cpp-targets - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) - install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/ - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - FILES_MATCHING PATTERN "*.h") - install(EXPORT yaml-cpp-targets - DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/yaml-cpp") - install(FILES - "${PROJECT_BINARY_DIR}/yaml-cpp-config.cmake" - "${PROJECT_BINARY_DIR}/yaml-cpp-config-version.cmake" - DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/yaml-cpp") - install(FILES "${PROJECT_BINARY_DIR}/yaml-cpp.pc" - DESTINATION ${CMAKE_INSTALL_DATADIR}/pkgconfig) -endif() - -if(YAML_CPP_BUILD_TESTS) - add_subdirectory(test) -endif() - -if(YAML_CPP_BUILD_TOOLS) - add_subdirectory(util) -endif() - -if (YAML_CPP_CLANG_FORMAT_EXE) - add_custom_target(format - COMMAND clang-format --style=file -i $ - COMMAND_EXPAND_LISTS - COMMENT "Running clang-format" - VERBATIM) -endif() From 0cf15d7b3980f1c2dc0aa4ec4db9077f7fbcd1ab Mon Sep 17 00:00:00 2001 From: Darby Johnston Date: Tue, 16 Sep 2025 12:57:37 -0700 Subject: [PATCH 17/24] Debugging Signed-off-by: Darby Johnston --- .github/workflows/ci-workflow.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index 9ada7ac..2a4b6be 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -91,11 +91,13 @@ jobs: submodules: recursive # \bug DYLD_LIBRARY_PATH is not being set here? + # \bug Remove OpenEXR install. - name: Setup run: | echo "$PWD/install-Debug/bin" >> $GITHUB_PATH echo "DYLD_LIBRARY_PATH=$PWD/install-Debug/lib:$DYLD_LIBRARY_PATH" >> $GITHUB_ENV cmake --version + brew uninstall --ignore-dependencies openexr imath || true - name: Build run: sh toucan/sbuild-macos.sh Debug From af2073ca3d81820f01ac942e8b0dbf5d6305c8c6 Mon Sep 17 00:00:00 2001 From: Darby Johnston Date: Tue, 16 Sep 2025 15:08:33 -0700 Subject: [PATCH 18/24] Refactoring Signed-off-by: Darby Johnston --- lib/toucanRender/ImageGraph.cpp | 87 +++++++++++++-------------------- lib/toucanRender/ImageGraph.h | 11 +++-- 2 files changed, 43 insertions(+), 55 deletions(-) diff --git a/lib/toucanRender/ImageGraph.cpp b/lib/toucanRender/ImageGraph.cpp index c11327f..3c7fce4 100644 --- a/lib/toucanRender/ImageGraph.cpp +++ b/lib/toucanRender/ImageGraph.cpp @@ -151,15 +151,11 @@ namespace toucan metaData["color"] = vecToAny(IMATH_NAMESPACE::V4f(0.F, 0.F, 0.F, 1.F)); std::shared_ptr node = host->createNode(metaData, "toucan:Fill"); - // Get the stack effects. + // Apply time warps. auto stack = _timelineWrapper->getTimeline()->tracks(); const auto& stackEffects = stack->effects(); - std::vector > stackEffectsNodes; OTIO_NS::RationalTime t = time - _timeRange.start_time(); - if (!stackEffects.empty()) - { - stackEffectsNodes = _effects(host, t, stack->available_range(), stackEffects); - } + t = _timeWarps(t, stack->available_range(), stackEffects); // Loop over the tracks. for (const auto& i : stack->children()) @@ -168,24 +164,19 @@ namespace toucan { if (track->kind() == OTIO_NS::Track::Kind::video && !track->find_clips().empty()) { - // Get the effects. - const auto& effects = track->effects(); - std::vector > effectsNodes; + // Apply time warps. + const auto& trackEffects = track->effects(); OTIO_NS::RationalTime t2 = t; - if (!effects.empty()) + if (!trackEffects.empty()) { - effectsNodes = _effects(host, t2, track->available_range(), effects); + t2 = _timeWarps(t2, track->available_range(), trackEffects); } // Process this track. auto trackNode = _track(host, t2, track); - // Add the effects. - if (!effectsNodes.empty()) - { - effectsNodes.front()->setInputs({ trackNode }); - trackNode = effectsNodes.back(); - } + // Add the track effects. + trackNode = _effects(host, t2, trackEffects, trackNode); // Composite this track over the previous track. std::vector > nodes; @@ -205,11 +196,7 @@ namespace toucan } // Add the stack effects. - if (!stackEffectsNodes.empty()) - { - stackEffectsNodes.front()->setInputs({ node }); - node = stackEffectsNodes.back(); - } + node = _effects(host, t, stackEffects, node); return node; } @@ -344,13 +331,9 @@ namespace toucan OTIO_NS::RationalTime t = time; - // Get the effects. + // Apply time warps. const auto& effects = item->effects(); - std::vector > effectsNodes; - if (!effects.empty()) - { - effectsNodes = _effects(host, t, item->available_range(), effects); - } + t = _timeWarps(t, item->available_range(), effects); // Get the media. if (auto clip = OTIO_NS::dynamic_retainer_cast(item)) @@ -426,45 +409,45 @@ namespace toucan } // Add the effects. - if (!effectsNodes.empty()) - { - effectsNodes.front()->setInputs({ out }); - out = effectsNodes.back(); - } + out = _effects(host, t, effects, out); return out; } - std::vector > ImageGraph::_effects( - const std::shared_ptr& host, - OTIO_NS::RationalTime& time, + OTIO_NS::RationalTime ImageGraph::_timeWarps( + const OTIO_NS::RationalTime& time, const OTIO_NS::TimeRange& timeRange, const std::vector >& effects) { - std::vector > out; + OTIO_NS::RationalTime out = time; for (const auto& effect : effects) { if (auto linearTimeWarp = dynamic_cast(effect.value)) { const double s = linearTimeWarp->time_scalar(); - time = OTIO_NS::RationalTime( - (time - timeRange.start_time()).value() * s, + out = OTIO_NS::RationalTime( + (out - timeRange.start_time()).value() * s, time.rate()).round(); } - else + } + return out; + } + + std::shared_ptr ImageGraph::_effects( + const std::shared_ptr& host, + const OTIO_NS::RationalTime& time, + const std::vector >& effects, + const std::shared_ptr& input) + { + std::shared_ptr out = input; + for (const auto& effect : effects) + { + if (auto imageEffect = host->createNode( + effect->metadata(), + effect->effect_name(), + { out })) { - std::vector > inputs; - if (!out.empty()) - { - inputs.push_back(out.back()); - } - if (auto imageEffect = host->createNode( - effect->metadata(), - effect->effect_name(), - inputs)) - { - out.push_back(imageEffect); - } + out = imageEffect; } } return out; diff --git a/lib/toucanRender/ImageGraph.h b/lib/toucanRender/ImageGraph.h index f474782..6c9b59a 100644 --- a/lib/toucanRender/ImageGraph.h +++ b/lib/toucanRender/ImageGraph.h @@ -54,12 +54,17 @@ namespace toucan const OTIO_NS::RationalTime&, const OTIO_NS::SerializableObject::Retainer&); - std::vector > _effects( - const std::shared_ptr&, - OTIO_NS::RationalTime&, + OTIO_NS::RationalTime _timeWarps( + const OTIO_NS::RationalTime&, const OTIO_NS::TimeRange&, const std::vector >&); + std::shared_ptr _effects( + const std::shared_ptr&, + const OTIO_NS::RationalTime&, + const std::vector >&, + const std::shared_ptr&); + std::weak_ptr _context; std::filesystem::path _path; std::shared_ptr _timelineWrapper; From 47e1161e2e55df019579fb5118eabe176317f4ed Mon Sep 17 00:00:00 2001 From: Darby Johnston Date: Tue, 16 Sep 2025 15:23:50 -0700 Subject: [PATCH 19/24] Update OpenImageIO version Signed-off-by: Darby Johnston --- cmake/SuperBuild/BuildOpenImageIO.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/SuperBuild/BuildOpenImageIO.cmake b/cmake/SuperBuild/BuildOpenImageIO.cmake index d6df8bc..8e4b840 100644 --- a/cmake/SuperBuild/BuildOpenImageIO.cmake +++ b/cmake/SuperBuild/BuildOpenImageIO.cmake @@ -1,7 +1,7 @@ include(ExternalProject) set(OpenImageIO_GIT_REPOSITORY "https://github.com/AcademySoftwareFoundation/OpenImageIO.git") -set(OpenImageIO_GIT_TAG "v3.0.9.1") +set(OpenImageIO_GIT_TAG "v3.0.10.0") set(OpenImageIO_DEPS) if(toucan_PNG) From b40d63acbc7467319d0f021637aff780e04a6070 Mon Sep 17 00:00:00 2001 From: Darby Johnston Date: Wed, 17 Sep 2025 11:15:43 -0700 Subject: [PATCH 20/24] Remove macOS 13 job Signed-off-by: Darby Johnston --- .github/workflows/ci-workflow.yml | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index 2a4b6be..9772e2e 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -78,36 +78,6 @@ jobs: - name: Upload artifact URL run: echo 'Artifact URL ${{ steps.linux-package-artifact.outputs.artifact-url }}' - macos-13-build: - runs-on: macos-13 - - env: - FFMPEG_MINIMAL: ON - - steps: - - uses: actions/checkout@v4 - with: - path: toucan - submodules: recursive - - # \bug DYLD_LIBRARY_PATH is not being set here? - # \bug Remove OpenEXR install. - - name: Setup - run: | - echo "$PWD/install-Debug/bin" >> $GITHUB_PATH - echo "DYLD_LIBRARY_PATH=$PWD/install-Debug/lib:$DYLD_LIBRARY_PATH" >> $GITHUB_ENV - cmake --version - brew uninstall --ignore-dependencies openexr imath || true - - - name: Build - run: sh toucan/sbuild-macos.sh Debug - - - name: Install - run: cmake --build build-Debug --config Debug --target install - - - name: Tests - run: build-Debug/tests/toucan-test/toucan-test toucan/data - macos-build: runs-on: macos-latest From d3a86100dd31a811c9535f5ed397a4d22c531bee Mon Sep 17 00:00:00 2001 From: Darby Johnston Date: Wed, 17 Sep 2025 18:30:33 -0700 Subject: [PATCH 21/24] Refactoring Signed-off-by: Darby Johnston --- lib/toucanRender/ImageGraph.cpp | 50 ++++++++------ lib/toucanRender/ImageGraph.h | 12 ++-- lib/toucanView/ThumbnailGenerator.cpp | 80 +++++++---------------- lib/toucanView/ThumbnailGenerator.h | 34 +++++----- lib/toucanView/ThumbnailsWidget.cpp | 25 +++---- lib/toucanView/ThumbnailsWidget.h | 3 - lib/toucanView/TimelineWidget.cpp | 4 +- lib/toucanView/VideoClipItem.cpp | 1 - tests/toucanRenderTest/ImageGraphTest.cpp | 6 +- 9 files changed, 98 insertions(+), 117 deletions(-) diff --git a/lib/toucanRender/ImageGraph.cpp b/lib/toucanRender/ImageGraph.cpp index 3c7fce4..a3bd0ca 100644 --- a/lib/toucanRender/ImageGraph.cpp +++ b/lib/toucanRender/ImageGraph.cpp @@ -143,13 +143,17 @@ namespace toucan std::shared_ptr ImageGraph::exec( const std::shared_ptr& host, - const OTIO_NS::RationalTime& time) + const OTIO_NS::RationalTime& time, + const OTIO_NS::Item* itemNode) { + _host = host; + _itemNode = itemNode; + // Set the background color. OTIO_NS::AnyDictionary metaData; metaData["size"] = vecToAny(_imageSize); metaData["color"] = vecToAny(IMATH_NAMESPACE::V4f(0.F, 0.F, 0.F, 1.F)); - std::shared_ptr node = host->createNode(metaData, "toucan:Fill"); + auto node = host->createNode(metaData, "toucan:Fill"); // Apply time warps. auto stack = _timelineWrapper->getTimeline()->tracks(); @@ -173,10 +177,10 @@ namespace toucan } // Process this track. - auto trackNode = _track(host, t2, track); + auto trackNode = _track(t2, track); // Add the track effects. - trackNode = _effects(host, t2, trackEffects, trackNode); + trackNode = _effects(t2, trackEffects, trackNode); // Composite this track over the previous track. std::vector > nodes; @@ -196,13 +200,21 @@ namespace toucan } // Add the stack effects. - node = _effects(host, t, stackEffects, node); + node = _effects(t, stackEffects, node); + + // Clean up. + _host.reset(); + _itemNode = nullptr; + if (_outNode) + { + node = _outNode; + } + _outNode.reset(); return node; } std::shared_ptr ImageGraph::_track( - const std::shared_ptr& host, const OTIO_NS::RationalTime& time, const OTIO_NS::SerializableObject::Retainer& track) { @@ -224,7 +236,6 @@ namespace toucan if (trimmedRangeInParent.has_value() && trimmedRangeInParent.value().contains(time)) { out = _item( - host, track->transformed_time(time, item), item); if (i > 0) @@ -263,19 +274,18 @@ namespace toucan trimmedRangeInParent.value().duration().value(); auto a = _item( - host, track->transformed_time(time, prevItem), prevItem); auto metaData = prevTransition->metadata(); metaData["value"] = value; - auto node = host->createNode( + auto node = _host->createNode( metaData, prevTransition->transition_type(), { a, out }); if (!node) { - node = host->createNode( + node = _host->createNode( metaData, "toucan:Dissolve", { a, out }); @@ -296,19 +306,18 @@ namespace toucan trimmedRangeInParent.value().duration().value(); auto b = _item( - host, track->transformed_time(time, nextItem), nextItem); auto metaData = nextTransition->metadata(); metaData["value"] = value; - auto node = host->createNode( + auto node = _host->createNode( metaData, nextTransition->transition_type(), { out, b }); if (!node) { - node = host->createNode( + node = _host->createNode( metaData, "toucan:Dissolve", { out, b }); @@ -323,7 +332,6 @@ namespace toucan } std::shared_ptr ImageGraph::_item( - const std::shared_ptr& host, const OTIO_NS::RationalTime& time, const OTIO_NS::SerializableObject::Retainer& item) { @@ -396,7 +404,7 @@ namespace toucan } else if (auto generatorRef = dynamic_cast(mediaRef)) { - out = host->createNode( + out = _host->createNode( generatorRef->parameters(), generatorRef->generator_kind()); } @@ -405,11 +413,16 @@ namespace toucan { OTIO_NS::AnyDictionary metaData; metaData["size"] = vecToAny(_imageSize); - out = host->createNode(metaData, "toucan:Fill"); + out = _host->createNode(metaData, "toucan:Fill"); } // Add the effects. - out = _effects(host, t, effects, out); + out = _effects(t, effects, out); + + if (item == _itemNode) + { + _outNode = out; + } return out; } @@ -434,7 +447,6 @@ namespace toucan } std::shared_ptr ImageGraph::_effects( - const std::shared_ptr& host, const OTIO_NS::RationalTime& time, const std::vector >& effects, const std::shared_ptr& input) @@ -442,7 +454,7 @@ namespace toucan std::shared_ptr out = input; for (const auto& effect : effects) { - if (auto imageEffect = host->createNode( + if (auto imageEffect = _host->createNode( effect->metadata(), effect->effect_name(), { out })) diff --git a/lib/toucanRender/ImageGraph.h b/lib/toucanRender/ImageGraph.h index 6c9b59a..246732d 100644 --- a/lib/toucanRender/ImageGraph.h +++ b/lib/toucanRender/ImageGraph.h @@ -9,7 +9,6 @@ #include #include -#include #include #include @@ -41,16 +40,15 @@ namespace toucan //! Get an image graph for the given time. std::shared_ptr exec( const std::shared_ptr&, - const OTIO_NS::RationalTime&); + const OTIO_NS::RationalTime&, + const OTIO_NS::Item* = nullptr); private: std::shared_ptr _track( - const std::shared_ptr&, const OTIO_NS::RationalTime&, const OTIO_NS::SerializableObject::Retainer&); std::shared_ptr _item( - const std::shared_ptr&, const OTIO_NS::RationalTime&, const OTIO_NS::SerializableObject::Retainer&); @@ -60,7 +58,6 @@ namespace toucan const std::vector >&); std::shared_ptr _effects( - const std::shared_ptr&, const OTIO_NS::RationalTime&, const std::vector >&, const std::shared_ptr&); @@ -73,5 +70,10 @@ namespace toucan int _imageChannels = 0; std::string _imageDataType; ftk::LRUCache > _readCache; + + // Temporary variables available during execution. + std::shared_ptr _host; + const OTIO_NS::Item* _itemNode = nullptr; + std::shared_ptr _outNode; }; } diff --git a/lib/toucanView/ThumbnailGenerator.cpp b/lib/toucanView/ThumbnailGenerator.cpp index 9815fcf..ae521a8 100644 --- a/lib/toucanView/ThumbnailGenerator.cpp +++ b/lib/toucanView/ThumbnailGenerator.cpp @@ -3,7 +3,10 @@ #include "ThumbnailGenerator.h" +#include +#include #include +#include #include @@ -16,13 +19,13 @@ namespace toucan { std::string getThumbnailCacheKey( - const OTIO_NS::MediaReference* ref, + const OTIO_NS::Item* item, const OTIO_NS::RationalTime& time, int height) { std::vector s; std::stringstream ss; - ss << ref; + ss << item; s.push_back(ss.str()); s.push_back(ftk::Format("{0}@{1}").arg(time.value()).arg(time.rate())); s.push_back(ftk::Format("{0}").arg(height)); @@ -31,13 +34,19 @@ namespace toucan ThumbnailGenerator::ThumbnailGenerator( const std::shared_ptr& context, + const std::shared_ptr& host, const std::shared_ptr& timelineWrapper) : + _host(host), _timelineWrapper(timelineWrapper) { _logSystem = context->getSystem(); + _graph = std::make_shared( + context, + timelineWrapper->getPath().parent_path(), + timelineWrapper); + _thread.running = true; - _thread.readCache.setMax(100); _thread.thread = std::thread( [this] { @@ -63,10 +72,12 @@ namespace toucan } std::future ThumbnailGenerator::getAspect( - const OTIO_NS::MediaReference* ref) + const OTIO_NS::Item* item, + const OTIO_NS::RationalTime& time) { auto request = std::make_shared(); - request->ref = ref; + request->item = item; + request->time = time; auto out = request->promise.get_future(); bool valid = false; { @@ -89,17 +100,15 @@ namespace toucan } ThumbnailRequest ThumbnailGenerator::getThumbnail( - const OTIO_NS::MediaReference* ref, + const OTIO_NS::Item* item, const OTIO_NS::RationalTime& time, - const OTIO_NS::TimeRange& availableRange, int height) { _requestId++; auto request = std::make_shared(); request->id = _requestId; - request->ref = ref; + request->item = item; request->time = time; - request->availableRange = availableRange; request->height = height; ThumbnailRequest out; out.id = _requestId; @@ -177,25 +186,12 @@ namespace toucan } if (aspectRequest) { - std::shared_ptr read; - try - { - read = _timelineWrapper->createReadNode(aspectRequest->ref); - } - catch (const std::exception& e) + float aspect = 1.F; + if (auto node = _graph->exec(_host, aspectRequest->time, aspectRequest->item)) { - _logSystem->print( - "toucan::ThumbnailGenerator", - e.what(), - ftk::LogType::Error); - } - float aspect = 0.F; - if (read) - { - read->setTime(read->getTimeRange().start_time()); - const OIIO::ImageBuf buf = read->exec(); - const OIIO::ImageSpec& spec = buf.spec(); - if (spec.height > 0) + OIIO::ImageBuf buf = node->exec(); + const auto& spec = buf.spec(); + if (spec.width > 0 && spec.height > 0) { aspect = spec.width / static_cast(spec.height); } @@ -204,36 +200,10 @@ namespace toucan } if (request) { - std::shared_ptr read; - if (!_thread.readCache.get(request->ref, read)) - { - try - { - read = _timelineWrapper->createReadNode(request->ref); - _thread.readCache.add(request->ref, read); - } - catch (const std::exception& e) - { - _logSystem->print( - "toucan::ThumbnailGenerator", - e.what(), - ftk::LogType::Error); - } - } - OIIO::ImageBuf buf; - if (read) + if (auto node = _graph->exec(_host, request->time, request->item)) { - //! \bug Workaround for files that are missing timecode. - OTIO_NS::RationalTime t = request->time; - if (request->availableRange.start_time() != - read->getTimeRange().start_time()) - { - t -= request->availableRange.start_time(); - } - - read->setTime(t); - buf = read->exec(); + buf = node->exec(); } std::shared_ptr thumbnail; diff --git a/lib/toucanView/ThumbnailGenerator.h b/lib/toucanView/ThumbnailGenerator.h index 676d120..1d50040 100644 --- a/lib/toucanView/ThumbnailGenerator.h +++ b/lib/toucanView/ThumbnailGenerator.h @@ -3,10 +3,9 @@ #pragma once -#include +#include #include -#include #include #include @@ -18,9 +17,14 @@ namespace toucan { + class IReadNode; + class ImageEffectHost; + class ImageGraph; + class TimelineWrapper; + //! Get a thumbnail cache key. std::string getThumbnailCacheKey( - const OTIO_NS::MediaReference*, + const OTIO_NS::Item*, const OTIO_NS::RationalTime&, int height); @@ -39,21 +43,20 @@ namespace toucan public: ThumbnailGenerator( const std::shared_ptr&, + const std::shared_ptr&, const std::shared_ptr&); ~ThumbnailGenerator(); - //! Get a media aspect ratio. - std::future getAspect(const OTIO_NS::MediaReference*); + //! Get an aspect ratio. + std::future getAspect( + const OTIO_NS::Item*, + const OTIO_NS::RationalTime&); - //! Get a media thumbnail. - //! - //! \bug The availableRange parameter is a workaround for files that - //! are missing timecode. + //! Get a thumbnail. ThumbnailRequest getThumbnail( - const OTIO_NS::MediaReference*, + const OTIO_NS::Item*, const OTIO_NS::RationalTime&, - const OTIO_NS::TimeRange& availableRange, int height); //! Cancel thumbnail requests. @@ -64,20 +67,22 @@ namespace toucan void _cancel(); std::shared_ptr _logSystem; + std::shared_ptr _host; std::shared_ptr _timelineWrapper; + std::shared_ptr _graph; struct AspectRequest { - const OTIO_NS::MediaReference* ref = nullptr; + const OTIO_NS::Item* item = nullptr; + OTIO_NS::RationalTime time; std::promise promise; }; struct Request { uint64_t id = 0; - const OTIO_NS::MediaReference* ref = nullptr; + const OTIO_NS::Item* item = nullptr; OTIO_NS::RationalTime time; - OTIO_NS::TimeRange availableRange; int height = 0; std::promise > promise; }; @@ -97,7 +102,6 @@ namespace toucan std::condition_variable cv; std::thread thread; std::atomic running; - ftk::LRUCache > readCache; }; Thread _thread; }; diff --git a/lib/toucanView/ThumbnailsWidget.cpp b/lib/toucanView/ThumbnailsWidget.cpp index 73ce6e7..45082e2 100644 --- a/lib/toucanView/ThumbnailsWidget.cpp +++ b/lib/toucanView/ThumbnailsWidget.cpp @@ -4,6 +4,7 @@ #include "ThumbnailsWidget.h" #include +#include #include #include @@ -14,7 +15,6 @@ namespace toucan const std::shared_ptr& context, const std::shared_ptr& timelineWrapper, const OTIO_NS::Clip* clip, - const OTIO_NS::MediaReference* ref, const std::shared_ptr& thumbnailGenerator, const std::shared_ptr > >& thumbnailCache, const OTIO_NS::TimeRange& timeRange, @@ -24,11 +24,12 @@ namespace toucan _timelineWrapper = timelineWrapper; _clip = clip; - _ref = ref; _thumbnailGenerator = thumbnailGenerator; _thumbnailCache = thumbnailCache; - _thumbnailAspectRequest = _thumbnailGenerator->getAspect(ref); + _thumbnailAspectRequest = _thumbnailGenerator->getAspect( + _clip, + timeRange.start_time()); } ThumbnailsWidget::~ThumbnailsWidget() @@ -38,14 +39,13 @@ namespace toucan const std::shared_ptr& context, const std::shared_ptr& timelineWrapper, const OTIO_NS::Clip* clip, - const OTIO_NS::MediaReference* ref, const std::shared_ptr& thumbnailGenerator, const std::shared_ptr > >& thumbnailCache, const OTIO_NS::TimeRange& timeRange, const std::shared_ptr& parent) { auto out = std::make_shared(); - out->_init(context, timelineWrapper, clip, ref, thumbnailGenerator, thumbnailCache, timeRange, parent); + out->_init(context, timelineWrapper, clip, thumbnailGenerator, thumbnailCache, timeRange, parent); return out; } @@ -76,7 +76,7 @@ namespace toucan if (i->future.valid() && i->future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { - const std::string cacheKey = getThumbnailCacheKey(_ref, i->time, _size.thumbnailHeight); + const std::string cacheKey = getThumbnailCacheKey(_clip, i->time, _size.thumbnailHeight); const auto image = i->future.get(); _thumbnailCache->add(cacheKey, image); _setDrawUpdate(); @@ -132,11 +132,9 @@ namespace toucan const ftk::Box2I g2(x, y, thumbnailWidth, _size.thumbnailHeight); if (ftk::intersects(g2, drawRect)) { - const OTIO_NS::RationalTime t = _timelineWrapper->getTimeline()->tracks()->transformed_time( - posToTime(x), - _clip) - _timelineWrapper->getTimeRange().start_time(); + const OTIO_NS::RationalTime t = posToTime(x); + const std::string cacheKey = getThumbnailCacheKey(_clip, t, _size.thumbnailHeight); std::shared_ptr image; - const std::string cacheKey = getThumbnailCacheKey(_ref, t, _size.thumbnailHeight); if (_thumbnailCache->get(cacheKey, image)) { if (image) @@ -158,9 +156,8 @@ namespace toucan if (j == _thumbnailRequests.end()) { _thumbnailRequests.push_back(_thumbnailGenerator->getThumbnail( - _ref, + _clip, t, - _clip->available_range(), _size.thumbnailHeight)); } } @@ -171,9 +168,7 @@ namespace toucan auto i = _thumbnailRequests.begin(); while (i != _thumbnailRequests.end()) { - const int x = timeToPos( - _timelineWrapper->getTimeRange().start_time() + - _clip->transformed_time(i->time, _timelineWrapper->getTimeline()->tracks())); + const int x = timeToPos(i->time); const ftk::Box2I g2(x, y, thumbnailWidth, _size.thumbnailHeight); if (!ftk::intersects(g2, drawRect)) { diff --git a/lib/toucanView/ThumbnailsWidget.h b/lib/toucanView/ThumbnailsWidget.h index 5bc1895..56fc5ec 100644 --- a/lib/toucanView/ThumbnailsWidget.h +++ b/lib/toucanView/ThumbnailsWidget.h @@ -22,7 +22,6 @@ namespace toucan const std::shared_ptr&, const std::shared_ptr&, const OTIO_NS::Clip*, - const OTIO_NS::MediaReference*, const std::shared_ptr&, const std::shared_ptr > >&, const OTIO_NS::TimeRange&, @@ -36,7 +35,6 @@ namespace toucan const std::shared_ptr&, const std::shared_ptr&, const OTIO_NS::Clip*, - const OTIO_NS::MediaReference*, const std::shared_ptr&, const std::shared_ptr > >&, const OTIO_NS::TimeRange&, @@ -57,7 +55,6 @@ namespace toucan std::shared_ptr _timelineWrapper; const OTIO_NS::Clip* _clip = nullptr; - const OTIO_NS::MediaReference* _ref = nullptr; float _thumbnailAspect = 0.F; std::shared_ptr _thumbnailGenerator; std::shared_ptr > > _thumbnailCache; diff --git a/lib/toucanView/TimelineWidget.cpp b/lib/toucanView/TimelineWidget.cpp index 4d582d5..80a1727 100644 --- a/lib/toucanView/TimelineWidget.cpp +++ b/lib/toucanView/TimelineWidget.cpp @@ -63,12 +63,14 @@ namespace toucan } auto context = getContext(); + auto app = appWeak.lock(); _thumbnailGenerator = std::make_shared( context, + app->getHost(), file->getTimelineWrapper()); ItemData data; - data.app = appWeak.lock(); + data.app = app; data.file = file; data.thumbnailGenerator = _thumbnailGenerator; data.thumbnailCache = std::make_shared > >(); diff --git a/lib/toucanView/VideoClipItem.cpp b/lib/toucanView/VideoClipItem.cpp index d43e971..c79ee5f 100644 --- a/lib/toucanView/VideoClipItem.cpp +++ b/lib/toucanView/VideoClipItem.cpp @@ -57,7 +57,6 @@ namespace toucan context, timelineWrapper, _clip, - _clip->media_reference(), data.thumbnailGenerator, data.thumbnailCache, timeRange, diff --git a/tests/toucanRenderTest/ImageGraphTest.cpp b/tests/toucanRenderTest/ImageGraphTest.cpp index dbf9d71..10362a6 100644 --- a/tests/toucanRenderTest/ImageGraphTest.cpp +++ b/tests/toucanRenderTest/ImageGraphTest.cpp @@ -59,10 +59,10 @@ namespace toucan { std::cout << " " << otioFile << ": " << time.value() << "/" << timeRange.duration().value() << std::endl; - if (auto op = graph->exec(host, time)) + if (auto node = graph->exec(host, time)) { - // Execute the image operation graph. - const auto buf = op->exec(); + // Execute the image graph. + const auto buf = node->exec(); // Save the image. const std::string fileName = getSequenceFrame( From 1d28f679ce1961f7a0c9b9f399bd10e69104dd36 Mon Sep 17 00:00:00 2001 From: Darby Johnston Date: Thu, 18 Sep 2025 16:53:48 -0700 Subject: [PATCH 22/24] Refactoring Signed-off-by: Darby Johnston --- lib/toucanView/AudioClipItem.cpp | 1 - lib/toucanView/GapItem.cpp | 1 - lib/toucanView/GapItem.h | 1 - lib/toucanView/IItem.cpp | 11 ----------- lib/toucanView/IItem.h | 5 ----- lib/toucanView/StackItem.cpp | 21 ++++++++++++++++++++- lib/toucanView/StackItem.h | 6 +++++- lib/toucanView/ThumbnailsWidget.cpp | 16 ++++++++-------- lib/toucanView/ThumbnailsWidget.h | 8 ++++---- lib/toucanView/TimeLayout.cpp | 4 ++-- lib/toucanView/TimelineItem.cpp | 3 ++- lib/toucanView/TimelineWidget.cpp | 1 + lib/toucanView/TrackItem.cpp | 1 - 13 files changed, 42 insertions(+), 37 deletions(-) diff --git a/lib/toucanView/AudioClipItem.cpp b/lib/toucanView/AudioClipItem.cpp index 8831c58..5138422 100644 --- a/lib/toucanView/AudioClipItem.cpp +++ b/lib/toucanView/AudioClipItem.cpp @@ -108,7 +108,6 @@ namespace toucan _layout->setGeometry(value); _geom.g2 = ftk::margin(value, -_size.border, 0, -_size.border, 0); _geom.g3 = ftk::margin(_label->getGeometry(), -_size.border, 0, -_size.border, 0); - _selectionRect = _geom.g3; } ftk::Box2I AudioClipItem::getChildrenClipRect() const diff --git a/lib/toucanView/GapItem.cpp b/lib/toucanView/GapItem.cpp index 765d01a..1090b31 100644 --- a/lib/toucanView/GapItem.cpp +++ b/lib/toucanView/GapItem.cpp @@ -99,7 +99,6 @@ namespace toucan _layout->setGeometry(value); _geom.g2 = ftk::margin(value, -_size.border, 0, -_size.border, 0); _geom.g3 = ftk::margin(_label->getGeometry(), -_size.border, 0, -_size.border, 0); - _selectionRect = _geom.g3; } ftk::Box2I GapItem::getChildrenClipRect() const diff --git a/lib/toucanView/GapItem.h b/lib/toucanView/GapItem.h index 65c333e..f52c749 100644 --- a/lib/toucanView/GapItem.h +++ b/lib/toucanView/GapItem.h @@ -44,7 +44,6 @@ namespace toucan private: void _textUpdate(); - const OTIO_NS::Timeline* _timeline = nullptr; const OTIO_NS::Gap* _gap = nullptr; std::string _text; ftk::Color4F _color; diff --git a/lib/toucanView/IItem.cpp b/lib/toucanView/IItem.cpp index 27855d3..bf1fdb0 100644 --- a/lib/toucanView/IItem.cpp +++ b/lib/toucanView/IItem.cpp @@ -40,11 +40,6 @@ namespace toucan return _object; } - const ftk::Box2I& IItem::getSelectionRect() const - { - return _selectionRect; - } - bool IItem::isSelected() const { return _selected; @@ -58,12 +53,6 @@ namespace toucan _setDrawUpdate(); } - void IItem::setGeometry(const ftk::Box2I& value) - { - ITimeWidget::setGeometry(value); - _selectionRect = value; - } - void IItem::mousePressEvent(ftk::MouseClickEvent& event) { ITimeWidget::mousePressEvent(event); diff --git a/lib/toucanView/IItem.h b/lib/toucanView/IItem.h index 884cdfc..6a80ef8 100644 --- a/lib/toucanView/IItem.h +++ b/lib/toucanView/IItem.h @@ -44,9 +44,6 @@ namespace toucan //! Get the OTIO object. const OTIO_NS::SerializableObjectWithMetadata* getObject() const; - - //! Get the item selection rectangle. - const ftk::Box2I& getSelectionRect() const; //! Get whether the item is selected. bool isSelected() const; @@ -54,7 +51,6 @@ namespace toucan //! Set whether the item is selected. void setSelected(bool); - void setGeometry(const ftk::Box2I&) override; void mousePressEvent(ftk::MouseClickEvent&) override; void mouseReleaseEvent(ftk::MouseClickEvent&) override; @@ -66,7 +62,6 @@ namespace toucan std::shared_ptr _file; const OTIO_NS::SerializableObjectWithMetadata* _object = nullptr; TimeUnits _timeUnits = TimeUnits::First; - ftk::Box2I _selectionRect; bool _selected = false; std::shared_ptr _menu; diff --git a/lib/toucanView/StackItem.cpp b/lib/toucanView/StackItem.cpp index 9431d29..c7fb24a 100644 --- a/lib/toucanView/StackItem.cpp +++ b/lib/toucanView/StackItem.cpp @@ -3,8 +3,11 @@ #include "StackItem.h" +#include "App.h" #include "File.h" +#include "ThumbnailsWidget.h" #include "TrackItem.h" +#include "WindowModel.h" #include #include @@ -45,6 +48,15 @@ namespace toucan _label = ItemLabel::create(context, _layout); _label->setName(_text); + _thumbnailsWidget = ThumbnailsWidget::create( + context, + timelineWrapper, + _stack, + data.thumbnailGenerator, + data.thumbnailCache, + timeRange, + _layout); + const auto& markers = stack->markers(); if (!markers.empty()) { @@ -79,6 +91,13 @@ namespace toucan } _textUpdate(); + + _thumbnailsObserver = ftk::ValueObserver::create( + data.app->getWindowModel()->observeThumbnails(), + [this](bool value) + { + _thumbnailsWidget->setVisible(value); + }); } StackItem::~StackItem() @@ -98,6 +117,7 @@ namespace toucan void StackItem::setScale(double value) { IItem::setScale(value); + _thumbnailsWidget->setScale(value); if (_markerLayout) { _markerLayout->setScale(value); @@ -111,7 +131,6 @@ namespace toucan _layout->setGeometry(value); _geom.g2 = ftk::margin(value, -_size.border, 0, -_size.border, 0); _geom.g3 = ftk::margin(_label->getGeometry(), -_size.border, 0, -_size.border, 0); - _selectionRect = _geom.g3; } ftk::Box2I StackItem::getChildrenClipRect() const diff --git a/lib/toucanView/StackItem.h b/lib/toucanView/StackItem.h index 7a27df2..ce73dd7 100644 --- a/lib/toucanView/StackItem.h +++ b/lib/toucanView/StackItem.h @@ -11,6 +11,8 @@ namespace toucan { + class ThumbnailsWidget; + //! Timeline stack item. class StackItem : public IItem { @@ -44,17 +46,19 @@ namespace toucan private: void _textUpdate(); - const OTIO_NS::Timeline* _timeline = nullptr; const OTIO_NS::Stack* _stack = nullptr; std::string _text; ftk::Color4F _color; std::shared_ptr _layout; std::shared_ptr _label; + std::shared_ptr _thumbnailsWidget; std::shared_ptr _markerLayout; std::vector > _markerItems; std::shared_ptr _timeLayout; + std::shared_ptr > _thumbnailsObserver; + struct SizeData { bool init = true; diff --git a/lib/toucanView/ThumbnailsWidget.cpp b/lib/toucanView/ThumbnailsWidget.cpp index 45082e2..14a93b5 100644 --- a/lib/toucanView/ThumbnailsWidget.cpp +++ b/lib/toucanView/ThumbnailsWidget.cpp @@ -14,7 +14,7 @@ namespace toucan void ThumbnailsWidget::_init( const std::shared_ptr& context, const std::shared_ptr& timelineWrapper, - const OTIO_NS::Clip* clip, + const OTIO_NS::Item* item, const std::shared_ptr& thumbnailGenerator, const std::shared_ptr > >& thumbnailCache, const OTIO_NS::TimeRange& timeRange, @@ -23,12 +23,12 @@ namespace toucan ITimeWidget::_init(context, timeRange, "toucan::ThumbnailsWidget", parent); _timelineWrapper = timelineWrapper; - _clip = clip; + _item = item; _thumbnailGenerator = thumbnailGenerator; _thumbnailCache = thumbnailCache; _thumbnailAspectRequest = _thumbnailGenerator->getAspect( - _clip, + _item, timeRange.start_time()); } @@ -38,14 +38,14 @@ namespace toucan std::shared_ptr ThumbnailsWidget::create( const std::shared_ptr& context, const std::shared_ptr& timelineWrapper, - const OTIO_NS::Clip* clip, + const OTIO_NS::Item* item, const std::shared_ptr& thumbnailGenerator, const std::shared_ptr > >& thumbnailCache, const OTIO_NS::TimeRange& timeRange, const std::shared_ptr& parent) { auto out = std::make_shared(); - out->_init(context, timelineWrapper, clip, thumbnailGenerator, thumbnailCache, timeRange, parent); + out->_init(context, timelineWrapper, item, thumbnailGenerator, thumbnailCache, timeRange, parent); return out; } @@ -76,7 +76,7 @@ namespace toucan if (i->future.valid() && i->future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { - const std::string cacheKey = getThumbnailCacheKey(_clip, i->time, _size.thumbnailHeight); + const std::string cacheKey = getThumbnailCacheKey(_item, i->time, _size.thumbnailHeight); const auto image = i->future.get(); _thumbnailCache->add(cacheKey, image); _setDrawUpdate(); @@ -133,7 +133,7 @@ namespace toucan if (ftk::intersects(g2, drawRect)) { const OTIO_NS::RationalTime t = posToTime(x); - const std::string cacheKey = getThumbnailCacheKey(_clip, t, _size.thumbnailHeight); + const std::string cacheKey = getThumbnailCacheKey(_item, t, _size.thumbnailHeight); std::shared_ptr image; if (_thumbnailCache->get(cacheKey, image)) { @@ -156,7 +156,7 @@ namespace toucan if (j == _thumbnailRequests.end()) { _thumbnailRequests.push_back(_thumbnailGenerator->getThumbnail( - _clip, + _item, t, _size.thumbnailHeight)); } diff --git a/lib/toucanView/ThumbnailsWidget.h b/lib/toucanView/ThumbnailsWidget.h index 56fc5ec..42a3f73 100644 --- a/lib/toucanView/ThumbnailsWidget.h +++ b/lib/toucanView/ThumbnailsWidget.h @@ -8,7 +8,7 @@ #include -#include +#include namespace toucan { @@ -21,7 +21,7 @@ namespace toucan void _init( const std::shared_ptr&, const std::shared_ptr&, - const OTIO_NS::Clip*, + const OTIO_NS::Item*, const std::shared_ptr&, const std::shared_ptr > >&, const OTIO_NS::TimeRange&, @@ -34,7 +34,7 @@ namespace toucan static std::shared_ptr create( const std::shared_ptr&, const std::shared_ptr&, - const OTIO_NS::Clip*, + const OTIO_NS::Item*, const std::shared_ptr&, const std::shared_ptr > >&, const OTIO_NS::TimeRange&, @@ -54,7 +54,7 @@ namespace toucan void _cancelThumbnails(); std::shared_ptr _timelineWrapper; - const OTIO_NS::Clip* _clip = nullptr; + const OTIO_NS::Item* _item = nullptr; float _thumbnailAspect = 0.F; std::shared_ptr _thumbnailGenerator; std::shared_ptr > > _thumbnailCache; diff --git a/lib/toucanView/TimeLayout.cpp b/lib/toucanView/TimeLayout.cpp index b7cec37..1958fad 100644 --- a/lib/toucanView/TimeLayout.cpp +++ b/lib/toucanView/TimeLayout.cpp @@ -106,6 +106,7 @@ namespace toucan void TimeLayout::setGeometry(const ftk::Box2I& value) { ITimeWidget::setGeometry(value); + const int h = value.h(); for (const auto& child : getChildren()) { if (auto timeWidget = std::dynamic_pointer_cast(child)) @@ -113,12 +114,11 @@ namespace toucan const OTIO_NS::TimeRange& timeRange = timeWidget->getTimeRange(); const int t0 = timeToPos(timeRange.start_time()); const int t1 = timeToPos(timeRange.end_time_exclusive()); - const ftk::Size2I& childSizeHint = child->getSizeHint(); child->setGeometry(ftk::Box2I( t0, value.min.y, std::max(t1 - t0, timeWidget->getMinWidth()), - childSizeHint.h)); + h)); } } } diff --git a/lib/toucanView/TimelineItem.cpp b/lib/toucanView/TimelineItem.cpp index f5b935e..f8eb861 100644 --- a/lib/toucanView/TimelineItem.cpp +++ b/lib/toucanView/TimelineItem.cpp @@ -6,6 +6,7 @@ #include "App.h" #include "FilesModel.h" #include "StackItem.h" +#include "ThumbnailsWidget.h" #include "WindowModel.h" #include @@ -462,7 +463,7 @@ namespace toucan { if (auto iitem = std::dynamic_pointer_cast(widget)) { - if (ftk::contains(iitem->getSelectionRect(), pos)) + if (ftk::contains(iitem->getGeometry(), pos)) { out = iitem; } diff --git a/lib/toucanView/TimelineWidget.cpp b/lib/toucanView/TimelineWidget.cpp index 80a1727..94b3b5d 100644 --- a/lib/toucanView/TimelineWidget.cpp +++ b/lib/toucanView/TimelineWidget.cpp @@ -258,6 +258,7 @@ namespace toucan void TimelineWidget::mouseReleaseEvent(ftk::MouseClickEvent& event) { IWidget::mouseReleaseEvent(event); + event.accept = true; } void TimelineWidget::scrollEvent(ftk::ScrollEvent& event) diff --git a/lib/toucanView/TrackItem.cpp b/lib/toucanView/TrackItem.cpp index 068155a..9f6b7c1 100644 --- a/lib/toucanView/TrackItem.cpp +++ b/lib/toucanView/TrackItem.cpp @@ -140,7 +140,6 @@ namespace toucan _layout->setGeometry(value); _geom.g2 = ftk::margin(value, -_size.border, 0, -_size.border, 0); _geom.g3 = ftk::margin(_label->getGeometry(), -_size.border, 0, -_size.border, 0); - _selectionRect = _geom.g3; } ftk::Box2I TrackItem::getChildrenClipRect() const From e09c53af3bfb954546de510d1f3d688ac1394b13 Mon Sep 17 00:00:00 2001 From: Darby Johnston Date: Thu, 18 Sep 2025 17:26:53 -0700 Subject: [PATCH 23/24] Adjust cache settings Signed-off-by: Darby Johnston --- lib/toucanRender/ImageGraph.cpp | 2 +- lib/toucanView/TimelineWidget.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/toucanRender/ImageGraph.cpp b/lib/toucanRender/ImageGraph.cpp index a3bd0ca..d6c1545 100644 --- a/lib/toucanRender/ImageGraph.cpp +++ b/lib/toucanRender/ImageGraph.cpp @@ -48,7 +48,7 @@ namespace toucan _timelineWrapper(timelineWrapper), _timeRange(timelineWrapper->getTimeRange()) { - _readCache.setMax(100); + _readCache.setMax(20); // Get the image information from the first video clip. for (auto clip : getVideoClips(_timelineWrapper->getTimeline())) diff --git a/lib/toucanView/TimelineWidget.cpp b/lib/toucanView/TimelineWidget.cpp index 94b3b5d..c2906a6 100644 --- a/lib/toucanView/TimelineWidget.cpp +++ b/lib/toucanView/TimelineWidget.cpp @@ -74,7 +74,7 @@ namespace toucan data.file = file; data.thumbnailGenerator = _thumbnailGenerator; data.thumbnailCache = std::make_shared > >(); - data.thumbnailCache->setMax(1000); + data.thumbnailCache->setMax(200); _timelineItem = TimelineItem::create(getContext(), data); _timelineItem->setScale(_scale); _timelineItem->setCurrentTimeCallback( From d24fa81d0110d89ad868eece6e9d988ef9f63535 Mon Sep 17 00:00:00 2001 From: Darby Johnston Date: Thu, 18 Sep 2025 17:35:48 -0700 Subject: [PATCH 24/24] Add app icon Signed-off-by: Darby Johnston --- lib/CMakeLists.txt | 1 + lib/toucanRender/CMakeLists.txt | 1 + lib/toucanResource/CMakeLists.txt | 25 +++++++++++++++++++++++++ lib/toucanView/MainWindow.cpp | 9 +++++++++ 4 files changed, 36 insertions(+) create mode 100644 lib/toucanResource/CMakeLists.txt diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 3deb957..94c141f 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,3 +1,4 @@ +add_subdirectory(toucanResource) add_subdirectory(toucanRender) if(toucan_VIEW) add_subdirectory(toucanView) diff --git a/lib/toucanRender/CMakeLists.txt b/lib/toucanRender/CMakeLists.txt index a27d40f..b2199c0 100644 --- a/lib/toucanRender/CMakeLists.txt +++ b/lib/toucanRender/CMakeLists.txt @@ -45,6 +45,7 @@ endif() add_library(toucanRender ${HEADERS} ${HEADERS_PRIVATE} ${SOURCE}) set(LIBS_PUBLIC + toucanResource ftk::feather-tk-core OTIO::opentimelineio OTIO::opentime diff --git a/lib/toucanResource/CMakeLists.txt b/lib/toucanResource/CMakeLists.txt new file mode 100644 index 0000000..926e01a --- /dev/null +++ b/lib/toucanResource/CMakeLists.txt @@ -0,0 +1,25 @@ +set(HEADERS) +set(SOURCE) +set(RESOURCES + images/toucan_64.svg) +foreach(RESOURCE ${RESOURCES}) + get_filename_component(RESOURCE_BASE ${RESOURCE} NAME_WE) + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${RESOURCE_BASE}.cpp + COMMAND $ ${CMAKE_SOURCE_DIR}/${RESOURCE} ${RESOURCE_BASE} toucan_resource + DEPENDS ${CMAKE_SOURCE_DIR}/${RESOURCE}) + list(APPEND SOURCE ${CMAKE_CURRENT_BINARY_DIR}/${RESOURCE_BASE}.cpp) +endforeach() + +add_library(toucanResource ${HEADERS} ${SOURCE}) +target_link_libraries(toucanResource) +set_target_properties(toucanResource PROPERTIES FOLDER lib) +set_target_properties(toucanResource PROPERTIES PUBLIC_HEADER "${HEADERS}") + +if(BUILD_SHARED_LIBS) + install( + TARGETS toucanResource + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin) +endif() diff --git a/lib/toucanView/MainWindow.cpp b/lib/toucanView/MainWindow.cpp index 9f4eb73..3586bc6 100644 --- a/lib/toucanView/MainWindow.cpp +++ b/lib/toucanView/MainWindow.cpp @@ -26,6 +26,11 @@ #include +namespace toucan_resource +{ + extern std::vector toucan_64; +} + namespace toucan { void MainWindow::_init( @@ -54,6 +59,10 @@ namespace toucan {} setDisplayScale(displayScale); + auto iconSystem = context->getSystem(); + iconSystem->add("toucan_64", toucan_resource::toucan_64); + setIcon(iconSystem->get("toucan_64", 1.0)); + _layout = ftk::VerticalLayout::create(context, shared_from_this()); _layout->setSpacingRole(ftk::SizeRole::None);