From 83997e60d7fd2fa5d7738054b78694fe44aef149 Mon Sep 17 00:00:00 2001 From: Darby Johnston Date: Mon, 19 Aug 2024 15:29:26 -0700 Subject: [PATCH] Refactoring --- CMakeLists.txt | 2 + README.md | 7 +- cmake/SuperBuild/BuildOpenColorIO.cmake | 32 ++++ cmake/SuperBuild/BuildOpenImageIO.cmake | 4 +- cmake/SuperBuild/Buildexpat.cmake | 19 +++ cmake/SuperBuild/Buildminizip-ng.cmake | 27 ++++ cmake/SuperBuild/Buildpystring.cmake | 17 +++ cmake/SuperBuild/Buildyaml-cpp.cmake | 18 +++ cmake/SuperBuild/CMakeLists.txt | 5 + .../SuperBuild/pystring-patch/CMakeLists.txt | 24 +++ data/ColorSpace.otio | 144 ++++++++++++++++++ images/ColorSpace.png | Bin 0 -> 50398 bytes legal/LICENSE_expat.txt | 21 +++ legal/LICENSE_minizip-ng.txt | 17 +++ legal/LICENSE_pystring.txt | 27 ++++ legal/LICENSE_yaml-cpp.txt | 19 +++ lib/toucan/CMakeLists.txt | 2 +- lib/toucan/ColorSpace.cpp | 87 +++++++++++ lib/toucan/ColorSpace.h | 60 ++++++++ lib/toucan/Init.cpp | 4 + lib/toucan/Util.cpp | 5 +- plugins/CMakeLists.txt | 2 +- plugins/ColorSpacePlugin.cpp | 101 ++++++++++++ plugins/ColorSpacePlugin.h | 31 ++++ tests/TimelineGraphTest.cpp | 1 + 25 files changed, 668 insertions(+), 8 deletions(-) create mode 100644 cmake/SuperBuild/BuildOpenColorIO.cmake create mode 100644 cmake/SuperBuild/Buildexpat.cmake create mode 100644 cmake/SuperBuild/Buildminizip-ng.cmake create mode 100644 cmake/SuperBuild/Buildpystring.cmake create mode 100644 cmake/SuperBuild/Buildyaml-cpp.cmake create mode 100644 cmake/SuperBuild/pystring-patch/CMakeLists.txt create mode 100644 data/ColorSpace.otio create mode 100644 images/ColorSpace.png create mode 100644 legal/LICENSE_expat.txt create mode 100644 legal/LICENSE_minizip-ng.txt create mode 100644 legal/LICENSE_pystring.txt create mode 100644 legal/LICENSE_yaml-cpp.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index ec431f8..5a2a2d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,8 @@ find_package(PNG REQUIRED) find_package(JPEG REQUIRED) find_package(TIFF REQUIRED) find_package(OpenEXR REQUIRED) +find_package(minizip REQUIRED) +find_package(OpenColorIO REQUIRED) find_package(OpenImageIO REQUIRED) find_package(OTIO REQUIRED) find_package(OpenFX REQUIRED) diff --git a/README.md b/README.md index 3dd82a0..188acf2 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ with image sequences, effects, and transitions. Dependencies: * [OpenTimelineIO](https://github.com/AcademySoftwareFoundation/OpenTimelineIO) - Timelines * [OpenImageIO](https://github.com/AcademySoftwareFoundation/OpenImageIO) - Image I/O and processing +* [OpenColorIO](https://github.com/AcademySoftwareFoundation/OpenColorIO) - Color space conversion * [OpenFX](https://github.com/AcademySoftwareFoundation/openfx) - Plugins Features @@ -19,7 +20,7 @@ Features * Filters: Blur, Color Map, Invert, Power, Saturate, Unsharp Mask * Transforms: Flip, Flop, Resize, Rotate * Transitions: Dissolve, Horizontal Wipe, Vertical Wipe -* Color space conversion: Premultiply Alpha, Un-Premultiply Alpha +* Color spaces: Color Convert, Premultiply Alpha, Un-Premultiply Alpha Example Renders =============== @@ -69,6 +70,10 @@ Transforms; resize, rotate, flip, and flop: ![Transforms](images/Transform.png) +Color space conversion: + +![Color Space](images/ColorSpace.png) + Building ======== diff --git a/cmake/SuperBuild/BuildOpenColorIO.cmake b/cmake/SuperBuild/BuildOpenColorIO.cmake new file mode 100644 index 0000000..3b3b25e --- /dev/null +++ b/cmake/SuperBuild/BuildOpenColorIO.cmake @@ -0,0 +1,32 @@ +include(ExternalProject) + +set(OpenColorIO_GIT_REPOSITORY "https://github.com/AcademySoftwareFoundation/OpenColorIO.git") +set(OpenColorIO_GIT_TAG "v2.3.2") + +set(OpenColorIO_ARGS + ${toucan_EXTERNAL_PROJECT_ARGS} + -DCMAKE_INSTALL_LIBDIR=lib + -DOCIO_BUILD_APPS=OFF + -DOCIO_BUILD_TESTS=OFF + -DOCIO_BUILD_GPU_TESTS=OFF + -DOCIO_BUILD_PYTHON=OFF + -DOCIO_INSTALL_EXT_PACKAGES=NONE) +cmake_host_system_information(RESULT HAS_SSE2 QUERY HAS_SSE2) +list(APPEND OpenColorIO_ARGS -DOCIO_USE_SSE2=${HAS_SSE2}) +#if(APPLE) +# execute_process( +# COMMAND uname -m +# RESULT_VARIABLE result +# OUTPUT_VARIABLE OpenColorIO_OSX_NATIVE_ARCH +# OUTPUT_STRIP_TRAILING_WHITESPACE) +# list(APPEND OpenColorIO_ARGS -DCMAKE_OSX_ARCHITECTURES=${OpenColorIO_OSX_NATIVE_ARCH}) +#endif() + +ExternalProject_Add( + OpenColorIO + PREFIX ${CMAKE_CURRENT_BINARY_DIR}/OpenColorIO + DEPENDS Imath yaml-cpp expat pystring minizip-ng ZLIB + GIT_REPOSITORY ${OpenColorIO_GIT_REPOSITORY} + GIT_TAG ${OpenColorIO_GIT_TAG} + LIST_SEPARATOR | + CMAKE_ARGS ${OpenColorIO_ARGS}) diff --git a/cmake/SuperBuild/BuildOpenImageIO.cmake b/cmake/SuperBuild/BuildOpenImageIO.cmake index d8e964e..7af354e 100644 --- a/cmake/SuperBuild/BuildOpenImageIO.cmake +++ b/cmake/SuperBuild/BuildOpenImageIO.cmake @@ -13,6 +13,7 @@ set(OpenImageIO_ARGS -DOIIO_INSTALL_FONTS=ON -DUSE_FREETYPE=ON -DUSE_PNG=ON + -DUSE_OPENCOLORIO=ON -DUSE_BZIP2=OFF -DUSE_DCMTK=OFF -DUSE_FFMPEG=OFF @@ -21,7 +22,6 @@ set(OpenImageIO_ARGS -DUSE_LIBHEIF=OFF -DUSE_LIBRAW=OFF -DUSE_NUKE=OFF - -DUSE_OPENCOLORIO=OFF -DUSE_OPENCV=OFF -DUSE_OPENJPEG=OFF -DUSE_OPENVDB=OFF @@ -34,7 +34,7 @@ set(OpenImageIO_ARGS ExternalProject_Add( OpenImageIO PREFIX ${CMAKE_CURRENT_BINARY_DIR}/OpenImageIO - DEPENDS TIFF PNG libjpeg-turbo OpenEXR Freetype + DEPENDS TIFF PNG libjpeg-turbo OpenEXR OpenColorIO Freetype GIT_REPOSITORY ${OpenImageIO_GIT_REPOSITORY} GIT_TAG ${OpenImageIO_GIT_TAG} CMAKE_ARGS ${OpenImageIO_ARGS}) diff --git a/cmake/SuperBuild/Buildexpat.cmake b/cmake/SuperBuild/Buildexpat.cmake new file mode 100644 index 0000000..ecd53da --- /dev/null +++ b/cmake/SuperBuild/Buildexpat.cmake @@ -0,0 +1,19 @@ +include(ExternalProject) + +set(expat_GIT_REPOSITORY "https://github.com/libexpat/libexpat.git") +set(expat_GIT_TAG "R_2_5_0") + +set(expat_ARGS + ${toucan_EXTERNAL_PROJECT_ARGS} + -DEXPAT_BUILD_TOOLS=OFF + -DEXPAT_BUILD_EXAMPLES=OFF + -DEXPAT_BUILD_TESTS=OFF) + +ExternalProject_Add( + expat + PREFIX ${CMAKE_CURRENT_BINARY_DIR}/expat + GIT_REPOSITORY ${expat_GIT_REPOSITORY} + GIT_TAG ${expat_GIT_TAG} + SOURCE_SUBDIR expat + LIST_SEPARATOR | + CMAKE_ARGS ${expat_ARGS}) diff --git a/cmake/SuperBuild/Buildminizip-ng.cmake b/cmake/SuperBuild/Buildminizip-ng.cmake new file mode 100644 index 0000000..56f8350 --- /dev/null +++ b/cmake/SuperBuild/Buildminizip-ng.cmake @@ -0,0 +1,27 @@ +include(ExternalProject) + +set(minizip-ng_GIT_REPOSITORY "https://github.com/zlib-ng/minizip-ng.git") +set(minizip-ng_GIT_TAG "3.0.7") + +set(minizip-ng_ARGS + ${toucan_EXTERNAL_PROJECT_ARGS} + -DMZ_BZIP2=OFF + -DMZ_LZMA=OFF + -DMZ_ZSTD=OFF + -DMZ_LIBCOMP=OFF + -DMZ_FETCH_LIBS=OFF + -DMZ_PKCRYPT=OFF + -DMZ_WZAES=OFF + -DMZ_OPENSSL=OFF + -DMZ_BCRYPT=OFF + -DMZ_LIBBSD=OFF + -DMZ_ICONV=OFF) + +ExternalProject_Add( + minizip-ng + PREFIX ${CMAKE_CURRENT_BINARY_DIR}/minizip-ng + DEPENDS ZLIB + GIT_REPOSITORY ${minizip-ng_GIT_REPOSITORY} + GIT_TAG ${minizip-ng_GIT_TAG} + LIST_SEPARATOR | + CMAKE_ARGS ${minizip-ng_ARGS}) diff --git a/cmake/SuperBuild/Buildpystring.cmake b/cmake/SuperBuild/Buildpystring.cmake new file mode 100644 index 0000000..c7c21a3 --- /dev/null +++ b/cmake/SuperBuild/Buildpystring.cmake @@ -0,0 +1,17 @@ +include(ExternalProject) + +set(pystring_GIT_REPOSITORY "https://github.com/imageworks/pystring.git") +set(pystring_GIT_TAG "v1.1.4") + +set(pystring_ARGS ${toucan_EXTERNAL_PROJECT_ARGS}) + +ExternalProject_Add( + pystring + 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 new file mode 100644 index 0000000..5a53d1b --- /dev/null +++ b/cmake/SuperBuild/Buildyaml-cpp.cmake @@ -0,0 +1,18 @@ +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_ARGS + ${toucan_EXTERNAL_PROJECT_ARGS} + -DYAML_CPP_BUILD_CONTRIB=OFF + -DYAML_CPP_BUILD_TOOLS=OFF + -DYAML_CPP_BUILD_TESTS=OFF) + +ExternalProject_Add( + yaml-cpp + PREFIX ${CMAKE_CURRENT_BINARY_DIR}/yaml-cpp + GIT_REPOSITORY ${yaml-cpp_GIT_REPOSITORY} + GIT_TAG ${yaml-cpp_GIT_TAG} + LIST_SEPARATOR | + CMAKE_ARGS ${yaml-cpp_ARGS}) diff --git a/cmake/SuperBuild/CMakeLists.txt b/cmake/SuperBuild/CMakeLists.txt index 7dee1c9..ca71225 100644 --- a/cmake/SuperBuild/CMakeLists.txt +++ b/cmake/SuperBuild/CMakeLists.txt @@ -53,6 +53,11 @@ include(BuildJPEG) include(BuildTIFF) include(BuildImath) include(BuildOpenEXR) +include(Buildexpat) +include(Buildminizip-ng) +include(Buildpystring) +include(Buildyaml-cpp) +include(BuildOpenColorIO) include(BuildOpenImageIO) include(BuildOpenTimelineIO) include(BuildOpenFX) diff --git a/cmake/SuperBuild/pystring-patch/CMakeLists.txt b/cmake/SuperBuild/pystring-patch/CMakeLists.txt new file mode 100644 index 0000000..8e11add --- /dev/null +++ b/cmake/SuperBuild/pystring-patch/CMakeLists.txt @@ -0,0 +1,24 @@ +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 +) + diff --git a/data/ColorSpace.otio b/data/ColorSpace.otio new file mode 100644 index 0000000..73bbdb6 --- /dev/null +++ b/data/ColorSpace.otio @@ -0,0 +1,144 @@ +{ + "OTIO_SCHEMA": "Timeline.1", + "metadata": {}, + "name": "ColorSpace", + "tracks": { + "OTIO_SCHEMA": "Stack.1", + "children": [ + { + "OTIO_SCHEMA": "Track.1", + "children": [ + { + "OTIO_SCHEMA": "Clip.1", + "media_reference": { + "OTIO_SCHEMA": "ExternalReference.1", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 3 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 0 + } + }, + "target_url": "Charlie.jpg" + }, + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 3 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 0 + } + }, + "name": "Charlie" + }, + { + "OTIO_SCHEMA": "Clip.1", + "media_reference": { + "OTIO_SCHEMA": "ExternalReference.1", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 3 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 0 + } + }, + "target_url": "Charlie.jpg" + }, + "effects": [ + { + "OTIO_SCHEMA": "ColorConvertEffect.1", + "effect_name": "", + "fromspace": "Linear Rec.709 (sRGB)", + "tospace": "ACEScct", + "unpremult": true, + "context_key": "", + "context_value": "", + "color_config": "" + } + ], + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 3 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 0 + } + }, + "name": "Charlie" + }, + { + "OTIO_SCHEMA": "Clip.1", + "media_reference": { + "OTIO_SCHEMA": "ExternalReference.1", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 3 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 0 + } + }, + "target_url": "Charlie.jpg" + }, + "effects": [ + { + "OTIO_SCHEMA": "ColorConvertEffect.1", + "effect_name": "", + "fromspace": "Gamma 2.4 Rec.709 - Texture", + "tospace": "Linear P3-D65", + "unpremult": true, + "context_key": "", + "context_value": "", + "color_config": "" + } + ], + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 3 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 0 + } + }, + "name": "Charlie" + } + ], + "kind": "Video", + "name": "Video" + } + ], + "name": "Stack" + } +} diff --git a/images/ColorSpace.png b/images/ColorSpace.png new file mode 100644 index 0000000000000000000000000000000000000000..af30c6d5e01be59da28911417da09d0183fe81ec GIT binary patch literal 50398 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 w`F)o1{1uZNG&Jz(( >& inputs) : + IImageNode("ColorConvert", inputs), + _data(data) + {} + + ColorConvertNode::~ColorConvertNode() + {} + + const ColorConvertData& ColorConvertNode::getData() const + { + return _data; + } + + void ColorConvertNode::setData(const ColorConvertData& value) + { + _data = value; + } + + OIIO::ImageBuf ColorConvertNode::exec( + const OTIO_NS::RationalTime& time, + const std::shared_ptr& host) + { + OIIO::ImageBuf buf; + if (!_inputs.empty() && _inputs[0]) + { + OTIO_NS::RationalTime offsetTime = time; + if (!_timeOffset.is_invalid_time()) + { + offsetTime -= _timeOffset; + } + const auto input = _inputs[0]->exec(offsetTime, host); + const auto& spec = input.spec(); + buf = OIIO::ImageBuf(spec); + PropertySet propSet; + propSet.setString("fromspace", 0, _data.fromspace.c_str()); + propSet.setString("tospace", 0, _data.tospace.c_str()); + propSet.setInt("unpremult", 0, _data.unpremult); + propSet.setString("context_key", 0, _data.context_key.c_str()); + propSet.setString("context_value", 0, _data.context_value.c_str()); + propSet.setString("color_config", 0, _data.color_config.c_str()); + host->filter("Toucan:ColorConvert", input, buf, propSet); + } + return buf; + } + + ColorConvertEffect::ColorConvertEffect( + std::string const& name, + std::string const& effect_name, + OTIO_NS::AnyDictionary const& metadata) : + IEffect(name, effect_name, metadata) + {} + + ColorConvertEffect::~ColorConvertEffect() + {} + + std::shared_ptr ColorConvertEffect::createNode( + const std::vector >& inputs) + { + return std::make_shared(_data, inputs); + } + + bool ColorConvertEffect::read_from(Reader& reader) + { + bool out = + reader.read("fromspace", &_data.fromspace) && + reader.read("tospace", &_data.tospace) && + reader.read("unpremult", &_data.unpremult) && + reader.read("context_key", &_data.context_key) && + reader.read("context_value", &_data.context_value) && + reader.read("color_config", &_data.color_config) && + IEffect::read_from(reader); + return out; + } + + void ColorConvertEffect::write_to(Writer& writer) const + { + IEffect::write_to(writer); + writer.write("fromspace", _data.fromspace); + writer.write("tospace", _data.tospace); + writer.write("unpremult", _data.unpremult); + writer.write("context_key", _data.context_key); + writer.write("context_value", _data.context_value); + writer.write("color_config", _data.color_config); + } + PremultNode::PremultNode( const std::vector >& inputs) : IImageNode("Premult", inputs) diff --git a/lib/toucan/ColorSpace.h b/lib/toucan/ColorSpace.h index f42c99d..02fcbcd 100644 --- a/lib/toucan/ColorSpace.h +++ b/lib/toucan/ColorSpace.h @@ -8,6 +8,66 @@ namespace toucan { + //! Color convert data. + struct ColorConvertData + { + std::string fromspace; + std::string tospace; + bool unpremult = true; + std::string context_key; + std::string context_value; + std::string color_config; + }; + + //! Color convert node. + class ColorConvertNode : public IImageNode + { + public: + ColorConvertNode( + const ColorConvertData & = ColorConvertData(), + const std::vector > & = {}); + + virtual ~ColorConvertNode(); + + const ColorConvertData& getData() const; + void setData(const ColorConvertData&); + + OIIO::ImageBuf exec( + const OTIO_NS::RationalTime&, + const std::shared_ptr&) override; + + private: + ColorConvertData _data; + }; + + //! Color convert OTIO effect. + class ColorConvertEffect : public IEffect + { + public: + struct Schema + { + static auto constexpr name = "ColorConvertEffect"; + static int constexpr version = 1; + }; + + ColorConvertEffect( + std::string const& name = std::string(), + std::string const& effect_name = std::string(), + OTIO_NS::AnyDictionary const& metadata = OTIO_NS::AnyDictionary()); + + std::shared_ptr createNode( + const std::vector >& inputs) override; + + protected: + virtual ~ColorConvertEffect(); + + bool read_from(Reader&) override; + void write_to(Writer&) const override; + + private: + ColorConvertData _data; + }; + //! Premultiply alpha node. class PremultNode : public IImageNode { diff --git a/lib/toucan/Init.cpp b/lib/toucan/Init.cpp index ca72bd5..42c40b6 100644 --- a/lib/toucan/Init.cpp +++ b/lib/toucan/Init.cpp @@ -4,6 +4,7 @@ #include "Init.h" +#include "ColorSpace.h" #include "Comp.h" #include "Draw.h" #include "Filter.h" @@ -20,16 +21,19 @@ namespace toucan { OTIO_NS::TypeRegistry::instance().register_type(); OTIO_NS::TypeRegistry::instance().register_type(); + OTIO_NS::TypeRegistry::instance().register_type(); OTIO_NS::TypeRegistry::instance().register_type(); OTIO_NS::TypeRegistry::instance().register_type(); OTIO_NS::TypeRegistry::instance().register_type(); OTIO_NS::TypeRegistry::instance().register_type(); OTIO_NS::TypeRegistry::instance().register_type(); OTIO_NS::TypeRegistry::instance().register_type(); + OTIO_NS::TypeRegistry::instance().register_type(); OTIO_NS::TypeRegistry::instance().register_type(); OTIO_NS::TypeRegistry::instance().register_type(); OTIO_NS::TypeRegistry::instance().register_type(); OTIO_NS::TypeRegistry::instance().register_type(); + OTIO_NS::TypeRegistry::instance().register_type(); OTIO_NS::TypeRegistry::instance().register_type(); } } diff --git a/lib/toucan/Util.cpp b/lib/toucan/Util.cpp index 2c6d88f..45f5067 100644 --- a/lib/toucan/Util.cpp +++ b/lib/toucan/Util.cpp @@ -79,11 +79,10 @@ namespace toucan const std::string& nameSuffix) { std::stringstream ss; - ss << path.string() << - namePrefix << + ss << namePrefix << std::setw(padding) << std::setfill('0') << frame << nameSuffix; - return ss.str(); + return (path / ss.str()).string(); } namespace diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index f3b05bd..de9abff 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -3,7 +3,7 @@ foreach(PLUGIN ColorSpace Draw Filter Generator Transform Transition) toucan${PLUGIN} MODULE ${PLUGIN}Plugin.h Plugin.h Util.h ${PLUGIN}Plugin.cpp Plugin.cpp Util.cpp) - target_link_libraries(toucan${PLUGIN} PUBLIC OpenImageIO::OpenImageIO) + target_link_libraries(toucan${PLUGIN} PUBLIC OpenImageIO::OpenImageIO MINIZIP::minizip) set_target_properties(toucan${PLUGIN} PROPERTIES FOLDER plugin) install(TARGETS toucan${PLUGIN} diff --git a/plugins/ColorSpacePlugin.cpp b/plugins/ColorSpacePlugin.cpp index 58f36da..d3b95b2 100644 --- a/plugins/ColorSpacePlugin.cpp +++ b/plugins/ColorSpacePlugin.cpp @@ -114,6 +114,106 @@ OfxStatus ColorSpacePlugin::_renderAction( return kOfxStatOK; } +ColorConvertPlugin* ColorConvertPlugin::_instance = nullptr; + +ColorConvertPlugin::ColorConvertPlugin() : + ColorSpacePlugin("Toucan", "ColorConvert") +{} + +ColorConvertPlugin::~ColorConvertPlugin() +{} + +void ColorConvertPlugin::setHostFunc(OfxHost* host) +{ + if (!_instance) + { + _instance = new ColorConvertPlugin; + } + _instance->_host = host; +} + +OfxStatus ColorConvertPlugin::mainEntryPoint( + const char* action, + const void* handle, + OfxPropertySetHandle inArgs, + OfxPropertySetHandle outArgs) +{ + return _instance->_entryPoint(action, handle, inArgs, outArgs); +} + +OfxStatus ColorConvertPlugin::_render( + const OIIO::ImageBuf& sourceBuf, + OIIO::ImageBuf& outputBuf, + const OfxRectI& renderWindow, + OfxPropertySetHandle inArgs) +{ + std::string fromspace; + std::string tospace; + char* s = nullptr; + _propertySuite->propGetString(inArgs, "fromspace", 0, &s); + if (s) + { + fromspace = s; + } + _propertySuite->propGetString(inArgs, "tospace", 0, &s); + if (s) + { + tospace = s; + } + + int premult = 0; + _propertySuite->propGetInt(inArgs, "premult", 0, &premult); + + std::string context_key; + std::string context_value; + _propertySuite->propGetString(inArgs, "context_key", 0, &s); + if (s) + { + context_key = s; + } + _propertySuite->propGetString(inArgs, "context_value", 0, &s); + if (s) + { + context_value = s; + } + + std::filesystem::path color_config; + _propertySuite->propGetString(inArgs, "color_config", 0, &s); + if (s) + { + color_config = s; + } + std::shared_ptr colorConfig; + auto i = _colorConfigs.find(color_config); + if (i != _colorConfigs.end()) + { + colorConfig = i->second; + } + else + { + colorConfig = std::make_shared(color_config.string()); + _colorConfigs[color_config] = colorConfig; + //for (int i = 0; i < colorConfig->getNumColorSpaces(); ++i) + //{ + // std::cout << "Color space: " << colorConfig->getColorSpaceNameByIndex(i) << std::endl; + //} + } + + if (colorConfig) + { + OIIO::ImageBufAlgo::colorconvert( + outputBuf, + sourceBuf, + fromspace, + tospace, + premult, + context_key, + context_value, + colorConfig.get()); + } + return kOfxStatOK; +} + PremultPlugin* PremultPlugin::_instance = nullptr; PremultPlugin::PremultPlugin() : @@ -192,6 +292,7 @@ namespace { std::vector plugins = { + { kOfxImageEffectPluginApi, 1, "Toucan:ColorConvert", 1, 0, ColorConvertPlugin::setHostFunc, ColorConvertPlugin::mainEntryPoint }, { kOfxImageEffectPluginApi, 1, "Toucan:Premult", 1, 0, PremultPlugin::setHostFunc, PremultPlugin::mainEntryPoint }, { kOfxImageEffectPluginApi, 1, "Toucan:Unpremult", 1, 0, UnpremultPlugin::setHostFunc, UnpremultPlugin::mainEntryPoint } }; diff --git a/plugins/ColorSpacePlugin.h b/plugins/ColorSpacePlugin.h index c9e85ea..2fa605f 100644 --- a/plugins/ColorSpacePlugin.h +++ b/plugins/ColorSpacePlugin.h @@ -6,8 +6,11 @@ #include "Plugin.h" +#include #include +#include + class ColorSpacePlugin : public Plugin { public: @@ -32,6 +35,34 @@ class ColorSpacePlugin : public Plugin OfxPropertySetHandle outArgs) override; }; +class ColorConvertPlugin : public ColorSpacePlugin +{ +public: + ColorConvertPlugin(); + + virtual ~ColorConvertPlugin(); + + static void setHostFunc(OfxHost*); + + static OfxStatus mainEntryPoint( + const char* action, + const void* handle, + OfxPropertySetHandle inArgs, + OfxPropertySetHandle outArgs); + +protected: + OfxStatus _render( + const OIIO::ImageBuf&, + OIIO::ImageBuf&, + const OfxRectI& renderWindow, + OfxPropertySetHandle inArgs) override; + +private: + static ColorConvertPlugin* _instance; + + std::map > _colorConfigs; +}; + class PremultPlugin : public ColorSpacePlugin { public: diff --git a/tests/TimelineGraphTest.cpp b/tests/TimelineGraphTest.cpp index 3421287..3fe5e4b 100644 --- a/tests/TimelineGraphTest.cpp +++ b/tests/TimelineGraphTest.cpp @@ -18,6 +18,7 @@ namespace toucan std::cout << "timelineGraphTest" << std::endl; const std::vector otioFiles = { + "ColorSpace", "CompositeTracks", "Draw", "Filter",