Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ python/src/xstudio/version.py
.vs/
.DS_Store
/build/
xstudio_install/
**/qml/*_qml_export.h
14 changes: 8 additions & 6 deletions cmake/macros.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,12 @@ macro(default_options_local name)
$<BUILD_INTERFACE:${ROOT_DIR}/extern/include>
)
if (APPLE)
set_target_properties(${name}
PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/xSTUDIO.app/Contents/Frameworks"
)
set_target_properties(${name}
PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/xSTUDIO.app/Contents/Frameworks"
INSTALL_RPATH "@executable_path/../Frameworks"
INSTALL_RPATH_USE_LINK_PATH TRUE
)
else()
set_target_properties(${name}
PROPERTIES
Expand Down Expand Up @@ -204,7 +206,7 @@ macro(default_plugin_options name)
COMMAND ${CMAKE_COMMAND} -E copy "$<TARGET_FILE:${PROJECT_NAME}>" "${CMAKE_CURRENT_BINARY_DIR}/plugin"
)
endif()

endmacro()

macro(add_plugin_qml name _dir)
Expand Down Expand Up @@ -366,7 +368,7 @@ macro(add_python_plugin NAME)
copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/${NAME} ${CMAKE_BINARY_DIR}/bin/plugin-python/${NAME})

endif()

endmacro()

macro(create_plugin NAME VERSION DEPS)
Expand Down
10 changes: 9 additions & 1 deletion src/global/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,12 @@ if(UNIX AND NOT APPLE)
target_link_libraries(${PROJECT_NAME} PRIVATE asound) # Link against asound on Linux
endif()

set_target_properties(${PROJECT_NAME} PROPERTIES LINK_DEPENDS_NO_SHARED true)
if(APPLE)
set_target_properties(${PROJECT_NAME} PROPERTIES
LINK_DEPENDS_NO_SHARED true
INSTALL_RPATH "@executable_path/../Frameworks"
BUILD_WITH_INSTALL_RPATH TRUE
)
else()
set_target_properties(${PROJECT_NAME} PROPERTIES LINK_DEPENDS_NO_SHARED true)
endif()
10 changes: 8 additions & 2 deletions src/launch/xstudio/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ set(SOURCES
xstudio.cpp
xstudio_win_resource.rc
../../../../ui/qml/xstudio/qml.qrc
)
)
else()
set(SOURCES
xstudio.cpp
../../../../ui/qml/xstudio/qml.qrc
)
)
endif()
if(WIN32)
# Add the /bigobj option for xstudio.cpp
Expand Down Expand Up @@ -134,6 +134,12 @@ elseif(APPLE)
configure_file(macdeploy.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/macdeploy.cmake @ONLY)
install(SCRIPT ${CMAKE_CURRENT_BINARY_DIR}/macdeploy.cmake)

# Add custom command to fix library paths after build
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -DAPP_BUNDLE_DIR=${CMAKE_BINARY_DIR}/xSTUDIO.app
-P ${CMAKE_CURRENT_SOURCE_DIR}/fixup_macos_bundle.cmake
)

else()

set_target_properties(${PROJECT_NAME}
Expand Down
15 changes: 15 additions & 0 deletions src/launch/xstudio/src/fixup_macos_bundle.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Fix library paths in the app bundle to ensure they're relative
file(GLOB_RECURSE LIBRARIES "${APP_BUNDLE_DIR}/Contents/Frameworks/*.dylib")
foreach(LIB ${LIBRARIES})
get_filename_component(LIB_NAME ${LIB} NAME)
execute_process(COMMAND install_name_tool -id "@rpath/${LIB_NAME}" ${LIB})
endforeach()

# Fix executable references to libraries
execute_process(
COMMAND
install_name_tool
-add_rpath
"@executable_path/../Frameworks"
${APP_BUNDLE_DIR}/Contents/MacOS/xstudio.bin
)
18 changes: 17 additions & 1 deletion src/launch/xstudio/src/macdeploy.cmake.in
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
message("Running @macdeployqt_exe@ with args: ${CMAKE_BINARY_DIR}/xSTUDIO.app -qmldir=@CMAKE_SOURCE_DIR@/ui")
execute_process(COMMAND "@macdeployqt_exe@" ${CMAKE_BINARY_DIR}/xSTUDIO.app -qmldir=@CMAKE_SOURCE_DIR@/ui
WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}")
WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}")

# Fix library paths to ensure they're relative to the bundle
execute_process(
COMMAND
install_name_tool -id
"@rpath/libglobal.dylib"
${CMAKE_BINARY_DIR}/xSTUDIO.app/Contents/Frameworks/libglobal.dylib
)
execute_process(
COMMAND
install_name_tool
-change
"@rpath/libglobal.dylib"
"@executable_path/../Frameworks/libglobal.dylib"
${CMAKE_BINARY_DIR}/xSTUDIO.app/Contents/MacOS/xstudio.bin
)
1 change: 1 addition & 0 deletions src/plugin/media_metadata/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
add_src_and_test(ffprobe)
add_src_and_test(openexr)
add_src_and_test(openimageio)

build_studio_plugins("${STUDIO_PLUGINS}")
8 changes: 8 additions & 0 deletions src/plugin/media_metadata/openimageio/src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
find_package(OpenImageIO)

SET(LINK_DEPS
xstudio::media_metadata
OpenImageIO::OpenImageIO
)

create_plugin_with_alias(media_metadata_openimageio xstudio::media_metadata::openimageio ${XSTUDIO_GLOBAL_VERSION} "${LINK_DEPS}")
177 changes: 177 additions & 0 deletions src/plugin/media_metadata/openimageio/src/openimageio_metadata.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
#include "openimageio_metadata.hpp"

#include <filesystem>
#include <unordered_set>

#include <OpenImageIO/imageio.h>
#include <OpenImageIO/typedesc.h>

#include "xstudio/utility/helpers.hpp"

namespace fs = std::filesystem;

using namespace xstudio::media_metadata;
using namespace xstudio::utility;
using namespace xstudio;

OpenImageIOMediaMetadata::OpenImageIOMediaMetadata() : MediaMetadata("OpenImageIO") {}

MMCertainty OpenImageIOMediaMetadata::supported(
const caf::uri &uri, const std::array<uint8_t, 16> &signature) {
// Step 1: List of supported extensions by OIIO
static const std::unordered_set<std::string> supported_extensions = {
"JPG",
"JPEG",
"PNG",
"TIF",
"TIFF",
"TGA",
"BMP",
"PSD",
"HDR",
"DPX",
"ACES",
"JP2",
"J2K",
"WEBP",
"EXR",
};

// Step 2: Convert the URI to a POSIX path string and fs::path
std::string path = uri_to_posix_path(uri);
fs::path p(path);

// Step 3: Check if the file exists and is a regular file
// Return not supported if the file does not exist or is not a regular file
if (!fs::exists(p) || !fs::is_regular_file(p)) {
return MMC_NO;
}

// Step 4: Get the upper-case extension (handling platform differences)
#ifdef _WIN32
std::string ext = ltrim_char(to_upper_path(p.extension()), '.');
#else
std::string ext = ltrim_char(to_upper(p.extension().string()), '.');
#endif

// Step 5: Check if the extension is in the supported list
// Return fully supported if the extension is in the supported list
if (supported_extensions.count(ext)) {
return MMC_FULLY;
}

// Step 6: Try to detect via OIIO if the extension is supported
// Return maybe supported if the extension is supported by OIIO
auto in = OIIO::ImageInput::open(path);
if (in) {
in->close();
return MMC_MAYBE;
}

// Step 7: Return not supported if all checks fail
return MMC_NO;
}

nlohmann::json OpenImageIOMediaMetadata::read_metadata(const caf::uri &uri) {
std::string path = uri_to_posix_path(uri);

try {
// Step 1: Open the image using OpenImageIO
auto in = OIIO::ImageInput::open(path);
if (!in) {
throw std::runtime_error("Cannot open: " + OIIO::geterror());
}

// Step 2: Get the image specification
const OIIO::ImageSpec &spec = in->spec();

// Step 3: Initialize a JSON object for metadata
nlohmann::json metadata;

// Step 4: Populate basic image properties (width, height, resolution)
metadata["width"] = spec.width;
metadata["height"] = spec.height;
metadata["resolution"] = fmt::format("{} x {}", spec.width, spec.height);

// Step 5: Extract and format the file extension (format)
fs::path p(path);
#ifdef _WIN32
std::string ext = ltrim_char(to_upper_path(p.extension()), '.');
#else
std::string ext = ltrim_char(to_upper(p.extension().string()), '.');
#endif
metadata["format"] = ext;

// Step 6: Determine bit depth and add to metadata
if (spec.format == OIIO::TypeDesc::UINT8) {
metadata["bit_depth"] = "8 bits";
} else if (spec.format == OIIO::TypeDesc::UINT16) {
metadata["bit_depth"] = "16 bits";
} else if (spec.format == OIIO::TypeDesc::HALF) {
metadata["bit_depth"] = "16 bits float";
} else if (spec.format == OIIO::TypeDesc::FLOAT) {
metadata["bit_depth"] = "32 bits float";
} else {
metadata["bit_depth"] = "unknown";
}

// Step 7: Read pixel aspect ratio and aspect ratio
metadata["pixel_aspect"] = spec.get_float_attribute("PixelAspectRatio", 1.0f);
metadata["aspect_ratio"] = spec.get_float_attribute("XResolution", spec.width)
/ spec.get_float_attribute("YResolution", spec.height);

// Step 8: Add extra attributes to metadata
for (const auto &param : spec.extra_attribs) {
metadata[param.name().string()] = param.get_string();
}

// Step 9: Return metadata
return metadata;
} catch (const std::exception &e) {
spdlog::error("Failed to read metadata from {}: {}", path, e.what());
return nlohmann::json::object();
}
}

std::optional<MediaMetadata::StandardFields>
OpenImageIOMediaMetadata::fill_standard_fields(const nlohmann::json &metadata) {
// Step 1: Initialize StandardFields and set default format
StandardFields fields;
fields.format_ = "OpenImageIO";

// Step 2: Fill in the resolution if present in metadata
if (metadata.contains("resolution")) {
fields.resolution_ = metadata["resolution"].get<std::string>();
}

// Step 3: Fill in the image extension (format) if present in metadata
if (metadata.contains("format")) {
fields.format_ = metadata["format"].get<std::string>();
}

// Step 4: Fill in the bit depth if present in metadata
if (metadata.contains("bit_depth")) {
fields.bit_depth_ = metadata["bit_depth"].get<std::string>();
}

// Step 5: Fill in the pixel aspect ratio if present in metadata
if (metadata.contains("pixel_aspect")) {
fields.pixel_aspect_ = metadata["pixel_aspect"].get<float>();
}

return std::make_optional(fields);
}

// Point d'entrée du plugin
extern "C" {
plugin_manager::PluginFactoryCollection *plugin_factory_collection_ptr() {
return new plugin_manager::PluginFactoryCollection(
std::vector<std::shared_ptr<plugin_manager::PluginFactory>>({std::make_shared<
MediaMetadataPlugin<MediaMetadataActor<OpenImageIOMediaMetadata>>>(
Uuid("8f3c4a7e-9b2d-4e1f-a5c8-3d6f7e8a9b1c"),
"OpenImageIO",
"xStudio",
"OpenImageIO Media Metadata Reader",
semver::version("1.0.0"))}));
}
}
27 changes: 27 additions & 0 deletions src/plugin/media_metadata/openimageio/src/openimageio_metadata.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once

#include <array>

#include <nlohmann/json.hpp>

#include "xstudio/media_metadata/media_metadata.hpp"

namespace xstudio {
namespace media_metadata {

class OpenImageIOMediaMetadata : public MediaMetadata {
public:
OpenImageIOMediaMetadata();
~OpenImageIOMediaMetadata() override = default;

MMCertainty
supported(const caf::uri &uri, const std::array<uint8_t, 16> &signature) override;

protected:
nlohmann::json read_metadata(const caf::uri &uri) override;
std::optional<StandardFields>
fill_standard_fields(const nlohmann::json &metadata) override;
};

} // namespace media_metadata
} // namespace xstudio
7 changes: 7 additions & 0 deletions src/plugin/media_metadata/openimageio/test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
include(CTest)

SET(LINK_DEPS
CAF::core
)

create_tests("${LINK_DEPS}")
1 change: 1 addition & 0 deletions src/plugin/media_reader/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ add_src_and_test(ffmpeg)
add_src_and_test(openexr)
add_src_and_test(ppm)
add_src_and_test(blank)
add_src_and_test(openimageio)

build_studio_plugins("${STUDIO_PLUGINS}")
16 changes: 16 additions & 0 deletions src/plugin/media_reader/openimageio/src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
find_package(OpenImageIO REQUIRED)

SET(LINK_DEPS
xstudio::media_reader
OpenImageIO::OpenImageIO
)

create_plugin_with_alias(
media_reader_oiio
xstudio::media_reader::oiio
${XSTUDIO_GLOBAL_VERSION}
"${LINK_DEPS}"
)

target_include_directories(media_reader_oiio PRIVATE ${OPENIMAGEIO_INCLUDE_DIR})

Loading