diff --git a/build_files/cmake/platform/dependency_targets.cmake b/build_files/cmake/platform/dependency_targets.cmake index c783998e022..dfe4d6a74f1 100644 --- a/build_files/cmake/platform/dependency_targets.cmake +++ b/build_files/cmake/platform/dependency_targets.cmake @@ -51,3 +51,15 @@ if(WITH_TBB) target_include_directories(bf_deps_eigen SYSTEM INTERFACE ${TBB_INCLUDE_DIRS}) target_link_libraries(bf_deps_eigen INTERFACE ${TBB_LIBRARIES}) endif() + +# ----------------------------------------------------------------------------- +# Configure OpenColorIO + +add_library(bf_deps_optional_opencolorio INTERFACE) +add_library(bf::dependencies::optional::opencolorio ALIAS bf_deps_optional_opencolorio) + +if(WITH_OPENCOLORIO) + target_compile_definitions(bf_deps_optional_opencolorio INTERFACE WITH_OPENCOLORIO) + target_include_directories(bf_deps_optional_opencolorio SYSTEM INTERFACE ${OPENCOLORIO_INCLUDE_DIRS}) + target_link_libraries(bf_deps_optional_opencolorio INTERFACE ${OPENCOLORIO_LIBRARIES}) +endif() diff --git a/intern/CMakeLists.txt b/intern/CMakeLists.txt index 0d6c22d5be9..22d395cfe4d 100644 --- a/intern/CMakeLists.txt +++ b/intern/CMakeLists.txt @@ -9,7 +9,6 @@ add_subdirectory(ghost) add_subdirectory(guardedalloc) add_subdirectory(libmv) add_subdirectory(memutil) -add_subdirectory(opencolorio) add_subdirectory(opensubdiv) add_subdirectory(mikktspace) add_subdirectory(eigen) diff --git a/intern/opencolorio/CMakeLists.txt b/intern/opencolorio/CMakeLists.txt deleted file mode 100644 index 21c1112b55f..00000000000 --- a/intern/opencolorio/CMakeLists.txt +++ /dev/null @@ -1,92 +0,0 @@ -# SPDX-FileCopyrightText: 2012 Blender Authors -# -# SPDX-License-Identifier: GPL-2.0-or-later - -set(INC - . - ../../source/blender/gpu/intern -) - -set(INC_SYS - -) - -set(SRC - ocio_capi.cc - fallback_impl.cc - - ocio_capi.h - ocio_impl.h - ocio_shader_shared.hh -) - -set(LIB - PRIVATE bf::blenlib - PRIVATE bf::dna - PRIVATE bf::gpu - PRIVATE bf::intern::guardedalloc - PRIVATE bf::intern::clog -) - -if(WITH_OPENCOLORIO) - add_definitions( - -DWITH_OCIO - ) - - add_definitions(${OPENCOLORIO_DEFINITIONS}) - - list(APPEND INC_SYS - ${OPENCOLORIO_INCLUDE_DIRS} - ${Epoxy_INCLUDE_DIRS} - ) - - list(APPEND SRC - ocio_impl.cc - ocio_impl_glsl.cc - ) - - list(APPEND LIB - ${OPENCOLORIO_LIBRARIES} - ) - - set(GLSL_SRC - gpu_shader_display_transform_vert.glsl - gpu_shader_display_transform_frag.glsl - - ocio_shader_shared.hh - ) - - set(GLSL_C) - foreach(GLSL_FILE ${GLSL_SRC}) - glsl_to_c(${GLSL_FILE} GLSL_C) - endforeach() - - blender_add_lib(bf_ocio_shaders "${GLSL_C}" "" "" "") - - list(APPEND LIB - bf_ocio_shaders - ) - - set(GLSL_SOURCE_CONTENT "") - set(GLSL_METADATA_CONTENT "") - foreach(GLSL_FILE ${GLSL_SRC}) - get_filename_component(GLSL_FILE_NAME ${GLSL_FILE} NAME) - string(REPLACE "." "_" GLSL_FILE_NAME_UNDERSCORES ${GLSL_FILE_NAME}) - string(APPEND GLSL_SOURCE_CONTENT "SHADER_SOURCE\(${GLSL_FILE_NAME_UNDERSCORES}, \"${GLSL_FILE_NAME}\", \"${GLSL_FILE}\"\)\n") - string(APPEND GLSL_METADATA_CONTENT "#include \"${GLSL_FILE}.hh\"\n") - endforeach() - - set(glsl_source_list_file "${CMAKE_CURRENT_BINARY_DIR}/glsl_ocio_source_list.h") - file(GENERATE OUTPUT ${glsl_source_list_file} CONTENT "${GLSL_SOURCE_CONTENT}") - list(APPEND SRC ${glsl_source_list_file}) - set(glsl_metadata_list_file "${CMAKE_CURRENT_BINARY_DIR}/glsl_ocio_metadata_list.hh") - file(GENERATE OUTPUT ${glsl_metadata_list_file} CONTENT "${GLSL_METADATA_CONTENT}") - list(APPEND SRC ${glsl_metadata_list_file}) - list(APPEND INC ${CMAKE_CURRENT_BINARY_DIR}) - - target_include_directories(bf_ocio_shaders PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) - -endif() - - -blender_add_lib(bf_intern_opencolorio "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/intern/opencolorio/fallback_impl.cc b/intern/opencolorio/fallback_impl.cc deleted file mode 100644 index 1d136b45bc7..00000000000 --- a/intern/opencolorio/fallback_impl.cc +++ /dev/null @@ -1,546 +0,0 @@ -/* SPDX-FileCopyrightText: 2012 Blender Authors - * - * SPDX-License-Identifier: GPL-2.0-or-later */ - -#include -#include - -#include "BLI_math_color.h" -#include "MEM_guardedalloc.h" - -#include "ocio_impl.h" - -using std::max; - -#define CONFIG_DEFAULT ((OCIO_ConstConfigRcPtr *)1) - -enum TransformType { - TRANSFORM_LINEAR_TO_SRGB, - TRANSFORM_SRGB_TO_LINEAR, - TRANSFORM_SCALE, - TRANSFORM_EXPONENT, - TRANSFORM_NONE, - TRANSFORM_UNKNOWN, -}; - -#define COLORSPACE_LINEAR ((OCIO_ConstColorSpaceRcPtr *)1) -#define COLORSPACE_SRGB ((OCIO_ConstColorSpaceRcPtr *)2) -#define COLORSPACE_DATA ((OCIO_ConstColorSpaceRcPtr *)3) - -struct OCIO_PackedImageDescription { - float *data; - long width; - long height; - long numChannels; - long chanStrideBytes; - long xStrideBytes; - long yStrideBytes; -}; - -struct FallbackTransform { - FallbackTransform() = default; - virtual ~FallbackTransform() = default; - - void applyRGB(float *pixel) - { - if (type == TRANSFORM_LINEAR_TO_SRGB) { - pixel[0] *= scale; - pixel[1] *= scale; - pixel[2] *= scale; - - linearrgb_to_srgb_v3_v3(pixel, pixel); - - pixel[0] = powf(max(0.0f, pixel[0]), exponent); - pixel[1] = powf(max(0.0f, pixel[1]), exponent); - pixel[2] = powf(max(0.0f, pixel[2]), exponent); - } - else if (type == TRANSFORM_SRGB_TO_LINEAR) { - srgb_to_linearrgb_v3_v3(pixel, pixel); - } - else if (type == TRANSFORM_EXPONENT) { - pixel[0] = powf(max(0.0f, pixel[0]), exponent); - pixel[1] = powf(max(0.0f, pixel[1]), exponent); - pixel[2] = powf(max(0.0f, pixel[2]), exponent); - } - else if (type == TRANSFORM_SCALE) { - pixel[0] *= scale; - pixel[1] *= scale; - pixel[2] *= scale; - } - } - - void applyRGBA(float *pixel) - { - applyRGB(pixel); - } - - bool isNoOp() - { - /* Rely on the short-circuiting based on name-space comparison in the IMB_colormanagement. */ - return false; - } - - TransformType type = TRANSFORM_UNKNOWN; - /* Scale transform. */ - float scale = 1.0f; - /* Exponent transform. */ - float exponent = 1.0f; - - MEM_CXX_CLASS_ALLOC_FUNCS("FallbackTransform"); -}; - -struct FallbackProcessor { - FallbackProcessor(const FallbackTransform &transform) : transform(transform) {} - - void applyRGB(float *pixel) - { - transform.applyRGB(pixel); - } - - void applyRGBA(float *pixel) - { - transform.applyRGBA(pixel); - } - - bool isNoOp() - { - return transform.isNoOp(); - } - - FallbackTransform transform; - - MEM_CXX_CLASS_ALLOC_FUNCS("FallbackProcessor"); -}; - -OCIO_ConstConfigRcPtr *FallbackImpl::getCurrentConfig() -{ - return CONFIG_DEFAULT; -} - -void FallbackImpl::setCurrentConfig(const OCIO_ConstConfigRcPtr * /*config*/) {} - -OCIO_ConstConfigRcPtr *FallbackImpl::configCreateFromEnv() -{ - return nullptr; -} - -OCIO_ConstConfigRcPtr *FallbackImpl::configCreateFromFile(const char * /*filename*/) -{ - return CONFIG_DEFAULT; -} - -void FallbackImpl::configRelease(OCIO_ConstConfigRcPtr * /*config*/) {} - -int FallbackImpl::configGetNumColorSpaces(OCIO_ConstConfigRcPtr * /*config*/) -{ - return 2; -} - -const char *FallbackImpl::configGetColorSpaceNameByIndex(OCIO_ConstConfigRcPtr * /*config*/, - int index) -{ - if (index == 0) { - return "Linear"; - } - if (index == 1) { - return "sRGB"; - } - - return nullptr; -} - -OCIO_ConstColorSpaceRcPtr *FallbackImpl::configGetColorSpace(OCIO_ConstConfigRcPtr * /*config*/, - const char *name) -{ - if (strcmp(name, "scene_linear") == 0) { - return COLORSPACE_LINEAR; - } - if (strcmp(name, "color_picking") == 0) { - return COLORSPACE_SRGB; - } - if (strcmp(name, "texture_paint") == 0) { - return COLORSPACE_LINEAR; - } - if (strcmp(name, "default_byte") == 0) { - return COLORSPACE_SRGB; - } - if (strcmp(name, "default_float") == 0) { - return COLORSPACE_LINEAR; - } - if (strcmp(name, "default_sequencer") == 0) { - return COLORSPACE_SRGB; - } - if (strcmp(name, "Linear") == 0) { - return COLORSPACE_LINEAR; - } - if (strcmp(name, "sRGB") == 0) { - return COLORSPACE_SRGB; - } - if (strcmp(name, "data") == 0) { - return COLORSPACE_DATA; - } - - return nullptr; -} - -int FallbackImpl::configGetIndexForColorSpace(OCIO_ConstConfigRcPtr *config, const char *name) -{ - OCIO_ConstColorSpaceRcPtr *cs = configGetColorSpace(config, name); - - if (cs == COLORSPACE_LINEAR) { - return 0; - } - if (cs == COLORSPACE_SRGB) { - return 1; - } - if (cs == COLORSPACE_DATA) { - return 2; - } - return -1; -} - -const char *FallbackImpl::getColorSpaceFromFilepath(OCIO_ConstConfigRcPtr * /*config*/, - const char * /*filepath*/) -{ - return nullptr; -} - -const char *FallbackImpl::configGetDefaultDisplay(OCIO_ConstConfigRcPtr * /*config*/) -{ - return "sRGB"; -} - -int FallbackImpl::configGetNumDisplays(OCIO_ConstConfigRcPtr * /*config*/) -{ - return 1; -} - -const char *FallbackImpl::configGetDisplay(OCIO_ConstConfigRcPtr * /*config*/, int index) -{ - if (index == 0) { - return "sRGB"; - } - return nullptr; -} - -const char *FallbackImpl::configGetDefaultView(OCIO_ConstConfigRcPtr * /*config*/, - const char * /*display*/) -{ - return "Standard"; -} - -int FallbackImpl::configGetNumViews(OCIO_ConstConfigRcPtr * /*config*/, const char * /*display*/) -{ - return 1; -} - -const char *FallbackImpl::configGetView(OCIO_ConstConfigRcPtr * /*config*/, - const char * /*display*/, - int index) -{ - if (index == 0) { - return "Standard"; - } - return nullptr; -} - -const char *FallbackImpl::configGetDisplayColorSpaceName(OCIO_ConstConfigRcPtr * /*config*/, - const char * /*display*/, - const char * /*view*/) -{ - return "sRGB"; -} - -void FallbackImpl::configGetDefaultLumaCoefs(OCIO_ConstConfigRcPtr * /*config*/, float *rgb) -{ - /* Here we simply use the older Blender assumed primaries of - * ITU-BT.709 / sRGB, or 0.2126729 0.7151522 0.0721750. Brute - * force stupid, but only plausible option given no color management - * system in place. - */ - - rgb[0] = 0.2126f; - rgb[1] = 0.7152f; - rgb[2] = 0.0722f; -} - -void FallbackImpl::configGetXYZtoSceneLinear(OCIO_ConstConfigRcPtr * /*config*/, - float xyz_to_scene_linear[3][3]) -{ - /* Default to ITU-BT.709. */ - memcpy(xyz_to_scene_linear, OCIO_XYZ_TO_REC709, sizeof(OCIO_XYZ_TO_REC709)); -} - -int FallbackImpl::configGetNumLooks(OCIO_ConstConfigRcPtr * /*config*/) -{ - return 0; -} - -const char *FallbackImpl::configGetLookNameByIndex(OCIO_ConstConfigRcPtr * /*config*/, - int /*index*/) -{ - return ""; -} - -OCIO_ConstLookRcPtr *FallbackImpl::configGetLook(OCIO_ConstConfigRcPtr * /*config*/, - const char * /*name*/) -{ - return nullptr; -} - -const char *FallbackImpl::lookGetProcessSpace(OCIO_ConstLookRcPtr * /*look*/) -{ - return nullptr; -} - -void FallbackImpl::lookRelease(OCIO_ConstLookRcPtr * /*look*/) {} - -int FallbackImpl::colorSpaceIsInvertible(OCIO_ConstColorSpaceRcPtr * /*cs*/) -{ - return 1; -} - -int FallbackImpl::colorSpaceIsData(OCIO_ConstColorSpaceRcPtr * /*cs*/) -{ - return 0; -} - -void FallbackImpl::colorSpaceIsBuiltin(OCIO_ConstConfigRcPtr * /*config*/, - OCIO_ConstColorSpaceRcPtr *cs, - bool &is_scene_linear, - bool &is_srgb) -{ - if (cs == COLORSPACE_LINEAR) { - is_scene_linear = true; - is_srgb = false; - } - else if (cs == COLORSPACE_SRGB) { - is_scene_linear = false; - is_srgb = true; - } - else { - is_scene_linear = false; - is_srgb = false; - } -} - -void FallbackImpl::colorSpaceRelease(OCIO_ConstColorSpaceRcPtr * /*cs*/) {} - -OCIO_ConstProcessorRcPtr *FallbackImpl::configGetProcessorWithNames(OCIO_ConstConfigRcPtr *config, - const char *srcName, - const char *dstName) -{ - OCIO_ConstColorSpaceRcPtr *cs_src = configGetColorSpace(config, srcName); - OCIO_ConstColorSpaceRcPtr *cs_dst = configGetColorSpace(config, dstName); - FallbackTransform transform; - if (cs_src == COLORSPACE_DATA || cs_dst == COLORSPACE_DATA) { - transform.type = TRANSFORM_NONE; - } - else if (cs_src == COLORSPACE_LINEAR && cs_dst == COLORSPACE_SRGB) { - transform.type = TRANSFORM_LINEAR_TO_SRGB; - } - else if (cs_src == COLORSPACE_SRGB && cs_dst == COLORSPACE_LINEAR) { - transform.type = TRANSFORM_SRGB_TO_LINEAR; - } - else { - transform.type = TRANSFORM_UNKNOWN; - } - return (OCIO_ConstProcessorRcPtr *)new FallbackProcessor(transform); -} - -OCIO_ConstCPUProcessorRcPtr *FallbackImpl::processorGetCPUProcessor( - OCIO_ConstProcessorRcPtr *processor) -{ - /* Just make a copy of the processor so that we are compatible with OCIO - * which does need it as a separate object. */ - FallbackProcessor *fallback_processor = (FallbackProcessor *)processor; - return (OCIO_ConstCPUProcessorRcPtr *)new FallbackProcessor(*fallback_processor); -} - -void FallbackImpl::processorRelease(OCIO_ConstProcessorRcPtr *processor) -{ - delete (FallbackProcessor *)(processor); -} - -bool FallbackImpl::cpuProcessorIsNoOp(OCIO_ConstCPUProcessorRcPtr *cpu_processor) -{ - return ((FallbackProcessor *)cpu_processor)->isNoOp(); -} - -void FallbackImpl::cpuProcessorApply(OCIO_ConstCPUProcessorRcPtr *cpu_processor, - OCIO_PackedImageDesc *img) -{ - /* OCIO_TODO stride not respected, channels must be 3 or 4 */ - OCIO_PackedImageDescription *desc = (OCIO_PackedImageDescription *)img; - int channels = desc->numChannels; - float *pixels = desc->data; - int width = desc->width; - int height = desc->height; - int x, y; - - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - float *pixel = pixels + channels * (y * width + x); - - if (channels == 4) { - cpuProcessorApplyRGBA(cpu_processor, pixel); - } - else if (channels == 3) { - cpuProcessorApplyRGB(cpu_processor, pixel); - } - } - } -} - -void FallbackImpl::cpuProcessorApply_predivide(OCIO_ConstCPUProcessorRcPtr *cpu_processor, - OCIO_PackedImageDesc *img) -{ - /* OCIO_TODO stride not respected, channels must be 3 or 4 */ - OCIO_PackedImageDescription *desc = (OCIO_PackedImageDescription *)img; - int channels = desc->numChannels; - float *pixels = desc->data; - int width = desc->width; - int height = desc->height; - int x, y; - - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - float *pixel = pixels + channels * (y * width + x); - - if (channels == 4) { - cpuProcessorApplyRGBA_predivide(cpu_processor, pixel); - } - else if (channels == 3) { - cpuProcessorApplyRGB(cpu_processor, pixel); - } - } - } -} - -void FallbackImpl::cpuProcessorApplyRGB(OCIO_ConstCPUProcessorRcPtr *cpu_processor, float *pixel) -{ - ((FallbackProcessor *)cpu_processor)->applyRGB(pixel); -} - -void FallbackImpl::cpuProcessorApplyRGBA(OCIO_ConstCPUProcessorRcPtr *cpu_processor, float *pixel) -{ - ((FallbackProcessor *)cpu_processor)->applyRGBA(pixel); -} - -void FallbackImpl::cpuProcessorApplyRGBA_predivide(OCIO_ConstCPUProcessorRcPtr *cpu_processor, - float *pixel) -{ - if (pixel[3] == 1.0f || pixel[3] == 0.0f) { - cpuProcessorApplyRGBA(cpu_processor, pixel); - } - else { - float alpha, inv_alpha; - - alpha = pixel[3]; - inv_alpha = 1.0f / alpha; - - pixel[0] *= inv_alpha; - pixel[1] *= inv_alpha; - pixel[2] *= inv_alpha; - - cpuProcessorApplyRGBA(cpu_processor, pixel); - - pixel[0] *= alpha; - pixel[1] *= alpha; - pixel[2] *= alpha; - } -} - -void FallbackImpl::cpuProcessorRelease(OCIO_ConstCPUProcessorRcPtr *cpu_processor) -{ - delete (FallbackProcessor *)(cpu_processor); -} - -const char *FallbackImpl::colorSpaceGetName(OCIO_ConstColorSpaceRcPtr *cs) -{ - if (cs == COLORSPACE_LINEAR) { - return "Linear"; - } - if (cs == COLORSPACE_SRGB) { - return "sRGB"; - } - if (cs == COLORSPACE_DATA) { - return "data"; - } - return nullptr; -} - -const char *FallbackImpl::colorSpaceGetDescription(OCIO_ConstColorSpaceRcPtr * /*cs*/) -{ - return ""; -} - -const char *FallbackImpl::colorSpaceGetFamily(OCIO_ConstColorSpaceRcPtr * /*cs*/) -{ - return ""; -} - -int FallbackImpl::colorSpaceGetNumAliases(OCIO_ConstColorSpaceRcPtr * /*cs*/) -{ - return 0; -} -const char *FallbackImpl::colorSpaceGetAlias(OCIO_ConstColorSpaceRcPtr * /*cs*/, - const int /*index*/) -{ - return ""; -} - -OCIO_ConstProcessorRcPtr *FallbackImpl::createDisplayProcessor(OCIO_ConstConfigRcPtr * /*config*/, - const char * /*input*/, - const char * /*view*/, - const char * /*display*/, - const char * /*look*/, - const float scale, - const float exponent, - const float /*temperature*/, - const float /*tint*/, - const bool /*use_white_balance*/, - const bool inverse) -{ - FallbackTransform transform; - transform.type = (inverse) ? TRANSFORM_SRGB_TO_LINEAR : TRANSFORM_LINEAR_TO_SRGB; - transform.scale = (inverse && scale != 0.0f) ? 1.0f / scale : scale; - transform.exponent = (inverse && exponent != 0.0f) ? 1.0f / exponent : exponent; - - return (OCIO_ConstProcessorRcPtr *)new FallbackProcessor(transform); -} - -OCIO_PackedImageDesc *FallbackImpl::createOCIO_PackedImageDesc(float *data, - long width, - long height, - long numChannels, - long chanStrideBytes, - long xStrideBytes, - long yStrideBytes) -{ - OCIO_PackedImageDescription *desc = MEM_callocN( - "OCIO_PackedImageDescription"); - desc->data = data; - desc->width = width; - desc->height = height; - desc->numChannels = numChannels; - desc->chanStrideBytes = chanStrideBytes; - desc->xStrideBytes = xStrideBytes; - desc->yStrideBytes = yStrideBytes; - return (OCIO_PackedImageDesc *)desc; -} - -void FallbackImpl::OCIO_PackedImageDescRelease(OCIO_PackedImageDesc *id) -{ - MEM_freeN(reinterpret_cast(id)); -} - -const char *FallbackImpl::getVersionString() -{ - return "fallback"; -} - -int FallbackImpl::getVersionHex() -{ - return 0; -} diff --git a/intern/opencolorio/ocio_capi.cc b/intern/opencolorio/ocio_capi.cc deleted file mode 100644 index 1f795c5604b..00000000000 --- a/intern/opencolorio/ocio_capi.cc +++ /dev/null @@ -1,361 +0,0 @@ -/* SPDX-FileCopyrightText: 2012 Blender Authors - * - * SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "ocio_impl.h" - -static IOCIOImpl *impl = nullptr; - -void OCIO_init() -{ -#ifdef WITH_OCIO - impl = new OCIOImpl(); -#else - impl = new FallbackImpl(); -#endif -} - -void OCIO_exit() -{ - delete impl; - impl = nullptr; -} - -OCIO_ConstConfigRcPtr *OCIO_getCurrentConfig() -{ - return impl->getCurrentConfig(); -} - -OCIO_ConstConfigRcPtr *OCIO_configCreateFallback() -{ - delete impl; - impl = new FallbackImpl(); - - return impl->getCurrentConfig(); -} - -void OCIO_setCurrentConfig(const OCIO_ConstConfigRcPtr *config) -{ - impl->setCurrentConfig(config); -} - -OCIO_ConstConfigRcPtr *OCIO_configCreateFromEnv() -{ - return impl->configCreateFromEnv(); -} - -OCIO_ConstConfigRcPtr *OCIO_configCreateFromFile(const char *filename) -{ - return impl->configCreateFromFile(filename); -} - -void OCIO_configRelease(OCIO_ConstConfigRcPtr *config) -{ - impl->configRelease(config); -} - -int OCIO_configGetNumColorSpaces(OCIO_ConstConfigRcPtr *config) -{ - return impl->configGetNumColorSpaces(config); -} - -const char *OCIO_configGetColorSpaceNameByIndex(OCIO_ConstConfigRcPtr *config, int index) -{ - return impl->configGetColorSpaceNameByIndex(config, index); -} - -OCIO_ConstColorSpaceRcPtr *OCIO_configGetColorSpace(OCIO_ConstConfigRcPtr *config, - const char *name) -{ - return impl->configGetColorSpace(config, name); -} - -int OCIO_configGetIndexForColorSpace(OCIO_ConstConfigRcPtr *config, const char *name) -{ - return impl->configGetIndexForColorSpace(config, name); -} - -const char *OCIO_getColorSpaceFromFilepath(OCIO_ConstConfigRcPtr *config, const char *filepath) -{ - return impl->getColorSpaceFromFilepath(config, filepath); -} - -const char *OCIO_configGetDefaultDisplay(OCIO_ConstConfigRcPtr *config) -{ - return impl->configGetDefaultDisplay(config); -} - -int OCIO_configGetNumDisplays(OCIO_ConstConfigRcPtr *config) -{ - return impl->configGetNumDisplays(config); -} - -const char *OCIO_configGetDisplay(OCIO_ConstConfigRcPtr *config, int index) -{ - return impl->configGetDisplay(config, index); -} - -const char *OCIO_configGetDefaultView(OCIO_ConstConfigRcPtr *config, const char *display) -{ - return impl->configGetDefaultView(config, display); -} - -int OCIO_configGetNumViews(OCIO_ConstConfigRcPtr *config, const char *display) -{ - return impl->configGetNumViews(config, display); -} - -const char *OCIO_configGetView(OCIO_ConstConfigRcPtr *config, const char *display, int index) -{ - return impl->configGetView(config, display, index); -} - -const char *OCIO_configGetDisplayColorSpaceName(OCIO_ConstConfigRcPtr *config, - const char *display, - const char *view) -{ - return impl->configGetDisplayColorSpaceName(config, display, view); -} - -void OCIO_configGetDefaultLumaCoefs(OCIO_ConstConfigRcPtr *config, float *rgb) -{ - impl->configGetDefaultLumaCoefs(config, rgb); -} - -void OCIO_configGetXYZtoSceneLinear(OCIO_ConstConfigRcPtr *config, float xyz_to_scene_linear[3][3]) -{ - impl->configGetXYZtoSceneLinear(config, xyz_to_scene_linear); -} - -int OCIO_configGetNumLooks(OCIO_ConstConfigRcPtr *config) -{ - return impl->configGetNumLooks(config); -} - -const char *OCIO_configGetLookNameByIndex(OCIO_ConstConfigRcPtr *config, int index) -{ - return impl->configGetLookNameByIndex(config, index); -} - -OCIO_ConstLookRcPtr *OCIO_configGetLook(OCIO_ConstConfigRcPtr *config, const char *name) -{ - return impl->configGetLook(config, name); -} - -const char *OCIO_lookGetProcessSpace(OCIO_ConstLookRcPtr *look) -{ - return impl->lookGetProcessSpace(look); -} - -void OCIO_lookRelease(OCIO_ConstLookRcPtr *look) -{ - impl->lookRelease(look); -} - -int OCIO_colorSpaceIsInvertible(OCIO_ConstColorSpaceRcPtr *cs) -{ - return impl->colorSpaceIsInvertible(cs); -} - -int OCIO_colorSpaceIsData(OCIO_ConstColorSpaceRcPtr *cs) -{ - return impl->colorSpaceIsData(cs); -} - -void OCIO_colorSpaceIsBuiltin(OCIO_ConstConfigRcPtr *config, - OCIO_ConstColorSpaceRcPtr *cs, - bool *is_scene_linear, - bool *is_srgb) -{ - impl->colorSpaceIsBuiltin(config, cs, *is_scene_linear, *is_srgb); -} - -void OCIO_colorSpaceRelease(OCIO_ConstColorSpaceRcPtr *cs) -{ - impl->colorSpaceRelease(cs); -} - -OCIO_ConstProcessorRcPtr *OCIO_configGetProcessorWithNames(OCIO_ConstConfigRcPtr *config, - const char *srcName, - const char *dstName) -{ - return impl->configGetProcessorWithNames(config, srcName, dstName); -} - -void OCIO_processorRelease(OCIO_ConstProcessorRcPtr *processor) -{ - impl->processorRelease(processor); -} - -OCIO_ConstCPUProcessorRcPtr *OCIO_processorGetCPUProcessor(OCIO_ConstProcessorRcPtr *processor) -{ - return impl->processorGetCPUProcessor(processor); -} - -bool OCIO_cpuProcessorIsNoOp(OCIO_ConstCPUProcessorRcPtr *cpu_processor) -{ - return impl->cpuProcessorIsNoOp(cpu_processor); -} - -void OCIO_cpuProcessorApply(OCIO_ConstCPUProcessorRcPtr *cpu_processor, OCIO_PackedImageDesc *img) -{ - impl->cpuProcessorApply(cpu_processor, img); -} - -void OCIO_cpuProcessorApply_predivide(OCIO_ConstCPUProcessorRcPtr *cpu_processor, - OCIO_PackedImageDesc *img) -{ - impl->cpuProcessorApply_predivide(cpu_processor, img); -} - -void OCIO_cpuProcessorApplyRGB(OCIO_ConstCPUProcessorRcPtr *cpu_processor, float *pixel) -{ - impl->cpuProcessorApplyRGB(cpu_processor, pixel); -} - -void OCIO_cpuProcessorApplyRGBA(OCIO_ConstCPUProcessorRcPtr *cpu_processor, float *pixel) -{ - impl->cpuProcessorApplyRGBA(cpu_processor, pixel); -} - -void OCIO_cpuProcessorApplyRGBA_predivide(OCIO_ConstCPUProcessorRcPtr *processor, float *pixel) -{ - impl->cpuProcessorApplyRGBA_predivide(processor, pixel); -} - -void OCIO_cpuProcessorRelease(OCIO_ConstCPUProcessorRcPtr *cpu_processor) -{ - impl->cpuProcessorRelease(cpu_processor); -} - -const char *OCIO_colorSpaceGetName(OCIO_ConstColorSpaceRcPtr *cs) -{ - return impl->colorSpaceGetName(cs); -} - -const char *OCIO_colorSpaceGetDescription(OCIO_ConstColorSpaceRcPtr *cs) -{ - return impl->colorSpaceGetDescription(cs); -} - -const char *OCIO_colorSpaceGetFamily(OCIO_ConstColorSpaceRcPtr *cs) -{ - return impl->colorSpaceGetFamily(cs); -} - -int OCIO_colorSpaceGetNumAliases(OCIO_ConstColorSpaceRcPtr *cs) -{ - return impl->colorSpaceGetNumAliases(cs); -} - -const char *OCIO_colorSpaceGetAlias(OCIO_ConstColorSpaceRcPtr *cs, const int index) -{ - return impl->colorSpaceGetAlias(cs, index); -} - -OCIO_ConstProcessorRcPtr *OCIO_createDisplayProcessor(OCIO_ConstConfigRcPtr *config, - const char *input, - const char *view, - const char *display, - const char *look, - const float scale, - const float exponent, - const float temperature, - const float tint, - const bool use_white_balance, - const bool inverse) -{ - return impl->createDisplayProcessor(config, - input, - view, - display, - look, - scale, - exponent, - temperature, - tint, - use_white_balance, - inverse); -} - -OCIO_PackedImageDesc *OCIO_createOCIO_PackedImageDesc(float *data, - long width, - long height, - long numChannels, - long chanStrideBytes, - long xStrideBytes, - long yStrideBytes) -{ - return impl->createOCIO_PackedImageDesc( - data, width, height, numChannels, chanStrideBytes, xStrideBytes, yStrideBytes); -} - -void OCIO_PackedImageDescRelease(OCIO_PackedImageDesc *id) -{ - impl->OCIO_PackedImageDescRelease(id); -} - -bool OCIO_supportGPUShader() -{ - return impl->supportGPUShader(); -} - -bool OCIO_gpuDisplayShaderBind(OCIO_ConstConfigRcPtr *config, - const char *input, - const char *view, - const char *display, - const char *look, - OCIO_CurveMappingSettings *curve_mapping_settings, - const float scale, - const float exponent, - const float dither, - const float temperature, - const float tint, - const bool use_predivide, - const bool use_overlay, - const bool use_hdr, - const bool use_white_balance) -{ - return impl->gpuDisplayShaderBind(config, - input, - view, - display, - look, - curve_mapping_settings, - scale, - exponent, - dither, - temperature, - tint, - use_predivide, - use_overlay, - use_hdr, - use_white_balance); -} - -bool OCIO_gpuToSceneLinearShaderBind(OCIO_ConstConfigRcPtr *config, - const char *from_colorspace_name, - const bool use_predivide) -{ - return impl->gpuToSceneLinearShaderBind(config, from_colorspace_name, use_predivide); -} - -void OCIO_gpuShaderUnbind() -{ - impl->gpuShaderUnbind(); -} - -void OCIO_gpuCacheFree() -{ - impl->gpuCacheFree(); -} - -const char *OCIO_getVersionString() -{ - return impl->getVersionString(); -} - -int OCIO_getVersionHex() -{ - return impl->getVersionHex(); -} diff --git a/intern/opencolorio/ocio_capi.h b/intern/opencolorio/ocio_capi.h deleted file mode 100644 index 074fedbbbb2..00000000000 --- a/intern/opencolorio/ocio_capi.h +++ /dev/null @@ -1,227 +0,0 @@ -/* SPDX-FileCopyrightText: 2012 Blender Authors - * - * SPDX-License-Identifier: GPL-2.0-or-later */ - -#ifndef __OCIO_CAPI_H__ -#define __OCIO_CAPI_H__ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -using OCIO_GPUShader = struct OCIO_GPUShader; - -#define OCIO_DECLARE_HANDLE(name) \ - struct name; \ - typedef struct name *name##Ptr; - -#define OCIO_ROLE_DATA "data" -#define OCIO_ROLE_SCENE_LINEAR "scene_linear" -#define OCIO_ROLE_COLOR_PICKING "color_picking" -#define OCIO_ROLE_TEXTURE_PAINT "texture_paint" -#define OCIO_ROLE_DEFAULT_BYTE "default_byte" -#define OCIO_ROLE_DEFAULT_FLOAT "default_float" -#define OCIO_ROLE_DEFAULT_SEQUENCER "default_sequencer" -#define OCIO_ROLE_ACES_INTERCHANGE "aces_interchange" - -OCIO_DECLARE_HANDLE(OCIO_ConstConfigRc); -OCIO_DECLARE_HANDLE(OCIO_ConstColorSpaceRc); -OCIO_DECLARE_HANDLE(OCIO_ConstProcessorRc); -OCIO_DECLARE_HANDLE(OCIO_ConstCPUProcessorRc); -OCIO_DECLARE_HANDLE(OCIO_ConstContextRc); -OCIO_DECLARE_HANDLE(OCIO_PackedImageDesc); -OCIO_DECLARE_HANDLE(OCIO_ConstLookRc); - -/* Standard XYZ (D65) to linear Rec.709 transform. */ -static const float OCIO_XYZ_TO_REC709[3][3] = {{3.2404542f, -0.9692660f, 0.0556434f}, - {-1.5371385f, 1.8760108f, -0.2040259f}, - {-0.4985314f, 0.0415560f, 1.0572252f}}; -/* Standard ACES to XYZ (D65) transform. - * Matches OpenColorIO builtin transform: UTILITY - ACES-AP0_to_CIE-XYZ-D65_BFD. */ -static const float OCIO_ACES_TO_XYZ[3][3] = {{0.938280f, 0.337369f, 0.001174f}, - {-0.004451f, 0.729522f, -0.003711f}, - {0.016628f, -0.066890f, 1.091595f}}; - -/* This structure is used to pass curve mapping settings from - * blender's DNA structure stored in view transform settings - * to a generic OpenColorIO C-API. - */ -struct OCIO_CurveMappingSettings { - /* This is a LUT which contain values for all 4 curve mapping tables - * (combined, R, G and B). - * - * Element I for table T is stored at I * 4 + T element of this LUT. - * - * This array is usually returned by curvemapping_table_RGBA(). - */ - float *lut; - - /* Size of single curve mapping table, 1/4 size of lut array. */ - int lut_size; - - /* Extend extrapolation flags for all the tables. - * if use_extend_extrapolate != 0 means extrapolation for - * curve. - */ - int use_extend_extrapolate; - - /* Minimal X value of the curve mapping tables. */ - float mintable[4]; - - /* Per curve mapping table range. */ - float range[4]; - - /* Lower extension value, stored as per-component arrays. */ - float ext_in_x[4], ext_in_y[4]; - - /* Higher extension value, stored as per-component arrays. */ - float ext_out_x[4], ext_out_y[4]; - - /* First points of the tables, both X and Y values. - * Needed for easier and faster access when extrapolating. - */ - float first_x[4], first_y[4]; - - /* Last points of the tables, both X and Y values. - * Needed for easier and faster access when extrapolating. - */ - float last_x[4], last_y[4]; - - /* Premultiplication settings: black level and scale to match - * with white level. - */ - float black[3], bwmul[3]; - - /* Cache id of the original curve mapping, used to detect when - * upload of new settings to GPU is needed. - */ - size_t cache_id; -}; - -void OCIO_init(void); -void OCIO_exit(void); - -OCIO_ConstConfigRcPtr *OCIO_getCurrentConfig(void); -void OCIO_setCurrentConfig(const OCIO_ConstConfigRcPtr *config); - -OCIO_ConstConfigRcPtr *OCIO_configCreateFromEnv(void); -OCIO_ConstConfigRcPtr *OCIO_configCreateFromFile(const char *filename); -OCIO_ConstConfigRcPtr *OCIO_configCreateFallback(void); - -void OCIO_configRelease(OCIO_ConstConfigRcPtr *config); - -int OCIO_configGetNumColorSpaces(OCIO_ConstConfigRcPtr *config); -const char *OCIO_configGetColorSpaceNameByIndex(OCIO_ConstConfigRcPtr *config, int index); -OCIO_ConstColorSpaceRcPtr *OCIO_configGetColorSpace(OCIO_ConstConfigRcPtr *config, - const char *name); -int OCIO_configGetIndexForColorSpace(OCIO_ConstConfigRcPtr *config, const char *name); - -const char *OCIO_getColorSpaceFromFilepath(OCIO_ConstConfigRcPtr *config, const char *filepath); - -int OCIO_colorSpaceIsInvertible(OCIO_ConstColorSpaceRcPtr *cs); -int OCIO_colorSpaceIsData(OCIO_ConstColorSpaceRcPtr *cs); -void OCIO_colorSpaceIsBuiltin(OCIO_ConstConfigRcPtr *config, - OCIO_ConstColorSpaceRcPtr *cs, - bool *is_scene_linear, - bool *is_srgb); - -void OCIO_colorSpaceRelease(OCIO_ConstColorSpaceRcPtr *cs); - -const char *OCIO_configGetDefaultDisplay(OCIO_ConstConfigRcPtr *config); -int OCIO_configGetNumDisplays(OCIO_ConstConfigRcPtr *config); -const char *OCIO_configGetDisplay(OCIO_ConstConfigRcPtr *config, int index); -const char *OCIO_configGetDefaultView(OCIO_ConstConfigRcPtr *config, const char *display); -int OCIO_configGetNumViews(OCIO_ConstConfigRcPtr *config, const char *display); -const char *OCIO_configGetView(OCIO_ConstConfigRcPtr *config, const char *display, int index); -const char *OCIO_configGetDisplayColorSpaceName(OCIO_ConstConfigRcPtr *config, - const char *display, - const char *view); - -void OCIO_configGetDefaultLumaCoefs(OCIO_ConstConfigRcPtr *config, float *rgb); -void OCIO_configGetXYZtoSceneLinear(OCIO_ConstConfigRcPtr *config, - float xyz_to_scene_linear[3][3]); - -int OCIO_configGetNumLooks(OCIO_ConstConfigRcPtr *config); -const char *OCIO_configGetLookNameByIndex(OCIO_ConstConfigRcPtr *config, int index); -OCIO_ConstLookRcPtr *OCIO_configGetLook(OCIO_ConstConfigRcPtr *config, const char *name); - -const char *OCIO_lookGetProcessSpace(OCIO_ConstLookRcPtr *look); -void OCIO_lookRelease(OCIO_ConstLookRcPtr *look); - -OCIO_ConstProcessorRcPtr *OCIO_configGetProcessorWithNames(OCIO_ConstConfigRcPtr *config, - const char *srcName, - const char *dstName); -void OCIO_processorRelease(OCIO_ConstProcessorRcPtr *cpu_processor); - -OCIO_ConstCPUProcessorRcPtr *OCIO_processorGetCPUProcessor(OCIO_ConstProcessorRcPtr *processor); -bool OCIO_cpuProcessorIsNoOp(OCIO_ConstCPUProcessorRcPtr *cpu_processor); -void OCIO_cpuProcessorApply(OCIO_ConstCPUProcessorRcPtr *cpu_processor, - struct OCIO_PackedImageDesc *img); -void OCIO_cpuProcessorApply_predivide(OCIO_ConstCPUProcessorRcPtr *cpu_processor, - struct OCIO_PackedImageDesc *img); -void OCIO_cpuProcessorApplyRGB(OCIO_ConstCPUProcessorRcPtr *cpu_processor, float *pixel); -void OCIO_cpuProcessorApplyRGBA(OCIO_ConstCPUProcessorRcPtr *cpu_processor, float *pixel); -void OCIO_cpuProcessorApplyRGBA_predivide(OCIO_ConstCPUProcessorRcPtr *cpu_processor, - float *pixel); -void OCIO_cpuProcessorRelease(OCIO_ConstCPUProcessorRcPtr *processor); - -const char *OCIO_colorSpaceGetName(OCIO_ConstColorSpaceRcPtr *cs); -const char *OCIO_colorSpaceGetDescription(OCIO_ConstColorSpaceRcPtr *cs); -const char *OCIO_colorSpaceGetFamily(OCIO_ConstColorSpaceRcPtr *cs); -int OCIO_colorSpaceGetNumAliases(OCIO_ConstColorSpaceRcPtr *cs); -const char *OCIO_colorSpaceGetAlias(OCIO_ConstColorSpaceRcPtr *cs, const int index); - -OCIO_ConstProcessorRcPtr *OCIO_createDisplayProcessor(OCIO_ConstConfigRcPtr *config, - const char *input, - const char *view, - const char *display, - const char *look, - const float scale, - const float exponent, - const float temperature, - const float tint, - const bool use_white_balance, - const bool inverse); - -struct OCIO_PackedImageDesc *OCIO_createOCIO_PackedImageDesc(float *data, - long width, - long height, - long numChannels, - long chanStrideBytes, - long xStrideBytes, - long yStrideBytes); - -void OCIO_PackedImageDescRelease(struct OCIO_PackedImageDesc *id); - -bool OCIO_supportGPUShader(void); -bool OCIO_gpuDisplayShaderBind(OCIO_ConstConfigRcPtr *config, - const char *input, - const char *view, - const char *display, - const char *look, - OCIO_CurveMappingSettings *curve_mapping_settings, - const float scale, - const float exponent, - const float dither, - const float temperature, - const float tint, - const bool use_predivide, - const bool use_overlay, - const bool use_hdr, - const bool use_white_balance); -bool OCIO_gpuToSceneLinearShaderBind(OCIO_ConstConfigRcPtr *config, - const char *from_colorspace_name, - bool use_predivide); -void OCIO_gpuShaderUnbind(void); -void OCIO_gpuCacheFree(void); - -const char *OCIO_getVersionString(void); -int OCIO_getVersionHex(void); - -#ifdef __cplusplus -} -#endif - -#endif /* OCIO_CAPI_H */ diff --git a/intern/opencolorio/ocio_impl.cc b/intern/opencolorio/ocio_impl.cc deleted file mode 100644 index e3a4282cc6e..00000000000 --- a/intern/opencolorio/ocio_impl.cc +++ /dev/null @@ -1,844 +0,0 @@ -/* SPDX-FileCopyrightText: 2012 Blender Authors - * - * SPDX-License-Identifier: GPL-2.0-or-later */ - -#include -#include -#include - -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable : 4251 4275) -#endif -#include -#ifdef _MSC_VER -# pragma warning(pop) -#endif - -using namespace OCIO_NAMESPACE; - -#include "MEM_guardedalloc.h" - -#include "BLI_math_color.h" -#include "BLI_math_color.hh" -#include "BLI_math_matrix.h" -#include "BLI_math_matrix.hh" - -#include "CLG_log.h" - -#include "ocio_impl.h" - -#if defined(_MSC_VER) -# define __func__ __FUNCTION__ -#endif - -using blender::double4x4; -using blender::float3; -using blender::float3x3; - -static CLG_LogRef LOG = {"imbuf.color_management"}; - -static void OCIO_reportException(Exception &exception) -{ - CLOG_ERROR(&LOG, "OpenColorIO Error: %s", exception.what()); -} - -OCIO_ConstConfigRcPtr *OCIOImpl::getCurrentConfig() -{ - ConstConfigRcPtr *config = MEM_new(__func__); - - try { - *config = GetCurrentConfig(); - - if (*config) { - return (OCIO_ConstConfigRcPtr *)config; - } - } - catch (Exception &exception) { - OCIO_reportException(exception); - } - - MEM_delete(config); - - return nullptr; -} - -void OCIOImpl::setCurrentConfig(const OCIO_ConstConfigRcPtr *config) -{ - try { - SetCurrentConfig(*(ConstConfigRcPtr *)config); - } - catch (Exception &exception) { - OCIO_reportException(exception); - } -} - -OCIO_ConstConfigRcPtr *OCIOImpl::configCreateFromEnv() -{ - ConstConfigRcPtr *config = MEM_new(__func__); - - try { - *config = Config::CreateFromEnv(); - - if (*config) { - return (OCIO_ConstConfigRcPtr *)config; - } - } - catch (Exception &exception) { - OCIO_reportException(exception); - } - - MEM_delete(config); - - return nullptr; -} - -OCIO_ConstConfigRcPtr *OCIOImpl::configCreateFromFile(const char *filename) -{ - ConstConfigRcPtr *config = MEM_new(__func__); - - try { - *config = Config::CreateFromFile(filename); - - if (*config) { - return (OCIO_ConstConfigRcPtr *)config; - } - } - catch (Exception &exception) { - OCIO_reportException(exception); - } - - MEM_delete(config); - - return nullptr; -} - -void OCIOImpl::configRelease(OCIO_ConstConfigRcPtr *config) -{ - MEM_delete(reinterpret_cast(config)); -} - -int OCIOImpl::configGetNumColorSpaces(OCIO_ConstConfigRcPtr *config) -{ - try { - return (*(ConstConfigRcPtr *)config)->getNumColorSpaces(); - } - catch (Exception &exception) { - OCIO_reportException(exception); - } - - return 0; -} - -const char *OCIOImpl::configGetColorSpaceNameByIndex(OCIO_ConstConfigRcPtr *config, int index) -{ - try { - return (*(ConstConfigRcPtr *)config)->getColorSpaceNameByIndex(index); - } - catch (Exception &exception) { - OCIO_reportException(exception); - } - - return nullptr; -} - -OCIO_ConstColorSpaceRcPtr *OCIOImpl::configGetColorSpace(OCIO_ConstConfigRcPtr *config, - const char *name) -{ - ConstColorSpaceRcPtr *cs = MEM_new(__func__); - - try { - *cs = (*(ConstConfigRcPtr *)config)->getColorSpace(name); - - if (*cs) { - return (OCIO_ConstColorSpaceRcPtr *)cs; - } - } - catch (Exception &exception) { - OCIO_reportException(exception); - } - - MEM_delete(cs); - - return nullptr; -} - -int OCIOImpl::configGetIndexForColorSpace(OCIO_ConstConfigRcPtr *config, const char *name) -{ - try { - return (*(ConstConfigRcPtr *)config)->getIndexForColorSpace(name); - } - catch (Exception &exception) { - OCIO_reportException(exception); - } - - return -1; -} - -const char *OCIOImpl::getColorSpaceFromFilepath(OCIO_ConstConfigRcPtr *config, - const char *filepath) -{ - ConstConfigRcPtr &cfg = *(ConstConfigRcPtr *)config; - - /* If Blender specific default_byte or default_float roles exist, don't use the - * default rule which can't distinguish between these two cases automatically. */ - if (cfg->filepathOnlyMatchesDefaultRule(filepath) && - (cfg->hasRole(OCIO_ROLE_DEFAULT_BYTE) || cfg->hasRole(OCIO_ROLE_DEFAULT_FLOAT))) - { - return nullptr; - } - - return cfg->getColorSpaceFromFilepath(filepath); -} - -const char *OCIOImpl::configGetDefaultDisplay(OCIO_ConstConfigRcPtr *config) -{ - try { - return (*(ConstConfigRcPtr *)config)->getDefaultDisplay(); - } - catch (Exception &exception) { - OCIO_reportException(exception); - } - - return nullptr; -} - -int OCIOImpl::configGetNumDisplays(OCIO_ConstConfigRcPtr *config) -{ - try { - return (*(ConstConfigRcPtr *)config)->getNumDisplays(); - } - catch (Exception &exception) { - OCIO_reportException(exception); - } - - return 0; -} - -const char *OCIOImpl::configGetDisplay(OCIO_ConstConfigRcPtr *config, int index) -{ - try { - return (*(ConstConfigRcPtr *)config)->getDisplay(index); - } - catch (Exception &exception) { - OCIO_reportException(exception); - } - - return nullptr; -} - -const char *OCIOImpl::configGetDefaultView(OCIO_ConstConfigRcPtr *config, const char *display) -{ - try { - return (*(ConstConfigRcPtr *)config)->getDefaultView(display); - } - catch (Exception &exception) { - OCIO_reportException(exception); - } - - return nullptr; -} - -int OCIOImpl::configGetNumViews(OCIO_ConstConfigRcPtr *config, const char *display) -{ - try { - return (*(ConstConfigRcPtr *)config)->getNumViews(display); - } - catch (Exception &exception) { - OCIO_reportException(exception); - } - - return 0; -} - -const char *OCIOImpl::configGetView(OCIO_ConstConfigRcPtr *config, const char *display, int index) -{ - try { - return (*(ConstConfigRcPtr *)config)->getView(display, index); - } - catch (Exception &exception) { - OCIO_reportException(exception); - } - - return nullptr; -} - -const char *OCIOImpl::configGetDisplayColorSpaceName(OCIO_ConstConfigRcPtr *config, - const char *display, - const char *view) -{ - try { - const char *name = (*(ConstConfigRcPtr *)config)->getDisplayViewColorSpaceName(display, view); - /* OpenColorIO does not resolve this token for us, so do it ourselves. */ - if (strcasecmp(name, "") == 0) { - return display; - } - return name; - } - catch (Exception &exception) { - OCIO_reportException(exception); - } - - return nullptr; -} - -void OCIOImpl::configGetDefaultLumaCoefs(OCIO_ConstConfigRcPtr *config, float *rgb) -{ - try { - double rgb_double[3]; - (*(ConstConfigRcPtr *)config)->getDefaultLumaCoefs(rgb_double); - rgb[0] = rgb_double[0]; - rgb[1] = rgb_double[1]; - rgb[2] = rgb_double[2]; - } - catch (Exception &exception) { - OCIO_reportException(exception); - } -} - -static bool to_scene_linear_matrix(ConstConfigRcPtr &config, - const char *colorspace, - float to_scene_linear[3][3]) -{ - ConstProcessorRcPtr processor; - try { - processor = config->getProcessor(colorspace, ROLE_SCENE_LINEAR); - } - catch (Exception &exception) { - OCIO_reportException(exception); - return false; - } - - if (!processor) { - return false; - } - - ConstCPUProcessorRcPtr cpu_processor = processor->getDefaultCPUProcessor(); - if (!cpu_processor) { - return false; - } - - unit_m3(to_scene_linear); - cpu_processor->applyRGB(to_scene_linear[0]); - cpu_processor->applyRGB(to_scene_linear[1]); - cpu_processor->applyRGB(to_scene_linear[2]); - return true; -} - -void OCIOImpl::configGetXYZtoSceneLinear(OCIO_ConstConfigRcPtr *config_, - float xyz_to_scene_linear[3][3]) -{ - ConstConfigRcPtr config = (*(ConstConfigRcPtr *)config_); - - /* Default to ITU-BT.709 in case no appropriate transform found. - * Note XYZ is defined here as having a D65 white point. */ - memcpy(xyz_to_scene_linear, OCIO_XYZ_TO_REC709, sizeof(OCIO_XYZ_TO_REC709)); - - /* Get from OpenColorO config if it has the required roles. */ - if (!config->hasRole(ROLE_SCENE_LINEAR)) { - return; - } - - if (config->hasRole("aces_interchange")) { - /* Standard OpenColorIO role, defined as ACES AP0 (ACES2065-1). */ - float aces_to_scene_linear[3][3]; - if (to_scene_linear_matrix(config, "aces_interchange", aces_to_scene_linear)) { - float xyz_to_aces[3][3]; - invert_m3_m3(xyz_to_aces, OCIO_ACES_TO_XYZ); - - mul_m3_m3m3(xyz_to_scene_linear, aces_to_scene_linear, xyz_to_aces); - } - } - else if (config->hasRole("XYZ")) { - /* Custom role used before the standard existed. */ - to_scene_linear_matrix(config, "XYZ", xyz_to_scene_linear); - } -} - -int OCIOImpl::configGetNumLooks(OCIO_ConstConfigRcPtr *config) -{ - try { - return (*(ConstConfigRcPtr *)config)->getNumLooks(); - } - catch (Exception &exception) { - OCIO_reportException(exception); - } - - return 0; -} - -const char *OCIOImpl::configGetLookNameByIndex(OCIO_ConstConfigRcPtr *config, int index) -{ - try { - return (*(ConstConfigRcPtr *)config)->getLookNameByIndex(index); - } - catch (Exception &exception) { - OCIO_reportException(exception); - } - - return nullptr; -} - -OCIO_ConstLookRcPtr *OCIOImpl::configGetLook(OCIO_ConstConfigRcPtr *config, const char *name) -{ - ConstLookRcPtr *look = MEM_new(__func__); - - try { - *look = (*(ConstConfigRcPtr *)config)->getLook(name); - - if (*look) { - return (OCIO_ConstLookRcPtr *)look; - } - } - catch (Exception &exception) { - OCIO_reportException(exception); - } - - MEM_delete(look); - - return nullptr; -} - -const char *OCIOImpl::lookGetProcessSpace(OCIO_ConstLookRcPtr *look) -{ - return (*(ConstLookRcPtr *)look)->getProcessSpace(); -} - -void OCIOImpl::lookRelease(OCIO_ConstLookRcPtr *look) -{ - MEM_delete(reinterpret_cast(look)); -} - -int OCIOImpl::colorSpaceIsInvertible(OCIO_ConstColorSpaceRcPtr *cs_) -{ - ConstColorSpaceRcPtr *cs = (ConstColorSpaceRcPtr *)cs_; - const char *family = (*cs)->getFamily(); - - if (!strcmp(family, "rrt") || !strcmp(family, "display")) { - /* assume display and rrt transformations are not invertible in fact some of them could be, - * but it doesn't make much sense to allow use them as invertible. */ - return false; - } - - if ((*cs)->isData()) { - /* data color spaces don't have transformation at all */ - return true; - } - - if ((*cs)->getTransform(COLORSPACE_DIR_TO_REFERENCE)) { - /* if there's defined transform to reference space, - * color space could be converted to scene linear. */ - return true; - } - - return true; -} - -int OCIOImpl::colorSpaceIsData(OCIO_ConstColorSpaceRcPtr *cs) -{ - return (*(ConstColorSpaceRcPtr *)cs)->isData(); -} - -static float compare_floats(float a, float b, float abs_diff, int ulp_diff) -{ - /* Returns true if the absolute difference is smaller than abs_diff (for numbers near zero) - * or their relative difference is less than ulp_diff ULPs. Based on: - * https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ */ - if (fabsf(a - b) < abs_diff) { - return true; - } - - if ((a < 0.0f) != (b < 0.0f)) { - return false; - } - - return (abs((*(int *)&a) - (*(int *)&b)) < ulp_diff); -} - -void OCIOImpl::colorSpaceIsBuiltin(OCIO_ConstConfigRcPtr *config_, - OCIO_ConstColorSpaceRcPtr *cs_, - bool &is_scene_linear, - bool &is_srgb) -{ - ConstConfigRcPtr *config = (ConstConfigRcPtr *)config_; - ConstColorSpaceRcPtr *cs = (ConstColorSpaceRcPtr *)cs_; - ConstProcessorRcPtr processor; - - try { - processor = (*config)->getProcessor((*cs)->getName(), "scene_linear"); - } - catch (Exception &) { - /* Silently ignore if no conversion possible, then it's not scene linear or sRGB. */ - is_scene_linear = false; - is_srgb = false; - return; - } - - ConstCPUProcessorRcPtr cpu_processor = processor->getDefaultCPUProcessor(); - - is_scene_linear = true; - is_srgb = true; - for (int i = 0; i < 256; i++) { - float v = i / 255.0f; - - float cR[3] = {v, 0, 0}; - float cG[3] = {0, v, 0}; - float cB[3] = {0, 0, v}; - float cW[3] = {v, v, v}; - cpu_processor->applyRGB(cR); - cpu_processor->applyRGB(cG); - cpu_processor->applyRGB(cB); - cpu_processor->applyRGB(cW); - - /* Make sure that there is no channel crosstalk. */ - if (fabsf(cR[1]) > 1e-5f || fabsf(cR[2]) > 1e-5f || fabsf(cG[0]) > 1e-5f || - fabsf(cG[2]) > 1e-5f || fabsf(cB[0]) > 1e-5f || fabsf(cB[1]) > 1e-5f) - { - is_scene_linear = false; - is_srgb = false; - break; - } - /* Make sure that the three primaries combine linearly. */ - if (!compare_floats(cR[0], cW[0], 1e-6f, 64) || !compare_floats(cG[1], cW[1], 1e-6f, 64) || - !compare_floats(cB[2], cW[2], 1e-6f, 64)) - { - is_scene_linear = false; - is_srgb = false; - break; - } - /* Make sure that the three channels behave identically. */ - if (!compare_floats(cW[0], cW[1], 1e-6f, 64) || !compare_floats(cW[1], cW[2], 1e-6f, 64)) { - is_scene_linear = false; - is_srgb = false; - break; - } - - float out_v = (cW[0] + cW[1] + cW[2]) * (1.0f / 3.0f); - if (!compare_floats(v, out_v, 1e-6f, 64)) { - is_scene_linear = false; - } - if (!compare_floats(srgb_to_linearrgb(v), out_v, 1e-4f, 64)) { - is_srgb = false; - } - } -} - -void OCIOImpl::colorSpaceRelease(OCIO_ConstColorSpaceRcPtr *cs) -{ - MEM_delete(reinterpret_cast(cs)); -} - -OCIO_ConstProcessorRcPtr *OCIOImpl::configGetProcessorWithNames(OCIO_ConstConfigRcPtr *config, - const char *srcName, - const char *dstName) -{ - ConstProcessorRcPtr *processor = MEM_new(__func__); - - try { - *processor = (*(ConstConfigRcPtr *)config)->getProcessor(srcName, dstName); - - if (*processor) { - return (OCIO_ConstProcessorRcPtr *)processor; - } - } - catch (Exception &exception) { - OCIO_reportException(exception); - } - - MEM_delete(processor); - - return nullptr; -} - -void OCIOImpl::processorRelease(OCIO_ConstProcessorRcPtr *processor) -{ - MEM_delete(reinterpret_cast(processor)); -} - -OCIO_ConstCPUProcessorRcPtr *OCIOImpl::processorGetCPUProcessor( - OCIO_ConstProcessorRcPtr *processor) -{ - ConstCPUProcessorRcPtr *cpu_processor = MEM_new(__func__); - *cpu_processor = (*(ConstProcessorRcPtr *)processor)->getDefaultCPUProcessor(); - return (OCIO_ConstCPUProcessorRcPtr *)cpu_processor; -} - -void OCIOImpl::cpuProcessorApply(OCIO_ConstCPUProcessorRcPtr *cpu_processor, - OCIO_PackedImageDesc *img) -{ - try { - (*(ConstCPUProcessorRcPtr *)cpu_processor)->apply(*(PackedImageDesc *)img); - } - catch (Exception &exception) { - OCIO_reportException(exception); - } -} - -void OCIOImpl::cpuProcessorApply_predivide(OCIO_ConstCPUProcessorRcPtr *cpu_processor, - OCIO_PackedImageDesc *img_) -{ - try { - PackedImageDesc *img = (PackedImageDesc *)img_; - int channels = img->getNumChannels(); - - if (channels == 4) { - /* Convert from premultiplied alpha to straight alpha. */ - assert(img->isFloat()); - float *pixel = (float *)img->getData(); - size_t pixel_count = img->getWidth() * img->getHeight(); - for (size_t i = 0; i < pixel_count; i++, pixel += 4) { - float alpha = pixel[3]; - if (alpha != 0.0f && alpha != 1.0f) { - float inv_alpha = 1.0f / alpha; - pixel[0] *= inv_alpha; - pixel[1] *= inv_alpha; - pixel[2] *= inv_alpha; - } - } - } - - (*(ConstCPUProcessorRcPtr *)cpu_processor)->apply(*img); - - if (channels == 4) { - /* Back to premultiplied alpha. */ - assert(img->isFloat()); - float *pixel = (float *)img->getData(); - size_t pixel_count = img->getWidth() * img->getHeight(); - for (size_t i = 0; i < pixel_count; i++, pixel += 4) { - float alpha = pixel[3]; - if (alpha != 0.0f && alpha != 1.0f) { - pixel[0] *= alpha; - pixel[1] *= alpha; - pixel[2] *= alpha; - } - } - } - } - catch (Exception &exception) { - OCIO_reportException(exception); - } -} - -bool OCIOImpl::cpuProcessorIsNoOp(OCIO_ConstCPUProcessorRcPtr *cpu_processor) -{ - return (*(ConstCPUProcessorRcPtr *)cpu_processor)->isNoOp(); -} - -void OCIOImpl::cpuProcessorApplyRGB(OCIO_ConstCPUProcessorRcPtr *cpu_processor, float *pixel) -{ - (*(ConstCPUProcessorRcPtr *)cpu_processor)->applyRGB(pixel); -} - -void OCIOImpl::cpuProcessorApplyRGBA(OCIO_ConstCPUProcessorRcPtr *cpu_processor, float *pixel) -{ - (*(ConstCPUProcessorRcPtr *)cpu_processor)->applyRGBA(pixel); -} - -void OCIOImpl::cpuProcessorApplyRGBA_predivide(OCIO_ConstCPUProcessorRcPtr *cpu_processor, - float *pixel) -{ - if (pixel[3] == 1.0f || pixel[3] == 0.0f) { - (*(ConstCPUProcessorRcPtr *)cpu_processor)->applyRGBA(pixel); - } - else { - float alpha, inv_alpha; - - alpha = pixel[3]; - inv_alpha = 1.0f / alpha; - - pixel[0] *= inv_alpha; - pixel[1] *= inv_alpha; - pixel[2] *= inv_alpha; - - (*(ConstCPUProcessorRcPtr *)cpu_processor)->applyRGBA(pixel); - - pixel[0] *= alpha; - pixel[1] *= alpha; - pixel[2] *= alpha; - } -} - -void OCIOImpl::cpuProcessorRelease(OCIO_ConstCPUProcessorRcPtr *cpu_processor) -{ - MEM_delete(reinterpret_cast(cpu_processor)); -} - -const char *OCIOImpl::colorSpaceGetName(OCIO_ConstColorSpaceRcPtr *cs) -{ - return (*(ConstColorSpaceRcPtr *)cs)->getName(); -} - -const char *OCIOImpl::colorSpaceGetDescription(OCIO_ConstColorSpaceRcPtr *cs) -{ - return (*(ConstColorSpaceRcPtr *)cs)->getDescription(); -} - -const char *OCIOImpl::colorSpaceGetFamily(OCIO_ConstColorSpaceRcPtr *cs) -{ - return (*(ConstColorSpaceRcPtr *)cs)->getFamily(); -} - -int OCIOImpl::colorSpaceGetNumAliases(OCIO_ConstColorSpaceRcPtr *cs) -{ - return (*(ConstColorSpaceRcPtr *)cs)->getNumAliases(); -} -const char *OCIOImpl::colorSpaceGetAlias(OCIO_ConstColorSpaceRcPtr *cs, const int index) -{ - return (*(ConstColorSpaceRcPtr *)cs)->getAlias(index); -} - -OCIO_ConstProcessorRcPtr *OCIOImpl::createDisplayProcessor(OCIO_ConstConfigRcPtr *config_, - const char *input, - const char *view, - const char *display, - const char *look, - const float scale, - const float exponent, - const float temperature, - const float tint, - const bool use_white_balance, - const bool inverse) - -{ - ConstConfigRcPtr config = *(ConstConfigRcPtr *)config_; - GroupTransformRcPtr group = GroupTransform::Create(); - - /* Linear transforms. */ - if (scale != 1.0f || use_white_balance) { - /* Always apply exposure and/or white balance in scene linear. */ - ColorSpaceTransformRcPtr ct = ColorSpaceTransform::Create(); - ct->setSrc(input); - ct->setDst(ROLE_SCENE_LINEAR); - group->appendTransform(ct); - - /* Make further transforms aware of the color space change. */ - input = ROLE_SCENE_LINEAR; - - /* Apply scale. */ - MatrixTransformRcPtr mt = MatrixTransform::Create(); - float3x3 matrix = float3x3::identity() * scale; - - /* Apply white balance. */ - if (use_white_balance) { - /* Compute white point of the scene space in XYZ.*/ - float3x3 xyz_to_scene; - configGetXYZtoSceneLinear(config_, xyz_to_scene.ptr()); - float3x3 scene_to_xyz = blender::math::invert(xyz_to_scene); - float3 target = scene_to_xyz * float3(1.0f); - - /* Add operations to the matrix. - * Note: Since we're multiplying from the right, the operations here will be performed in - * reverse list order (scene-to-XYZ, then adaption, then XYZ-to-scene, then exposure). */ - matrix *= xyz_to_scene; - matrix *= blender::math::chromatic_adaption_matrix( - blender::math::whitepoint_from_temp_tint(temperature, tint), target); - matrix *= scene_to_xyz; - } - - mt->setMatrix(double4x4(blender::math::transpose(matrix)).base_ptr()); - group->appendTransform(mt); - } - - /* Add look transform. */ - bool use_look = (look != nullptr && look[0] != 0); - if (use_look) { - const char *look_output = LookTransform::GetLooksResultColorSpace( - config, config->getCurrentContext(), look); - - if (look_output != nullptr && look_output[0] != 0) { - LookTransformRcPtr lt = LookTransform::Create(); - lt->setSrc(input); - lt->setDst(look_output); - lt->setLooks(look); - group->appendTransform(lt); - - /* Make further transforms aware of the color space change. */ - input = look_output; - } - else { - /* For empty looks, no output color space is returned. */ - use_look = false; - } - } - - /* Add view and display transform. */ - DisplayViewTransformRcPtr dvt = DisplayViewTransform::Create(); - dvt->setSrc(input); - dvt->setLooksBypass(use_look); - dvt->setView(view); - dvt->setDisplay(display); - group->appendTransform(dvt); - - /* Gamma. */ - if (exponent != 1.0f) { - ExponentTransformRcPtr et = ExponentTransform::Create(); - const double value[4] = {exponent, exponent, exponent, 1.0}; - et->setValue(value); - group->appendTransform(et); - } - - if (inverse) { - group->setDirection(TRANSFORM_DIR_INVERSE); - } - - /* Create processor from transform. This is the moment were OCIO validates - * the entire transform, no need to check for the validity of inputs above. */ - ConstProcessorRcPtr *p = MEM_new(__func__); - - try { - *p = config->getProcessor(group); - - if (*p) { - return (OCIO_ConstProcessorRcPtr *)p; - } - } - catch (Exception &exception) { - OCIO_reportException(exception); - } - - MEM_delete(p); - return nullptr; -} - -OCIO_PackedImageDesc *OCIOImpl::createOCIO_PackedImageDesc(float *data, - long width, - long height, - long numChannels, - long chanStrideBytes, - long xStrideBytes, - long yStrideBytes) -{ - try { - PackedImageDesc *id = MEM_new(__func__, - data, - width, - height, - numChannels, - BIT_DEPTH_F32, - chanStrideBytes, - xStrideBytes, - yStrideBytes); - - return (OCIO_PackedImageDesc *)id; - } - catch (Exception &exception) { - OCIO_reportException(exception); - } - - return nullptr; -} - -void OCIOImpl::OCIO_PackedImageDescRelease(OCIO_PackedImageDesc *id) -{ - MEM_delete(reinterpret_cast(id)); -} - -const char *OCIOImpl::getVersionString() -{ - return GetVersion(); -} - -int OCIOImpl::getVersionHex() -{ - return GetVersionHex(); -} diff --git a/intern/opencolorio/ocio_impl.h b/intern/opencolorio/ocio_impl.h deleted file mode 100644 index 7aa2dd6b123..00000000000 --- a/intern/opencolorio/ocio_impl.h +++ /dev/null @@ -1,397 +0,0 @@ -/* SPDX-FileCopyrightText: 2012 Blender Authors - * - * SPDX-License-Identifier: GPL-2.0-or-later */ - -#ifndef __OCIO_IMPL_H__ -#define __OCIO_IMPL_H__ - -#include "ocio_capi.h" - -class IOCIOImpl { - public: - virtual ~IOCIOImpl() = default; - - virtual OCIO_ConstConfigRcPtr *getCurrentConfig() = 0; - virtual void setCurrentConfig(const OCIO_ConstConfigRcPtr *config) = 0; - - virtual OCIO_ConstConfigRcPtr *configCreateFromEnv() = 0; - virtual OCIO_ConstConfigRcPtr *configCreateFromFile(const char *filename) = 0; - - virtual void configRelease(OCIO_ConstConfigRcPtr *config) = 0; - - virtual int configGetNumColorSpaces(OCIO_ConstConfigRcPtr *config) = 0; - virtual const char *configGetColorSpaceNameByIndex(OCIO_ConstConfigRcPtr *config, int index) = 0; - virtual OCIO_ConstColorSpaceRcPtr *configGetColorSpace(OCIO_ConstConfigRcPtr *config, - const char *name) = 0; - virtual int configGetIndexForColorSpace(OCIO_ConstConfigRcPtr *config, const char *name) = 0; - - virtual const char *getColorSpaceFromFilepath(OCIO_ConstConfigRcPtr *config, - const char *filepath) = 0; - - virtual int colorSpaceIsInvertible(OCIO_ConstColorSpaceRcPtr *cs) = 0; - virtual int colorSpaceIsData(OCIO_ConstColorSpaceRcPtr *cs) = 0; - virtual void colorSpaceIsBuiltin(OCIO_ConstConfigRcPtr *config, - OCIO_ConstColorSpaceRcPtr *cs, - bool &is_scene_linear, - bool &is_srgb) = 0; - - virtual void colorSpaceRelease(OCIO_ConstColorSpaceRcPtr *cs) = 0; - - virtual const char *configGetDefaultDisplay(OCIO_ConstConfigRcPtr *config) = 0; - virtual int configGetNumDisplays(OCIO_ConstConfigRcPtr *config) = 0; - virtual const char *configGetDisplay(OCIO_ConstConfigRcPtr *config, int index) = 0; - virtual const char *configGetDefaultView(OCIO_ConstConfigRcPtr *config, const char *display) = 0; - virtual int configGetNumViews(OCIO_ConstConfigRcPtr *config, const char *display) = 0; - virtual const char *configGetView(OCIO_ConstConfigRcPtr *config, - const char *display, - int index) = 0; - virtual const char *configGetDisplayColorSpaceName(OCIO_ConstConfigRcPtr *config, - const char *display, - const char *view) = 0; - - virtual void configGetDefaultLumaCoefs(OCIO_ConstConfigRcPtr *config, float *rgb) = 0; - virtual void configGetXYZtoSceneLinear(OCIO_ConstConfigRcPtr *config, - float xyz_to_scene_linear[3][3]) = 0; - - virtual int configGetNumLooks(OCIO_ConstConfigRcPtr *config) = 0; - virtual const char *configGetLookNameByIndex(OCIO_ConstConfigRcPtr *config, int index) = 0; - virtual OCIO_ConstLookRcPtr *configGetLook(OCIO_ConstConfigRcPtr *config, const char *name) = 0; - - virtual const char *lookGetProcessSpace(OCIO_ConstLookRcPtr *look) = 0; - virtual void lookRelease(OCIO_ConstLookRcPtr *look) = 0; - - virtual OCIO_ConstProcessorRcPtr *configGetProcessorWithNames(OCIO_ConstConfigRcPtr *config, - const char *srcName, - const char *dstName) = 0; - virtual void processorRelease(OCIO_ConstProcessorRcPtr *processor) = 0; - - virtual OCIO_ConstCPUProcessorRcPtr *processorGetCPUProcessor(OCIO_ConstProcessorRcPtr *p) = 0; - virtual bool cpuProcessorIsNoOp(OCIO_ConstCPUProcessorRcPtr *cpu_processor) = 0; - virtual void cpuProcessorApply(OCIO_ConstCPUProcessorRcPtr *cpu_processor, - OCIO_PackedImageDesc *img) = 0; - virtual void cpuProcessorApply_predivide(OCIO_ConstCPUProcessorRcPtr *cpu_processor, - OCIO_PackedImageDesc *img) = 0; - virtual void cpuProcessorApplyRGB(OCIO_ConstCPUProcessorRcPtr *cpu_processor, float *pixel) = 0; - virtual void cpuProcessorApplyRGBA(OCIO_ConstCPUProcessorRcPtr *cpu_processor, float *pixel) = 0; - virtual void cpuProcessorApplyRGBA_predivide(OCIO_ConstCPUProcessorRcPtr *cpu_processor, - float *pixel) = 0; - virtual void cpuProcessorRelease(OCIO_ConstCPUProcessorRcPtr *cpu_processor) = 0; - - virtual const char *colorSpaceGetName(OCIO_ConstColorSpaceRcPtr *cs) = 0; - virtual const char *colorSpaceGetDescription(OCIO_ConstColorSpaceRcPtr *cs) = 0; - virtual const char *colorSpaceGetFamily(OCIO_ConstColorSpaceRcPtr *cs) = 0; - virtual int colorSpaceGetNumAliases(OCIO_ConstColorSpaceRcPtr *cs) = 0; - virtual const char *colorSpaceGetAlias(OCIO_ConstColorSpaceRcPtr *cs, const int index) = 0; - - virtual OCIO_ConstProcessorRcPtr *createDisplayProcessor(OCIO_ConstConfigRcPtr *config, - const char *input, - const char *view, - const char *display, - const char *look, - const float scale, - const float exponent, - const float temperature, - const float tint, - const bool use_white_balance, - const bool inverse) = 0; - - virtual OCIO_PackedImageDesc *createOCIO_PackedImageDesc(float *data, - long width, - long height, - long numChannels, - long chanStrideBytes, - long xStrideBytes, - long yStrideBytes) = 0; - - virtual void OCIO_PackedImageDescRelease(OCIO_PackedImageDesc *p) = 0; - - /* Optional GPU support. */ - virtual bool supportGPUShader() - { - return false; - } - virtual bool gpuDisplayShaderBind(OCIO_ConstConfigRcPtr * /*config*/, - const char * /*input*/, - const char * /*view*/, - const char * /*display*/, - const char * /*look*/, - OCIO_CurveMappingSettings * /*curve_mapping_settings*/, - const float /*scale*/, - const float /*exponent*/, - const float /*dither*/, - const float /*temperature*/, - const float /*tint*/, - const bool /*use_predivide*/, - const bool /*use_overlay*/, - const bool /*use_hdr*/, - const bool /*use_white_balance*/) - { - return false; - } - virtual bool gpuToSceneLinearShaderBind(OCIO_ConstConfigRcPtr * /*config*/, - const char * /*from_colorspace_name*/, - const bool /*use_predivide*/) - { - return false; - } - - virtual void gpuShaderUnbind() {} - virtual void gpuCacheFree() {} - - virtual const char *getVersionString() = 0; - virtual int getVersionHex() = 0; -}; - -class FallbackImpl : public IOCIOImpl { - public: - FallbackImpl() = default; - - OCIO_ConstConfigRcPtr *getCurrentConfig() override; - void setCurrentConfig(const OCIO_ConstConfigRcPtr *config) override; - - OCIO_ConstConfigRcPtr *configCreateFromEnv() override; - OCIO_ConstConfigRcPtr *configCreateFromFile(const char *filename) override; - - void configRelease(OCIO_ConstConfigRcPtr *config) override; - - int configGetNumColorSpaces(OCIO_ConstConfigRcPtr *config) override; - const char *configGetColorSpaceNameByIndex(OCIO_ConstConfigRcPtr *config, int index) override; - OCIO_ConstColorSpaceRcPtr *configGetColorSpace(OCIO_ConstConfigRcPtr *config, - const char *name) override; - int configGetIndexForColorSpace(OCIO_ConstConfigRcPtr *config, const char *name) override; - - const char *getColorSpaceFromFilepath(OCIO_ConstConfigRcPtr *config, - const char *filepath) override; - - int colorSpaceIsInvertible(OCIO_ConstColorSpaceRcPtr *cs) override; - int colorSpaceIsData(OCIO_ConstColorSpaceRcPtr *cs) override; - void colorSpaceIsBuiltin(OCIO_ConstConfigRcPtr *config, - OCIO_ConstColorSpaceRcPtr *cs, - bool &is_scene_linear, - bool &is_srgb) override; - - void colorSpaceRelease(OCIO_ConstColorSpaceRcPtr *cs) override; - - const char *configGetDefaultDisplay(OCIO_ConstConfigRcPtr *config) override; - int configGetNumDisplays(OCIO_ConstConfigRcPtr *config) override; - const char *configGetDisplay(OCIO_ConstConfigRcPtr *config, int index) override; - const char *configGetDefaultView(OCIO_ConstConfigRcPtr *config, const char *display) override; - int configGetNumViews(OCIO_ConstConfigRcPtr *config, const char *display) override; - const char *configGetView(OCIO_ConstConfigRcPtr *config, - const char *display, - int index) override; - const char *configGetDisplayColorSpaceName(OCIO_ConstConfigRcPtr *config, - const char *display, - const char *view) override; - - void configGetDefaultLumaCoefs(OCIO_ConstConfigRcPtr *config, float *rgb) override; - void configGetXYZtoSceneLinear(OCIO_ConstConfigRcPtr *config, - float xyz_to_scene_linear[3][3]) override; - - int configGetNumLooks(OCIO_ConstConfigRcPtr *config) override; - const char *configGetLookNameByIndex(OCIO_ConstConfigRcPtr *config, int index) override; - OCIO_ConstLookRcPtr *configGetLook(OCIO_ConstConfigRcPtr *config, const char *name) override; - - const char *lookGetProcessSpace(OCIO_ConstLookRcPtr *look) override; - void lookRelease(OCIO_ConstLookRcPtr *look) override; - - OCIO_ConstProcessorRcPtr *configGetProcessorWithNames(OCIO_ConstConfigRcPtr *config, - const char *srcName, - const char *dstName) override; - void processorRelease(OCIO_ConstProcessorRcPtr *processor) override; - - OCIO_ConstCPUProcessorRcPtr *processorGetCPUProcessor( - OCIO_ConstProcessorRcPtr *processor) override; - bool cpuProcessorIsNoOp(OCIO_ConstCPUProcessorRcPtr *cpu_processor) override; - void cpuProcessorApply(OCIO_ConstCPUProcessorRcPtr *cpu_processor, - OCIO_PackedImageDesc *img) override; - void cpuProcessorApply_predivide(OCIO_ConstCPUProcessorRcPtr *cpu_processor, - OCIO_PackedImageDesc *img) override; - void cpuProcessorApplyRGB(OCIO_ConstCPUProcessorRcPtr *cpu_processor, float *pixel) override; - void cpuProcessorApplyRGBA(OCIO_ConstCPUProcessorRcPtr *cpu_processor, float *pixel) override; - void cpuProcessorApplyRGBA_predivide(OCIO_ConstCPUProcessorRcPtr *cpu_processor, - float *pixel) override; - void cpuProcessorRelease(OCIO_ConstCPUProcessorRcPtr *cpu_processor) override; - - const char *colorSpaceGetName(OCIO_ConstColorSpaceRcPtr *cs) override; - const char *colorSpaceGetDescription(OCIO_ConstColorSpaceRcPtr *cs) override; - const char *colorSpaceGetFamily(OCIO_ConstColorSpaceRcPtr *cs) override; - int colorSpaceGetNumAliases(OCIO_ConstColorSpaceRcPtr *cs) override; - const char *colorSpaceGetAlias(OCIO_ConstColorSpaceRcPtr *cs, const int index) override; - - OCIO_ConstProcessorRcPtr *createDisplayProcessor(OCIO_ConstConfigRcPtr *config, - const char *input, - const char *view, - const char *display, - const char *look, - const float scale, - const float exponent, - const float temperature, - const float tint, - const bool use_white_balance, - const bool inverse) override; - - OCIO_PackedImageDesc *createOCIO_PackedImageDesc(float *data, - long width, - long height, - long numChannels, - long chanStrideBytes, - long xStrideBytes, - long yStrideBytes) override; - - void OCIO_PackedImageDescRelease(OCIO_PackedImageDesc *id) override; - - const char *getVersionString() override; - int getVersionHex() override; -}; - -#ifdef WITH_OCIO -class OCIOImpl : public IOCIOImpl { - public: - OCIOImpl() = default; - - OCIO_ConstConfigRcPtr *getCurrentConfig() override; - void setCurrentConfig(const OCIO_ConstConfigRcPtr *config) override; - - OCIO_ConstConfigRcPtr *configCreateFromEnv() override; - OCIO_ConstConfigRcPtr *configCreateFromFile(const char *filename) override; - - void configRelease(OCIO_ConstConfigRcPtr *config) override; - - int configGetNumColorSpaces(OCIO_ConstConfigRcPtr *config) override; - const char *configGetColorSpaceNameByIndex(OCIO_ConstConfigRcPtr *config, int index) override; - OCIO_ConstColorSpaceRcPtr *configGetColorSpace(OCIO_ConstConfigRcPtr *config, - const char *name) override; - int configGetIndexForColorSpace(OCIO_ConstConfigRcPtr *config, const char *name) override; - - const char *getColorSpaceFromFilepath(OCIO_ConstConfigRcPtr *config, - const char *filepath) override; - - int colorSpaceIsInvertible(OCIO_ConstColorSpaceRcPtr *cs) override; - int colorSpaceIsData(OCIO_ConstColorSpaceRcPtr *cs) override; - void colorSpaceIsBuiltin(OCIO_ConstConfigRcPtr *config, - OCIO_ConstColorSpaceRcPtr *cs, - bool &is_scene_linear, - bool &is_srgb) override; - - void colorSpaceRelease(OCIO_ConstColorSpaceRcPtr *cs) override; - - const char *configGetDefaultDisplay(OCIO_ConstConfigRcPtr *config) override; - int configGetNumDisplays(OCIO_ConstConfigRcPtr *config) override; - const char *configGetDisplay(OCIO_ConstConfigRcPtr *config, int index) override; - const char *configGetDefaultView(OCIO_ConstConfigRcPtr *config, const char *display) override; - int configGetNumViews(OCIO_ConstConfigRcPtr *config, const char *display) override; - const char *configGetView(OCIO_ConstConfigRcPtr *config, - const char *display, - int index) override; - const char *configGetDisplayColorSpaceName(OCIO_ConstConfigRcPtr *config, - const char *display, - const char *view) override; - - void configGetDefaultLumaCoefs(OCIO_ConstConfigRcPtr *config, float *rgb) override; - void configGetXYZtoSceneLinear(OCIO_ConstConfigRcPtr *config, - float xyz_to_scene_linear[3][3]) override; - - int configGetNumLooks(OCIO_ConstConfigRcPtr *config) override; - const char *configGetLookNameByIndex(OCIO_ConstConfigRcPtr *config, int index) override; - OCIO_ConstLookRcPtr *configGetLook(OCIO_ConstConfigRcPtr *config, const char *name) override; - - const char *lookGetProcessSpace(OCIO_ConstLookRcPtr *look) override; - void lookRelease(OCIO_ConstLookRcPtr *look) override; - - OCIO_ConstProcessorRcPtr *configGetProcessorWithNames(OCIO_ConstConfigRcPtr *config, - const char *srcName, - const char *dstName) override; - void processorRelease(OCIO_ConstProcessorRcPtr *processor) override; - - OCIO_ConstCPUProcessorRcPtr *processorGetCPUProcessor( - OCIO_ConstProcessorRcPtr *processor) override; - bool cpuProcessorIsNoOp(OCIO_ConstCPUProcessorRcPtr *cpu_processor) override; - void cpuProcessorApply(OCIO_ConstCPUProcessorRcPtr *cpu_processor, - OCIO_PackedImageDesc *img) override; - void cpuProcessorApply_predivide(OCIO_ConstCPUProcessorRcPtr *cpu_processor, - OCIO_PackedImageDesc *img) override; - void cpuProcessorApplyRGB(OCIO_ConstCPUProcessorRcPtr *cpu_processor, float *pixel) override; - void cpuProcessorApplyRGBA(OCIO_ConstCPUProcessorRcPtr *cpu_processor, float *pixel) override; - void cpuProcessorApplyRGBA_predivide(OCIO_ConstCPUProcessorRcPtr *cpu_processor, - float *pixel) override; - void cpuProcessorRelease(OCIO_ConstCPUProcessorRcPtr *cpu_processor) override; - - const char *colorSpaceGetName(OCIO_ConstColorSpaceRcPtr *cs) override; - const char *colorSpaceGetDescription(OCIO_ConstColorSpaceRcPtr *cs) override; - const char *colorSpaceGetFamily(OCIO_ConstColorSpaceRcPtr *cs) override; - int colorSpaceGetNumAliases(OCIO_ConstColorSpaceRcPtr *cs) override; - const char *colorSpaceGetAlias(OCIO_ConstColorSpaceRcPtr *cs, const int index) override; - - OCIO_ConstProcessorRcPtr *createDisplayProcessor(OCIO_ConstConfigRcPtr *config, - const char *input, - const char *view, - const char *display, - const char *look, - const float scale, - const float exponent, - const float temperature, - const float tint, - const bool use_white_balance, - const bool inverse) override; - - OCIO_PackedImageDesc *createOCIO_PackedImageDesc(float *data, - long width, - long height, - long numChannels, - long chanStrideBytes, - long xStrideBytes, - long yStrideBytes) override; - - void OCIO_PackedImageDescRelease(OCIO_PackedImageDesc *id) override; - - bool supportGPUShader() override; - - /** - * Setup GPU contexts for a transform defined by processor using GLSL. - * All LUT allocating baking and shader compilation happens here. - * - * Once this function is called, callee could start drawing images - * using regular 2D texture. - * - * When all drawing is finished, gpuShaderUnbind must be called to - * restore GPU context to its previous state. - */ - bool gpuDisplayShaderBind(OCIO_ConstConfigRcPtr *config, - const char *input, - const char *view, - const char *display, - const char *look, - OCIO_CurveMappingSettings *curve_mapping_settings, - const float scale, - const float exponent, - const float dither, - const float temperature, - const float tint, - const bool use_predivide, - const bool use_overlay, - const bool use_hdr, - const bool use_white_balance) override; - - /** - * Setup GPU contexts for a GPU-side transform form the given space to scene linear. - * - * Once this function is called, callee could start drawing images using regular 2D texture - * (in the same way as GPU_SHADER_3D_IMAGE_COLOR immediate mode shader). - * - * When all drawing is finished, gpuShaderUnbind must be called to restore GPU context to its - * previous state. - */ - bool gpuToSceneLinearShaderBind(OCIO_ConstConfigRcPtr *config, - const char *from_colorspace_name, - bool use_predivide) override; - - void gpuShaderUnbind() override; - void gpuCacheFree() override; - - const char *getVersionString() override; - int getVersionHex() override; -}; -#endif - -#endif /* OCIO_IMPL_H */ diff --git a/intern/opencolorio/ocio_impl_glsl.cc b/intern/opencolorio/ocio_impl_glsl.cc deleted file mode 100644 index 577c5dcb176..00000000000 --- a/intern/opencolorio/ocio_impl_glsl.cc +++ /dev/null @@ -1,935 +0,0 @@ -/* SPDX-FileCopyrightText: 2003-2010 Sony Pictures Imageworks Inc., et al. All Rights Reserved. - * (BSD-3-Clause). - * SPDX-FileCopyrightText: 2013 Blender Authors (GPL-2.0-or-later). - * - * SPDX-License-Identifier: GPL-2.0-or-later AND BSD-3-Clause */ - -#include -#include -#include -#include - -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable : 4251 4275) -#endif -#include -#ifdef _MSC_VER -# pragma warning(pop) -#endif - -#include "GPU_immediate.hh" -#include "GPU_shader.hh" -#include "GPU_uniform_buffer.hh" - -#include "gpu_shader_create_info.hh" - -using namespace OCIO_NAMESPACE; - -#include "BLI_math_color.hh" -#include "BLI_math_matrix.hh" - -#include "ocio_impl.h" -#include "ocio_shader_shared.hh" - -using blender::float3x3; - -/* **** OpenGL drawing routines using GLSL for color space transform ***** */ - -enum OCIO_GPUTextureSlots { - TEXTURE_SLOT_IMAGE = 0, - TEXTURE_SLOT_OVERLAY = 1, - TEXTURE_SLOT_CURVE_MAPPING = 2, - TEXTURE_SLOT_LUTS_OFFSET = 3, -}; - -enum OCIO_GPUUniformBufSlots { - UNIFORMBUF_SLOT_DISPLAY = 0, - UNIFORMBUF_SLOT_CURVEMAP = 1, - UNIFORMBUF_SLOT_LUTS = 2, -}; - -struct OCIO_GPUShader { - /* GPU shader. */ - struct GPUShader *shader = nullptr; - - /** Uniform parameters. */ - OCIO_GPUParameters parameters = {}; - GPUUniformBuf *parameters_buffer = nullptr; - - /* Destructor. */ - ~OCIO_GPUShader() - { - if (shader) { - GPU_shader_free(shader); - } - if (parameters_buffer) { - GPU_uniformbuf_free(parameters_buffer); - } - } -}; - -struct OCIO_GPULutTexture { - GPUTexture *texture = nullptr; - std::string sampler_name; -}; - -struct OCIO_GPUUniform { - GpuShaderDesc::UniformData data; - std::string name; -}; - -struct OCIO_GPUTextures { - /** LUT Textures */ - std::vector luts; - - /* Dummy in case of no overlay. */ - GPUTexture *dummy = nullptr; - - /* Uniforms */ - std::vector uniforms; - GPUUniformBuf *uniforms_buffer = nullptr; - - /* Destructor. */ - ~OCIO_GPUTextures() - { - for (OCIO_GPULutTexture &lut : luts) { - GPU_texture_free(lut.texture); - } - if (dummy) { - GPU_texture_free(dummy); - } - if (uniforms_buffer) { - GPU_uniformbuf_free(uniforms_buffer); - } - } -}; - -struct OCIO_GPUCurveMappping { - /** GPU Uniform Buffer handle. 0 if not allocated. */ - GPUUniformBuf *buffer = nullptr; - /** OpenGL Texture handles. 0 if not allocated. */ - GPUTexture *texture = nullptr; - /* To detect when to update the uniforms and textures. */ - size_t cache_id = 0; - - /* Destructor. */ - ~OCIO_GPUCurveMappping() - { - if (texture) { - GPU_texture_free(texture); - } - if (buffer) { - GPU_uniformbuf_free(buffer); - } - } -}; - -struct OCIO_GPUDisplayShader { - OCIO_GPUShader shader; - OCIO_GPUTextures textures; - OCIO_GPUCurveMappping curvemap; - - /* Cache variables. */ - std::string input; - std::string view; - std::string display; - std::string look; - bool use_curve_mapping = false; - - /** Error checking. */ - bool valid = false; - - bool equals(const char *input, - const char *view, - const char *display, - const char *look, - const bool use_curve_mapping) const - { - return (this->input == input && this->view == view && this->display == display && - this->look == look && this->use_curve_mapping == use_curve_mapping); - } -}; - -static const int SHADER_CACHE_MAX_SIZE = 8; -std::list SHADER_CACHE; - -/* -------------------------------------------------------------------- */ -/** \name Shader - * \{ */ - -static void string_replace_all(std::string &haystack, - const std::string &needle, - const std::string &other) -{ - size_t i = 0, index; - while ((index = haystack.find(needle, i)) != std::string::npos) { - haystack.replace(index, needle.size(), other); - i = index + other.size(); - } -} - -static bool createGPUShader(OCIO_GPUShader &shader, - OCIO_GPUTextures &textures, - const GpuShaderDescRcPtr &shaderdesc_to_scene_linear, - const GpuShaderDescRcPtr &shaderdesc_to_display, - const bool use_curve_mapping) -{ - using namespace blender::gpu::shader; - - std::string source; - source += shaderdesc_to_scene_linear->getShaderText(); - source += "\n"; - source += shaderdesc_to_display->getShaderText(); - source += "\n"; - - { - /* Replace all uniform declarations by a comment. - * This avoids double declarations from the backend. */ - size_t index = 0; - while (true) { - index = source.find("uniform ", index); - if (index == -1) { - break; - } - source.replace(index, 2, "//"); - index += 2; - } - } - - source = GPU_shader_preprocess_source(source); - - /* Comparison operator in Metal returns per-element comparison and returns a vector of booleans. - * Need a special syntax to see if two vec3 are matched. - * - * NOTE: The replacement is optimized for transforming code generated by - * GradingPrimaryTransform. A more general approach is possible, but for now prefer processing - * speed. - * - * NOTE: The syntax works for all backends Blender supports. */ - string_replace_all( - source, "if ( gamma != vec3(1., 1., 1.) )", "if (! all(equal(gamma, vec3(1., 1., 1.))) )"); - - StageInterfaceInfo iface("OCIO_Interface", ""); - iface.smooth(Type::float2_t, "texCoord_interp"); - - ShaderCreateInfo info("OCIO_Display"); - /* Work around OpenColorIO not supporting latest GLSL yet. */ - info.define("texture1D", "texture"); - info.define("texture2D", "texture"); - info.define("texture3D", "texture"); - /* Work around unsupported in keyword in Metal GLSL emulation. */ -#ifdef __APPLE__ - info.define("in", ""); -#endif - info.typedef_source("ocio_shader_shared.hh"); - info.sampler(TEXTURE_SLOT_IMAGE, ImageType::Float2D, "image_texture"); - info.sampler(TEXTURE_SLOT_OVERLAY, ImageType::Float2D, "overlay_texture"); - info.uniform_buf(UNIFORMBUF_SLOT_DISPLAY, "OCIO_GPUParameters", "parameters"); - info.push_constant(Type::float4x4_t, "ModelViewProjectionMatrix"); - info.vertex_in(0, Type::float2_t, "pos"); - info.vertex_in(1, Type::float2_t, "texCoord"); - info.vertex_out(iface); - info.fragment_out(0, Type::float4_t, "fragColor"); - info.vertex_source("gpu_shader_display_transform_vert.glsl"); - info.fragment_source("gpu_shader_display_transform_frag.glsl"); - info.fragment_source_generated = source; - - /* #96502: Work around for incorrect OCIO GLSL code generation when using - * GradingPrimaryTransform. Should be reevaluated when changing to a next version of OCIO. - * (currently v2.1.1). */ - info.define("inf 1e32"); - - if (use_curve_mapping) { - info.define("USE_CURVE_MAPPING"); - info.uniform_buf(UNIFORMBUF_SLOT_CURVEMAP, "OCIO_GPUCurveMappingParameters", "curve_mapping"); - info.sampler(TEXTURE_SLOT_CURVE_MAPPING, ImageType::Float1D, "curve_mapping_texture"); - } - - /* Set LUT textures. */ - int slot = TEXTURE_SLOT_LUTS_OFFSET; - for (OCIO_GPULutTexture &texture : textures.luts) { - const int dimensions = GPU_texture_dimensions(texture.texture); - ImageType type = (dimensions == 1) ? ImageType::Float1D : - (dimensions == 2) ? ImageType::Float2D : - ImageType::Float3D; - - info.sampler(slot++, type, texture.sampler_name.c_str()); - } - - /* Set LUT uniforms. */ - if (!textures.uniforms.empty()) { - /* NOTE: For simplicity, we pad everything to size of vec4 avoiding sorting and alignment - * issues. It is unlikely that this becomes a real issue. */ - size_t ubo_size = textures.uniforms.size() * sizeof(float) * 4; - void *ubo_data_buf = malloc(ubo_size); - - uint32_t *ubo_data = reinterpret_cast(ubo_data_buf); - - std::stringstream ss; - ss << "struct OCIO_GPULutParameters {\n"; - - int index = 0; - for (OCIO_GPUUniform &uniform : textures.uniforms) { - index += 1; - const GpuShaderDesc::UniformData &data = uniform.data; - const char *name = uniform.name.c_str(); - char prefix = ' '; - int vec_len; - switch (data.m_type) { - case UNIFORM_DOUBLE: { - vec_len = 1; - float value = float(data.m_getDouble()); - memcpy(ubo_data, &value, sizeof(float)); - break; - } - case UNIFORM_BOOL: { - prefix = 'b'; - vec_len = 1; - int value = int(data.m_getBool()); - memcpy(ubo_data, &value, sizeof(int)); - break; - } - case UNIFORM_FLOAT3: - vec_len = 3; - memcpy(ubo_data, data.m_getFloat3().data(), sizeof(float) * 3); - break; - case UNIFORM_VECTOR_FLOAT: - vec_len = data.m_vectorFloat.m_getSize(); - memcpy(ubo_data, data.m_vectorFloat.m_getVector(), sizeof(float) * vec_len); - break; - case UNIFORM_VECTOR_INT: - prefix = 'i'; - vec_len = data.m_vectorInt.m_getSize(); - memcpy(ubo_data, data.m_vectorInt.m_getVector(), sizeof(int) * vec_len); - break; - default: - continue; - } - /* Align every member to 16bytes. */ - ubo_data += 4; - /* Use a generic variable name because some GLSL compilers can interpret the preprocessor - * define as recursive. */ - ss << " " << prefix << "vec4 var" << index << ";\n"; - /* Use a define to keep the generated code working. */ - blender::StringRef suffix = blender::StringRefNull("xyzw").substr(0, vec_len); - ss << "#define " << name << " lut_parameters.var" << index << "." << suffix << "\n"; - } - ss << "};\n"; - info.typedef_source_generated = ss.str(); - - info.uniform_buf(UNIFORMBUF_SLOT_LUTS, "OCIO_GPULutParameters", "lut_parameters"); - - textures.uniforms_buffer = GPU_uniformbuf_create_ex( - ubo_size, ubo_data_buf, "OCIO_LutParameters"); - - free(ubo_data_buf); - } - - shader.shader = GPU_shader_create_from_info(reinterpret_cast(&info)); - - return (shader.shader != nullptr); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Textures - * \{ */ - -static bool addGPUUniform(OCIO_GPUTextures &textures, - const GpuShaderDescRcPtr &shader_desc, - int index) -{ - OCIO_GPUUniform uniform; - uniform.name = shader_desc->getUniform(index, uniform.data); - if (uniform.data.m_type == UNIFORM_UNKNOWN) { - return false; - } - - textures.uniforms.push_back(uniform); - return true; -} - -static bool addGPULut1D2D(OCIO_GPUTextures &textures, - const GpuShaderDescRcPtr &shader_desc, - int index) -{ - const char *texture_name = nullptr; - const char *sampler_name = nullptr; - unsigned int width = 0; - unsigned int height = 0; - GpuShaderCreator::TextureType channel = GpuShaderCreator::TEXTURE_RGB_CHANNEL; - Interpolation interpolation = INTERP_LINEAR; -#if OCIO_VERSION_HEX >= 0x02030000 - /* Always use 2D textures in OpenColorIO 2.3, simpler and same performance. */ - GpuShaderDesc::TextureDimensions dimensions = GpuShaderDesc::TEXTURE_2D; - shader_desc->getTexture( - index, texture_name, sampler_name, width, height, channel, dimensions, interpolation); -#else - shader_desc->getTexture( - index, texture_name, sampler_name, width, height, channel, interpolation); -#endif - - const float *values; - shader_desc->getTextureValues(index, values); - if (texture_name == nullptr || sampler_name == nullptr || width == 0 || height == 0 || - values == nullptr) - { - return false; - } - - eGPUTextureFormat format = (channel == GpuShaderCreator::TEXTURE_RGB_CHANNEL) ? GPU_RGB16F : - GPU_R16F; - - OCIO_GPULutTexture lut; -#if OCIO_VERSION_HEX < 0x02030000 - /* There does not appear to be an explicit way to check if a texture is 1D or 2D. - * It depends on more than height. So check instead by looking at the source. */ - std::string sampler1D_name = std::string("sampler1D ") + sampler_name; - if (strstr(shader_desc->getShaderText(), sampler1D_name.c_str()) != nullptr) { - lut.texture = GPU_texture_create_1d( - texture_name, width, 1, format, GPU_TEXTURE_USAGE_SHADER_READ, values); - } - else -#endif - { - lut.texture = GPU_texture_create_2d( - texture_name, width, height, 1, format, GPU_TEXTURE_USAGE_SHADER_READ, values); - } - if (lut.texture == nullptr) { - return false; - } - - GPU_texture_filter_mode(lut.texture, interpolation != INTERP_NEAREST); - GPU_texture_extend_mode(lut.texture, GPU_SAMPLER_EXTEND_MODE_EXTEND); - - lut.sampler_name = sampler_name; - - textures.luts.push_back(lut); - return true; -} - -static bool addGPULut3D(OCIO_GPUTextures &textures, - const GpuShaderDescRcPtr &shader_desc, - int index) -{ - const char *texture_name = nullptr; - const char *sampler_name = nullptr; - unsigned int edgelen = 0; - Interpolation interpolation = INTERP_LINEAR; - shader_desc->get3DTexture(index, texture_name, sampler_name, edgelen, interpolation); - - const float *values; - shader_desc->get3DTextureValues(index, values); - if (texture_name == nullptr || sampler_name == nullptr || edgelen == 0 || values == nullptr) { - return false; - } - - OCIO_GPULutTexture lut; - lut.texture = GPU_texture_create_3d(texture_name, - edgelen, - edgelen, - edgelen, - 1, - GPU_RGB16F, - GPU_TEXTURE_USAGE_SHADER_READ, - values); - if (lut.texture == nullptr) { - return false; - } - - GPU_texture_filter_mode(lut.texture, interpolation != INTERP_NEAREST); - GPU_texture_extend_mode(lut.texture, GPU_SAMPLER_EXTEND_MODE_EXTEND); - - lut.sampler_name = sampler_name; - - textures.luts.push_back(lut); - return true; -} - -static bool createGPUTextures(OCIO_GPUTextures &textures, - const GpuShaderDescRcPtr &shaderdesc_to_scene_linear, - const GpuShaderDescRcPtr &shaderdesc_to_display) -{ - textures.dummy = GPU_texture_create_error(2, false); - - textures.luts.clear(); - textures.uniforms.clear(); - - for (int index = 0; index < shaderdesc_to_scene_linear->getNumUniforms(); index++) { - if (!addGPUUniform(textures, shaderdesc_to_scene_linear, index)) { - return false; - } - } - for (int index = 0; index < shaderdesc_to_scene_linear->getNumTextures(); index++) { - if (!addGPULut1D2D(textures, shaderdesc_to_scene_linear, index)) { - return false; - } - } - for (int index = 0; index < shaderdesc_to_scene_linear->getNum3DTextures(); index++) { - if (!addGPULut3D(textures, shaderdesc_to_scene_linear, index)) { - return false; - } - } - for (int index = 0; index < shaderdesc_to_display->getNumUniforms(); index++) { - if (!addGPUUniform(textures, shaderdesc_to_display, index)) { - return false; - } - } - for (int index = 0; index < shaderdesc_to_display->getNumTextures(); index++) { - if (!addGPULut1D2D(textures, shaderdesc_to_display, index)) { - return false; - } - } - for (int index = 0; index < shaderdesc_to_display->getNum3DTextures(); index++) { - if (!addGPULut3D(textures, shaderdesc_to_display, index)) { - return false; - } - } - - return true; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Curve Mapping - * \{ */ - -static bool createGPUCurveMapping(OCIO_GPUCurveMappping &curvemap, - OCIO_CurveMappingSettings *curve_mapping_settings) -{ - if (curve_mapping_settings) { - int lut_size = curve_mapping_settings->lut_size; - - curvemap.texture = GPU_texture_create_1d( - "OCIOCurveMap", lut_size, 1, GPU_RGBA16F, GPU_TEXTURE_USAGE_SHADER_READ, nullptr); - GPU_texture_filter_mode(curvemap.texture, false); - GPU_texture_extend_mode(curvemap.texture, GPU_SAMPLER_EXTEND_MODE_EXTEND); - - curvemap.buffer = GPU_uniformbuf_create(sizeof(OCIO_GPUCurveMappingParameters)); - - if (curvemap.texture == nullptr || curvemap.buffer == nullptr) { - return false; - } - } - - return true; -} - -static void updateGPUCurveMapping(OCIO_GPUCurveMappping &curvemap, - OCIO_CurveMappingSettings *curve_mapping_settings) -{ - /* Test if we need to update. The caller ensures the curve_mapping_settings - * changes when its contents changes. */ - if (curve_mapping_settings == nullptr || curvemap.cache_id == curve_mapping_settings->cache_id) { - return; - } - - curvemap.cache_id = curve_mapping_settings->cache_id; - - /* Update texture. */ - const int offset[3] = {0, 0, 0}; - const int extent[3] = {curve_mapping_settings->lut_size, 0, 0}; - const float *pixels = curve_mapping_settings->lut; - GPU_texture_update_sub( - curvemap.texture, GPU_DATA_FLOAT, pixels, UNPACK3(offset), UNPACK3(extent)); - - /* Update uniforms. */ - OCIO_GPUCurveMappingParameters data; - for (int i = 0; i < 4; i++) { - data.range[i] = curve_mapping_settings->range[i]; - data.mintable[i] = curve_mapping_settings->mintable[i]; - data.ext_in_x[i] = curve_mapping_settings->ext_in_x[i]; - data.ext_in_y[i] = curve_mapping_settings->ext_in_y[i]; - data.ext_out_x[i] = curve_mapping_settings->ext_out_x[i]; - data.ext_out_y[i] = curve_mapping_settings->ext_out_y[i]; - data.first_x[i] = curve_mapping_settings->first_x[i]; - data.first_y[i] = curve_mapping_settings->first_y[i]; - data.last_x[i] = curve_mapping_settings->last_x[i]; - data.last_y[i] = curve_mapping_settings->last_y[i]; - } - for (int i = 0; i < 3; i++) { - data.black[i] = curve_mapping_settings->black[i]; - data.bwmul[i] = curve_mapping_settings->bwmul[i]; - } - data.lut_size = curve_mapping_settings->lut_size; - data.use_extend_extrapolate = curve_mapping_settings->use_extend_extrapolate; - - GPU_uniformbuf_update(curvemap.buffer, &data); -} - -static void updateGPUDisplayParameters(OCIO_GPUShader &shader, - float exponent, - float4x4 scene_linear_matrix, - float dither, - bool use_predivide, - bool use_overlay, - bool use_hdr) -{ - bool do_update = false; - if (shader.parameters_buffer == nullptr) { - shader.parameters_buffer = GPU_uniformbuf_create(sizeof(OCIO_GPUParameters)); - do_update = true; - } - OCIO_GPUParameters &data = shader.parameters; - if (data.scene_linear_matrix != scene_linear_matrix) { - data.scene_linear_matrix = scene_linear_matrix; - do_update = true; - } - if (data.exponent != exponent) { - data.exponent = exponent; - do_update = true; - } - if (data.dither != dither) { - data.dither = dither; - do_update = true; - } - if (bool(data.use_predivide) != use_predivide) { - data.use_predivide = use_predivide; - do_update = true; - } - if (bool(data.use_overlay) != use_overlay) { - data.use_overlay = use_overlay; - do_update = true; - } - if (bool(data.use_hdr) != use_hdr) { - data.use_hdr = use_hdr; - do_update = true; - } - if (do_update) { - GPU_uniformbuf_update(shader.parameters_buffer, &data); - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name OCIO GPU Shader Implementation - * \{ */ - -bool OCIOImpl::supportGPUShader() -{ - /* Minimum supported version 3.3 does meet all requirements. */ - return true; -} - -static OCIO_GPUDisplayShader *getGPUDisplayShaderFromCache(const char *input, - const char *view, - const char *display, - const char *look, - const bool use_curve_mapping) -{ - for (std::list::iterator it = SHADER_CACHE.begin(); - it != SHADER_CACHE.end(); - it++) - { - if (it->equals(input, view, display, look, use_curve_mapping)) { - /* Move to front of the cache to mark as most recently used. */ - if (it != SHADER_CACHE.begin()) { - SHADER_CACHE.splice(SHADER_CACHE.begin(), SHADER_CACHE, it); - } - return &(*it); - } - } - return nullptr; -} - -/** - * Create default-initialized OCIO_GPUDisplayShader and put it to cache. - * The function ensures the cache has up to SHADER_CACHE_MAX_SIZE entries. - */ -static OCIO_GPUDisplayShader &gpuDisplayShaderCreateAndCache() -{ - /* Remove least recently used element from cache. */ - if (SHADER_CACHE.size() >= SHADER_CACHE_MAX_SIZE) { - SHADER_CACHE.pop_back(); - } - - /* Create GPU shader. */ - SHADER_CACHE.emplace_front(); - OCIO_GPUDisplayShader &display_shader = SHADER_CACHE.front(); - - return display_shader; -} - -static void createGPUShaderDescriptors(OCIO_GPUDisplayShader &display_shader, - ConstProcessorRcPtr processor_to_scene_linear, - ConstProcessorRcPtr processor_to_display, - const bool use_curve_mapping) -{ - GpuShaderDescRcPtr shaderdesc_to_scene_linear = GpuShaderDesc::CreateShaderDesc(); - shaderdesc_to_scene_linear->setLanguage(GPU_LANGUAGE_GLSL_1_3); - shaderdesc_to_scene_linear->setFunctionName("OCIO_to_scene_linear"); - shaderdesc_to_scene_linear->setResourcePrefix("to_scene"); - processor_to_scene_linear->getDefaultGPUProcessor()->extractGpuShaderInfo( - shaderdesc_to_scene_linear); - shaderdesc_to_scene_linear->finalize(); - - GpuShaderDescRcPtr shaderdesc_to_display = GpuShaderDesc::CreateShaderDesc(); - shaderdesc_to_display->setLanguage(GPU_LANGUAGE_GLSL_1_3); - shaderdesc_to_display->setFunctionName("OCIO_to_display"); - shaderdesc_to_display->setResourcePrefix("to_display"); - processor_to_display->getDefaultGPUProcessor()->extractGpuShaderInfo(shaderdesc_to_display); - shaderdesc_to_display->finalize(); - - /* Create GPU shader and textures. */ - if (createGPUTextures( - display_shader.textures, shaderdesc_to_scene_linear, shaderdesc_to_display) && - createGPUShader(display_shader.shader, - display_shader.textures, - shaderdesc_to_scene_linear, - shaderdesc_to_display, - use_curve_mapping)) - { - display_shader.valid = true; - } -} - -static OCIO_GPUDisplayShader &getGPUDisplayShader( - OCIO_ConstConfigRcPtr *config, - const char *input, - const char *view, - const char *display, - const char *look, - OCIO_CurveMappingSettings *curve_mapping_settings) -{ - const bool use_curve_mapping = (curve_mapping_settings != nullptr); - - /* Find existing shader in cache. */ - OCIO_GPUDisplayShader *cached_shader = getGPUDisplayShaderFromCache( - input, view, display, look, use_curve_mapping); - if (cached_shader) { - return *cached_shader; - } - - OCIO_GPUDisplayShader &display_shader = gpuDisplayShaderCreateAndCache(); - display_shader.input = input; - display_shader.view = view; - display_shader.display = display; - display_shader.look = look; - display_shader.use_curve_mapping = use_curve_mapping; - display_shader.valid = false; - - /* Create Processors. - * - * Scale, white balance and exponent are handled outside of OCIO shader so we - * can handle them as uniforms at the binding stage. OCIO would otherwise bake - * them into the shader code, requiring slow recompiles when interactively - * adjusting them. - * - * Note that OCIO does have the concept of dynamic properties, however there - * is no dynamic gamma and exposure is part of more expensive operations only. - * - * Since exposure and white balance must happen in scene linear, we use two - * processors. The input is usually scene linear already and so that conversion - * is often a no-op. - */ - OCIO_ConstProcessorRcPtr *processor_to_scene_linear = OCIO_configGetProcessorWithNames( - config, input, ROLE_SCENE_LINEAR); - OCIO_ConstProcessorRcPtr *processor_to_display = OCIO_createDisplayProcessor( - config, ROLE_SCENE_LINEAR, view, display, look, 1.0f, 1.0f, 0.0f, 0.0f, false, false); - - /* Create shader descriptions. */ - if (processor_to_scene_linear && processor_to_display) { - createGPUShaderDescriptors(display_shader, - *(ConstProcessorRcPtr *)processor_to_scene_linear, - *(ConstProcessorRcPtr *)processor_to_display, - use_curve_mapping); - if (display_shader.valid) { - display_shader.valid &= createGPUCurveMapping(display_shader.curvemap, - curve_mapping_settings); - } - } - - /* Free processors. */ - if (processor_to_scene_linear) { - OCIO_processorRelease(processor_to_scene_linear); - } - if (processor_to_display) { - OCIO_processorRelease(processor_to_display); - } - - return display_shader; -} - -static OCIO_GPUDisplayShader &getGPUToLinearDisplayShader(OCIO_ConstConfigRcPtr *config, - const char *input) -{ - /* Find existing shader in cache. - * Assume that empty names for display, view, and look are not valid for OCIO configuration, and - * so they can be used to indicate that the processor is used to convert from the given space to - * the linear. */ - /* TODO(sergey): Using separate storage for to-linear processor caches might be better. */ - OCIO_GPUDisplayShader *cached_shader = getGPUDisplayShaderFromCache(input, "", "", "", false); - if (cached_shader) { - return *cached_shader; - } - - OCIO_GPUDisplayShader &display_shader = gpuDisplayShaderCreateAndCache(); - display_shader.input = input; - display_shader.use_curve_mapping = false; - display_shader.valid = false; - - OCIO_ConstProcessorRcPtr *processor_to_scene_linear = OCIO_configGetProcessorWithNames( - config, input, ROLE_SCENE_LINEAR); - OCIO_ConstProcessorRcPtr *processor_to_display = OCIO_configGetProcessorWithNames( - config, input, input); - - /* Create shader descriptions. */ - if (processor_to_scene_linear && processor_to_display) { - createGPUShaderDescriptors(display_shader, - *(ConstProcessorRcPtr *)processor_to_scene_linear, - *(ConstProcessorRcPtr *)processor_to_display, - false); - } - - /* Free processors. */ - if (processor_to_scene_linear) { - OCIO_processorRelease(processor_to_scene_linear); - } - if (processor_to_display) { - OCIO_processorRelease(processor_to_display); - } - - return display_shader; -} - -/* Bind the shader and update parameters and uniforms. */ -static bool gpuShaderBind(OCIO_ConstConfigRcPtr *config, - OCIO_GPUDisplayShader &display_shader, - OCIO_CurveMappingSettings *curve_mapping_settings, - const float scale, - const float exponent, - const float dither, - const float temperature, - const float tint, - const bool use_predivide, - const bool use_overlay, - const bool use_hdr, - const bool use_white_balance) -{ - if (!display_shader.valid) { - return false; - } - - /* Verify the shader is valid. */ - OCIO_GPUTextures &textures = display_shader.textures; - OCIO_GPUShader &shader = display_shader.shader; - OCIO_GPUCurveMappping &curvemap = display_shader.curvemap; - - /* Update and bind curve mapping data. */ - if (curve_mapping_settings) { - updateGPUCurveMapping(curvemap, curve_mapping_settings); - GPU_uniformbuf_bind(curvemap.buffer, UNIFORMBUF_SLOT_CURVEMAP); - GPU_texture_bind(curvemap.texture, TEXTURE_SLOT_CURVE_MAPPING); - } - - /* Bind textures to sampler units. Texture 0 is set by caller. - * Uniforms have already been set for texture bind points. */ - if (!use_overlay) { - /* Avoid missing binds. */ - GPU_texture_bind(textures.dummy, TEXTURE_SLOT_OVERLAY); - } - for (int i = 0; i < textures.luts.size(); i++) { - GPU_texture_bind(textures.luts[i].texture, TEXTURE_SLOT_LUTS_OFFSET + i); - } - - if (textures.uniforms_buffer) { - GPU_uniformbuf_bind(textures.uniforms_buffer, UNIFORMBUF_SLOT_LUTS); - } - - float3x3 matrix = float3x3::identity() * scale; - if (use_white_balance) { - /* Compute white point of the scene space in XYZ.*/ - float3x3 xyz_to_scene; - OCIO_configGetXYZtoSceneLinear(config, xyz_to_scene.ptr()); - float3x3 scene_to_xyz = blender::math::invert(xyz_to_scene); - float3 target = scene_to_xyz * float3(1.0f); - - /* Add operations to the matrix. - * Note: Since we're multiplying from the right, the operations here will be performed in - * reverse list order (scene-to-XYZ, then adaption, then XYZ-to-scene, then exposure). */ - matrix *= xyz_to_scene; - matrix *= blender::math::chromatic_adaption_matrix( - blender::math::whitepoint_from_temp_tint(temperature, tint), target); - matrix *= scene_to_xyz; - } - updateGPUDisplayParameters( - shader, exponent, float4x4(matrix), dither, use_predivide, use_overlay, use_hdr); - GPU_uniformbuf_bind(shader.parameters_buffer, UNIFORMBUF_SLOT_DISPLAY); - - /* TODO(fclem): remove remains of IMM. */ - immBindShader(shader.shader); - - return true; -} - -bool OCIOImpl::gpuDisplayShaderBind(OCIO_ConstConfigRcPtr *config, - const char *input, - const char *view, - const char *display, - const char *look, - OCIO_CurveMappingSettings *curve_mapping_settings, - const float scale, - const float exponent, - const float dither, - const float temperature, - const float tint, - const bool use_predivide, - const bool use_overlay, - const bool use_hdr, - const bool use_white_balance) -{ - /* Get GPU shader from cache or create new one. */ - OCIO_GPUDisplayShader &display_shader = getGPUDisplayShader( - config, input, view, display, look, curve_mapping_settings); - - return gpuShaderBind(config, - display_shader, - curve_mapping_settings, - scale, - exponent, - dither, - temperature, - tint, - use_predivide, - use_overlay, - use_hdr, - use_white_balance); -} - -bool OCIOImpl::gpuToSceneLinearShaderBind(OCIO_ConstConfigRcPtr *config, - const char *from_colorspace_name, - const bool use_predivide) -{ - /* Get GPU shader from cache or create new one. */ - OCIO_GPUDisplayShader &display_shader = getGPUToLinearDisplayShader(config, - from_colorspace_name); - - return gpuShaderBind(config, - display_shader, - nullptr, /* curve_mapping_settings */ - 1.0f, /* scale */ - 1.0f, /* exponent */ - 0.0f, /* dither */ - 6500.0f, /* temperature */ - 10.0f, /* tint */ - use_predivide, - false /* use_overlay */, - true, /* use_hdr */ - false /* use_white_balance */); -} - -void OCIOImpl::gpuShaderUnbind() -{ - immUnbindProgram(); -} - -void OCIOImpl::gpuCacheFree() -{ - SHADER_CACHE.clear(); -} - -/** \} */ diff --git a/source/blender/blenfont/BLF_api.hh b/source/blender/blenfont/BLF_api.hh index e8e0e2d1bc6..cf014f0600b 100644 --- a/source/blender/blenfont/BLF_api.hh +++ b/source/blender/blenfont/BLF_api.hh @@ -25,11 +25,15 @@ /* File name of the default fixed-pitch font. */ #define BLF_DEFAULT_MONOSPACED_FONT "DejaVuSansMono.woff2" -struct ColorManagedDisplay; struct ListBase; struct ResultBLF; struct rcti; +namespace blender::ocio { +class Display; +} // namespace blender::ocio +using ColorManagedDisplay = blender::ocio::Display; + enum class FontShadowType { None = 0, Blur3x3 = 3, @@ -349,8 +353,12 @@ void BLF_shadow_offset(int fontid, int x, int y); * The image is assumed to have 4 color channels (RGBA) per pixel. * When done, call this function with null buffer pointers. */ -void BLF_buffer( - int fontid, float *fbuf, unsigned char *cbuf, int w, int h, ColorManagedDisplay *display); +void BLF_buffer(int fontid, + float *fbuf, + unsigned char *cbuf, + int w, + int h, + const ColorManagedDisplay *display); /** * Opaque structure used to push/pop values set by the #BLF_buffer function. diff --git a/source/blender/blenfont/intern/blf.cc b/source/blender/blenfont/intern/blf.cc index a81fe3f2fbe..cd76fd88604 100644 --- a/source/blender/blenfont/intern/blf.cc +++ b/source/blender/blenfont/intern/blf.cc @@ -947,7 +947,8 @@ void BLF_shadow_offset(int fontid, int x, int y) } } -void BLF_buffer(int fontid, float *fbuf, uchar *cbuf, int w, int h, ColorManagedDisplay *display) +void BLF_buffer( + int fontid, float *fbuf, uchar *cbuf, int w, int h, const ColorManagedDisplay *display) { FontBLF *font = blf_get(fontid); diff --git a/source/blender/blenfont/intern/blf_internal_types.hh b/source/blender/blenfont/intern/blf_internal_types.hh index 47846cc2018..fdd5c72332b 100644 --- a/source/blender/blenfont/intern/blf_internal_types.hh +++ b/source/blender/blenfont/intern/blf_internal_types.hh @@ -24,7 +24,6 @@ #include -struct ColorManagedDisplay; struct FontBLF; struct GlyphCacheBLF; struct GlyphBLF; @@ -35,6 +34,11 @@ class VertBuf; } // namespace blender::gpu struct GPUVertBufRaw; +namespace blender::ocio { +class Display; +} // namespace blender::ocio +using ColorManagedDisplay = blender::ocio::Display; + #include FT_MULTIPLE_MASTERS_H /* Variable font support. */ /** Maximum variation axes per font. */ @@ -227,7 +231,7 @@ struct FontBufInfoBLF { int dims[2]; /** Display device used for color management. */ - ColorManagedDisplay *display; + const ColorManagedDisplay *display; /** The color, the alphas is get from the glyph! (color is sRGB space). */ float col_init[4]; diff --git a/source/blender/blenkernel/intern/colortools.cc b/source/blender/blenkernel/intern/colortools.cc index 8eed4d093fe..0c38f24386b 100644 --- a/source/blender/blenkernel/intern/colortools.cc +++ b/source/blender/blenkernel/intern/colortools.cc @@ -1886,8 +1886,9 @@ void BKE_color_managed_view_settings_init_render( const ColorManagedDisplaySettings *display_settings, const char *view_transform) { - ColorManagedDisplay *display = IMB_colormanagement_display_get_named( + const ColorManagedDisplay *display = IMB_colormanagement_display_get_named( display_settings->display_device); + BLI_assert(display); if (!view_transform) { view_transform = IMB_colormanagement_display_get_default_view_transform_name(display); diff --git a/source/blender/blenkernel/intern/image.cc b/source/blender/blenkernel/intern/image.cc index 359808af9c8..4e1d72f2cac 100644 --- a/source/blender/blenkernel/intern/image.cc +++ b/source/blender/blenkernel/intern/image.cc @@ -2010,7 +2010,7 @@ void BKE_image_stamp_buf(Scene *scene, int x, y, y_ofs; int h_fixed; const int mono = blf_mono_font_render; /* XXX */ - ColorManagedDisplay *display; + const ColorManagedDisplay *display; const char *display_device; /* vars for calculating wordwrap */ diff --git a/source/blender/blenlib/BLI_string_utils.hh b/source/blender/blenlib/BLI_string_utils.hh index becb3501d95..9a88241eeef 100644 --- a/source/blender/blenlib/BLI_string_utils.hh +++ b/source/blender/blenlib/BLI_string_utils.hh @@ -40,6 +40,13 @@ char *BLI_string_replaceN(const char *__restrict str, const char *__restrict substr_new) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2, 3) ATTR_MALLOC; +/** + * In-place replacement all occurrences of needle in haystack with other. + */ +void BLI_string_replace(std::string &haystack, + blender::StringRef needle, + blender::StringRef other); + /** * In-place replace every \a src to \a dst in \a str. * diff --git a/source/blender/blenlib/intern/string_utils.cc b/source/blender/blenlib/intern/string_utils.cc index cb4aaa6b21c..9574d124a28 100644 --- a/source/blender/blenlib/intern/string_utils.cc +++ b/source/blender/blenlib/intern/string_utils.cc @@ -88,6 +88,18 @@ char *BLI_string_replaceN(const char *__restrict str, return BLI_strdup(str); } +void BLI_string_replace(std::string &haystack, + const blender::StringRef needle, + const blender::StringRef other) +{ + size_t i = 0; + size_t index; + while ((index = haystack.find(needle, i)) != std::string::npos) { + haystack.replace(index, size_t(needle.size()), other); + i = index + size_t(other.size()); + } +} + void BLI_string_replace_char(char *str, char src, char dst) { while (*str) { diff --git a/source/blender/blenlib/tests/BLI_string_utils_test.cc b/source/blender/blenlib/tests/BLI_string_utils_test.cc index fef47414b20..8ee4f5678db 100644 --- a/source/blender/blenlib/tests/BLI_string_utils_test.cc +++ b/source/blender/blenlib/tests/BLI_string_utils_test.cc @@ -12,6 +12,21 @@ namespace blender { +TEST(BLI_string_utils, BLI_string_replace) +{ + { + std::string s = "foo bar baz"; + BLI_string_replace(s, "bar", "hello"); + EXPECT_EQ(s, "foo hello baz"); + } + + { + std::string s = "foo bar baz world bar"; + BLI_string_replace(s, "bar", "hello"); + EXPECT_EQ(s, "foo hello baz world hello"); + } +} + TEST(BLI_string_utils, BLI_uniquename_cb) { const Vector current_names{"Foo", "Bar", "Bar.003", "Baz.001", "Big.999"}; diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index cc678f0a90a..da9b09cf9d2 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -161,6 +161,7 @@ set(LIB PRIVATE bf::blenlib PRIVATE bf::dna PRIVATE bf::intern::guardedalloc + PRIVATE bf::dependencies::optional::opencolorio ) set(GLSL_SRC @@ -355,20 +356,6 @@ if(WITH_OPENIMAGEDENOISE) ) endif() -if(WITH_OPENCOLORIO) - add_definitions( - -DWITH_OCIO - ) - - list(APPEND INC_SYS - ${OPENCOLORIO_INCLUDE_DIRS} - ) - - list(APPEND LIB - ${OPENCOLORIO_LIBRARIES} - ) -endif() - if(WITH_FFTW3) list(APPEND INC_SYS ${FFTW3_INCLUDE_DIRS} diff --git a/source/blender/compositor/cached_resources/intern/ocio_color_space_conversion_shader.cc b/source/blender/compositor/cached_resources/intern/ocio_color_space_conversion_shader.cc index 850fd07039d..cffd024f0bf 100644 --- a/source/blender/compositor/cached_resources/intern/ocio_color_space_conversion_shader.cc +++ b/source/blender/compositor/cached_resources/intern/ocio_color_space_conversion_shader.cc @@ -24,7 +24,7 @@ #include "COM_ocio_color_space_conversion_shader.hh" #include "COM_result.hh" -#if defined(WITH_OCIO) +#if defined(WITH_OPENCOLORIO) # include #endif @@ -55,7 +55,7 @@ bool operator==(const OCIOColorSpaceConversionShaderKey &a, * GPU Shader Creator. */ -#if defined(WITH_OCIO) +#if defined(WITH_OPENCOLORIO) namespace OCIO = OCIO_NAMESPACE; using namespace blender::gpu::shader; @@ -486,7 +486,7 @@ OCIOColorSpaceConversionShader::OCIOColorSpaceConversionShader(Context &context, * processor. */ shader_creator_ = GPUShaderCreator::Create(context.get_precision()); -#if defined(WITH_OCIO) +#if defined(WITH_OPENCOLORIO) /* Get a GPU processor that transforms the source color space to the target color space. */ try { OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig(); @@ -543,7 +543,7 @@ OCIOColorSpaceConversionShader &OCIOColorSpaceConversionShaderContainer::get(Con std::string source, std::string target) { -#if defined(WITH_OCIO) +#if defined(WITH_OPENCOLORIO) /* Use the config cache ID in the cache key in case the configuration changed at runtime. */ std::string config_cache_id = OCIO::GetCurrentConfig()->getCacheID(); #else diff --git a/source/blender/editors/interface/eyedroppers/eyedropper_color.cc b/source/blender/editors/interface/eyedroppers/eyedropper_color.cc index 7c2d1369ece..759f4a6ce03 100644 --- a/source/blender/editors/interface/eyedroppers/eyedropper_color.cc +++ b/source/blender/editors/interface/eyedroppers/eyedropper_color.cc @@ -60,7 +60,7 @@ #include "eyedropper_intern.hh" struct Eyedropper { - ColorManagedDisplay *display = nullptr; + const ColorManagedDisplay *display = nullptr; PointerRNA ptr = {}; PropertyRNA *prop = nullptr; @@ -493,7 +493,7 @@ bool eyedropper_color_sample_fl(bContext *C, WM_window_pixels_read_sample_from_offscreen(C, win, event_xy_win, r_col); } const char *display_device = CTX_data_scene(C)->display_settings.display_device; - ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device); + const ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device); IMB_colormanagement_display_to_scene_linear_v3(r_col, display); return true; } diff --git a/source/blender/editors/interface/eyedroppers/eyedropper_grease_pencil_color.cc b/source/blender/editors/interface/eyedroppers/eyedropper_grease_pencil_color.cc index 6f751d554c5..fa58eefa8fc 100644 --- a/source/blender/editors/interface/eyedroppers/eyedropper_grease_pencil_color.cc +++ b/source/blender/editors/interface/eyedroppers/eyedropper_grease_pencil_color.cc @@ -61,7 +61,7 @@ enum class MaterialMode : int8_t { }; struct EyedropperGreasePencil { - ColorManagedDisplay *display = nullptr; + const ColorManagedDisplay *display = nullptr; bool accum_start = false; /* has mouse been pressed */ float3 accum_col = {}; diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc index 6896aa54a86..063f7065957 100644 --- a/source/blender/editors/interface/interface.cc +++ b/source/blender/editors/interface/interface.cc @@ -4171,14 +4171,14 @@ void UI_block_align_end(uiBlock *block) block->flag &= ~UI_BUT_ALIGN; /* all 4 flags */ } -ColorManagedDisplay *ui_block_cm_display_get(uiBlock *block) +const ColorManagedDisplay *ui_block_cm_display_get(uiBlock *block) { return IMB_colormanagement_display_get_named(block->display_device); } void ui_block_cm_to_display_space_v3(uiBlock *block, float pixel[3]) { - ColorManagedDisplay *display = ui_block_cm_display_get(block); + const ColorManagedDisplay *display = ui_block_cm_display_get(block); IMB_colormanagement_scene_linear_to_display_v3(pixel, display); } diff --git a/source/blender/editors/interface/interface_draw.cc b/source/blender/editors/interface/interface_draw.cc index a71f6a72605..3465065591e 100644 --- a/source/blender/editors/interface/interface_draw.cc +++ b/source/blender/editors/interface/interface_draw.cc @@ -1227,7 +1227,7 @@ static void ui_draw_colorband_handle(uint shdr_pos, const rcti *rect, float x, const float rgb[3], - ColorManagedDisplay *display, + const ColorManagedDisplay *display, bool active) { const float sizey = BLI_rcti_size_y(rect); @@ -1328,7 +1328,7 @@ static void ui_draw_colorband_handle(uint shdr_pos, void ui_draw_but_COLORBAND(uiBut *but, const uiWidgetColors *wcol, const rcti *rect) { - ColorManagedDisplay *display = ui_block_cm_display_get(but->block); + const ColorManagedDisplay *display = ui_block_cm_display_get(but->block); uint pos_id, col_id; uiButColorBand *but_coba = (uiButColorBand *)but; diff --git a/source/blender/editors/interface/interface_intern.hh b/source/blender/editors/interface/interface_intern.hh index 4df62ccab47..ed75c01652b 100644 --- a/source/blender/editors/interface/interface_intern.hh +++ b/source/blender/editors/interface/interface_intern.hh @@ -26,7 +26,6 @@ struct AnimationEvalContext; struct ARegion; struct bContext; struct bContextStore; -struct ColorManagedDisplay; struct CurveMapping; struct CurveProfile; namespace blender::gpu { @@ -50,6 +49,11 @@ struct wmKeyConfig; struct wmOperatorType; struct wmTimer; +namespace blender::ocio { +class Display; +} // namespace blender::ocio +using ColorManagedDisplay = blender::ocio::Display; + /* ****************** general defines ************** */ #define RNA_NO_INDEX -1 @@ -865,7 +869,7 @@ void ui_but_override_flag(Main *bmain, uiBut *but); void ui_block_bounds_calc(uiBlock *block); -ColorManagedDisplay *ui_block_cm_display_get(uiBlock *block); +const ColorManagedDisplay *ui_block_cm_display_get(uiBlock *block); void ui_block_cm_to_display_space_v3(uiBlock *block, float pixel[3]); /* `interface_regions.cc` */ diff --git a/source/blender/editors/interface/regions/interface_region_tooltip.cc b/source/blender/editors/interface/regions/interface_region_tooltip.cc index 322095435ab..7b9dab26de7 100644 --- a/source/blender/editors/interface/regions/interface_region_tooltip.cc +++ b/source/blender/editors/interface/regions/interface_region_tooltip.cc @@ -1205,7 +1205,7 @@ static std::unique_ptr ui_tooltip_data_from_button_or_extra_icon( image_data.border = true; image_data.premultiplied = false; - ColorManagedDisplay *display = ui_block_cm_display_get(but->block); + const ColorManagedDisplay *display = ui_block_cm_display_get(but->block); if (color[3] == 1.0f) { /* No transparency so draw the entire area solid without checkerboard. */ image_data.background = uiTooltipImageBackground::None; diff --git a/source/blender/editors/render/render_internal.cc b/source/blender/editors/render/render_internal.cc index fbd607afacb..c9699432cf1 100644 --- a/source/blender/editors/render/render_internal.cc +++ b/source/blender/editors/render/render_internal.cc @@ -89,7 +89,6 @@ struct RenderJob : public RenderJobBase { ScrArea *area; ColorManagedViewSettings view_settings; ColorManagedDisplaySettings display_settings; - bool supports_glsl_draw; bool interface_locked; }; @@ -659,9 +658,7 @@ static void image_rect_update(void *rjv, RenderResult *rr, rcti *renrect) * this case GLSL doesn't have original float buffer to * operate with. */ - if (!rj->supports_glsl_draw || ibuf->channels == 1 || - ED_draw_imbuf_method(ibuf) != IMAGE_DRAW_METHOD_GLSL) - { + if (ibuf->channels == 1 || ED_draw_imbuf_method(ibuf) != IMAGE_DRAW_METHOD_GLSL) { image_buffer_rect_update(rj, rr, ibuf, &rj->iuser, &tile_rect, offset_x, offset_y, viewname); } ImageTile *image_tile = BKE_image_get_tile(ima, 0); @@ -1048,7 +1045,6 @@ static wmOperatorStatus screen_render_invoke(bContext *C, wmOperator *op, const rj->orig_layer = 0; rj->last_layer = 0; rj->area = area; - rj->supports_glsl_draw = IMB_colormanagement_support_glsl_draw(&scene->view_settings); BKE_color_managed_display_settings_copy(&rj->display_settings, &scene->display_settings); BKE_color_managed_view_settings_copy(&rj->view_settings, &scene->view_settings); diff --git a/source/blender/editors/sculpt_paint/paint_cursor.cc b/source/blender/editors/sculpt_paint/paint_cursor.cc index 941a6fe7c91..8e8802aefd8 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.cc +++ b/source/blender/editors/sculpt_paint/paint_cursor.cc @@ -177,7 +177,7 @@ static void load_tex_task_cb_ex(void *__restrict userdata, const float radius = data->radius; bool convert_to_linear = false; - ColorSpace *colorspace = nullptr; + const ColorSpace *colorspace = nullptr; const int thread_id = BLI_task_parallel_thread_id(tls); diff --git a/source/blender/editors/sculpt_paint/paint_image.cc b/source/blender/editors/sculpt_paint/paint_image.cc index 7799df8c787..2437a85d671 100644 --- a/source/blender/editors/sculpt_paint/paint_image.cc +++ b/source/blender/editors/sculpt_paint/paint_image.cc @@ -373,7 +373,7 @@ void paint_brush_color_get(Scene *scene, bool invert, float distance, float pressure, - ColorManagedDisplay *display, + const ColorManagedDisplay *display, float r_color[3]) { if (invert) { diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.cc b/source/blender/editors/sculpt_paint/paint_image_2d.cc index f4e65f49d3a..28b50efda91 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.cc +++ b/source/blender/editors/sculpt_paint/paint_image_2d.cc @@ -377,7 +377,7 @@ static ImBuf *brush_painter_imbuf_new( BrushPainterCache *cache = &tile->cache; const char *display_device = scene->display_settings.display_device; - ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device); + const ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device); rctf tex_mapping = painter->tex_mapping; ImagePool *pool = painter->pool; @@ -468,7 +468,7 @@ static void brush_painter_imbuf_update(BrushPainter *painter, BrushPainterCache *cache = &tile->cache; const char *display_device = scene->display_settings.display_device; - ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device); + const ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device); rctf tex_mapping = painter->tex_mapping; ImagePool *pool = painter->pool; diff --git a/source/blender/editors/sculpt_paint/paint_intern.hh b/source/blender/editors/sculpt_paint/paint_intern.hh index 57406173370..c8eea24d975 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.hh +++ b/source/blender/editors/sculpt_paint/paint_intern.hh @@ -22,7 +22,6 @@ enum class PaintMode : int8_t; struct ARegion; struct bContext; struct Brush; -struct ColorManagedDisplay; struct Depsgraph; struct Image; struct ImagePool; @@ -57,7 +56,12 @@ namespace ed::sculpt_paint { struct PaintStroke; struct StrokeCache; } // namespace ed::sculpt_paint + +namespace ocio { +class Display; +} } // namespace blender +using ColorManagedDisplay = blender::ocio::Display; /* paint_stroke.cc */ @@ -345,7 +349,7 @@ void paint_brush_color_get(Scene *scene, bool invert, float distance, float pressure, - ColorManagedDisplay *display, + const ColorManagedDisplay *display, float r_color[3]); bool paint_use_opacity_masking(Brush *brush); void paint_brush_init_tex(Brush *brush); diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 911b24b582a..b8e63251f87 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -397,6 +397,7 @@ set(LIB PRIVATE bf::intern::guardedalloc PRIVATE bf::extern::fmtlib PRIVATE bf::nodes + PRIVATE bf::dependencies::optional::opencolorio ) # Select Backend source based on availability @@ -738,10 +739,6 @@ if(WITH_MOD_FLUID) add_definitions(-DWITH_FLUID) endif() -if(WITH_OPENCOLORIO) - add_definitions(-DWITH_OCIO) -endif() - if(WITH_OPENSUBDIV) add_definitions(-DWITH_OPENSUBDIV) endif() @@ -760,16 +757,13 @@ target_link_libraries(bf_gpu PUBLIC bf_compositor_shaders bf_draw_shaders bf_gpu_shaders + bf_imbuf_opencolorio_shaders ) if(WITH_OPENGL_BACKEND AND UNIX) target_link_libraries(bf_gpu PUBLIC rt) endif() -if(WITH_OPENCOLORIO) - target_link_libraries(bf_gpu PUBLIC bf_ocio_shaders) -endif() - if(WITH_OPENSUBDIV) target_link_libraries(bf_gpu PUBLIC bf_osd_shaders) endif() diff --git a/source/blender/gpu/intern/gpu_shader_dependency.cc b/source/blender/gpu/intern/gpu_shader_dependency.cc index 67d648675d5..abe4e99e6c9 100644 --- a/source/blender/gpu/intern/gpu_shader_dependency.cc +++ b/source/blender/gpu/intern/gpu_shader_dependency.cc @@ -37,9 +37,7 @@ extern "C" { #include "glsl_compositor_source_list.h" #include "glsl_draw_source_list.h" #include "glsl_gpu_source_list.h" -#ifdef WITH_OCIO -# include "glsl_ocio_source_list.h" -#endif +#include "glsl_ocio_source_list.h" #ifdef WITH_OPENSUBDIV # include "glsl_osd_source_list.h" #endif @@ -371,9 +369,7 @@ namespace shader { #include "glsl_compositor_metadata_list.hh" #include "glsl_draw_metadata_list.hh" #include "glsl_gpu_metadata_list.hh" -#ifdef WITH_OCIO -# include "glsl_ocio_metadata_list.hh" -#endif +#include "glsl_ocio_metadata_list.hh" #ifdef WITH_OPENSUBDIV # include "glsl_osd_metadata_list.hh" #endif @@ -407,9 +403,7 @@ void gpu_shader_dependency_init() #include "glsl_compositor_source_list.h" #include "glsl_draw_source_list.h" #include "glsl_gpu_source_list.h" -#ifdef WITH_OCIO -# include "glsl_ocio_source_list.h" -#endif +#include "glsl_ocio_source_list.h" #ifdef WITH_OPENSUBDIV # include "glsl_osd_source_list.h" #endif diff --git a/source/blender/imbuf/CMakeLists.txt b/source/blender/imbuf/CMakeLists.txt index bb46465684b..a4faef400d2 100644 --- a/source/blender/imbuf/CMakeLists.txt +++ b/source/blender/imbuf/CMakeLists.txt @@ -3,6 +3,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later add_subdirectory(movie) +add_subdirectory(opencolorio) set(INC PUBLIC . @@ -78,7 +79,7 @@ set(LIB PRIVATE bf::intern::atomic PRIVATE bf::intern::guardedalloc bf_intern_memutil - bf_intern_opencolorio + PRIVATE bf::imbuf::opencolorio PRIVATE bf::extern::nanosvg ${JPEG_LIBRARIES} @@ -142,10 +143,6 @@ if(WITH_IMAGE_WEBP) add_definitions(-DWITH_IMAGE_WEBP) endif() -list(APPEND INC - ../../../intern/opencolorio -) - if(WIN32) list(APPEND INC ../../../intern/utfconv diff --git a/source/blender/imbuf/IMB_colormanagement.hh b/source/blender/imbuf/IMB_colormanagement.hh index 4c4629bf35d..cf73c7766b6 100644 --- a/source/blender/imbuf/IMB_colormanagement.hh +++ b/source/blender/imbuf/IMB_colormanagement.hh @@ -24,8 +24,12 @@ struct ImageFormatData; struct Main; struct bContext; -struct ColorManagedDisplay; -struct ColorSpace; +namespace blender::ocio { +class ColorSpace; +class Display; +} // namespace blender::ocio +using ColorSpace = blender::ocio::ColorSpace; +using ColorManagedDisplay = blender::ocio::Display; /* -------------------------------------------------------------------- */ /** \name Generic Functions @@ -47,10 +51,10 @@ const char *IMB_colormanagement_get_float_colorspace(ImBuf *ibuf); const char *IMB_colormanagement_get_rect_colorspace(ImBuf *ibuf); const char *IMB_colormanagement_space_from_filepath_rules(const char *filepath); -ColorSpace *IMB_colormanagement_space_get_named(const char *name); -bool IMB_colormanagement_space_is_data(ColorSpace *colorspace); -bool IMB_colormanagement_space_is_scene_linear(ColorSpace *colorspace); -bool IMB_colormanagement_space_is_srgb(ColorSpace *colorspace); +const ColorSpace *IMB_colormanagement_space_get_named(const char *name); +bool IMB_colormanagement_space_is_data(const ColorSpace *colorspace); +bool IMB_colormanagement_space_is_scene_linear(const ColorSpace *colorspace); +bool IMB_colormanagement_space_is_srgb(const ColorSpace *colorspace); bool IMB_colormanagement_space_name_is_data(const char *name); bool IMB_colormanagement_space_name_is_scene_linear(const char *name); bool IMB_colormanagement_space_name_is_srgb(const char *name); @@ -145,25 +149,31 @@ void IMB_colormanagement_transform_v4(float pixel[4], * For performance, use #IMB_colormanagement_colorspace_to_scene_linear * when converting an array of pixels. */ -void IMB_colormanagement_colorspace_to_scene_linear_v3(float pixel[3], ColorSpace *colorspace); +void IMB_colormanagement_colorspace_to_scene_linear_v3(float pixel[3], + const ColorSpace *colorspace); void IMB_colormanagement_colorspace_to_scene_linear_v4(float pixel[4], bool predivide, - ColorSpace *colorspace); + const ColorSpace *colorspace); /** * Convert pixel from scene linear space to specified color space. * For performance, use #IMB_colormanagement_scene_linear_to_colorspace * when converting an array of pixels. */ -void IMB_colormanagement_scene_linear_to_colorspace_v3(float pixel[3], ColorSpace *colorspace); +void IMB_colormanagement_scene_linear_to_colorspace_v3(float pixel[3], + const ColorSpace *colorspace); /** * Converts a (width)x(height) block of float pixels from given color space to * scene linear space. This is much higher performance than converting pixels * one by one. */ -void IMB_colormanagement_colorspace_to_scene_linear( - float *buffer, int width, int height, int channels, ColorSpace *colorspace, bool predivide); +void IMB_colormanagement_colorspace_to_scene_linear(float *buffer, + int width, + int height, + int channels, + const ColorSpace *colorspace, + bool predivide); /** * Converts a (width)x(height) block of float pixels from scene linear space @@ -171,7 +181,7 @@ void IMB_colormanagement_colorspace_to_scene_linear( * one by one. */ void IMB_colormanagement_scene_linear_to_colorspace( - float *buffer, int width, int height, int channels, ColorSpace *colorspace); + float *buffer, int width, int height, int channels, const ColorSpace *colorspace); void IMB_colormanagement_imbuf_to_byte_texture(unsigned char *out_buffer, int offset_x, @@ -216,12 +226,14 @@ BLI_INLINE void IMB_colormanagement_srgb_to_scene_linear_v3(float scene_linear[3 * used by performance-critical areas such as color-related widgets where we want to reduce * amount of per-widget allocations. */ -void IMB_colormanagement_scene_linear_to_display_v3(float pixel[3], ColorManagedDisplay *display); +void IMB_colormanagement_scene_linear_to_display_v3(float pixel[3], + const ColorManagedDisplay *display); /** * Same as #IMB_colormanagement_scene_linear_to_display_v3, * but converts color in opposite direction. */ -void IMB_colormanagement_display_to_scene_linear_v3(float pixel[3], ColorManagedDisplay *display); +void IMB_colormanagement_display_to_scene_linear_v3(float pixel[3], + const ColorManagedDisplay *display); void IMB_colormanagement_pixel_to_display_space_v4( float result[4], @@ -311,10 +323,10 @@ const char *IMB_colormanagement_display_get_default_name(); /** * Used by performance-critical pixel processing areas, such as color widgets. */ -ColorManagedDisplay *IMB_colormanagement_display_get_named(const char *name); +const ColorManagedDisplay *IMB_colormanagement_display_get_named(const char *name); const char *IMB_colormanagement_display_get_none_name(); const char *IMB_colormanagement_display_get_default_view_transform_name( - ColorManagedDisplay *display); + const ColorManagedDisplay *display); /** \} */ @@ -322,8 +334,8 @@ const char *IMB_colormanagement_display_get_default_view_transform_name( /** \name View Functions * \{ */ -int IMB_colormanagement_view_get_named_index(const char *name); -const char *IMB_colormanagement_view_get_indexed_name(int index); +int IMB_colormanagement_view_get_named_index(const char *display, const char *name); +const char *IMB_colormanagement_view_get_indexed_name(const char *display, int index); /** \} */ @@ -441,10 +453,6 @@ void IMB_colormanagement_processor_free(ColormanageProcessor *cm_processor); /** \name OpenGL Drawing Routines Using GLSL for Color Space Transform * \{ */ -/** - * Test if GLSL drawing is supported for combination of graphics card and this configuration. - */ -bool IMB_colormanagement_support_glsl_draw(const ColorManagedViewSettings *view_settings); /** * Configures GLSL shader for conversion from scene linear to display space. */ @@ -471,7 +479,7 @@ bool IMB_colormanagement_setup_glsl_draw(const ColorManagedViewSettings *view_se bool IMB_colormanagement_setup_glsl_draw_from_space( const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, - ColorSpace *from_colorspace, + const ColorSpace *from_colorspace, float dither, bool predivide, bool do_overlay_merge); @@ -484,7 +492,7 @@ bool IMB_colormanagement_setup_glsl_draw_ctx(const bContext *C, float dither, bo * but color management settings are guessing from a given context. */ bool IMB_colormanagement_setup_glsl_draw_from_space_ctx(const bContext *C, - ColorSpace *from_colorspace, + const ColorSpace *from_colorspace, float dither, bool predivide); diff --git a/source/blender/imbuf/IMB_imbuf.hh b/source/blender/imbuf/IMB_imbuf.hh index ec62c2f88e8..e15a9307227 100644 --- a/source/blender/imbuf/IMB_imbuf.hh +++ b/source/blender/imbuf/IMB_imbuf.hh @@ -20,12 +20,15 @@ struct ImBuf; struct rctf; struct rcti; -struct ColorManagedDisplay; - struct GSet; struct ImageFormatData; struct Stereo3dFormat; +namespace blender::ocio { +class Display; +} // namespace blender::ocio +using ColorManagedDisplay = blender::ocio::Display; + /** * Module init/exit. */ @@ -495,8 +498,13 @@ void IMB_rectfill(ImBuf *drect, const float col[4]); * order the area between x1 and x2, and y1 and y2 is filled. * \param display: color-space reference for display space. */ -void IMB_rectfill_area( - ImBuf *ibuf, const float col[4], int x1, int y1, int x2, int y2, ColorManagedDisplay *display); +void IMB_rectfill_area(ImBuf *ibuf, + const float col[4], + int x1, + int y1, + int x2, + int y2, + const ColorManagedDisplay *display); /** * Replace pixels of image area with solid color. * \param ibuf: an image to be filled with color. It must be 4 channel image. @@ -519,7 +527,7 @@ void buf_rectfill_area(unsigned char *rect, int width, int height, const float col[4], - ColorManagedDisplay *display, + const ColorManagedDisplay *display, int x1, int y1, int x2, diff --git a/source/blender/imbuf/IMB_imbuf_types.hh b/source/blender/imbuf/IMB_imbuf_types.hh index 1f89b842ae3..79faffaf313 100644 --- a/source/blender/imbuf/IMB_imbuf_types.hh +++ b/source/blender/imbuf/IMB_imbuf_types.hh @@ -16,10 +16,14 @@ #include "IMB_imbuf_enums.h" struct ColormanageCache; -struct ColorSpace; struct GPUTexture; struct IDProperty; +namespace blender::ocio { +class ColorSpace; +} +using ColorSpace = blender::ocio::ColorSpace; + #define IMB_MIPMAP_LEVELS 20 #define IMB_FILEPATH_SIZE 1024 @@ -146,14 +150,14 @@ struct ImBufByteBuffer { uint8_t *data; ImBufOwnership ownership; - ColorSpace *colorspace; + const ColorSpace *colorspace; }; struct ImBufFloatBuffer { float *data; ImBufOwnership ownership; - ColorSpace *colorspace; + const ColorSpace *colorspace; }; struct ImBufGPU { diff --git a/source/blender/imbuf/intern/IMB_colormanagement_intern.hh b/source/blender/imbuf/intern/IMB_colormanagement_intern.hh index 46f397cb1f1..960e8cbfca0 100644 --- a/source/blender/imbuf/intern/IMB_colormanagement_intern.hh +++ b/source/blender/imbuf/intern/IMB_colormanagement_intern.hh @@ -8,71 +8,28 @@ #pragma once +#include "BLI_math_matrix_types.hh" +#include "BLI_math_vector_types.hh" #include "DNA_listBase.h" -struct ImBuf; -struct OCIO_ConstCPUProcessorRc; -using OCIO_ConstCPUProcessorRcPtr = struct OCIO_ConstCPUProcessorRc *; +namespace blender::ocio { +class ColorSpace; +class CPUProcessor; +} // namespace blender::ocio -extern float imbuf_luma_coefficients[3]; -extern float imbuf_scene_linear_to_xyz[3][3]; -extern float imbuf_xyz_to_scene_linear[3][3]; -extern float imbuf_scene_linear_to_aces[3][3]; -extern float imbuf_aces_to_scene_linear[3][3]; -extern float imbuf_scene_linear_to_rec709[3][3]; -extern float imbuf_rec709_to_scene_linear[3][3]; +using ColorSpace = blender::ocio::ColorSpace; + +struct ImBuf; + +extern blender::float3 imbuf_luma_coefficients; +extern blender::float3x3 imbuf_scene_linear_to_xyz; +extern blender::float3x3 imbuf_xyz_to_scene_linear; +extern blender::float3x3 imbuf_scene_linear_to_aces; +extern blender::float3x3 imbuf_aces_to_scene_linear; +extern blender::float3x3 imbuf_scene_linear_to_rec709; +extern blender::float3x3 imbuf_rec709_to_scene_linear; #define MAX_COLORSPACE_NAME 64 -#define MAX_COLORSPACE_DESCRIPTION 512 - -struct ColorSpace { - ColorSpace *next, *prev; - int index; - char name[MAX_COLORSPACE_NAME]; - char description[MAX_COLORSPACE_DESCRIPTION]; - - OCIO_ConstCPUProcessorRcPtr *to_scene_linear; - OCIO_ConstCPUProcessorRcPtr *from_scene_linear; - - char (*aliases)[MAX_COLORSPACE_NAME]; - int num_aliases; - - bool is_invertible; - bool is_data; - - /* Additional info computed only when needed since it's not cheap. */ - struct { - bool cached; - bool is_srgb; - bool is_scene_linear; - } info; -}; - -struct ColorManagedDisplay { - ColorManagedDisplay *next, *prev; - int index; - char name[MAX_COLORSPACE_NAME]; - ListBase views; /* LinkData.data -> ColorManagedView */ - - OCIO_ConstCPUProcessorRcPtr *to_scene_linear; - OCIO_ConstCPUProcessorRcPtr *from_scene_linear; -}; - -struct ColorManagedView { - ColorManagedView *next, *prev; - int index; - char name[MAX_COLORSPACE_NAME]; -}; - -struct ColorManagedLook { - ColorManagedLook *next, *prev; - int index; - char name[MAX_COLORSPACE_NAME]; - char ui_name[MAX_COLORSPACE_NAME]; - char view[MAX_COLORSPACE_NAME]; - char process_space[MAX_COLORSPACE_NAME]; - bool is_noop; -}; /* ** Initialization / De-initialization ** */ @@ -81,31 +38,8 @@ void colormanagement_exit(); void colormanage_cache_free(ImBuf *ibuf); -const char *colormanage_display_get_default_name(); -ColorManagedDisplay *colormanage_display_get_default(); -ColorManagedDisplay *colormanage_display_add(const char *name); -ColorManagedDisplay *colormanage_display_get_named(const char *name); -ColorManagedDisplay *colormanage_display_get_indexed(int index); - -const char *colormanage_view_get_default_name(const ColorManagedDisplay *display); -ColorManagedView *colormanage_view_get_default(const ColorManagedDisplay *display); -ColorManagedView *colormanage_view_add(const char *name); -ColorManagedView *colormanage_view_get_indexed(int index); -ColorManagedView *colormanage_view_get_named(const char *name); -ColorManagedView *colormanage_view_get_named_for_display(const char *display_name, - const char *name); - -ColorSpace *colormanage_colorspace_add(const char *name, - const char *description, - bool is_invertible, - bool is_data); -ColorSpace *colormanage_colorspace_get_named(const char *name); -ColorSpace *colormanage_colorspace_get_roled(int role); -ColorSpace *colormanage_colorspace_get_indexed(int index); - -ColorManagedLook *colormanage_look_add(const char *name, const char *process_space, bool is_noop); -ColorManagedLook *colormanage_look_get_named(const char *name); -ColorManagedLook *colormanage_look_get_indexed(int index); +const ColorSpace *colormanage_colorspace_get_named(const char *name); +const ColorSpace *colormanage_colorspace_get_roled(int role); void colormanage_imbuf_set_default_spaces(ImBuf *ibuf); void colormanage_imbuf_make_linear(ImBuf *ibuf, const char *from_colorspace); diff --git a/source/blender/imbuf/intern/colormanagement.cc b/source/blender/imbuf/intern/colormanagement.cc index b07a64b35c3..bc2ac6d1eee 100644 --- a/source/blender/imbuf/intern/colormanagement.cc +++ b/source/blender/imbuf/intern/colormanagement.cc @@ -31,6 +31,9 @@ #include "BLI_listbase.h" #include "BLI_math_color.h" #include "BLI_math_color.hh" +#include "BLI_math_matrix.hh" +#include "BLI_math_matrix_types.hh" +#include "BLI_math_vector_types.hh" #include "BLI_path_utils.hh" #include "BLI_rect.h" #include "BLI_string.h" @@ -50,14 +53,21 @@ #include "SEQ_iterator.hh" -#include +#include "OCIO_api.hh" +using blender::float3; using blender::float3x3; +using blender::StringRefNull; + +namespace ocio = blender::ocio; +namespace math = blender::math; /* -------------------------------------------------------------------- */ /** \name Global declarations * \{ */ +static std::unique_ptr g_config = nullptr; + #define DISPLAY_BUFFER_CHANNELS 4 /* ** list of all supported color spaces, displays and views */ @@ -70,25 +80,15 @@ static char global_role_default_float[MAX_COLORSPACE_NAME]; static char global_role_default_sequencer[MAX_COLORSPACE_NAME]; static char global_role_aces_interchange[MAX_COLORSPACE_NAME]; -static ListBase global_colorspaces = {nullptr, nullptr}; -static ListBase global_displays = {nullptr, nullptr}; -static ListBase global_views = {nullptr, nullptr}; -static ListBase global_looks = {nullptr, nullptr}; - -static int global_tot_colorspace = 0; -static int global_tot_display = 0; -static int global_tot_view = 0; -static int global_tot_looks = 0; - /* Luma coefficients and XYZ to RGB to be initialized by OCIO. */ -float imbuf_luma_coefficients[3] = {0.0f}; -float imbuf_scene_linear_to_xyz[3][3] = {{0.0f}}; -float imbuf_xyz_to_scene_linear[3][3] = {{0.0f}}; -float imbuf_scene_linear_to_rec709[3][3] = {{0.0f}}; -float imbuf_rec709_to_scene_linear[3][3] = {{0.0f}}; -float imbuf_scene_linear_to_aces[3][3] = {{0.0f}}; -float imbuf_aces_to_scene_linear[3][3] = {{0.0f}}; +float3 imbuf_luma_coefficients(0.0f); +float3x3 imbuf_scene_linear_to_xyz = float3x3::zero(); +float3x3 imbuf_xyz_to_scene_linear = float3x3::zero(); +float3x3 imbuf_scene_linear_to_rec709 = float3x3::zero(); +float3x3 imbuf_rec709_to_scene_linear = float3x3::zero(); +float3x3 imbuf_scene_linear_to_aces = float3x3::zero(); +float3x3 imbuf_aces_to_scene_linear = float3x3::zero(); /* lock used by pre-cached processors getters, so processor wouldn't * be created several times @@ -98,7 +98,7 @@ float imbuf_aces_to_scene_linear[3][3] = {{0.0f}}; static pthread_mutex_t processor_lock = BLI_MUTEX_INITIALIZER; struct ColormanageProcessor { - OCIO_ConstCPUProcessorRcPtr *cpu_processor; + std::shared_ptr cpu_processor; CurveMapping *curve_mapping; bool is_data_result; }; @@ -111,13 +111,12 @@ static struct global_gpu_state { CurveMapping *curve_mapping, *orig_curve_mapping; bool use_curve_mapping; int curve_mapping_timestamp; - OCIO_CurveMappingSettings curve_mapping_settings; } global_gpu_state = {false}; static struct global_color_picking_state { /* Cached processor for color picking conversion. */ - OCIO_ConstCPUProcessorRcPtr *cpu_processor_to; - OCIO_ConstCPUProcessorRcPtr *cpu_processor_from; + std::shared_ptr cpu_processor_to; + std::shared_ptr cpu_processor_from; bool failed; } global_color_picking_state = {nullptr}; @@ -297,10 +296,12 @@ static void colormanage_cachedata_set(ImBuf *ibuf, ColormanageCacheData *data) static void colormanage_view_settings_to_cache(ImBuf *ibuf, ColormanageCacheViewSettings *cache_view_settings, - const ColorManagedViewSettings *view_settings) + const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings) { int look = IMB_colormanagement_look_get_named_index(view_settings->look); - int view = IMB_colormanagement_view_get_named_index(view_settings->view_transform); + int view = IMB_colormanagement_view_get_named_index(display_settings->display_device, + view_settings->view_transform); cache_view_settings->look = look; cache_view_settings->view = view; @@ -359,14 +360,14 @@ static uchar *colormanage_cache_get(ImBuf *ibuf, { ColormanageCacheKey key; ImBuf *cache_ibuf; - int view_flag = 1 << (view_settings->view - 1); + int view_flag = 1 << view_settings->view; CurveMapping *curve_mapping = view_settings->curve_mapping; int curve_mapping_timestamp = curve_mapping ? curve_mapping->changed_timestamp : 0; colormanage_settings_to_key(&key, view_settings, display_settings); /* check whether image was marked as dirty for requested transform */ - if ((ibuf->display_buffer_flags[display_settings->display - 1] & view_flag) == 0) { + if ((ibuf->display_buffer_flags[display_settings->display] & view_flag) == 0) { return nullptr; } @@ -415,7 +416,7 @@ static void colormanage_cache_put(ImBuf *ibuf, ColormanageCacheKey key; ImBuf *cache_ibuf; ColormanageCacheData *cache_data; - int view_flag = 1 << (view_settings->view - 1); + int view_flag = 1 << view_settings->view; MovieCache *moviecache = colormanage_moviecache_ensure(ibuf); CurveMapping *curve_mapping = view_settings->curve_mapping; int curve_mapping_timestamp = curve_mapping ? curve_mapping->changed_timestamp : 0; @@ -423,7 +424,7 @@ static void colormanage_cache_put(ImBuf *ibuf, colormanage_settings_to_key(&key, view_settings, display_settings); /* mark display buffer as valid */ - ibuf->display_buffer_flags[display_settings->display - 1] |= view_flag; + ibuf->display_buffer_flags[display_settings->display] |= view_flag; /* buffer itself */ cache_ibuf = IMB_allocImBuf(ibuf->x, ibuf->y, ibuf->planes, 0); @@ -462,23 +463,21 @@ static void colormanage_cache_handle_release(void *cache_handle) /** \name Initialization / De-initialization * \{ */ -static bool colormanage_role_color_space_name_get(OCIO_ConstConfigRcPtr *config, +static bool colormanage_role_color_space_name_get(ocio::Config &config, char *colorspace_name, const char *role, const char *backup_role, const bool optional = false) { - OCIO_ConstColorSpaceRcPtr *ociocs; - - ociocs = OCIO_configGetColorSpace(config, role); + const ColorSpace *ociocs = config.get_color_space(role); if (ociocs == nullptr && backup_role) { - ociocs = OCIO_configGetColorSpace(config, backup_role); + ociocs = config.get_color_space(backup_role); } if (ociocs == nullptr) { /* Overall fallback role. */ - ociocs = OCIO_configGetColorSpace(config, "default"); + ociocs = config.get_color_space("default"); } if (ociocs == nullptr) { @@ -489,15 +488,12 @@ static bool colormanage_role_color_space_name_get(OCIO_ConstConfigRcPtr *config, return false; } - const char *name = OCIO_colorSpaceGetName(ociocs); - /* assume function was called with buffer properly allocated to MAX_COLORSPACE_NAME chars */ - BLI_strncpy(colorspace_name, name, MAX_COLORSPACE_NAME); - OCIO_colorSpaceRelease(ociocs); + BLI_strncpy(colorspace_name, ociocs->name().c_str(), MAX_COLORSPACE_NAME); return true; } -static bool colormanage_load_config(OCIO_ConstConfigRcPtr *config) +static bool colormanage_load_config(ocio::Config &config) { bool ok = true; @@ -519,221 +515,105 @@ static bool colormanage_load_config(OCIO_ConstConfigRcPtr *config) colormanage_role_color_space_name_get( config, global_role_aces_interchange, OCIO_ROLE_ACES_INTERCHANGE, nullptr, true); - /* load colorspaces */ - const int tot_colorspace = OCIO_configGetNumColorSpaces(config); - for (int index = 0; index < tot_colorspace; index++) { - const char *name = OCIO_configGetColorSpaceNameByIndex(config, index); - - OCIO_ConstColorSpaceRcPtr *ocio_colorspace = OCIO_configGetColorSpace(config, name); - const char *description = OCIO_colorSpaceGetDescription(ocio_colorspace); - const bool is_invertible = OCIO_colorSpaceIsInvertible(ocio_colorspace); - const bool is_data = OCIO_colorSpaceIsData(ocio_colorspace); - - ColorSpace *colorspace = colormanage_colorspace_add(name, description, is_invertible, is_data); - - colorspace->num_aliases = OCIO_colorSpaceGetNumAliases(ocio_colorspace); - if (colorspace->num_aliases > 0) { - colorspace->aliases = MEM_calloc_arrayN( - size_t(colorspace->num_aliases), "ColorSpace aliases"); - for (int i = 0; i < colorspace->num_aliases; i++) { - BLI_strncpy(colorspace->aliases[i], - OCIO_colorSpaceGetAlias(ocio_colorspace, i), - MAX_COLORSPACE_NAME); - } - } - - OCIO_colorSpaceRelease(ocio_colorspace); - } - - /* load displays */ - const int tot_display = OCIO_configGetNumDisplays(config); - int viewindex2 = 0; - - for (int index = 0; index < tot_display; index++) { - const char *displayname = OCIO_configGetDisplay(config, index); - - ColorManagedDisplay *display = colormanage_display_add(displayname); - - /* load views */ - const int tot_display_view = OCIO_configGetNumViews(config, displayname); - for (int viewindex = 0; viewindex < tot_display_view; viewindex++, viewindex2++) { - const char *viewname = OCIO_configGetView(config, displayname, viewindex); - - /* first check if view transform with given name was already loaded */ - ColorManagedView *view = colormanage_view_get_named(viewname); - - if (!view) { - view = colormanage_view_add(viewname); - } - - LinkData *display_view = BLI_genericNodeN(view); - - BLI_addtail(&display->views, display_view); - } - } - - global_tot_display = tot_display; - if (global_tot_display == 0) { + if (g_config->get_num_displays() == 0) { if (!G.quiet) { printf("Color management: Error, could not find any displays\n"); } ok = false; } - else if (global_tot_view == 0) { + /* NOTE: The look "None" is expected to be hard-coded to exist in the OpenColorIO integration. */ + if (g_config->get_num_looks() == 0) { if (!G.quiet) { - printf("Color management: Error, could not find any views\n"); + printf("Color management: Error, could not find any looks\n"); } ok = false; } - /* load looks */ - const int tot_looks = OCIO_configGetNumLooks(config); - colormanage_look_add("None", "", true); - for (int index = 0; index < tot_looks; index++) { - const char *name = OCIO_configGetLookNameByIndex(config, index); - OCIO_ConstLookRcPtr *ocio_look = OCIO_configGetLook(config, name); - const char *process_space = OCIO_lookGetProcessSpace(ocio_look); - OCIO_lookRelease(ocio_look); - - colormanage_look_add(name, process_space, false); + for (const int display_index : blender::IndexRange(g_config->get_num_displays())) { + const ocio::Display *display = g_config->get_display_by_index(display_index); + if (display->get_num_views() <= 0) { + if (!G.quiet) { + printf("Color management: Error, could not find any views for display %s\n", + display->name().c_str()); + } + ok = false; + break; + } } /* Load luminance coefficients. */ - OCIO_configGetDefaultLumaCoefs(config, imbuf_luma_coefficients); + imbuf_luma_coefficients = config.get_default_luma_coefs(); /* Load standard color spaces. */ - OCIO_configGetXYZtoSceneLinear(config, imbuf_xyz_to_scene_linear); - invert_m3_m3(imbuf_scene_linear_to_xyz, imbuf_xyz_to_scene_linear); + imbuf_xyz_to_scene_linear = config.get_xyz_to_scene_linear_matrix(); + imbuf_scene_linear_to_xyz = math::invert(imbuf_xyz_to_scene_linear); - mul_m3_m3m3(imbuf_scene_linear_to_rec709, OCIO_XYZ_TO_REC709, imbuf_scene_linear_to_xyz); - invert_m3_m3(imbuf_rec709_to_scene_linear, imbuf_scene_linear_to_rec709); + imbuf_scene_linear_to_rec709 = ocio::XYZ_TO_REC709 * imbuf_scene_linear_to_xyz; + imbuf_rec709_to_scene_linear = math::invert(imbuf_scene_linear_to_rec709); - mul_m3_m3m3(imbuf_aces_to_scene_linear, imbuf_xyz_to_scene_linear, OCIO_ACES_TO_XYZ); - invert_m3_m3(imbuf_scene_linear_to_aces, imbuf_aces_to_scene_linear); + imbuf_aces_to_scene_linear = imbuf_xyz_to_scene_linear * ocio::ACES_TO_XYZ; + imbuf_scene_linear_to_aces = math::invert(imbuf_aces_to_scene_linear); return ok; } static void colormanage_free_config() { - ColorSpace *colorspace; - ColorManagedDisplay *display; - - /* free color spaces */ - colorspace = static_cast(global_colorspaces.first); - while (colorspace) { - ColorSpace *colorspace_next = colorspace->next; - - /* Free precomputed processors. */ - if (colorspace->to_scene_linear) { - OCIO_cpuProcessorRelease((OCIO_ConstCPUProcessorRcPtr *)colorspace->to_scene_linear); - } - if (colorspace->from_scene_linear) { - OCIO_cpuProcessorRelease((OCIO_ConstCPUProcessorRcPtr *)colorspace->from_scene_linear); - } - - /* free color space itself */ - MEM_SAFE_FREE(colorspace->aliases); - MEM_freeN(colorspace); - - colorspace = colorspace_next; - } - BLI_listbase_clear(&global_colorspaces); - global_tot_colorspace = 0; - - /* free displays */ - display = static_cast(global_displays.first); - while (display) { - ColorManagedDisplay *display_next = display->next; - - /* free precomputer processors */ - if (display->to_scene_linear) { - OCIO_cpuProcessorRelease((OCIO_ConstCPUProcessorRcPtr *)display->to_scene_linear); - } - if (display->from_scene_linear) { - OCIO_cpuProcessorRelease((OCIO_ConstCPUProcessorRcPtr *)display->from_scene_linear); - } - - /* free list of views */ - BLI_freelistN(&display->views); - - MEM_freeN(display); - display = display_next; - } - BLI_listbase_clear(&global_displays); - global_tot_display = 0; - - /* free views */ - BLI_freelistN(&global_views); - global_tot_view = 0; - - /* free looks */ - BLI_freelistN(&global_looks); - global_tot_looks = 0; + g_config = nullptr; } void colormanagement_init() { - OCIO_ConstConfigRcPtr *config = nullptr; - - OCIO_init(); - /* First try config from environment variable. */ const char *ocio_env = BLI_getenv("OCIO"); if (ocio_env && ocio_env[0] != '\0') { - config = OCIO_configCreateFromEnv(); - if (config != nullptr) { + g_config = ocio::Config::create_from_environment(); + if (g_config != nullptr) { if (!G.quiet) { printf("Color management: Using %s as a configuration file\n", ocio_env); } - OCIO_setCurrentConfig(config); - const bool ok = colormanage_load_config(config); - OCIO_configRelease(config); + const bool ok = colormanage_load_config(*g_config); if (!ok) { if (!G.quiet) { printf("Color management: Failed to load config from environment\n"); } colormanage_free_config(); - config = nullptr; } } } /* Then try bundled configuration file. */ - if (config == nullptr) { + if (g_config == nullptr) { const std::optional configdir = BKE_appdir_folder_id(BLENDER_DATAFILES, "colormanagement"); if (configdir.has_value()) { char configfile[FILE_MAX]; BLI_path_join(configfile, sizeof(configfile), configdir->c_str(), BCM_CONFIG_FILE); - config = OCIO_configCreateFromFile(configfile); + g_config = ocio::Config::create_from_file(configfile); - if (config != nullptr) { - OCIO_setCurrentConfig(config); - const bool ok = colormanage_load_config(config); - OCIO_configRelease(config); + if (g_config != nullptr) { + const bool ok = colormanage_load_config(*g_config); if (!ok) { if (!G.quiet) { printf("Color management: Failed to load bundled config\n"); } colormanage_free_config(); - config = nullptr; } } } } /* Then use fallback. */ - if (config == nullptr) { + if (g_config == nullptr) { if (!G.quiet) { printf("Color management: Using fallback mode for management\n"); } - config = OCIO_configCreateFallback(); - colormanage_load_config(config); + g_config = ocio::Config::create_fallback(); + colormanage_load_config(*g_config); } BLI_init_srgb_conversion(); @@ -741,29 +621,14 @@ void colormanagement_init() void colormanagement_exit() { - OCIO_gpuCacheFree(); - if (global_gpu_state.curve_mapping) { BKE_curvemapping_free(global_gpu_state.curve_mapping); } - if (global_gpu_state.curve_mapping_settings.lut) { - MEM_freeN(global_gpu_state.curve_mapping_settings.lut); - } - - if (global_color_picking_state.cpu_processor_to) { - OCIO_cpuProcessorRelease(global_color_picking_state.cpu_processor_to); - } - - if (global_color_picking_state.cpu_processor_from) { - OCIO_cpuProcessorRelease(global_color_picking_state.cpu_processor_from); - } - memset(&global_gpu_state, 0, sizeof(global_gpu_state)); memset(&global_color_picking_state, 0, sizeof(global_color_picking_state)); colormanage_free_config(); - OCIO_exit(); } /** \} */ @@ -778,8 +643,9 @@ static bool has_explicit_look_for_view(const char *view_name) return false; } - LISTBASE_FOREACH (ColorManagedLook *, look, &global_looks) { - if (STREQ(look->view, view_name)) { + for (const int look_index : blender::IndexRange(g_config->get_num_looks())) { + const ocio::Look *look = g_config->get_look_by_index(look_index); + if (look->view() == view_name) { return true; } } @@ -787,7 +653,7 @@ static bool has_explicit_look_for_view(const char *view_name) return false; } -static bool colormanage_compatible_look(const ColorManagedLook *look, +static bool colormanage_compatible_look(const ocio::Look *look, const char *view_name, const bool has_explicit_look) { @@ -798,7 +664,7 @@ static bool colormanage_compatible_look(const ColorManagedLook *look, /* Skip looks only relevant to specific view transforms. * If the view transform has view-specific look ignore non-specific looks. */ - if (view_name && STREQ(look->view, view_name)) { + if (view_name && look->view() == view_name) { return true; } @@ -806,19 +672,19 @@ static bool colormanage_compatible_look(const ColorManagedLook *look, return false; } - return look->view[0] == '\0'; + return look->view().is_empty(); } -static bool colormanage_compatible_look(const ColorManagedLook *look, const char *view_name) +static bool colormanage_compatible_look(const ocio::Look *look, const char *view_name) { const bool has_explicit_look = has_explicit_look_for_view(view_name); return colormanage_compatible_look(look, view_name, has_explicit_look); } -static bool colormanage_use_look(const char *look, const char *view_name) +static bool colormanage_use_look(const char *look_name, const char *view_name) { - ColorManagedLook *look_descr = colormanage_look_get_named(look); - return (look_descr->is_noop == false && colormanage_compatible_look(look_descr, view_name)); + const ocio::Look *look = g_config->get_look_by_name(look_name); + return (look->is_noop == false && colormanage_compatible_look(look, view_name)); } void colormanage_cache_free(ImBuf *ibuf) @@ -861,224 +727,56 @@ void IMB_colormanagement_display_settings_from_ctx( } } -static const char *get_display_colorspace_name(const ColorManagedViewSettings *view_settings, - const ColorManagedDisplaySettings *display_settings) -{ - OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig(); - - const char *display = display_settings->display_device; - const char *view = view_settings->view_transform; - const char *colorspace_name; - - colorspace_name = OCIO_configGetDisplayColorSpaceName(config, display, view); - - OCIO_configRelease(config); - - return colorspace_name; -} - -static ColorSpace *display_transform_get_colorspace( +static const ColorSpace *get_display_colorspace( const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings) { - const char *colorspace_name = get_display_colorspace_name(view_settings, display_settings); - - if (colorspace_name) { - return colormanage_colorspace_get_named(colorspace_name); - } - - return nullptr; + return g_config->get_display_view_color_space(display_settings->display_device, + view_settings->view_transform); } -static OCIO_ConstCPUProcessorRcPtr *create_display_buffer_processor(const char *look, - const char *view_transform, - const char *display, - const float exposure, - const float gamma, - const float temperature, - const float tint, - const bool use_white_balance, - const char *from_colorspace) +static std::shared_ptr get_display_buffer_processor( + const char *look, + const char *view_transform, + const char *display, + const float exposure, + const float gamma, + const float temperature, + const float tint, + const bool use_white_balance, + const char *from_colorspace) { - OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig(); - const bool use_look = colormanage_use_look(look, view_transform); - const float scale = (exposure == 0.0f) ? 1.0f : powf(2.0f, exposure); - const float exponent = (gamma == 1.0f) ? 1.0f : 1.0f / max_ff(FLT_EPSILON, gamma); + ocio::DisplayParameters display_parameters; + display_parameters.from_colorspace = from_colorspace; + display_parameters.view = view_transform; + display_parameters.display = display; + display_parameters.look = (colormanage_use_look(look, view_transform)) ? look : ""; + display_parameters.scale = (exposure == 0.0f) ? 1.0f : powf(2.0f, exposure); + display_parameters.exponent = (gamma == 1.0f) ? 1.0f : 1.0f / max_ff(FLT_EPSILON, gamma); + display_parameters.temperature = temperature; + display_parameters.tint = tint; + display_parameters.use_white_balance = use_white_balance; + display_parameters.inverse = false; - OCIO_ConstProcessorRcPtr *processor = OCIO_createDisplayProcessor(config, - from_colorspace, - view_transform, - display, - (use_look) ? look : "", - scale, - exponent, - temperature, - tint, - use_white_balance, - false); - - OCIO_configRelease(config); - - if (processor == nullptr) { - return nullptr; - } - - OCIO_ConstCPUProcessorRcPtr *cpu_processor = OCIO_processorGetCPUProcessor(processor); - OCIO_processorRelease(processor); - - return cpu_processor; -} - -static OCIO_ConstProcessorRcPtr *create_colorspace_transform_processor(const char *from_colorspace, - const char *to_colorspace) -{ - OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig(); - OCIO_ConstProcessorRcPtr *processor; - - processor = OCIO_configGetProcessorWithNames(config, from_colorspace, to_colorspace); - - OCIO_configRelease(config); - - return processor; -} - -static OCIO_ConstCPUProcessorRcPtr *colorspace_to_scene_linear_cpu_processor( - ColorSpace *colorspace) -{ - if (colorspace->to_scene_linear == nullptr) { - BLI_mutex_lock(&processor_lock); - - if (colorspace->to_scene_linear == nullptr) { - OCIO_ConstProcessorRcPtr *processor = create_colorspace_transform_processor( - colorspace->name, global_role_scene_linear); - - if (processor != nullptr) { - colorspace->to_scene_linear = OCIO_processorGetCPUProcessor(processor); - OCIO_processorRelease(processor); - } - } - - BLI_mutex_unlock(&processor_lock); - } - - return (OCIO_ConstCPUProcessorRcPtr *)colorspace->to_scene_linear; -} - -static OCIO_ConstCPUProcessorRcPtr *colorspace_from_scene_linear_cpu_processor( - ColorSpace *colorspace) -{ - if (colorspace->from_scene_linear == nullptr) { - BLI_mutex_lock(&processor_lock); - - if (colorspace->from_scene_linear == nullptr) { - OCIO_ConstProcessorRcPtr *processor = create_colorspace_transform_processor( - global_role_scene_linear, colorspace->name); - - if (processor != nullptr) { - colorspace->from_scene_linear = OCIO_processorGetCPUProcessor(processor); - OCIO_processorRelease(processor); - } - } - - BLI_mutex_unlock(&processor_lock); - } - - return (OCIO_ConstCPUProcessorRcPtr *)colorspace->from_scene_linear; -} - -static OCIO_ConstCPUProcessorRcPtr *display_from_scene_linear_processor( - ColorManagedDisplay *display) -{ - if (display->from_scene_linear == nullptr) { - BLI_mutex_lock(&processor_lock); - - if (display->from_scene_linear == nullptr) { - const char *view_name = colormanage_view_get_default_name(display); - OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig(); - OCIO_ConstProcessorRcPtr *processor = nullptr; - - if (view_name && config) { - processor = OCIO_createDisplayProcessor(config, - global_role_scene_linear, - view_name, - display->name, - nullptr, - 1.0f, - 1.0f, - 0.0f, - 0.0f, - false, - false); - - OCIO_configRelease(config); - } - - if (processor != nullptr) { - display->from_scene_linear = OCIO_processorGetCPUProcessor(processor); - OCIO_processorRelease(processor); - } - } - - BLI_mutex_unlock(&processor_lock); - } - - return (OCIO_ConstCPUProcessorRcPtr *)display->from_scene_linear; -} - -static OCIO_ConstCPUProcessorRcPtr *display_to_scene_linear_processor(ColorManagedDisplay *display) -{ - if (display->to_scene_linear == nullptr) { - BLI_mutex_lock(&processor_lock); - - if (display->to_scene_linear == nullptr) { - const char *view_name = colormanage_view_get_default_name(display); - OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig(); - OCIO_ConstProcessorRcPtr *processor = nullptr; - - if (view_name && config) { - processor = OCIO_createDisplayProcessor(config, - global_role_scene_linear, - view_name, - display->name, - nullptr, - 1.0f, - 1.0f, - 0.0f, - 0.0f, - false, - true); - - OCIO_configRelease(config); - } - - if (processor != nullptr) { - display->to_scene_linear = OCIO_processorGetCPUProcessor(processor); - OCIO_processorRelease(processor); - } - } - - BLI_mutex_unlock(&processor_lock); - } - - return (OCIO_ConstCPUProcessorRcPtr *)display->to_scene_linear; + return g_config->get_display_cpu_processor(display_parameters); } void IMB_colormanagement_init_default_view_settings( ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings) { /* First, try use "Standard" view transform of the requested device. */ - ColorManagedView *default_view = colormanage_view_get_named_for_display( - display_settings->display_device, "Standard"); + const ocio::Display *display = g_config->get_display_by_name(display_settings->display_device); + if (!display) { + return; + } + const ocio::View *default_view = display->get_view_by_name("Standard"); /* If that fails, we fall back to the default view transform of the display * as per OCIO configuration. */ if (default_view == nullptr) { - ColorManagedDisplay *display = colormanage_display_get_named(display_settings->display_device); - if (display != nullptr) { - default_view = colormanage_view_get_default(display); - } + default_view = display->get_default_view(); } if (default_view != nullptr) { - STRNCPY(view_settings->view_transform, default_view->name); + STRNCPY(view_settings->view_transform, default_view->name().c_str()); } else { view_settings->view_transform[0] = '\0'; @@ -1112,14 +810,20 @@ static void curve_mapping_apply_pixel(const CurveMapping *curve_mapping, void colormanage_imbuf_set_default_spaces(ImBuf *ibuf) { - ibuf->byte_buffer.colorspace = colormanage_colorspace_get_named(global_role_default_byte); + /* Regression tests are allocating ImBuf. Guard against access of uninitialized color + * management configuration. */ + /* TODO(sergey): Always allocate the fallback color management configuration for such cases? */ + if (!g_config) { + return; + } + ibuf->byte_buffer.colorspace = g_config->get_color_space(global_role_default_byte); } void colormanage_imbuf_make_linear(ImBuf *ibuf, const char *from_colorspace) { - ColorSpace *colorspace = colormanage_colorspace_get_named(from_colorspace); + const ColorSpace *colorspace = g_config->get_color_space(from_colorspace); - if (colorspace && colorspace->is_data) { + if (colorspace && colorspace->is_data()) { ibuf->colormanage_flag |= IMB_COLORMANAGE_IS_DATA; return; } @@ -1151,13 +855,13 @@ void colormanage_imbuf_make_linear(ImBuf *ibuf, const char *from_colorspace) static void colormanage_check_display_settings(ColorManagedDisplaySettings *display_settings, const char *what, - const ColorManagedDisplay *default_display) + const ocio::Display *default_display) { if (display_settings->display_device[0] == '\0') { - STRNCPY(display_settings->display_device, default_display->name); + STRNCPY(display_settings->display_device, default_display->name().c_str()); } else { - ColorManagedDisplay *display = colormanage_display_get_named(display_settings->display_device); + const ocio::Display *display = g_config->get_display_by_name(display_settings->display_device); if (!display) { if (!G.quiet) { @@ -1166,10 +870,10 @@ static void colormanage_check_display_settings(ColorManagedDisplaySettings *disp "(\"%s\").\n", display_settings->display_device, what, - default_display->name); + default_display->name().c_str()); } - STRNCPY(display_settings->display_device, default_display->name); + STRNCPY(display_settings->display_device, default_display->name().c_str()); } } } @@ -1178,40 +882,30 @@ static void colormanage_check_view_settings(ColorManagedDisplaySettings *display ColorManagedViewSettings *view_settings, const char *what) { - ColorManagedDisplay *display; - ColorManagedView *default_view = nullptr; + const ocio::Display *display = g_config->get_display_by_name(display_settings->display_device); + if (!display) { + return; + } const char *default_look_name = IMB_colormanagement_look_get_default_name(); if (view_settings->view_transform[0] == '\0') { - display = colormanage_display_get_named(display_settings->display_device); - - if (display) { - default_view = colormanage_view_get_default(display); - } - + const ocio::View *default_view = display->get_default_view(); if (default_view) { - STRNCPY(view_settings->view_transform, default_view->name); + STRNCPY(view_settings->view_transform, default_view->name().c_str()); } } else { - ColorManagedView *view = colormanage_view_get_named(view_settings->view_transform); - + const ocio::View *view = display->get_view_by_name(view_settings->view_transform); if (!view) { - display = colormanage_display_get_named(display_settings->display_device); - - if (display) { - default_view = colormanage_view_get_default(display); - } - + const ocio::View *default_view = display->get_default_view(); if (default_view) { if (!G.quiet) { printf("Color management: %s view \"%s\" not found, setting default \"%s\".\n", what, view_settings->view_transform, - default_view->name); + default_view->name().c_str()); } - - STRNCPY(view_settings->view_transform, default_view->name); + STRNCPY(view_settings->view_transform, default_view->name().c_str()); } } } @@ -1220,7 +914,7 @@ static void colormanage_check_view_settings(ColorManagedDisplaySettings *display STRNCPY(view_settings->look, default_look_name); } else { - ColorManagedLook *look = colormanage_look_get_named(view_settings->look); + const ocio::Look *look = g_config->get_look_by_name(view_settings->look); if (look == nullptr) { if (!G.quiet) { printf("Color management: %s look \"%s\" not found, setting default \"%s\".\n", @@ -1260,7 +954,7 @@ static void colormanage_check_colorspace_settings( /* pass */ } else { - ColorSpace *colorspace = colormanage_colorspace_get_named(colorspace_settings->name); + const ColorSpace *colorspace = g_config->get_color_space(colorspace_settings->name); if (!colorspace) { if (!G.quiet) { @@ -1286,7 +980,7 @@ static bool strip_callback(Strip *strip, void * /*user_data*/) void IMB_colormanagement_check_file_config(Main *bmain) { - ColorManagedDisplay *default_display = colormanage_display_get_default(); + const ocio::Display *default_display = g_config->get_default_display(); if (!default_display) { /* happens when OCIO configuration is incorrect */ return; @@ -1327,21 +1021,20 @@ void IMB_colormanagement_check_file_config(Main *bmain) void IMB_colormanagement_validate_settings(const ColorManagedDisplaySettings *display_settings, ColorManagedViewSettings *view_settings) { - ColorManagedDisplay *display = colormanage_display_get_named(display_settings->display_device); - ColorManagedView *default_view = colormanage_view_get_default(display); + const ocio::Display *display = g_config->get_display_by_name(display_settings->display_device); + const ocio::View *default_view = display->get_default_view(); bool found = false; - LISTBASE_FOREACH (LinkData *, view_link, &display->views) { - ColorManagedView *view = static_cast(view_link->data); - - if (STREQ(view->name, view_settings->view_transform)) { + for (const int view_index : blender::IndexRange(display->get_num_views())) { + const ocio::View *view = display->get_view_by_index(view_index); + if (view->name() == view_settings->view_transform) { found = true; break; } } if (!found && default_view) { - STRNCPY(view_settings->view_transform, default_view->name); + STRNCPY(view_settings->view_transform, default_view->name().c_str()); } } @@ -1377,9 +1070,9 @@ const char *IMB_colormanagement_role_colorspace_name_get(int role) void IMB_colormanagement_check_is_data(ImBuf *ibuf, const char *name) { - ColorSpace *colorspace = colormanage_colorspace_get_named(name); + const ColorSpace *colorspace = g_config->get_color_space(name); - if (colorspace && colorspace->is_data) { + if (colorspace && colorspace->is_data()) { ibuf->colormanage_flag |= IMB_COLORMANAGE_IS_DATA; } else { @@ -1406,11 +1099,11 @@ void IMB_colormanagegent_copy_settings(ImBuf *ibuf_src, ImBuf *ibuf_dst) void IMB_colormanagement_assign_float_colorspace(ImBuf *ibuf, const char *name) { - ColorSpace *colorspace = colormanage_colorspace_get_named(name); + const ColorSpace *colorspace = g_config->get_color_space(name); ibuf->float_buffer.colorspace = colorspace; - if (colorspace && colorspace->is_data) { + if (colorspace && colorspace->is_data()) { ibuf->colormanage_flag |= IMB_COLORMANAGE_IS_DATA; } else { @@ -1420,11 +1113,11 @@ void IMB_colormanagement_assign_float_colorspace(ImBuf *ibuf, const char *name) void IMB_colormanagement_assign_byte_colorspace(ImBuf *ibuf, const char *name) { - ColorSpace *colorspace = colormanage_colorspace_get_named(name); + const ColorSpace *colorspace = g_config->get_color_space(name); ibuf->byte_buffer.colorspace = colorspace; - if (colorspace && colorspace->is_data) { + if (colorspace && colorspace->is_data()) { ibuf->colormanage_flag |= IMB_COLORMANAGE_IS_DATA; } else { @@ -1435,7 +1128,7 @@ void IMB_colormanagement_assign_byte_colorspace(ImBuf *ibuf, const char *name) const char *IMB_colormanagement_get_float_colorspace(ImBuf *ibuf) { if (ibuf->float_buffer.colorspace) { - return ibuf->float_buffer.colorspace->name; + return ibuf->float_buffer.colorspace->name().c_str(); } return IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR); @@ -1444,7 +1137,7 @@ const char *IMB_colormanagement_get_float_colorspace(ImBuf *ibuf) const char *IMB_colormanagement_get_rect_colorspace(ImBuf *ibuf) { if (ibuf->byte_buffer.colorspace) { - return ibuf->byte_buffer.colorspace->name; + return ibuf->byte_buffer.colorspace->name().c_str(); } return IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DEFAULT_BYTE); @@ -1452,77 +1145,53 @@ const char *IMB_colormanagement_get_rect_colorspace(ImBuf *ibuf) const char *IMB_colormanagement_space_from_filepath_rules(const char *filepath) { - OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig(); - const char *colorspace = OCIO_getColorSpaceFromFilepath(config, filepath); - OCIO_configRelease(config); - return colorspace; + return g_config->get_color_space_from_filepath(filepath); } -ColorSpace *IMB_colormanagement_space_get_named(const char *name) +const ColorSpace *IMB_colormanagement_space_get_named(const char *name) { - return colormanage_colorspace_get_named(name); + return g_config->get_color_space(name); } -bool IMB_colormanagement_space_is_data(ColorSpace *colorspace) +bool IMB_colormanagement_space_is_data(const ColorSpace *colorspace) { - return (colorspace && colorspace->is_data); + return (colorspace && colorspace->is_data()); } -static void colormanage_ensure_srgb_scene_linear_info(ColorSpace *colorspace) +bool IMB_colormanagement_space_is_scene_linear(const ColorSpace *colorspace) { - if (colorspace && !colorspace->info.cached) { - OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig(); - OCIO_ConstColorSpaceRcPtr *ocio_colorspace = OCIO_configGetColorSpace(config, - colorspace->name); - - bool is_scene_linear, is_srgb; - OCIO_colorSpaceIsBuiltin(config, ocio_colorspace, &is_scene_linear, &is_srgb); - - OCIO_colorSpaceRelease(ocio_colorspace); - OCIO_configRelease(config); - - colorspace->info.is_scene_linear = is_scene_linear; - colorspace->info.is_srgb = is_srgb; - colorspace->info.cached = true; - } + return (colorspace && colorspace->is_scene_linear()); } -bool IMB_colormanagement_space_is_scene_linear(ColorSpace *colorspace) +bool IMB_colormanagement_space_is_srgb(const ColorSpace *colorspace) { - colormanage_ensure_srgb_scene_linear_info(colorspace); - return (colorspace && colorspace->info.is_scene_linear); -} - -bool IMB_colormanagement_space_is_srgb(ColorSpace *colorspace) -{ - colormanage_ensure_srgb_scene_linear_info(colorspace); - return (colorspace && colorspace->info.is_srgb); + return (colorspace && colorspace->is_srgb()); } bool IMB_colormanagement_space_name_is_data(const char *name) { - ColorSpace *colorspace = colormanage_colorspace_get_named(name); - return (colorspace && colorspace->is_data); + const ColorSpace *colorspace = g_config->get_color_space(name); + return (colorspace && colorspace->is_data()); } bool IMB_colormanagement_space_name_is_scene_linear(const char *name) { - ColorSpace *colorspace = colormanage_colorspace_get_named(name); + const ColorSpace *colorspace = g_config->get_color_space(name); return (colorspace && IMB_colormanagement_space_is_scene_linear(colorspace)); } bool IMB_colormanagement_space_name_is_srgb(const char *name) { - ColorSpace *colorspace = colormanage_colorspace_get_named(name); + const ColorSpace *colorspace = g_config->get_color_space(name); return (colorspace && IMB_colormanagement_space_is_srgb(colorspace)); } const char *IMB_colormanagement_srgb_colorspace_name_get() { - LISTBASE_FOREACH (ColorSpace *, colorspace, &global_colorspaces) { - colormanage_ensure_srgb_scene_linear_info(colorspace); - if (colorspace->info.is_srgb) { - return colorspace->name; + for (const int colorspace_index : blender::IndexRange(g_config->get_num_color_spaces())) { + const ColorSpace *colorspace = g_config->get_color_space_by_index(colorspace_index); + if (colorspace->is_srgb()) { + return colorspace->name().c_str(); } } @@ -1534,9 +1203,9 @@ const char *IMB_colormanagement_srgb_colorspace_name_get() "sRGB", nullptr}; for (int i = 0; names[i]; i++) { - ColorSpace *colorspace = colormanage_colorspace_get_named(names[i]); + const ColorSpace *colorspace = g_config->get_color_space(names[i]); if (colorspace) { - return colorspace->name; + return colorspace->name().c_str(); } } @@ -1884,7 +1553,7 @@ static void display_buffer_apply_threaded(ImBuf *ibuf, init_data.display_buffer_byte = display_buffer_byte; if (ibuf->byte_buffer.colorspace != nullptr) { - init_data.byte_colorspace = ibuf->byte_buffer.colorspace->name; + init_data.byte_colorspace = ibuf->byte_buffer.colorspace->name().c_str(); } else { /* happens for viewer images, which are not so simple to determine where to @@ -1895,7 +1564,7 @@ static void display_buffer_apply_threaded(ImBuf *ibuf, if (ibuf->float_buffer.colorspace != nullptr) { /* sequencer stores float buffers in non-linear space */ - init_data.float_colorspace = ibuf->float_buffer.colorspace->name; + init_data.float_colorspace = ibuf->float_buffer.colorspace->name().c_str(); } else { init_data.float_colorspace = nullptr; @@ -1916,19 +1585,19 @@ static bool is_colorspace_same_as_display(const ColorSpace *colorspace, const ColorManagedDisplaySettings *display_settings) { if ((view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) != 0 || + (view_settings->flag & COLORMANAGE_VIEW_USE_WHITE_BALANCE) != 0 || view_settings->exposure != 0.0f || view_settings->gamma != 1.0f) { return false; } - ColorManagedLook *look_descr = colormanage_look_get_named(view_settings->look); - if (look_descr != nullptr && !STREQ(look_descr->process_space, "")) { + const ocio::Look *look = g_config->get_look_by_name(view_settings->look); + if (look != nullptr && !look->process_space().is_empty()) { return false; } - const char *from_colorspace = colorspace->name; - const char *to_colorspace = get_display_colorspace_name(view_settings, display_settings); - if (to_colorspace && STREQ(from_colorspace, to_colorspace)) { + const ColorSpace *display_colorspace = get_display_colorspace(view_settings, display_settings); + if (display_colorspace == colorspace) { return true; } @@ -2232,103 +1901,109 @@ void IMB_colormanagement_transform_v4(float pixel[4], IMB_colormanagement_processor_free(cm_processor); } -void IMB_colormanagement_colorspace_to_scene_linear_v3(float pixel[3], ColorSpace *colorspace) +void IMB_colormanagement_colorspace_to_scene_linear_v3(float pixel[3], + const ColorSpace *colorspace) { if (colorspace == nullptr) { /* should never happen */ printf("%s: perform conversion from unknown color space\n", __func__); return; } - OCIO_ConstCPUProcessorRcPtr *processor = colorspace_to_scene_linear_cpu_processor(colorspace); + const ocio::CPUProcessor *processor = colorspace->get_to_scene_linear_cpu_processor(); if (processor == nullptr) { return; } - OCIO_cpuProcessorApplyRGB(processor, pixel); + processor->apply_rgb(pixel); } -void IMB_colormanagement_scene_linear_to_colorspace_v3(float pixel[3], ColorSpace *colorspace) +void IMB_colormanagement_scene_linear_to_colorspace_v3(float pixel[3], + const ColorSpace *colorspace) { if (colorspace == nullptr) { /* should never happen */ printf("%s: perform conversion from unknown color space\n", __func__); return; } - OCIO_ConstCPUProcessorRcPtr *processor = colorspace_from_scene_linear_cpu_processor(colorspace); + const ocio::CPUProcessor *processor = colorspace->get_from_scene_linear_cpu_processor(); if (processor == nullptr) { return; } - OCIO_cpuProcessorApplyRGB(processor, pixel); + processor->apply_rgb(pixel); } void IMB_colormanagement_colorspace_to_scene_linear_v4(float pixel[4], - bool predivide, - ColorSpace *colorspace) + const bool predivide, + const ColorSpace *colorspace) { if (colorspace == nullptr) { /* should never happen */ printf("%s: perform conversion from unknown color space\n", __func__); return; } - OCIO_ConstCPUProcessorRcPtr *processor = colorspace_to_scene_linear_cpu_processor(colorspace); + const ocio::CPUProcessor *processor = colorspace->get_to_scene_linear_cpu_processor(); if (processor == nullptr) { return; } if (predivide) { - OCIO_cpuProcessorApplyRGBA_predivide(processor, pixel); + processor->apply_rgba_predivide(pixel); } else { - OCIO_cpuProcessorApplyRGBA(processor, pixel); + processor->apply_rgba(pixel); } } -void IMB_colormanagement_colorspace_to_scene_linear( - float *buffer, int width, int height, int channels, ColorSpace *colorspace, bool predivide) +void IMB_colormanagement_colorspace_to_scene_linear(float *buffer, + const int width, + const int height, + const int channels, + const ColorSpace *colorspace, + const bool predivide) { if (colorspace == nullptr) { /* should never happen */ printf("%s: perform conversion from unknown color space\n", __func__); return; } - OCIO_ConstCPUProcessorRcPtr *processor = colorspace_to_scene_linear_cpu_processor(colorspace); + const ocio::CPUProcessor *processor = colorspace->get_to_scene_linear_cpu_processor(); if (processor == nullptr) { return; } - OCIO_PackedImageDesc *img = OCIO_createOCIO_PackedImageDesc(buffer, - width, - height, - channels, - sizeof(float), - size_t(channels) * sizeof(float), - size_t(channels) * sizeof(float) * - width); + ocio::PackedImage img(buffer, + width, + height, + channels, + ocio::BitDepth::BIT_DEPTH_F32, + sizeof(float), + size_t(channels) * sizeof(float), + size_t(channels) * sizeof(float) * width); if (predivide) { - OCIO_cpuProcessorApply_predivide(processor, img); + processor->apply_predivide(img); } else { - OCIO_cpuProcessorApply(processor, img); + processor->apply(img); } - - OCIO_PackedImageDescRelease(img); } -void IMB_colormanagement_scene_linear_to_colorspace( - float *buffer, int width, int height, int channels, ColorSpace *colorspace) +void IMB_colormanagement_scene_linear_to_colorspace(float *buffer, + const int width, + const int height, + const int channels, + const ColorSpace *colorspace) { if (colorspace == nullptr) { /* should never happen */ printf("%s: perform conversion from unknown color space\n", __func__); return; } - OCIO_ConstCPUProcessorRcPtr *processor = colorspace_from_scene_linear_cpu_processor(colorspace); + const ocio::CPUProcessor *processor = colorspace->get_from_scene_linear_cpu_processor(); if (processor == nullptr) { return; } - OCIO_PackedImageDesc *img = OCIO_createOCIO_PackedImageDesc(buffer, - width, - height, - channels, - sizeof(float), - size_t(channels) * sizeof(float), - size_t(channels) * sizeof(float) * - width); - OCIO_cpuProcessorApply(processor, img); - OCIO_PackedImageDescRelease(img); + const ocio::PackedImage img(buffer, + width, + height, + channels, + ocio::BitDepth::BIT_DEPTH_F32, + sizeof(float), + size_t(channels) * sizeof(float), + size_t(channels) * sizeof(float) * width); + processor->apply(img); } void IMB_colormanagement_imbuf_to_byte_texture(uchar *out_buffer, @@ -2437,10 +2112,10 @@ void IMB_colormanagement_imbuf_to_float_texture(float *out_buffer, const uchar *in_buffer = ibuf->byte_buffer.data; const bool use_premultiply = IMB_alpha_affects_rgb(ibuf) && store_premultiplied; - OCIO_ConstCPUProcessorRcPtr *processor = (ibuf->byte_buffer.colorspace) ? - colorspace_to_scene_linear_cpu_processor( - ibuf->byte_buffer.colorspace) : - nullptr; + const ocio::CPUProcessor *processor = + (ibuf->byte_buffer.colorspace) ? + ibuf->byte_buffer.colorspace->get_to_scene_linear_cpu_processor() : + nullptr; threading::parallel_for(IndexRange(height), 128, [&](const IndexRange y_range) { for (const int y : y_range) { @@ -2453,7 +2128,7 @@ void IMB_colormanagement_imbuf_to_float_texture(float *out_buffer, float pixel[4]; rgba_uchar_to_float(pixel, in); if (processor) { - OCIO_cpuProcessorApplyRGB(processor, pixel); + processor->apply_rgb(pixel); } else { srgb_to_linearrgb_v3_v3(pixel, pixel); @@ -2476,12 +2151,11 @@ void IMB_colormanagement_scene_linear_to_color_picking_v3(float color_picking[3] BLI_mutex_lock(&processor_lock); if (!global_color_picking_state.cpu_processor_to && !global_color_picking_state.failed) { - OCIO_ConstProcessorRcPtr *processor = create_colorspace_transform_processor( + std::shared_ptr cpu_processor = g_config->get_cpu_processor( global_role_scene_linear, global_role_color_picking); - if (processor != nullptr) { - global_color_picking_state.cpu_processor_to = OCIO_processorGetCPUProcessor(processor); - OCIO_processorRelease(processor); + if (cpu_processor) { + global_color_picking_state.cpu_processor_to = cpu_processor; } else { global_color_picking_state.failed = true; @@ -2494,7 +2168,7 @@ void IMB_colormanagement_scene_linear_to_color_picking_v3(float color_picking[3] copy_v3_v3(color_picking, scene_linear); if (global_color_picking_state.cpu_processor_to) { - OCIO_cpuProcessorApplyRGB(global_color_picking_state.cpu_processor_to, color_picking); + global_color_picking_state.cpu_processor_to->apply_rgb(color_picking); } } @@ -2506,12 +2180,11 @@ void IMB_colormanagement_color_picking_to_scene_linear_v3(float scene_linear[3], BLI_mutex_lock(&processor_lock); if (!global_color_picking_state.cpu_processor_from && !global_color_picking_state.failed) { - OCIO_ConstProcessorRcPtr *processor = create_colorspace_transform_processor( + std::shared_ptr cpu_processor = g_config->get_cpu_processor( global_role_color_picking, global_role_scene_linear); - if (processor != nullptr) { - global_color_picking_state.cpu_processor_from = OCIO_processorGetCPUProcessor(processor); - OCIO_processorRelease(processor); + if (cpu_processor) { + global_color_picking_state.cpu_processor_from = cpu_processor; } else { global_color_picking_state.failed = true; @@ -2524,25 +2197,25 @@ void IMB_colormanagement_color_picking_to_scene_linear_v3(float scene_linear[3], copy_v3_v3(scene_linear, color_picking); if (global_color_picking_state.cpu_processor_from) { - OCIO_cpuProcessorApplyRGB(global_color_picking_state.cpu_processor_from, scene_linear); + global_color_picking_state.cpu_processor_from->apply_rgb(scene_linear); } } -void IMB_colormanagement_scene_linear_to_display_v3(float pixel[3], ColorManagedDisplay *display) +void IMB_colormanagement_scene_linear_to_display_v3(float pixel[3], + const ColorManagedDisplay *display) { - OCIO_ConstCPUProcessorRcPtr *processor = display_from_scene_linear_processor(display); - + const ocio::CPUProcessor *processor = display->get_from_scene_linear_cpu_processor(); if (processor != nullptr) { - OCIO_cpuProcessorApplyRGB(processor, pixel); + processor->apply_rgb(pixel); } } -void IMB_colormanagement_display_to_scene_linear_v3(float pixel[3], ColorManagedDisplay *display) +void IMB_colormanagement_display_to_scene_linear_v3(float pixel[3], + const ColorManagedDisplay *display) { - OCIO_ConstCPUProcessorRcPtr *processor = display_to_scene_linear_processor(display); - + const ocio::CPUProcessor *processor = display->get_to_scene_linear_cpu_processor(); if (processor != nullptr) { - OCIO_cpuProcessorApplyRGB(processor, pixel); + processor->apply_rgb(pixel); } } @@ -2684,7 +2357,7 @@ ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf, /* Float buffer isn't linear anymore, * image format write callback should check for this flag and assume * no space conversion should happen if ibuf->float_buffer.colorspace != nullptr. */ - colormanaged_ibuf->float_buffer.colorspace = display_transform_get_colorspace( + colormanaged_ibuf->float_buffer.colorspace = get_display_colorspace( &image_format->view_settings, &image_format->display_settings); if (byte_output) { colormanaged_ibuf->byte_buffer.colorspace = colormanaged_ibuf->float_buffer.colorspace; @@ -2699,11 +2372,11 @@ ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf, !(byte_output && ibuf->byte_buffer.data)) ? /* From float buffer. */ (ibuf->float_buffer.colorspace) ? - ibuf->float_buffer.colorspace->name : + ibuf->float_buffer.colorspace->name().c_str() : global_role_scene_linear : /* From byte buffer. */ (ibuf->byte_buffer.colorspace) ? - ibuf->byte_buffer.colorspace->name : + ibuf->byte_buffer.colorspace->name().c_str() : global_role_default_byte; const char *to_colorspace = image_format->linear_colorspace_settings.name; @@ -2810,7 +2483,8 @@ uchar *IMB_display_buffer_acquire(ImBuf *ibuf, } } - colormanage_view_settings_to_cache(ibuf, &cache_view_settings, applied_view_settings); + colormanage_view_settings_to_cache( + ibuf, &cache_view_settings, applied_view_settings, display_settings); colormanage_display_settings_to_cache(&cache_display_settings, display_settings); if (ibuf->invalid_rect.xmin != ibuf->invalid_rect.xmax) { @@ -2836,14 +2510,14 @@ uchar *IMB_display_buffer_acquire(ImBuf *ibuf, /* ensure color management bit fields exists */ if (!ibuf->display_buffer_flags) { - ibuf->display_buffer_flags = MEM_calloc_arrayN(global_tot_display, + ibuf->display_buffer_flags = MEM_calloc_arrayN(g_config->get_num_displays(), "imbuf display_buffer_flags"); } else if (ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) { /* all display buffers were marked as invalid from other areas, * now propagate this flag to internal color management routines */ - memset(ibuf->display_buffer_flags, 0, global_tot_display * sizeof(uint)); + memset(ibuf->display_buffer_flags, 0, g_config->get_num_displays() * sizeof(uint)); ibuf->userflags &= ~IB_DISPLAY_BUFFER_INVALID; } @@ -2958,119 +2632,51 @@ void IMB_display_buffer_release(void *cache_handle) /** \name Display Functions * \{ */ -const char *colormanage_display_get_default_name() -{ - OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig(); - const char *display_name; - - display_name = OCIO_configGetDefaultDisplay(config); - - OCIO_configRelease(config); - - return display_name; -} - -ColorManagedDisplay *colormanage_display_get_default() -{ - const char *display_name = colormanage_display_get_default_name(); - - if (display_name[0] == '\0') { - return nullptr; - } - - return colormanage_display_get_named(display_name); -} - -ColorManagedDisplay *colormanage_display_add(const char *name) -{ - ColorManagedDisplay *display; - int index = 0; - - if (global_displays.last) { - ColorManagedDisplay *last_display = static_cast(global_displays.last); - - index = last_display->index; - } - - display = MEM_callocN("ColorManagedDisplay"); - - display->index = index + 1; - - STRNCPY(display->name, name); - - BLI_addtail(&global_displays, display); - - return display; -} - -ColorManagedDisplay *colormanage_display_get_named(const char *name) -{ - LISTBASE_FOREACH (ColorManagedDisplay *, display, &global_displays) { - if (STREQ(display->name, name)) { - return display; - } - } - - return nullptr; -} - -ColorManagedDisplay *colormanage_display_get_indexed(int index) -{ - /* display indices are 1-based */ - return static_cast(BLI_findlink(&global_displays, index - 1)); -} - int IMB_colormanagement_display_get_named_index(const char *name) { - ColorManagedDisplay *display; - - display = colormanage_display_get_named(name); - + const ocio::Display *display = g_config->get_display_by_name(name); if (display) { return display->index; } - - return 0; + return -1; } -const char *IMB_colormanagement_display_get_indexed_name(int index) +const char *IMB_colormanagement_display_get_indexed_name(const int index) { - ColorManagedDisplay *display; - - display = colormanage_display_get_indexed(index); - - if (display) { - return display->name; + const ocio::Display *display = g_config->get_display_by_index(index); + if (!display) { + return ""; } - - return nullptr; + return display->name().c_str(); } const char *IMB_colormanagement_display_get_default_name() { - ColorManagedDisplay *display = colormanage_display_get_default(); - - return display->name; + const ocio::Display *display = g_config->get_default_display(); + return display->name().c_str(); } -ColorManagedDisplay *IMB_colormanagement_display_get_named(const char *name) +const ColorManagedDisplay *IMB_colormanagement_display_get_named(const char *name) { - return colormanage_display_get_named(name); + return g_config->get_display_by_name(name); } const char *IMB_colormanagement_display_get_none_name() { - if (colormanage_display_get_named("None") != nullptr) { + if (g_config->get_display_by_name("None") != nullptr) { return "None"; } - - return colormanage_display_get_default_name(); + return IMB_colormanagement_display_get_default_name(); } const char *IMB_colormanagement_display_get_default_view_transform_name( - ColorManagedDisplay *display) + const ColorManagedDisplay *display) { - return colormanage_view_get_default_name(display); + const ocio::View *default_view = display->get_default_view(); + if (!default_view) { + return ""; + } + return default_view->name().c_str(); } /** \} */ @@ -3079,136 +2685,73 @@ const char *IMB_colormanagement_display_get_default_view_transform_name( /** \name View Functions * \{ */ -const char *colormanage_view_get_default_name(const ColorManagedDisplay *display) +int IMB_colormanagement_view_get_named_index(const char *display, const char *name) { - OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig(); - const char *name = OCIO_configGetDefaultView(config, display->name); - - OCIO_configRelease(config); - - return name; -} - -ColorManagedView *colormanage_view_get_default(const ColorManagedDisplay *display) -{ - const char *name = colormanage_view_get_default_name(display); - - if (!name || name[0] == '\0') { - return nullptr; + const ocio::Display *ocio_display = g_config->get_display_by_name(display); + if (!ocio_display) { + return -1; } - return colormanage_view_get_named(name); -} - -ColorManagedView *colormanage_view_add(const char *name) -{ - ColorManagedView *view; - int index = global_tot_view; - - view = MEM_callocN("ColorManagedView"); - view->index = index + 1; - STRNCPY(view->name, name); - - BLI_addtail(&global_views, view); - - global_tot_view++; - - return view; -} - -ColorManagedView *colormanage_view_get_named(const char *name) -{ - LISTBASE_FOREACH (ColorManagedView *, view, &global_views) { - if (STREQ(view->name, name)) { - return view; - } + const ocio::View *view = ocio_display->get_view_by_name(name); + if (!view) { + return -1; } - return nullptr; + return view->index; } -ColorManagedView *colormanage_view_get_indexed(int index) +const char *IMB_colormanagement_view_get_indexed_name(const char *display, const int index) { - /* view transform indices are 1-based */ - return static_cast(BLI_findlink(&global_views, index - 1)); -} - -ColorManagedView *colormanage_view_get_named_for_display(const char *display_name, - const char *name) -{ - ColorManagedDisplay *display = colormanage_display_get_named(display_name); - if (display == nullptr) { - return nullptr; - } - LISTBASE_FOREACH (LinkData *, view_link, &display->views) { - ColorManagedView *view = static_cast(view_link->data); - if (STRCASEEQ(name, view->name)) { - return view; - } - } - return nullptr; -} - -int IMB_colormanagement_view_get_named_index(const char *name) -{ - ColorManagedView *view = colormanage_view_get_named(name); - - if (view) { - return view->index; + const ocio::Display *ocio_display = g_config->get_display_by_name(display); + if (!ocio_display) { + return ""; } - return 0; -} - -const char *IMB_colormanagement_view_get_indexed_name(int index) -{ - ColorManagedView *view = colormanage_view_get_indexed(index); - - if (view) { - return view->name; + const ocio::View *view = ocio_display->get_view_by_index(index); + if (!view) { + return ""; } - return nullptr; + return view->name().c_str(); } const char *IMB_colormanagement_view_get_default_name(const char *display_name) { - ColorManagedDisplay *display = colormanage_display_get_named(display_name); - ColorManagedView *view = nullptr; - - if (display) { - view = colormanage_view_get_default(display); + const ocio::Display *display = g_config->get_display_by_name(display_name); + if (!display) { + return ""; } - if (view) { - return view->name; + const ocio::View *view = display->get_default_view(); + if (!view) { + return ""; } - return nullptr; + return view->name().c_str(); } const char *IMB_colormanagement_view_get_raw_or_default_name(const char *display_name) { - ColorManagedDisplay *display = colormanage_display_get_named(display_name); + const ocio::Display *display = g_config->get_display_by_name(display_name); if (!display) { - return nullptr; + return ""; } - ColorManagedView *view = nullptr; + const ocio::View *view = nullptr; if (!view) { - view = colormanage_view_get_named_for_display(display_name, "Raw"); + view = display->get_view_by_name("Raw"); } if (!view) { - view = colormanage_view_get_default(display); + view = display->get_default_view(); } if (!view) { - return nullptr; + return ""; } - return view->name; + return view->name().c_str(); } /** \} */ @@ -3217,138 +2760,48 @@ const char *IMB_colormanagement_view_get_raw_or_default_name(const char *display /** \name Color Space Functions * \{ */ -static void colormanage_description_strip(char *description) +const ColorSpace *colormanage_colorspace_get_named(const char *name) { - int i, n; - - for (i = int(strlen(description)) - 1; i >= 0; i--) { - if (ELEM(description[i], '\r', '\n')) { - description[i] = '\0'; - } - else { - break; - } - } - - for (i = 0, n = strlen(description); i < n; i++) { - if (ELEM(description[i], '\r', '\n')) { - description[i] = ' '; - } - } + return g_config->get_color_space(name); } -ColorSpace *colormanage_colorspace_add(const char *name, - const char *description, - bool is_invertible, - bool is_data) -{ - ColorSpace *colorspace, *prev_space; - int counter = 1; - - colorspace = MEM_callocN("ColorSpace"); - - STRNCPY(colorspace->name, name); - - if (description) { - STRNCPY(colorspace->description, description); - - colormanage_description_strip(colorspace->description); - } - - colorspace->is_invertible = is_invertible; - colorspace->is_data = is_data; - - for (prev_space = static_cast(global_colorspaces.first); prev_space; - prev_space = prev_space->next) - { - if (BLI_strcasecmp(prev_space->name, colorspace->name) > 0) { - break; - } - - prev_space->index = counter++; - } - - if (!prev_space) { - BLI_addtail(&global_colorspaces, colorspace); - } - else { - BLI_insertlinkbefore(&global_colorspaces, prev_space, colorspace); - } - - colorspace->index = counter++; - for (; prev_space; prev_space = prev_space->next) { - prev_space->index = counter++; - } - - global_tot_colorspace++; - - return colorspace; -} - -ColorSpace *colormanage_colorspace_get_named(const char *name) -{ - LISTBASE_FOREACH (ColorSpace *, colorspace, &global_colorspaces) { - if (STREQ(colorspace->name, name)) { - return colorspace; - } - - for (int i = 0; i < colorspace->num_aliases; i++) { - if (STREQ(colorspace->aliases[i], name)) { - return colorspace; - } - } - } - - return nullptr; -} - -ColorSpace *colormanage_colorspace_get_roled(int role) +const ColorSpace *colormanage_colorspace_get_roled(const int role) { const char *role_colorspace = IMB_colormanagement_role_colorspace_name_get(role); return colormanage_colorspace_get_named(role_colorspace); } -ColorSpace *colormanage_colorspace_get_indexed(int index) -{ - /* color space indices are 1-based */ - return static_cast(BLI_findlink(&global_colorspaces, index - 1)); -} - int IMB_colormanagement_colorspace_get_named_index(const char *name) { - ColorSpace *colorspace = colormanage_colorspace_get_named(name); - + const ColorSpace *colorspace = colormanage_colorspace_get_named(name); if (colorspace) { return colorspace->index; } - - return 0; + return -1; } -const char *IMB_colormanagement_colorspace_get_indexed_name(int index) +const char *IMB_colormanagement_colorspace_get_indexed_name(const int index) { - ColorSpace *colorspace = colormanage_colorspace_get_indexed(index); - + const ColorSpace *colorspace = g_config->get_color_space_by_index(index); if (colorspace) { - return colorspace->name; + return colorspace->name().c_str(); } - return ""; } const char *IMB_colormanagement_colorspace_get_name(const ColorSpace *colorspace) { - return colorspace->name; + return colorspace->name().c_str(); } void IMB_colormanagement_colorspace_from_ibuf_ftype( ColorManagedColorspaceSettings *colorspace_settings, ImBuf *ibuf) { /* Don't modify non-color data space, it does not change with file type. */ - ColorSpace *colorspace = colormanage_colorspace_get_named(colorspace_settings->name); + const ColorSpace *colorspace = g_config->get_color_space(colorspace_settings->name); - if (colorspace && colorspace->is_data) { + if (colorspace && colorspace->is_data()) { return; } @@ -3369,103 +2822,54 @@ void IMB_colormanagement_colorspace_from_ibuf_ftype( /** \name Looks Functions * \{ */ -ColorManagedLook *colormanage_look_add(const char *name, const char *process_space, bool is_noop) -{ - ColorManagedLook *look; - int index = global_tot_looks; - - look = MEM_callocN("ColorManagedLook"); - look->index = index + 1; - STRNCPY(look->name, name); - STRNCPY(look->ui_name, name); - STRNCPY(look->process_space, process_space); - look->is_noop = is_noop; - - /* Detect view specific looks. */ - const char *separator_offset = strstr(look->name, " - "); - if (separator_offset) { - BLI_strncpy(look->view, look->name, separator_offset - look->name + 1); - STRNCPY(look->ui_name, separator_offset + strlen(" - ")); - } - - BLI_addtail(&global_looks, look); - - global_tot_looks++; - - return look; -} - -ColorManagedLook *colormanage_look_get_named(const char *name) -{ - LISTBASE_FOREACH (ColorManagedLook *, look, &global_looks) { - if (STREQ(look->name, name)) { - return look; - } - } - - return nullptr; -} - -ColorManagedLook *colormanage_look_get_indexed(int index) -{ - /* look indices are 1-based */ - return static_cast(BLI_findlink(&global_looks, index - 1)); -} - int IMB_colormanagement_look_get_named_index(const char *name) { - ColorManagedLook *look = colormanage_look_get_named(name); - + const ocio::Look *look = g_config->get_look_by_name(name); if (look) { return look->index; } - - return 0; + return -1; } -const char *IMB_colormanagement_look_get_indexed_name(int index) +const char *IMB_colormanagement_look_get_indexed_name(const int index) { - ColorManagedLook *look; - - look = colormanage_look_get_indexed(index); - - if (look) { - return look->name; + const ocio::Look *look = g_config->get_look_by_index(index); + if (!look) { + return ""; } - - return nullptr; + return look->name().c_str(); } const char *IMB_colormanagement_look_get_default_name() { - const ColorManagedLook *default_look = static_cast(global_looks.first); - if (!default_look) { + const ocio::Look *look = g_config->get_look_by_index(0); + if (!look) { return ""; } - - return default_look->name; + return look->name().c_str(); } const char *IMB_colormanagement_look_validate_for_view(const char *view_name, const char *look_name) { - ColorManagedLook *look_descr = colormanage_look_get_named(look_name); - if (!look_descr) { + const ocio::Look *look = g_config->get_look_by_name(look_name); + if (!look) { return look_name; } /* Keep same look if compatible. */ - if (colormanage_compatible_look(look_descr, view_name)) { + if (colormanage_compatible_look(look, view_name)) { return look_name; } - /* Try to find another compatible look with the same UI name, in case - * of looks specialized for view transform, */ - LISTBASE_FOREACH (ColorManagedLook *, other_look, &global_looks) { - if (STREQ(look_descr->ui_name, other_look->ui_name) && + /* Try to find another compatible look with the same UI name, in case of looks specialized for + * view transform, */ + for (const int other_look_index : blender::IndexRange(g_config->get_num_looks())) { + const ocio::Look *other_look = g_config->get_look_by_index(other_look_index); + if (look->ui_name() == other_look->ui_name() && colormanage_compatible_look(other_look, view_name)) { - return other_look->name; + return other_look->name().c_str(); } } @@ -3480,12 +2884,14 @@ const char *IMB_colormanagement_look_validate_for_view(const char *view_name, void IMB_colormanagement_display_items_add(EnumPropertyItem **items, int *totitem) { - LISTBASE_FOREACH (ColorManagedDisplay *, display, &global_displays) { + for (const int display_index : blender::IndexRange(g_config->get_num_displays())) { + const ocio::Display *display = g_config->get_display_by_index(display_index); + EnumPropertyItem item; item.value = display->index; - item.name = display->name; - item.identifier = display->name; + item.name = display->name().c_str(); + item.identifier = display->name().c_str(); item.icon = 0; item.description = ""; @@ -3493,33 +2899,27 @@ void IMB_colormanagement_display_items_add(EnumPropertyItem **items, int *totite } } -static void colormanagement_view_item_add(EnumPropertyItem **items, - int *totitem, - ColorManagedView *view) -{ - EnumPropertyItem item; - - item.value = view->index; - item.name = view->name; - item.identifier = view->name; - item.icon = 0; - item.description = ""; - - RNA_enum_item_add(items, totitem, &item); -} - void IMB_colormanagement_view_items_add(EnumPropertyItem **items, int *totitem, const char *display_name) { - ColorManagedDisplay *display = colormanage_display_get_named(display_name); + const ocio::Display *display = g_config->get_display_by_name(display_name); + if (!display) { + return; + } - if (display) { - LISTBASE_FOREACH (LinkData *, display_view, &display->views) { - ColorManagedView *view = static_cast(display_view->data); + for (const int view_index : blender::IndexRange(display->get_num_views())) { + const ocio::View *view = display->get_view_by_index(view_index); - colormanagement_view_item_add(items, totitem, view); - } + EnumPropertyItem item; + + item.value = view->index; + item.name = view->name().c_str(); + item.identifier = view->name().c_str(); + item.icon = 0; + item.description = ""; + + RNA_enum_item_add(items, totitem, &item); } } @@ -3529,7 +2929,8 @@ void IMB_colormanagement_look_items_add(EnumPropertyItem **items, { const bool has_explicit_look = has_explicit_look_for_view(view_name); - LISTBASE_FOREACH (ColorManagedLook *, look, &global_looks) { + for (const int look_index : blender::IndexRange(g_config->get_num_looks())) { + const ocio::Look *look = g_config->get_look_by_index(look_index); if (!colormanage_compatible_look(look, view_name, has_explicit_look)) { continue; } @@ -3537,8 +2938,8 @@ void IMB_colormanagement_look_items_add(EnumPropertyItem **items, EnumPropertyItem item; item.value = look->index; - item.name = look->ui_name; - item.identifier = look->name; + item.name = look->ui_name().c_str(); + item.identifier = look->name().c_str(); item.icon = 0; item.description = ""; @@ -3548,18 +2949,19 @@ void IMB_colormanagement_look_items_add(EnumPropertyItem **items, void IMB_colormanagement_colorspace_items_add(EnumPropertyItem **items, int *totitem) { - LISTBASE_FOREACH (ColorSpace *, colorspace, &global_colorspaces) { - EnumPropertyItem item; - - if (!colorspace->is_invertible) { + for (const int colorspace_index : blender::IndexRange(g_config->get_num_color_spaces())) { + const ColorSpace *colorspace = g_config->get_sorted_color_space_by_index(colorspace_index); + if (!colorspace->is_invertible()) { continue; } + EnumPropertyItem item; + item.value = colorspace->index; - item.name = colorspace->name; - item.identifier = colorspace->name; + item.name = colorspace->name().c_str(); + item.identifier = colorspace->name().c_str(); item.icon = 0; - item.description = colorspace->description; + item.description = colorspace->description().c_str(); RNA_enum_item_add(items, totitem, &item); } @@ -3599,7 +3001,7 @@ static void partial_buffer_update_rect(ImBuf *ibuf, int x, y; int channels = ibuf->channels; float dither = ibuf->dither; - ColorSpace *rect_colorspace = ibuf->byte_buffer.colorspace; + const ColorSpace *rect_colorspace = ibuf->byte_buffer.colorspace; float *display_buffer_float = nullptr; const int width = xmax - xmin; const int height = ymax - ymin; @@ -3758,11 +3160,12 @@ static void imb_partial_display_buffer_update_ex( if (ibuf->display_buffer_flags) { int view_flag, display_index; - colormanage_view_settings_to_cache(ibuf, &cache_view_settings, view_settings); + colormanage_view_settings_to_cache( + ibuf, &cache_view_settings, view_settings, display_settings); colormanage_display_settings_to_cache(&cache_display_settings, display_settings); - view_flag = 1 << (cache_view_settings.view - 1); - display_index = cache_display_settings.display - 1; + view_flag = 1 << cache_view_settings.view; + display_index = cache_display_settings.display; BLI_thread_lock(LOCK_COLORMANAGE); @@ -3778,7 +3181,7 @@ static void imb_partial_display_buffer_update_ex( buffer_width = ibuf->x; /* Mark all other buffers as invalid. */ - memset(ibuf->display_buffer_flags, 0, global_tot_display * sizeof(uint)); + memset(ibuf->display_buffer_flags, 0, g_config->get_num_displays() * sizeof(uint)); ibuf->display_buffer_flags[display_index] |= view_flag; BLI_thread_unlock(LOCK_COLORMANAGE); @@ -3917,9 +3320,9 @@ ColormanageProcessor *IMB_colormanagement_display_processor_new( ColormanageProcessor *cm_processor; ColorManagedViewSettings default_view_settings; const ColorManagedViewSettings *applied_view_settings; - ColorSpace *display_space; + const ColorSpace *display_space; - cm_processor = MEM_callocN("colormanagement processor"); + cm_processor = MEM_new("colormanagement processor"); if (view_settings) { applied_view_settings = view_settings; @@ -3929,22 +3332,21 @@ ColormanageProcessor *IMB_colormanagement_display_processor_new( applied_view_settings = &default_view_settings; } - display_space = display_transform_get_colorspace(applied_view_settings, display_settings); + display_space = get_display_colorspace(applied_view_settings, display_settings); if (display_space) { - cm_processor->is_data_result = display_space->is_data; + cm_processor->is_data_result = display_space->is_data(); } const bool use_white_balance = applied_view_settings->flag & COLORMANAGE_VIEW_USE_WHITE_BALANCE; - cm_processor->cpu_processor = create_display_buffer_processor( - applied_view_settings->look, - applied_view_settings->view_transform, - display_settings->display_device, - applied_view_settings->exposure, - applied_view_settings->gamma, - applied_view_settings->temperature, - applied_view_settings->tint, - use_white_balance, - global_role_scene_linear); + cm_processor->cpu_processor = get_display_buffer_processor(applied_view_settings->look, + applied_view_settings->view_transform, + display_settings->display_device, + applied_view_settings->exposure, + applied_view_settings->gamma, + applied_view_settings->temperature, + applied_view_settings->tint, + use_white_balance, + global_role_scene_linear); if (applied_view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) { cm_processor->curve_mapping = BKE_curvemapping_copy(applied_view_settings->curve_mapping); @@ -3959,15 +3361,10 @@ ColormanageProcessor *IMB_colormanagement_colorspace_processor_new(const char *f { ColormanageProcessor *cm_processor; - cm_processor = MEM_callocN("colormanagement processor"); + cm_processor = MEM_new("colormanagement processor"); cm_processor->is_data_result = IMB_colormanagement_space_name_is_data(to_colorspace); - OCIO_ConstProcessorRcPtr *processor = create_colorspace_transform_processor(from_colorspace, - to_colorspace); - if (processor != nullptr) { - cm_processor->cpu_processor = OCIO_processorGetCPUProcessor(processor); - } - OCIO_processorRelease(processor); + cm_processor->cpu_processor = g_config->get_cpu_processor(from_colorspace, to_colorspace); return cm_processor; } @@ -3976,8 +3373,8 @@ bool IMB_colormanagement_processor_is_noop(ColormanageProcessor *cm_processor) { if (cm_processor->curve_mapping) { /* Consider processor which has curve mapping as a non no-op. - * This is mainly for the simplicity of the check, since the current cases where this function - * is used the curve mapping is never assigned. */ + * This is mainly for the simplicity of the check, since the current cases where this + * function is used the curve mapping is never assigned. */ return false; } @@ -3986,15 +3383,15 @@ bool IMB_colormanagement_processor_is_noop(ColormanageProcessor *cm_processor) * space does not exist in the configuration, or if there is a missing lookup table, or the * configuration is invalid due to other reasons. * - * The actual processing checks for the cpu_processor not being null pointer, and it if is then - * processing does not apply it. However, processing could still apply curve mapping. + * The actual processing checks for the cpu_processor not being null pointer, and it if is + * then processing does not apply it. However, processing could still apply curve mapping. * - * Hence a null-pointer here, which happens after the curve mapping check, but before accessing - * cpu_processor. */ + * Hence a null-pointer here, which happens after the curve mapping check, but before + * accessing cpu_processor. */ return true; } - return OCIO_cpuProcessorIsNoOp(cm_processor->cpu_processor); + return cm_processor->cpu_processor->is_noop(); } void IMB_colormanagement_processor_apply_v4(ColormanageProcessor *cm_processor, float pixel[4]) @@ -4004,7 +3401,7 @@ void IMB_colormanagement_processor_apply_v4(ColormanageProcessor *cm_processor, } if (cm_processor->cpu_processor) { - OCIO_cpuProcessorApplyRGBA(cm_processor->cpu_processor, pixel); + cm_processor->cpu_processor->apply_rgba(pixel); } } @@ -4016,7 +3413,8 @@ void IMB_colormanagement_processor_apply_v4_predivide(ColormanageProcessor *cm_p } if (cm_processor->cpu_processor) { - OCIO_cpuProcessorApplyRGBA_predivide(cm_processor->cpu_processor, pixel); + cm_processor->cpu_processor->apply_rgba_predivide(pixel); + ; } } @@ -4027,7 +3425,7 @@ void IMB_colormanagement_processor_apply_v3(ColormanageProcessor *cm_processor, } if (cm_processor->cpu_processor) { - OCIO_cpuProcessorApplyRGB(cm_processor->cpu_processor, pixel); + cm_processor->cpu_processor->apply_rgb(pixel); } } @@ -4073,25 +3471,22 @@ void IMB_colormanagement_processor_apply(ColormanageProcessor *cm_processor, } if (cm_processor->cpu_processor && channels >= 3) { - OCIO_PackedImageDesc *img; - /* apply OCIO processor */ - img = OCIO_createOCIO_PackedImageDesc(buffer, - width, - height, - channels, - sizeof(float), - size_t(channels) * sizeof(float), - size_t(channels) * sizeof(float) * width); + const ocio::PackedImage img(buffer, + width, + height, + channels, + ocio::BitDepth::BIT_DEPTH_F32, + sizeof(float), + size_t(channels) * sizeof(float), + size_t(channels) * sizeof(float) * width); if (predivide) { - OCIO_cpuProcessorApply_predivide(cm_processor->cpu_processor, img); + cm_processor->cpu_processor->apply_predivide(img); } else { - OCIO_cpuProcessorApply(cm_processor->cpu_processor, img); + cm_processor->cpu_processor->apply(img); } - - OCIO_PackedImageDescRelease(img); } } @@ -4118,50 +3513,13 @@ void IMB_colormanagement_processor_free(ColormanageProcessor *cm_processor) if (cm_processor->curve_mapping) { BKE_curvemapping_free(cm_processor->curve_mapping); } - if (cm_processor->cpu_processor) { - OCIO_cpuProcessorRelease(cm_processor->cpu_processor); - } - MEM_freeN(cm_processor); + MEM_delete(cm_processor); } /* **** OpenGL drawing routines using GLSL for color space transform ***** */ -static void curve_mapping_to_ocio_settings(CurveMapping *curve_mapping, - OCIO_CurveMappingSettings *curve_mapping_settings) -{ - int i; - - BKE_curvemapping_init(curve_mapping); - BKE_curvemapping_premultiply(curve_mapping, false); - BKE_curvemapping_table_RGBA( - curve_mapping, &curve_mapping_settings->lut, &curve_mapping_settings->lut_size); - - curve_mapping_settings->use_extend_extrapolate = (curve_mapping->flag & - CUMA_EXTEND_EXTRAPOLATE) != 0; - - for (i = 0; i < 4; i++) { - CurveMap *cuma = curve_mapping->cm + i; - curve_mapping_settings->range[i] = cuma->range; - curve_mapping_settings->mintable[i] = cuma->mintable; - curve_mapping_settings->ext_in_x[i] = cuma->ext_in[0]; - curve_mapping_settings->ext_in_y[i] = cuma->ext_in[1]; - curve_mapping_settings->ext_out_x[i] = cuma->ext_out[0]; - curve_mapping_settings->ext_out_y[i] = cuma->ext_out[1]; - curve_mapping_settings->first_x[i] = cuma->table[0].x; - curve_mapping_settings->first_y[i] = cuma->table[0].y; - curve_mapping_settings->last_x[i] = cuma->table[CM_TABLE].x; - curve_mapping_settings->last_y[i] = cuma->table[CM_TABLE].y; - } - - copy_v3_v3(curve_mapping_settings->black, curve_mapping->black); - copy_v3_v3(curve_mapping_settings->bwmul, curve_mapping->bwmul); - - curve_mapping_settings->cache_id = size_t(curve_mapping) + curve_mapping->changed_timestamp; -} - -static OCIO_CurveMappingSettings *update_glsl_curve_mapping( - const ColorManagedViewSettings *view_settings) +static CurveMapping *update_glsl_curve_mapping(const ColorManagedViewSettings *view_settings) { /* Using curve mapping? */ const bool use_curve_mapping = (view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) != 0; @@ -4170,57 +3528,36 @@ static OCIO_CurveMappingSettings *update_glsl_curve_mapping( } /* Already up to date? */ - OCIO_CurveMappingSettings *curve_mapping_settings = &global_gpu_state.curve_mapping_settings; if (view_settings->curve_mapping->changed_timestamp == global_gpu_state.curve_mapping_timestamp && view_settings->curve_mapping == global_gpu_state.orig_curve_mapping) { - return curve_mapping_settings; + return view_settings->curve_mapping; } - /* Need to update. */ - CurveMapping *new_curve_mapping = nullptr; - - /* We're using curve mapping's address as a cache ID, - * so we need to make sure re-allocation gives new address here. - * We do this by allocating new curve mapping before freeing old one. */ - if (use_curve_mapping) { - new_curve_mapping = BKE_curvemapping_copy(view_settings->curve_mapping); - } + /* We're using curve mapping's address as a cache ID, so we need to make sure re-allocation + * gives new address here. We do this by allocating new curve mapping before freeing old one. + */ + CurveMapping *new_curve_mapping = BKE_curvemapping_copy(view_settings->curve_mapping); if (global_gpu_state.curve_mapping) { BKE_curvemapping_free(global_gpu_state.curve_mapping); - MEM_freeN(curve_mapping_settings->lut); global_gpu_state.curve_mapping = nullptr; - curve_mapping_settings->lut = nullptr; } /* Fill in OCIO's curve mapping settings. */ - if (use_curve_mapping) { - curve_mapping_to_ocio_settings(new_curve_mapping, &global_gpu_state.curve_mapping_settings); + global_gpu_state.curve_mapping = new_curve_mapping; + global_gpu_state.curve_mapping_timestamp = view_settings->curve_mapping->changed_timestamp; + global_gpu_state.orig_curve_mapping = view_settings->curve_mapping; + global_gpu_state.use_curve_mapping = true; - global_gpu_state.curve_mapping = new_curve_mapping; - global_gpu_state.curve_mapping_timestamp = view_settings->curve_mapping->changed_timestamp; - global_gpu_state.orig_curve_mapping = view_settings->curve_mapping; - global_gpu_state.use_curve_mapping = true; - } - else { - global_gpu_state.orig_curve_mapping = nullptr; - global_gpu_state.use_curve_mapping = false; - } - - return curve_mapping_settings; -} - -bool IMB_colormanagement_support_glsl_draw(const ColorManagedViewSettings * /*view_settings*/) -{ - return OCIO_supportGPUShader(); + return global_gpu_state.curve_mapping; } bool IMB_colormanagement_setup_glsl_draw_from_space( const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, - ColorSpace *from_colorspace, + const ColorSpace *from_colorspace, float dither, bool predivide, bool do_overlay_merge) @@ -4232,54 +3569,44 @@ bool IMB_colormanagement_setup_glsl_draw_from_space( applied_view_settings = view_settings; } else { - /* If no view settings were specified, use default ones, which will - * attempt not to do any extra color correction. */ + /* If no view settings were specified, use default ones, which will attempt not to do any + * extra color correction. */ IMB_colormanagement_init_default_view_settings(&default_view_settings, display_settings); applied_view_settings = &default_view_settings; } - /* Ensure curve mapping is up to data. */ - OCIO_CurveMappingSettings *curve_mapping_settings = update_glsl_curve_mapping( - applied_view_settings); + /* Ensure curve mapping is up to date. */ + CurveMapping *applied_curve_mapping = update_glsl_curve_mapping(applied_view_settings); /* GPU shader parameters. */ - const char *input = from_colorspace ? from_colorspace->name : global_role_scene_linear; - const char *view = applied_view_settings->view_transform; - const char *display = display_settings->display_device; const bool use_look = colormanage_use_look(applied_view_settings->look, applied_view_settings->view_transform); - const char *look = (use_look) ? applied_view_settings->look : ""; const float exposure = applied_view_settings->exposure; const float gamma = applied_view_settings->gamma; - const float scale = (exposure == 0.0f) ? 1.0f : powf(2.0f, exposure); - const float exponent = (gamma == 1.0f) ? 1.0f : 1.0f / max_ff(FLT_EPSILON, gamma); - const float temperature = applied_view_settings->temperature; - const float tint = applied_view_settings->tint; - const bool use_white_balance = (applied_view_settings->flag & - COLORMANAGE_VIEW_USE_WHITE_BALANCE) != 0; - const bool use_hdr = GPU_hdr_support() && - (applied_view_settings->flag & COLORMANAGE_VIEW_USE_HDR) != 0; - OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig(); + /* TODO)sergey): Use designated initializer. */ + ocio::GPUDisplayParameters display_parameters; + display_parameters.from_colorspace = from_colorspace ? from_colorspace->name().c_str() : + global_role_scene_linear; + display_parameters.view = applied_view_settings->view_transform; + display_parameters.display = display_settings->display_device; + display_parameters.look = (use_look) ? applied_view_settings->look : ""; + display_parameters.curve_mapping = applied_curve_mapping; + display_parameters.scale = (exposure == 0.0f) ? 1.0f : powf(2.0f, exposure); + display_parameters.exponent = (gamma == 1.0f) ? 1.0f : 1.0f / max_ff(FLT_EPSILON, gamma); + display_parameters.dither = dither; + display_parameters.temperature = applied_view_settings->temperature; + display_parameters.tint = applied_view_settings->tint; + display_parameters.use_white_balance = (applied_view_settings->flag & + COLORMANAGE_VIEW_USE_WHITE_BALANCE) != 0; + display_parameters.use_predivide = predivide; + display_parameters.do_overlay_merge = do_overlay_merge; + display_parameters.use_hdr = GPU_hdr_support() && + (applied_view_settings->flag & COLORMANAGE_VIEW_USE_HDR) != 0; /* Bind shader. Internally GPU shaders are created and cached on demand. */ - global_gpu_state.gpu_shader_bound = OCIO_gpuDisplayShaderBind(config, - input, - view, - display, - look, - curve_mapping_settings, - scale, - exponent, - dither, - temperature, - tint, - predivide, - do_overlay_merge, - use_hdr, - use_white_balance); - - OCIO_configRelease(config); + global_gpu_state.gpu_shader_bound = g_config->get_gpu_shader_binder().display_bind( + display_parameters); return global_gpu_state.gpu_shader_bound; } @@ -4294,7 +3621,7 @@ bool IMB_colormanagement_setup_glsl_draw(const ColorManagedViewSettings *view_se } bool IMB_colormanagement_setup_glsl_draw_from_space_ctx(const bContext *C, - ColorSpace *from_colorspace, + const ColorSpace *from_colorspace, float dither, bool predivide) { @@ -4315,20 +3642,15 @@ bool IMB_colormanagement_setup_glsl_draw_ctx(const bContext *C, float dither, bo bool IMB_colormanagement_setup_glsl_draw_to_scene_linear(const char *from_colorspace_name, const bool predivide) { - OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig(); - - global_gpu_state.gpu_shader_bound = OCIO_gpuToSceneLinearShaderBind( - config, from_colorspace_name, predivide); - - OCIO_configRelease(config); - + global_gpu_state.gpu_shader_bound = g_config->get_gpu_shader_binder().to_scene_linear_bind( + from_colorspace_name, predivide); return global_gpu_state.gpu_shader_bound; } void IMB_colormanagement_finish_glsl_draw() { if (global_gpu_state.gpu_shader_bound) { - OCIO_gpuShaderUnbind(); + g_config->get_gpu_shader_binder().unbind(); global_gpu_state.gpu_shader_bound = false; } } diff --git a/source/blender/imbuf/intern/colormanagement_inline.h b/source/blender/imbuf/intern/colormanagement_inline.h index ff3d1746e2f..a663b18e31e 100644 --- a/source/blender/imbuf/intern/colormanagement_inline.h +++ b/source/blender/imbuf/intern/colormanagement_inline.h @@ -37,44 +37,44 @@ uchar IMB_colormanagement_get_luminance_byte(const uchar rgb[3]) void IMB_colormanagement_xyz_to_scene_linear(float scene_linear[3], const float xyz[3]) { - mul_v3_m3v3(scene_linear, imbuf_xyz_to_scene_linear, xyz); + mul_v3_m3v3(scene_linear, imbuf_xyz_to_scene_linear.ptr(), xyz); } void IMB_colormanagement_scene_linear_to_xyz(float xyz[3], const float scene_linear[3]) { - mul_v3_m3v3(xyz, imbuf_scene_linear_to_xyz, scene_linear); + mul_v3_m3v3(xyz, imbuf_scene_linear_to_xyz.ptr(), scene_linear); } void IMB_colormanagement_rec709_to_scene_linear(float scene_linear[3], const float rec709[3]) { - mul_v3_m3v3(scene_linear, imbuf_rec709_to_scene_linear, rec709); + mul_v3_m3v3(scene_linear, imbuf_rec709_to_scene_linear.ptr(), rec709); } void IMB_colormanagement_scene_linear_to_rec709(float rec709[3], const float scene_linear[3]) { - mul_v3_m3v3(rec709, imbuf_scene_linear_to_rec709, scene_linear); + mul_v3_m3v3(rec709, imbuf_scene_linear_to_rec709.ptr(), scene_linear); } void IMB_colormanagement_scene_linear_to_srgb_v3(float srgb[3], const float scene_linear[3]) { - mul_v3_m3v3(srgb, imbuf_scene_linear_to_rec709, scene_linear); + mul_v3_m3v3(srgb, imbuf_scene_linear_to_rec709.ptr(), scene_linear); linearrgb_to_srgb_v3_v3(srgb, srgb); } void IMB_colormanagement_srgb_to_scene_linear_v3(float scene_linear[3], const float srgb[3]) { srgb_to_linearrgb_v3_v3(scene_linear, srgb); - mul_m3_v3(imbuf_rec709_to_scene_linear, scene_linear); + mul_m3_v3(imbuf_rec709_to_scene_linear.ptr(), scene_linear); } void IMB_colormanagement_aces_to_scene_linear(float scene_linear[3], const float aces[3]) { - mul_v3_m3v3(scene_linear, imbuf_aces_to_scene_linear, aces); + mul_v3_m3v3(scene_linear, imbuf_aces_to_scene_linear.ptr(), aces); } void IMB_colormanagement_scene_linear_to_aces(float aces[3], const float scene_linear[3]) { - mul_v3_m3v3(aces, imbuf_scene_linear_to_aces, scene_linear); + mul_v3_m3v3(aces, imbuf_scene_linear_to_aces.ptr(), scene_linear); } #endif /* __IMB_COLORMANAGEMENT_INLINE_H__ */ diff --git a/source/blender/imbuf/intern/conversion.cc b/source/blender/imbuf/intern/conversion.cc index b50454b878d..c0b24bc98aa 100644 --- a/source/blender/imbuf/intern/conversion.cc +++ b/source/blender/imbuf/intern/conversion.cc @@ -19,6 +19,8 @@ #include "MEM_guardedalloc.h" +#include "OCIO_colorspace.hh" + /* -------------------------------------------------------------------- */ /** \name Generic Buffer Conversion @@ -616,11 +618,11 @@ void IMB_byte_from_float(ImBuf *ibuf) const char *from_colorspace = (ibuf->float_buffer.colorspace == nullptr) ? IMB_colormanagement_role_colorspace_name_get( COLOR_ROLE_SCENE_LINEAR) : - ibuf->float_buffer.colorspace->name; + ibuf->float_buffer.colorspace->name().c_str(); const char *to_colorspace = (ibuf->byte_buffer.colorspace == nullptr) ? IMB_colormanagement_role_colorspace_name_get( COLOR_ROLE_DEFAULT_BYTE) : - ibuf->byte_buffer.colorspace->name; + ibuf->byte_buffer.colorspace->name().c_str(); float *buffer = static_cast(MEM_dupallocN(ibuf->float_buffer.data)); diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index 68294b5fa8e..057ef52bffe 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -513,9 +513,9 @@ static void openexr_header_metadata(Header *header, ImBuf *ibuf) } /* Write chromaticities for ACES-2065-1, as required by ACES container format. */ - ColorSpace *colorspace = (ibuf->float_buffer.data) ? ibuf->float_buffer.colorspace : - (ibuf->byte_buffer.data) ? ibuf->byte_buffer.colorspace : - nullptr; + const ColorSpace *colorspace = (ibuf->float_buffer.data) ? ibuf->float_buffer.colorspace : + (ibuf->byte_buffer.data) ? ibuf->byte_buffer.colorspace : + nullptr; if (colorspace) { const char *aces_colorspace = IMB_colormanagement_role_colorspace_name_get( COLOR_ROLE_ACES_INTERCHANGE); diff --git a/source/blender/imbuf/intern/rectop.cc b/source/blender/imbuf/intern/rectop.cc index d7737fc2efe..6acb3f9e910 100644 --- a/source/blender/imbuf/intern/rectop.cc +++ b/source/blender/imbuf/intern/rectop.cc @@ -1060,7 +1060,7 @@ void buf_rectfill_area(uchar *rect, int width, int height, const float col[4], - ColorManagedDisplay *display, + const ColorManagedDisplay *display, int x1, int y1, int x2, @@ -1169,8 +1169,13 @@ void buf_rectfill_area(uchar *rect, } } -void IMB_rectfill_area( - ImBuf *ibuf, const float col[4], int x1, int y1, int x2, int y2, ColorManagedDisplay *display) +void IMB_rectfill_area(ImBuf *ibuf, + const float col[4], + int x1, + int y1, + int x2, + int y2, + const ColorManagedDisplay *display) { if (!ibuf) { return; diff --git a/source/blender/imbuf/opencolorio/CMakeLists.txt b/source/blender/imbuf/opencolorio/CMakeLists.txt new file mode 100644 index 00000000000..fb437563336 --- /dev/null +++ b/source/blender/imbuf/opencolorio/CMakeLists.txt @@ -0,0 +1,135 @@ +# SPDX-FileCopyrightText: 2025 Blender Authors +# +# SPDX-License-Identifier: GPL-2.0-or-later + +set(INC + . + ../../gpu/intern +) + +set(INC_SYS +) + +set(SRC + intern/config.cc + intern/cpu_processor_cache.hh + intern/description.cc + intern/description.hh + intern/gpu_shader_binder.cc + intern/ocio_shader_shared.hh + intern/opencolorio.hh + intern/source_processor.cc + intern/source_processor.hh + intern/version.cc + intern/view_specific_look.cc + intern/view_specific_look.hh + intern/white_point.cc + intern/white_point.hh + + intern/fallback/fallback_colorspace.hh + intern/fallback/fallback_config.cc + intern/fallback/fallback_config.hh + intern/fallback/fallback_cpu_processor.hh + intern/fallback/fallback_default_display.hh + intern/fallback/fallback_default_look.hh + intern/fallback/fallback_default_view.hh + intern/fallback/fallback_display_cpu_processor.cc + intern/fallback/fallback_display_cpu_processor.hh + intern/fallback/fallback_gpu_shader_binder.cc + intern/fallback/fallback_gpu_shader_binder.hh + intern/fallback/fallback_processor_cache.cc + intern/fallback/fallback_processor_cache.hh + + intern/libocio/error_handling.cc + intern/libocio/error_handling.hh + intern/libocio/libocio_colorspace.cc + intern/libocio/libocio_colorspace.hh + intern/libocio/libocio_config.cc + intern/libocio/libocio_config.hh + intern/libocio/libocio_cpu_processor.cc + intern/libocio/libocio_cpu_processor.hh + intern/libocio/libocio_display.cc + intern/libocio/libocio_display.hh + intern/libocio/libocio_display_processor.cc + intern/libocio/libocio_display_processor.hh + intern/libocio/libocio_gpu_shader_binder.cc + intern/libocio/libocio_gpu_shader_binder.hh + intern/libocio/libocio_look.cc + intern/libocio/libocio_look.hh + intern/libocio/libocio_view.hh + intern/libocio/libocio_processor.cc + intern/libocio/libocio_processor.hh + + OCIO_api.hh + OCIO_colorspace.hh + OCIO_config.hh + OCIO_cpu_processor.hh + OCIO_display.hh + OCIO_gpu_shader_binder.hh + OCIO_look.hh + OCIO_matrix.hh + OCIO_packed_image.hh + OCIO_role_names.hh + OCIO_version.hh + OCIO_view.hh +) + +set(LIB + PRIVATE bf::blenlib + PRIVATE bf::blenkernel + PRIVATE bf::dna + PRIVATE bf::gpu + PRIVATE bf::intern::guardedalloc + PRIVATE bf::intern::clog + PUBLIC bf::dependencies::optional::opencolorio + PRIVATE bf::extern::fmtlib +) + +blender_add_lib(bf_imbuf_opencolorio "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") +add_library(bf::imbuf::opencolorio ALIAS bf_imbuf_opencolorio) + +target_include_directories(bf_imbuf_opencolorio PUBLIC .) + +set(GLSL_SRC + shaders/gpu_shader_display_transform_vert.glsl + shaders/gpu_shader_display_transform_frag.glsl + + intern/ocio_shader_shared.hh +) + +set(GLSL_C) +foreach(GLSL_FILE ${GLSL_SRC}) + glsl_to_c(${GLSL_FILE} GLSL_C) +endforeach() + +blender_add_lib(bf_imbuf_opencolorio_shaders "${GLSL_C}" "" "" "") + +target_link_libraries(bf_imbuf_opencolorio PRIVATE bf_imbuf_opencolorio_shaders) + +set(GLSL_SOURCE_CONTENT "") +set(GLSL_METADATA_CONTENT "") +foreach(GLSL_FILE ${GLSL_SRC}) + get_filename_component(GLSL_FILE_NAME ${GLSL_FILE} NAME) + string(REPLACE "." "_" GLSL_FILE_NAME_UNDERSCORES ${GLSL_FILE_NAME}) + string(APPEND GLSL_SOURCE_CONTENT "SHADER_SOURCE\(${GLSL_FILE_NAME_UNDERSCORES}, \"${GLSL_FILE_NAME}\", \"${GLSL_FILE}\"\)\n") + string(APPEND GLSL_METADATA_CONTENT "#include \"${GLSL_FILE}.hh\"\n") +endforeach() + +set(glsl_source_list_file "${CMAKE_CURRENT_BINARY_DIR}/glsl_ocio_source_list.h") +file(GENERATE OUTPUT ${glsl_source_list_file} CONTENT "${GLSL_SOURCE_CONTENT}") +list(APPEND SRC ${glsl_source_list_file}) +set(glsl_metadata_list_file "${CMAKE_CURRENT_BINARY_DIR}/glsl_ocio_metadata_list.hh") +file(GENERATE OUTPUT ${glsl_metadata_list_file} CONTENT "${GLSL_METADATA_CONTENT}") +list(APPEND SRC ${glsl_metadata_list_file}) +list(APPEND INC ${CMAKE_CURRENT_BINARY_DIR}) + +target_include_directories(bf_imbuf_opencolorio_shaders PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) + +if(WITH_GTESTS) + set(TEST_SRC + intern/description_test.cc + intern/source_processor_test.cc + intern/view_specific_look_test.cc + ) + blender_add_test_suite_lib(imbuf_opencolorio "${TEST_SRC}" "${INC}" "${INC_SYS}" "${LIB};bf::imbuf::opencolorio") +endif() diff --git a/source/blender/imbuf/opencolorio/OCIO_api.hh b/source/blender/imbuf/opencolorio/OCIO_api.hh new file mode 100644 index 00000000000..3d37bb92ba4 --- /dev/null +++ b/source/blender/imbuf/opencolorio/OCIO_api.hh @@ -0,0 +1,16 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "OCIO_colorspace.hh" +#include "OCIO_config.hh" +#include "OCIO_cpu_processor.hh" +#include "OCIO_display.hh" +#include "OCIO_gpu_shader_binder.hh" +#include "OCIO_look.hh" +#include "OCIO_matrix.hh" +#include "OCIO_packed_image.hh" +#include "OCIO_role_names.hh" +#include "OCIO_view.hh" diff --git a/source/blender/imbuf/opencolorio/OCIO_colorspace.hh b/source/blender/imbuf/opencolorio/OCIO_colorspace.hh new file mode 100644 index 00000000000..5ac142aa5b6 --- /dev/null +++ b/source/blender/imbuf/opencolorio/OCIO_colorspace.hh @@ -0,0 +1,61 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_span.hh" +#include "BLI_string_ref.hh" + +namespace blender::ocio { + +class CPUProcessor; + +class ColorSpace { + public: + virtual ~ColorSpace() = default; + + /** + * Global index of the color space within the OpenColorIO configuration. + * The index is 0-based. + */ + int index = -1; + + /** + * Name and description of this space. + * + * The name is used to address to this color space from various places of the configuration. + * The description is used for UI to give better clue what the space is to artists. + */ + virtual StringRefNull name() const = 0; + virtual StringRefNull description() const = 0; + + /** + * Returns true if there is a conversion from this color space to the scene linear. + */ + virtual bool is_invertible() const = 0; + + /** + * Check whether this color space matches one of the built-in spaces like scene linear or sRGB + * (in its standard Blender notation). + */ + virtual bool is_scene_linear() const = 0; + virtual bool is_srgb() const = 0; + + /** + * The color space is a non-color data. + * Data color spaces do not change values of underlying pixels when converting to other color + * spaces. + */ + virtual bool is_data() const = 0; + + /** + * Quick access to CPU processors that convert color space from the current one to scene linear + * and vice versa. + * The call is allowed to be caching from the color space implementation perspective. + */ + const virtual CPUProcessor *get_to_scene_linear_cpu_processor() const = 0; + const virtual CPUProcessor *get_from_scene_linear_cpu_processor() const = 0; +}; + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/OCIO_config.hh b/source/blender/imbuf/opencolorio/OCIO_config.hh new file mode 100644 index 00000000000..cfdd2da1a7f --- /dev/null +++ b/source/blender/imbuf/opencolorio/OCIO_config.hh @@ -0,0 +1,217 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include + +#include "BLI_math_matrix_types.hh" +#include "BLI_math_vector_types.hh" +#include "BLI_string_ref.hh" + +namespace blender::ocio { + +class ColorSpace; +class Display; +class Look; +class CPUProcessor; +class GPUShaderBinder; + +struct DisplayParameters { + StringRefNull from_colorspace; + StringRefNull view; + StringRefNull display; + StringRefNull look; + float scale = 1.0f; + float exponent = 1.0f; + float temperature = 6500.0f; + float tint = 10.0f; + bool use_white_balance = false; + bool inverse = false; +}; + +class Config { + public: + /* -------------------------------------------------------------------- */ + /** \name Construction + * \{ */ + + virtual ~Config() = default; + + /** + * Create OpenColorIO configuration using configuration from the environment variables. + * If there is an error creating the configuration nullptr is returned. + */ + static std::unique_ptr create_from_environment(); + + /** + * Create OpenColorIO configuration using configuration from the given configuration file. + * If there is an error creating the configuration nullptr is returned. + */ + static std::unique_ptr create_from_file(StringRefNull filename); + + /** + * Create fallback implementation which is always guaranteed to work. + * + * It is used in cases actual OpenColorIO configuration has failed to be created so that Blender + * interface can be displayed. + * + * The fallback implementation is also used implicitly when BLender is compiled without + * OpenColorIO support. + */ + static std::unique_ptr create_fallback(); + + /** \} */ + + /* -------------------------------------------------------------------- */ + /** \name Color space information + * \{ */ + + /** + * Get the default coefficients for computing luma. + */ + virtual float3 get_default_luma_coefs() const = 0; + + /** + * Get conversion matrix from XYZ space to the scene linear. + * TODO(sergey): Specialize which exactly XYZ space it is. + */ + virtual float3x3 get_xyz_to_scene_linear_matrix() const = 0; + + /** + * Get the color space of the first rule that matched filepath. + * If there is no such color space nullptr is returned. + */ + virtual const char *get_color_space_from_filepath(const char *filepath) const = 0; + + /** \} */ + + /* -------------------------------------------------------------------- */ + /** \name Color space API + * \{ */ + + /** + * Get color space with the given name, role name, or alias. Color space names take precedence + * over roles. + * If the color space does not exist nullptr is returned. + */ + virtual const ColorSpace *get_color_space(StringRefNull name) const = 0; + + /** + * Get the number of color spaces in this configuration. + */ + virtual int get_num_color_spaces() const = 0; + + /** + * Get color space with the given index within the configuration. + * If the index is invalid nullptr is returned. + */ + virtual const ColorSpace *get_color_space_by_index(int index) const = 0; + + /** + * Get color space with the given index within the sorted array. + * This function allows to iterate color spaces in their alphabetical order. + * + * If the index is invalid nullptr is returned. + */ + virtual const ColorSpace *get_sorted_color_space_by_index(int index) const = 0; + + /** \} */ + + /* -------------------------------------------------------------------- */ + /** \name Display API + * \{ */ + + /** + * Get the default display in this configuration. + */ + virtual const Display *get_default_display() const = 0; + + /** + * Get display with the given name. + * If the display does not exist nullptr is returned. + */ + virtual const Display *get_display_by_name(StringRefNull name) const = 0; + + /** + * Get the number of displays in this configuration. + */ + virtual int get_num_displays() const = 0; + + /** + * Get display with the given index within the configuration. + * If the index is invalid nullptr is returned. + */ + virtual const Display *get_display_by_index(int index) const = 0; + + /** \} */ + + /* -------------------------------------------------------------------- */ + /** \name Display colorspace API + * \{ */ + + /** + * Returns the colorspace of the (display, view) pair. + * Note that this may be either a color space or a display color space. + */ + virtual const ColorSpace *get_display_view_color_space(StringRefNull display, + StringRefNull view) const = 0; + + /** \} */ + + /* -------------------------------------------------------------------- */ + /** \name Look API + * \{ */ + + /** + * Get look with the given name. + * If the look does not exist nullptr is returned. + */ + virtual const Look *get_look_by_name(StringRefNull name) const = 0; + + /** + * Get the number of looks in this configuration. + */ + virtual int get_num_looks() const = 0; + + /** + * Get look with the given index within the configuration. + * If the index is invalid nullptr is returned. + */ + virtual const Look *get_look_by_index(int index) const = 0; + + /** \} */ + + /* -------------------------------------------------------------------- */ + /** \name Processor API + * \{ */ + + /** + * Get processor which converts color space from the given from_colorspace to the display + * space. + */ + virtual std::shared_ptr get_display_cpu_processor( + const DisplayParameters &display_parameters) const = 0; + + /** + * Get processor which converts color between given color spaces. + */ + virtual std::shared_ptr get_cpu_processor( + StringRefNull from_colorspace, StringRefNull to_colorspace) const = 0; + + /** \} */ + + /* -------------------------------------------------------------------- */ + /** \name GPU-side processing + * \{ */ + + /** + * Get API which can be used to bind GPU shaders for color space conversion. + */ + virtual const GPUShaderBinder &get_gpu_shader_binder() const = 0; + + /** \} */ +}; + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/OCIO_cpu_processor.hh b/source/blender/imbuf/opencolorio/OCIO_cpu_processor.hh new file mode 100644 index 00000000000..77e70e95e77 --- /dev/null +++ b/source/blender/imbuf/opencolorio/OCIO_cpu_processor.hh @@ -0,0 +1,49 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +namespace blender::ocio { + +class PackedImage; + +class CPUProcessor { + public: + virtual ~CPUProcessor() = default; + + /** + * Returns true if the processor is known to not perform any actual color space conversion. + */ + virtual bool is_noop() const = 0; + + /** + * Apply the processor on a single pixel. + * The pixel is modified in-place. + */ + virtual void apply_rgb(float rgb[3]) const = 0; + + /** + * Apply the processor on a single pixel with straight (un-premultiplied) alpha. + * The pixel is modified in-place. + */ + virtual void apply_rgba(float rgba[4]) const = 0; + + /** + * Apply the processor on a single pixel with associated (premultiplied) alpha. + * The pixel is modified in-place. + */ + virtual void apply_rgba_predivide(float rgba[4]) const = 0; + + /** + * Apply processor on every pixel of the image with straight (un-premultiplied) alpha. + */ + virtual void apply(const PackedImage &image) const = 0; + + /** + * Apply processor on every pixel of the image with associated (premultiplied) alpha. + */ + virtual void apply_predivide(const PackedImage &image) const = 0; +}; + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/OCIO_display.hh b/source/blender/imbuf/opencolorio/OCIO_display.hh new file mode 100644 index 00000000000..917eee2c702 --- /dev/null +++ b/source/blender/imbuf/opencolorio/OCIO_display.hh @@ -0,0 +1,59 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_string_ref.hh" + +namespace blender::ocio { + +class CPUProcessor; +class View; + +class Display { + public: + virtual ~Display() = default; + + /** + * Global index of the display within the OpenColorIO configuration. + * The index is 0-based. + */ + int index = -1; + + /** + * Name of this display. + * The name is used to address to this display from various places of the configuration. + */ + virtual StringRefNull name() const = 0; + + /** + * Get default view of this display. */ + virtual const View *get_default_view() const = 0; + + /** + * Get view with the given name for this display. + * If the view does not exist nullptr is returned. + */ + virtual const View *get_view_by_name(StringRefNull name) const = 0; + + /** + * Get the number of view in this display. + */ + virtual int get_num_views() const = 0; + + /** + * Get view with the given index within the display. + * If the index is invalid nullptr is returned. + */ + virtual const View *get_view_by_index(int index) const = 0; + + /** + * Quick access to processors that convert color space from the display to scene linear and vice + * versa. The call is allowed to be caching from the color space implementation perspective. + */ + const virtual CPUProcessor *get_to_scene_linear_cpu_processor() const = 0; + const virtual CPUProcessor *get_from_scene_linear_cpu_processor() const = 0; +}; + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/OCIO_gpu_shader_binder.hh b/source/blender/imbuf/opencolorio/OCIO_gpu_shader_binder.hh new file mode 100644 index 00000000000..f64885a8810 --- /dev/null +++ b/source/blender/imbuf/opencolorio/OCIO_gpu_shader_binder.hh @@ -0,0 +1,110 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** + * Helper class which takes care of GPU shader bindings used to convert color spaces as a fragments + * shader. It defines public API to bind various color space conversion shaders, and it also takes + * care of shader caching to avoid re-compilations. + * + * Implementation-wise it takes care of common steps needed to compile the display transform shader + * (gpu_shader_display_transform_frag.glsl and gpu_shader_display_transform_vert.glsl). Subclasses + * takes care of generation code for functions OCIO_to_scene_linear() and OCIO_to_display(). + */ + +#pragma once + +#include +#include + +#include "BLI_string_ref.hh" + +struct CurveMapping; +struct GPUShader; + +namespace blender::ocio { + +class Config; + +namespace internal { +class GPUDisplayShader; +class GPUShaderCache; +} // namespace internal + +struct GPUDisplayParameters { + StringRefNull from_colorspace; + StringRefNull view; + StringRefNull display; + StringRefNull look; + CurveMapping *curve_mapping = nullptr; + float scale = 1.0f; + float exponent = 1.0f; + float dither = 0.0f; + float temperature = 6500.0f; + float tint = 10.0f; + bool use_white_balance = false; + bool use_predivide = false; + bool do_overlay_merge = false; + bool use_hdr = false; +}; + +class GPUShaderBinder { + /* Cache of shaders used for conversion to the display space. */ + std::unique_ptr display_cache_; + + /* Cache of shaders used for conversion to scene linear space. */ + std::unique_ptr scene_linear_cache_; + + public: + explicit GPUShaderBinder(const Config &config); + virtual ~GPUShaderBinder(); + + /** + * Bind GPU shader which performs conversion from the given color space to the display space. + * Drawing happens in the same immediate mode as when GPU_SHADER_3D_IMAGE_COLOR shader is used. + * + * Returns true if the GPU shader was successfully bound. + */ + bool display_bind(const GPUDisplayParameters &display_parameters) const; + + /** + * Configures and bind GPU shader for conversion from the given space to scene linear. + * Drawing happens in the same immediate mode as when GPU_SHADER_3D_IMAGE_COLOR shader is used. + * + * Returns true if the GPU shader was successfully bound. + */ + bool to_scene_linear_bind(StringRefNull from_colorspace, bool use_predivide) const; + + /** + * Unbind previously bound GPU shader. + * + * If the shader was not bound by neither display_bind() nor + * to_scene_linear_bind() the behavior is undefined. + */ + void unbind() const; + + protected: + const Config &config_; + + /** + * Construct display shader matching requested parameters. + * The shader has its cache variables (input color space name, view, display, look, whether + * curve mapping is used or not). + */ + virtual void construct_display_shader(internal::GPUDisplayShader &display_shader) const = 0; + + /** + * Construct display shader which will only perform the to-scene-linear part of conversion, + * leaving the to-display a no-op function. + */ + virtual void construct_scene_linear_shader(internal::GPUDisplayShader &display_shader) const = 0; + + /** + * Create GPU shader for the given display shader. + * Returns true if the shader was successfully created. + */ + static bool create_gpu_shader(internal::GPUDisplayShader &display_shader, + StringRefNull fragment_source); +}; + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/OCIO_look.hh b/source/blender/imbuf/opencolorio/OCIO_look.hh new file mode 100644 index 00000000000..435ce531aaa --- /dev/null +++ b/source/blender/imbuf/opencolorio/OCIO_look.hh @@ -0,0 +1,53 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_string_ref.hh" + +namespace blender::ocio { + +class Look { + public: + virtual ~Look() = default; + + /** + * Global index of the look within the OpenColorIO configuration. + * The index is 0-based. + * + * NOTE: The implementation ensures None as a look. It has index of 0. This makes it so looks in + * the OpenColorIO configurations are to be offset by 1 from this index. + */ + int index = -1; + + /** + * The look is known to not perform any actual color space conversion. + */ + bool is_noop = false; + + /** + * Name of this look. + * The name is used to address to this look from various places of the configuration. + */ + virtual StringRefNull name() const = 0; + + /** + * Name of the look presented in the interface. + * It is typically derived from the OpenColorIO's look name by stripping the view name prefix/ + */ + virtual StringRefNull ui_name() const = 0; + + /** + * When not empty the look is specific to the view with the given name. + */ + virtual StringRefNull view() const = 0; + + /** + * process_space defines the color space the image required to be in for the math to apply + * correctly. + */ + virtual StringRefNull process_space() const = 0; +}; + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/OCIO_matrix.hh b/source/blender/imbuf/opencolorio/OCIO_matrix.hh new file mode 100644 index 00000000000..1b94ccaa864 --- /dev/null +++ b/source/blender/imbuf/opencolorio/OCIO_matrix.hh @@ -0,0 +1,22 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_math_matrix_types.hh" + +namespace blender::ocio { + +/* Standard XYZ (D65) to linear Rec.709 transform. */ +static const float3x3 XYZ_TO_REC709{{3.2404542f, -0.9692660f, 0.0556434f}, + {-1.5371385f, 1.8760108f, -0.2040259f}, + {-0.4985314f, 0.0415560f, 1.0572252f}}; + +/* Standard ACES to XYZ (D65) transform. + * Matches OpenColorIO builtin transform: UTILITY - ACES-AP0_to_CIE-XYZ-D65_BFD. */ +static const float3x3 ACES_TO_XYZ = {{0.938280f, 0.337369f, 0.001174f}, + {-0.004451f, 0.729522f, -0.003711f}, + {0.016628f, -0.066890f, 1.091595f}}; + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/OCIO_packed_image.hh b/source/blender/imbuf/opencolorio/OCIO_packed_image.hh new file mode 100644 index 00000000000..f1c87ee3e0d --- /dev/null +++ b/source/blender/imbuf/opencolorio/OCIO_packed_image.hh @@ -0,0 +1,175 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include + +#include "BLI_assert.h" + +#if defined(WITH_OPENCOLORIO) +# include "intern/opencolorio.hh" +#endif + +namespace blender::ocio { + +enum class BitDepth { + BIT_DEPTH_UNKNOWN, + BIT_DEPTH_F32, +}; + +class PackedImage { +#if defined(WITH_OPENCOLORIO) + OCIO_NAMESPACE::PackedImageDesc image_desc_; + + static OCIO_NAMESPACE::BitDepth convert_bit_depth(const BitDepth bit_depth) + { + switch (bit_depth) { + case BitDepth::BIT_DEPTH_UNKNOWN: + return OCIO_NAMESPACE::BIT_DEPTH_UNKNOWN; + case BitDepth::BIT_DEPTH_F32: + return OCIO_NAMESPACE::BIT_DEPTH_F32; + } + BLI_assert_unreachable(); + return OCIO_NAMESPACE::BIT_DEPTH_UNKNOWN; + } + + static BitDepth convert_bit_depth(const OCIO_NAMESPACE::BitDepth bit_depth) + { + switch (bit_depth) { + case OCIO_NAMESPACE::BIT_DEPTH_UNKNOWN: + return BitDepth::BIT_DEPTH_UNKNOWN; + case OCIO_NAMESPACE::BIT_DEPTH_F32: + return BitDepth::BIT_DEPTH_F32; + default: + /* Other bit depths are currently not supported. */ + return BitDepth::BIT_DEPTH_UNKNOWN; + } + } + +#else + void *data_ = nullptr; + size_t width_ = 0; + size_t height_ = 0; + size_t num_channels_ = 0; + BitDepth bit_depth_ = BitDepth::BIT_DEPTH_UNKNOWN; + size_t chan_stride_in_bytes_ = 0; + size_t x_stride_in_bytes_ = 0; + size_t y_stride_in_bytes_ = 0; +#endif + + public: + PackedImage(void *data, + const size_t width, + const size_t height, + const size_t num_channels, + const BitDepth bit_depth, + const size_t chan_stride_in_bytes, + const size_t x_stride_in_bytes, + const size_t y_stride_in_bytes) +#if defined(WITH_OPENCOLORIO) + : image_desc_(data, + width, + height, + num_channels, + convert_bit_depth(bit_depth), + chan_stride_in_bytes, + x_stride_in_bytes, + y_stride_in_bytes) +#else + : data_(data), + width_(width), + height_(height), + num_channels_(num_channels), + bit_depth_(bit_depth), + chan_stride_in_bytes_(chan_stride_in_bytes), + x_stride_in_bytes_(x_stride_in_bytes), + y_stride_in_bytes_(y_stride_in_bytes) +#endif + { + } + +#if defined(WITH_OPENCOLORIO) + size_t get_width() const + { + return image_desc_.getWidth(); + } + size_t get_height() const + { + return image_desc_.getHeight(); + } + + size_t get_num_channels() const + { + return image_desc_.getNumChannels(); + } + + void *get_data() const + { + return image_desc_.getData(); + } + + BitDepth get_bit_depth() const + { + return convert_bit_depth(image_desc_.getBitDepth()); + } + + size_t get_chan_stride_in_bytes() const + { + return image_desc_.getChanStrideBytes(); + } + size_t get_x_stride_in_bytes() const + { + return image_desc_.getXStrideBytes(); + } + size_t get_y_stride_in_bytes() const + { + return image_desc_.getYStrideBytes(); + } + + operator const OCIO_NAMESPACE::PackedImageDesc &() const + { + return image_desc_; + } +#else + size_t get_width() const + { + return width_; + } + size_t get_height() const + { + return height_; + } + + size_t get_num_channels() const + { + return num_channels_; + } + + void *get_data() const + { + return data_; + } + + BitDepth get_bit_depth() const + { + return bit_depth_; + } + + size_t get_chan_stride_in_bytes() const + { + return chan_stride_in_bytes_; + } + size_t get_x_stride_in_bytes() const + { + return x_stride_in_bytes_; + } + size_t get_y_stride_in_bytes() const + { + return y_stride_in_bytes_; + } +#endif +}; + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/OCIO_role_names.hh b/source/blender/imbuf/opencolorio/OCIO_role_names.hh new file mode 100644 index 00000000000..62ac99ad74c --- /dev/null +++ b/source/blender/imbuf/opencolorio/OCIO_role_names.hh @@ -0,0 +1,14 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#define OCIO_ROLE_DATA "data" +#define OCIO_ROLE_SCENE_LINEAR "scene_linear" +#define OCIO_ROLE_COLOR_PICKING "color_picking" +#define OCIO_ROLE_TEXTURE_PAINT "texture_paint" +#define OCIO_ROLE_DEFAULT_BYTE "default_byte" +#define OCIO_ROLE_DEFAULT_FLOAT "default_float" +#define OCIO_ROLE_DEFAULT_SEQUENCER "default_sequencer" +#define OCIO_ROLE_ACES_INTERCHANGE "aces_interchange" diff --git a/source/blender/imbuf/opencolorio/OCIO_version.hh b/source/blender/imbuf/opencolorio/OCIO_version.hh new file mode 100644 index 00000000000..bd586303a3d --- /dev/null +++ b/source/blender/imbuf/opencolorio/OCIO_version.hh @@ -0,0 +1,21 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +namespace blender::ocio { + +struct Version { + int major = 0; + int minor = 0; + int patch = 0; +}; + +/** + * Get OpenColorIO library version. + * When compiled without OpenColorIO library returns {0, 0, 0}. + */ +Version get_version(); + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/OCIO_view.hh b/source/blender/imbuf/opencolorio/OCIO_view.hh new file mode 100644 index 00000000000..3db10d61514 --- /dev/null +++ b/source/blender/imbuf/opencolorio/OCIO_view.hh @@ -0,0 +1,28 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_string_ref.hh" + +namespace blender::ocio { + +class View { + public: + virtual ~View() = default; + + /** + * Index of the view within the display that owns it. + * The index is 0-based. + */ + int index = -1; + + /** + * Name of this view. + * The name is used to address to this view from various places of the configuration. + */ + virtual StringRefNull name() const = 0; +}; + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/intern/config.cc b/source/blender/imbuf/opencolorio/intern/config.cc new file mode 100644 index 00000000000..7803214812f --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/config.cc @@ -0,0 +1,40 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "OCIO_config.hh" + +#include "fallback/fallback_config.hh" + +#if defined(WITH_OPENCOLORIO) +# include "libocio/libocio_config.hh" +#endif + +namespace blender::ocio { + +std::unique_ptr Config::create_from_environment() +{ +#if defined(WITH_OPENCOLORIO) + return LibOCIOConfig::create_from_environment(); +#endif + + return nullptr; +} + +std::unique_ptr Config::create_from_file(const StringRefNull filename) +{ +#if defined(WITH_OPENCOLORIO) + return LibOCIOConfig::create_from_file(filename); +#else + (void)filename; +#endif + + return nullptr; +} + +std::unique_ptr Config::create_fallback() +{ + return std::make_unique(); +} + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/intern/cpu_processor_cache.hh b/source/blender/imbuf/opencolorio/intern/cpu_processor_cache.hh new file mode 100644 index 00000000000..1245b22e99b --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/cpu_processor_cache.hh @@ -0,0 +1,57 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include +#include +#include + +#include "OCIO_cpu_processor.hh" + +namespace blender::ocio { + +class CPUProcessorCache { + /* TODO(sergey): Figure out how this can be made per-cache. + * + * The issue here is that the cache might be part of an object which is used in a Vector(). Or, + * even simpler: Vector. + * + * If the mutex is per-object then this doesn't work as the mutex deletes the move constructor. + */ + static inline std::mutex mutex_; + + mutable bool processor_created_ = false; + mutable std::unique_ptr cpu_processor_; + + public: + CPUProcessorCache() = default; + CPUProcessorCache(const CPUProcessorCache &other) = delete; + CPUProcessorCache(CPUProcessorCache &&other) noexcept = default; + + ~CPUProcessorCache() = default; + + CPUProcessorCache &operator=(const CPUProcessorCache &other) = delete; + CPUProcessorCache &operator=(CPUProcessorCache &&other) = default; + + /** + * Get cached processor, or create the new one using create_processor() and cache it. + * + * If the create_processor() returns nullptr it is cached as nullptr. + */ + const CPUProcessor *get( + const std::function()> &create_processor) const + { + std::lock_guard lock(mutex_); + + if (!processor_created_) { + cpu_processor_ = create_processor(); + processor_created_ = true; + } + + return cpu_processor_.get(); + } +}; + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/intern/description.cc b/source/blender/imbuf/opencolorio/intern/description.cc new file mode 100644 index 00000000000..f30c5b930a5 --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/description.cc @@ -0,0 +1,28 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "description.hh" + +#include "BLI_utildefines.h" + +namespace blender::ocio { + +std::string cleanup_description(const StringRef description) +{ + if (description.is_empty()) { + return ""; + } + + std::string result = description.trim("\r\n"); + + for (char &ch : result) { + if (ELEM(ch, '\r', '\n')) { + ch = ' '; + } + } + + return result; +} + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/intern/description.hh b/source/blender/imbuf/opencolorio/intern/description.hh new file mode 100644 index 00000000000..f848174bd7b --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/description.hh @@ -0,0 +1,22 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_string_ref.hh" + +#include + +namespace blender::ocio { + +/** + * Cleanup description making it possible to easily show in the interface as a tooltip. + * + * This includes: + * - Stripping all trailing line break character. + * - Replacing all inner line break character with space. + */ +std::string cleanup_description(StringRef description); + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/intern/description_test.cc b/source/blender/imbuf/opencolorio/intern/description_test.cc new file mode 100644 index 00000000000..76f1fc95a4c --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/description_test.cc @@ -0,0 +1,18 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "description.hh" + +#include "testing/testing.h" + +namespace blender::ocio { + +TEST(ocio_description, cleanup_description) +{ + EXPECT_EQ(cleanup_description(""), ""); + EXPECT_EQ(cleanup_description("\n\rfoo\r\n"), "foo"); + EXPECT_EQ(cleanup_description("\n\rfoo\r\nbar\r\n"), "foo bar"); +} + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/intern/fallback/fallback_colorspace.hh b/source/blender/imbuf/opencolorio/intern/fallback/fallback_colorspace.hh new file mode 100644 index 00000000000..156a8282573 --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/fallback/fallback_colorspace.hh @@ -0,0 +1,85 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include + +#include "OCIO_colorspace.hh" + +#include "fallback_cpu_processor.hh" + +namespace blender::ocio { + +class FallbackColorSpace : public ColorSpace { + std::string name_; + + public: + enum class Type { + LINEAR, + SRGB, + DATA, + }; + + FallbackColorSpace(const int index, const StringRefNull name, const Type type) : type_(type) + { + this->index = index; + name_ = name; + } + + StringRefNull name() const override + { + return name_; + } + StringRefNull description() const override + { + return ""; + } + + bool is_invertible() const override + { + return true; + } + + bool is_scene_linear() const override + { + return type_ == Type::LINEAR; + } + bool is_srgb() const override + { + return type_ == Type::SRGB; + } + + bool is_data() const override + { + return type_ == Type::DATA; + } + + const CPUProcessor *get_to_scene_linear_cpu_processor() const override + { + if (type_ == Type::SRGB) { + static FallbackSRGBToLinearRGBCPUProcessor processor; + return &processor; + } + + static FallbackNOOPCPUProcessor processor; + return &processor; + } + + CPUProcessor *get_from_scene_linear_cpu_processor() const override + { + if (type_ == Type::SRGB) { + static FallbackLinearRGBToSRGBCPUProcessor processor; + return &processor; + } + + static FallbackNOOPCPUProcessor processor; + return &processor; + } + + private: + Type type_; +}; + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/intern/fallback/fallback_config.cc b/source/blender/imbuf/opencolorio/intern/fallback/fallback_config.cc new file mode 100644 index 00000000000..ee1910911e1 --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/fallback/fallback_config.cc @@ -0,0 +1,206 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "fallback_config.hh" + +#include "OCIO_display.hh" +#include "OCIO_matrix.hh" + +#include "fallback_colorspace.hh" +#include "fallback_display_cpu_processor.hh" + +namespace blender::ocio { +/* -------------------------------------------------------------------- */ +/** \name Color space information + * \{ */ + +float3 FallbackConfig::get_default_luma_coefs() const +{ + /* Here we simply use the older Blender assumed primaries of ITU-BT.709 / sRGB, or + * 0.2126729 0.7151522 0.0721750. Brute force stupid, but only plausible option given no color + * management system in place. */ + + return float3(0.2126f, 0.7152f, 0.0722f); +} + +float3x3 FallbackConfig::get_xyz_to_scene_linear_matrix() const +{ + /* Default to ITU-BT.709. */ + return XYZ_TO_REC709; +} + +const char *FallbackConfig::get_color_space_from_filepath(const char * /*filepath*/) const +{ + return nullptr; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color space API + * \{ */ + +const ColorSpace *FallbackConfig::get_color_space(const StringRefNull name) const +{ + for (const ColorSpace *color_space : color_spaces_) { + if (color_space->name() == name) { + return color_space; + } + } + + if (name == "scene_linear") { + return &colorspace_linear_; + } + if (name == "color_picking") { + return &colorspace_srgb_; + } + if (name == "texture_paint") { + return &colorspace_linear_; + } + if (name == "default_byte") { + return &colorspace_srgb_; + } + if (name == "default_float") { + return &colorspace_linear_; + } + if (name == "default_sequencer") { + return &colorspace_srgb_; + } + if (name == "Linear") { + return &colorspace_linear_; + } + if (name == "sRGB") { + return &colorspace_srgb_; + } + if (name == "data") { + return &colorspace_data_; + } + + return nullptr; +} + +int FallbackConfig::get_num_color_spaces() const +{ + return color_spaces_.size(); +} + +const ColorSpace *FallbackConfig::get_color_space_by_index(const int index) const +{ + if (index < 0 || index >= color_spaces_.size()) { + return nullptr; + } + return color_spaces_[index]; +} + +const ColorSpace *FallbackConfig::get_sorted_color_space_by_index(const int index) const +{ + return get_color_space_by_index(index); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Display API + * \{ */ + +const Display *FallbackConfig::get_default_display() const +{ + return &default_display_; +} + +const Display *FallbackConfig::get_display_by_name(const StringRefNull name) const +{ + if (name == default_display_.name()) { + return &default_display_; + } + return nullptr; +} + +int FallbackConfig::get_num_displays() const +{ + return 1; +} + +const Display *FallbackConfig::get_display_by_index(const int index) const +{ + if (index != 0) { + return nullptr; + } + return &default_display_; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Display colorspace API + * \{ */ + +const ColorSpace *FallbackConfig::get_display_view_color_space(const StringRefNull display, + const StringRefNull view) const +{ + if (display == default_display_.name() && view == default_display_.get_default_view()->name()) { + return &colorspace_srgb_; + } + return nullptr; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Look API + * \{ */ + +const Look *FallbackConfig::get_look_by_name(const StringRefNull name) const +{ + if (name == default_look_.name()) { + return &default_look_; + } + return nullptr; +} + +int FallbackConfig::get_num_looks() const +{ + return 1; +} + +const Look *FallbackConfig::get_look_by_index(int index) const +{ + if (index != 0) { + return nullptr; + } + return &default_look_; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Processor API + * \{ */ + +std::shared_ptr FallbackConfig::get_display_cpu_processor( + const DisplayParameters &display_parameters) const +{ + return create_fallback_display_cpu_processor(*this, display_parameters); +} + +std::shared_ptr FallbackConfig::get_cpu_processor( + const StringRefNull from_colorspace, const StringRefNull to_colorspace) const +{ + return processor_cache_.get(from_colorspace, to_colorspace); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Processor API + * \{ */ + +const GPUShaderBinder &FallbackConfig::get_gpu_shader_binder() const +{ + return gpu_shader_binder_; +} + +/** \} */ + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/intern/fallback/fallback_config.hh b/source/blender/imbuf/opencolorio/intern/fallback/fallback_config.hh new file mode 100644 index 00000000000..aecfcb3ece3 --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/fallback/fallback_config.hh @@ -0,0 +1,80 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "MEM_guardedalloc.h" + +#include "BLI_vector.hh" + +#include "OCIO_config.hh" + +#include "fallback_colorspace.hh" +#include "fallback_default_display.hh" +#include "fallback_default_look.hh" +#include "fallback_gpu_shader_binder.hh" +#include "fallback_processor_cache.hh" + +namespace blender::ocio { + +class ColorSpace; +class Display; +class View; + +class FallbackConfig : public Config { + /* Color spaces in this configuration. */ + FallbackColorSpace colorspace_linear_{0, "Linear", FallbackColorSpace::Type::LINEAR}; + FallbackColorSpace colorspace_data_{1, "Non-Color", FallbackColorSpace::Type::DATA}; + FallbackColorSpace colorspace_srgb_{2, "sRGB", FallbackColorSpace::Type::SRGB}; + + FallbackDefaultDisplay default_display_; + FallbackDefaultLook default_look_; + + /* Vectors that contain non-owning pointers to the color spaces and display. */ + Vector color_spaces_{ + &colorspace_linear_, &colorspace_data_, &colorspace_srgb_}; + + FallbackProcessorCache processor_cache_; + FallbackGPUShaderBinder gpu_shader_binder_{*this}; + + public: + /* Color space information. */ + float3 get_default_luma_coefs() const override; + float3x3 get_xyz_to_scene_linear_matrix() const override; + const char *get_color_space_from_filepath(const char *filepath) const override; + + /* Color space API. */ + const ColorSpace *get_color_space(StringRefNull name) const override; + int get_num_color_spaces() const override; + const ColorSpace *get_color_space_by_index(int index) const override; + const ColorSpace *get_sorted_color_space_by_index(int index) const override; + + /* Display API. */ + const Display *get_default_display() const override; + const Display *get_display_by_name(StringRefNull name) const override; + int get_num_displays() const override; + const Display *get_display_by_index(int index) const override; + + /* Display colorspace API. */ + const ColorSpace *get_display_view_color_space(StringRefNull display, + StringRefNull view) const override; + + /* Look API. */ + const Look *get_look_by_name(StringRefNull name) const override; + int get_num_looks() const override; + const Look *get_look_by_index(int index) const override; + + /* Processor API. */ + std::shared_ptr get_display_cpu_processor( + const DisplayParameters &display_parameters) const override; + std::shared_ptr get_cpu_processor( + StringRefNull from_colorspace, StringRefNull to_colorspace) const override; + + /* Processor API. */ + const GPUShaderBinder &get_gpu_shader_binder() const override; + + MEM_CXX_CLASS_ALLOC_FUNCS("FallbackConfig"); +}; + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/intern/fallback/fallback_cpu_processor.hh b/source/blender/imbuf/opencolorio/intern/fallback/fallback_cpu_processor.hh new file mode 100644 index 00000000000..1681cc07fdf --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/fallback/fallback_cpu_processor.hh @@ -0,0 +1,126 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_assert.h" +#include "BLI_math_color.h" + +#include "OCIO_cpu_processor.hh" +#include "OCIO_packed_image.hh" + +namespace blender::ocio { + +/** + * CPU processor implementation that does not perform any pixel modification. + */ +class FallbackNOOPCPUProcessor : public CPUProcessor { + public: + bool is_noop() const override + { + return true; + } + + void apply_rgb(float /*rgb*/[3]) const override {} + void apply_rgba(float /*rgba*/[4]) const override {} + + void apply_rgba_predivide(float /*rgba*/[4]) const override {} + + void apply(const PackedImage & /*image*/) const override {} + void apply_predivide(const PackedImage & /*image*/) const override {} +}; + +/** + * Processor which applies templated pixel_processor for every pixel that is to be converted. + */ +template +class FallbackCustomCPUProcessor : public CPUProcessor { + public: + bool is_noop() const override + { + return false; + } + + void apply_rgb(float rgb[3]) const override + { + pixel_processor(rgb, rgb); + } + void apply_rgba(float rgba[4]) const override + { + pixel_processor(rgba, rgba); + } + + void apply_rgba_predivide(float rgba[4]) const override + { + if (rgba[3] == 1.0f || rgba[3] == 0.0f) { + pixel_processor(rgba, rgba); + return; + } + + const float alpha = rgba[3]; + const float inv_alpha = 1.0f / alpha; + + rgba[0] *= inv_alpha; + rgba[1] *= inv_alpha; + rgba[2] *= inv_alpha; + + pixel_processor(rgba, rgba); + + rgba[0] *= alpha; + rgba[1] *= alpha; + rgba[2] *= alpha; + } + + void apply(const PackedImage &image) const override + { + /* TODO(sergey): Stride not respected, channels must be 3 or 4, bit depth is float32. */ + + BLI_assert(image.get_num_channels() >= 3); + BLI_assert(image.get_bit_depth() == BitDepth::BIT_DEPTH_F32); + + const int num_channels = image.get_num_channels(); + const size_t width = image.get_width(); + const size_t height = image.get_height(); + + float *pixels = static_cast(image.get_data()); + + for (size_t y = 0; y < height; y++) { + for (size_t x = 0; x < width; x++) { + float *pixel = pixels + num_channels * (y * width + x); + pixel_processor(pixel, pixel); + } + } + } + + void apply_predivide(const PackedImage &image) const override + { + /* TODO(sergey): Stride not respected, channels must be 3 or 4, bit depth is float32. */ + + BLI_assert(image.get_num_channels() >= 3); + BLI_assert(image.get_bit_depth() == BitDepth::BIT_DEPTH_F32); + + const int num_channels = image.get_num_channels(); + if (num_channels < 4) { + apply(image); + return; + } + + const size_t width = image.get_width(); + const size_t height = image.get_height(); + + float *pixels = static_cast(image.get_data()); + + for (size_t y = 0; y < height; y++) { + for (size_t x = 0; x < width; x++) { + float *pixel = pixels + num_channels * (y * width + x); + apply_rgba_predivide(pixel); + } + } + } +}; + +using FallbackLinearRGBToSRGBCPUProcessor = FallbackCustomCPUProcessor; +using FallbackSRGBToLinearRGBCPUProcessor = FallbackCustomCPUProcessor; + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/intern/fallback/fallback_default_display.hh b/source/blender/imbuf/opencolorio/intern/fallback/fallback_default_display.hh new file mode 100644 index 00000000000..bc431bb4ed6 --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/fallback/fallback_default_display.hh @@ -0,0 +1,71 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include + +#include "OCIO_display.hh" + +#include "fallback_cpu_processor.hh" +#include "fallback_default_view.hh" + +namespace blender::ocio { + +class FallbackDefaultDisplay : public Display { + std::string name_; + FallbackDefaultView default_view_; + + public: + FallbackDefaultDisplay() + { + this->index = 0; + name_ = "sRGB"; + } + + StringRefNull name() const override + { + return name_; + } + + const View *get_default_view() const override + { + return &default_view_; + } + + const View *get_view_by_name(const StringRefNull name) const override + { + if (name == default_view_.name()) { + return &default_view_; + } + return nullptr; + } + + int get_num_views() const override + { + return 1; + } + + const View *get_view_by_index(const int index) const override + { + if (index != 0) { + return nullptr; + } + return &default_view_; + } + + const CPUProcessor *get_to_scene_linear_cpu_processor() const override + { + static FallbackSRGBToLinearRGBCPUProcessor processor; + return &processor; + } + + const CPUProcessor *get_from_scene_linear_cpu_processor() const override + { + static FallbackLinearRGBToSRGBCPUProcessor processor; + return &processor; + } +}; + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/intern/fallback/fallback_default_look.hh b/source/blender/imbuf/opencolorio/intern/fallback/fallback_default_look.hh new file mode 100644 index 00000000000..e47a9c64f8c --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/fallback/fallback_default_look.hh @@ -0,0 +1,40 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "OCIO_look.hh" + +namespace blender::ocio { + +class FallbackDefaultLook : public Look { + public: + FallbackDefaultLook() + { + this->index = 0; + this->is_noop = true; + } + + StringRefNull name() const override + { + return "None"; + } + + StringRefNull ui_name() const override + { + return name(); + } + + StringRefNull view() const override + { + return ""; + } + + StringRefNull process_space() const override + { + return ""; + } +}; + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/intern/fallback/fallback_default_view.hh b/source/blender/imbuf/opencolorio/intern/fallback/fallback_default_view.hh new file mode 100644 index 00000000000..820f50951ec --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/fallback/fallback_default_view.hh @@ -0,0 +1,24 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "OCIO_view.hh" + +namespace blender::ocio { + +class FallbackDefaultView : public View { + public: + FallbackDefaultView() + { + this->index = 0; + } + + StringRefNull name() const override + { + return "Standard"; + } +}; + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/intern/fallback/fallback_display_cpu_processor.cc b/source/blender/imbuf/opencolorio/intern/fallback/fallback_display_cpu_processor.cc new file mode 100644 index 00000000000..74ad7cc5278 --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/fallback/fallback_display_cpu_processor.cc @@ -0,0 +1,234 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "fallback_display_cpu_processor.hh" + +#include "BLI_math_base.hh" +#include "BLI_math_color.h" +#include "BLI_math_matrix.h" +#include "BLI_math_matrix.hh" +#include "BLI_math_vector.h" + +#include "OCIO_config.hh" +#include "OCIO_cpu_processor.hh" +#include "OCIO_packed_image.hh" + +#include "../white_point.hh" + +namespace blender::ocio { + +namespace { + +using PixelSpaceProcessor3 = void (*)(float dst[3], const float src[3]); + +class NOOPDisplayCPUProcessor : public CPUProcessor { + public: + static std::shared_ptr get() + { + static auto processor = std::make_shared(); + return processor; + } + + bool is_noop() const override + { + return true; + } + + void apply_rgb(float /*rgb*/[3]) const override {} + void apply_rgba(float /*rgba*/[4]) const override {} + + void apply_rgba_predivide(float /*rgba*/[4]) const override {} + + void apply(const PackedImage & /*image*/) const override {} + void apply_predivide(const PackedImage & /*image*/) const override {} +}; + +class BaseDisplayCPUProcessor : public CPUProcessor { + public: + /* Matrix transform which is applied in the linear smace. + * + * NOTE: The matrix is inversed when the processor is configured to go from display space to + * linear. */ + float3x3 matrix = float3x3::identity(); + + float exponent = 1.0f; +}; + +template +class DisplayCPUProcessor : public BaseDisplayCPUProcessor { + public: + bool is_noop() const override + { + return false; + } + + void apply_rgb(float rgb[3]) const override + { + process_rgb(rgb); + } + void apply_rgba(float rgba[4]) const override + { + process_rgb(rgba); + } + + void apply_rgba_predivide(float rgba[4]) const override + { + if (rgba[3] == 1.0f || rgba[3] == 0.0f) { + process_rgb(rgba); + return; + } + + const float alpha = rgba[3]; + const float inv_alpha = 1.0f / alpha; + + rgba[0] *= inv_alpha; + rgba[1] *= inv_alpha; + rgba[2] *= inv_alpha; + + process_rgb(rgba); + + rgba[0] *= alpha; + rgba[1] *= alpha; + rgba[2] *= alpha; + } + + void apply(const PackedImage &image) const override + { + /* TODO(sergey): Stride not respected, channels must be 3 or 4, bit depth is float32. */ + + BLI_assert(image.get_num_channels() >= 3); + BLI_assert(image.get_bit_depth() == BitDepth::BIT_DEPTH_F32); + + const int num_channels = image.get_num_channels(); + const size_t width = image.get_width(); + const size_t height = image.get_height(); + + float *pixels = static_cast(image.get_data()); + + for (size_t y = 0; y < height; y++) { + for (size_t x = 0; x < width; x++) { + float *pixel = pixels + num_channels * (y * width + x); + process_rgb(pixel); + } + } + } + + void apply_predivide(const PackedImage &image) const override + { + /* TODO(sergey): Stride not respected, channels must be 3 or 4, bit depth is float32. */ + + BLI_assert(image.get_num_channels() >= 3); + BLI_assert(image.get_bit_depth() == BitDepth::BIT_DEPTH_F32); + + const int num_channels = image.get_num_channels(); + if (num_channels < 4) { + apply(image); + return; + } + + const size_t width = image.get_width(); + const size_t height = image.get_height(); + + float *pixels = static_cast(image.get_data()); + + for (size_t y = 0; y < height; y++) { + for (size_t x = 0; x < width; x++) { + float *pixel = pixels + num_channels * (y * width + x); + apply_rgba_predivide(pixel); + } + } + } + + private: + void process_rgb(float rgb[3]) const + { + if constexpr (is_inverse) { + if (exponent != 0) { + const float inv_exponent = 1.0f / exponent; + rgb[0] = rgb[0] != 0.0f ? math::pow(rgb[0], inv_exponent) : 0.0f; + rgb[1] = rgb[1] != 0.0f ? math::pow(rgb[1], inv_exponent) : 0.0f; + rgb[2] = rgb[2] != 0.0f ? math::pow(rgb[2], inv_exponent) : 0.0f; + } + else { + rgb[0] = 0; + rgb[1] = 0; + rgb[2] = 0; + } + + pixel_space_processor(rgb, rgb); + + mul_v3_m3v3(rgb, this->matrix.ptr(), rgb); + } + else { + mul_v3_m3v3(rgb, this->matrix.ptr(), rgb); + + pixel_space_processor(rgb, rgb); + + rgb[0] = math::pow(math::max(0.0f, rgb[0]), exponent); + rgb[1] = math::pow(math::max(0.0f, rgb[1]), exponent); + rgb[2] = math::pow(math::max(0.0f, rgb[2]), exponent); + } + } +}; + +} // namespace + +std::shared_ptr create_fallback_display_cpu_processor( + const Config &config, const DisplayParameters &display_parameters) +{ + if (display_parameters.display != "sRGB") { + return NOOPDisplayCPUProcessor::get(); + } + + if (display_parameters.view != "Standard") { + return NOOPDisplayCPUProcessor::get(); + } + if (display_parameters.look != "" && display_parameters.look != "None") { + return NOOPDisplayCPUProcessor::get(); + } + + if (display_parameters.from_colorspace == "Non-Color") { + return NOOPDisplayCPUProcessor::get(); + } + + std::shared_ptr processor; + + if (display_parameters.from_colorspace == "Linear") { + if (display_parameters.inverse) { + processor = std::make_shared>(); + } + else { + processor = std::make_shared>(); + } + } + else if (display_parameters.from_colorspace == "sRGB") { + if (display_parameters.inverse) { + processor = std::make_shared>(); + } + else { + processor = std::make_shared>(); + } + } + else { + return NOOPDisplayCPUProcessor::get(); + } + + processor->matrix = float3x3::identity() * display_parameters.scale; + processor->exponent = display_parameters.exponent; + + /* Apply white balance. */ + if (display_parameters.use_white_balance) { + const float3x3 matrix = calculate_white_point_matrix( + config, display_parameters.temperature, display_parameters.tint); + processor->matrix *= matrix; + } + + if (display_parameters.inverse) { + processor->matrix = math::invert(processor->matrix); + } + + return processor; +} + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/intern/fallback/fallback_display_cpu_processor.hh b/source/blender/imbuf/opencolorio/intern/fallback/fallback_display_cpu_processor.hh new file mode 100644 index 00000000000..dbbbce09247 --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/fallback/fallback_display_cpu_processor.hh @@ -0,0 +1,18 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include + +namespace blender::ocio { + +class Config; +class CPUProcessor; +struct DisplayParameters; + +std::shared_ptr create_fallback_display_cpu_processor( + const Config &config, const DisplayParameters &display_parameters); + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/intern/fallback/fallback_gpu_shader_binder.cc b/source/blender/imbuf/opencolorio/intern/fallback/fallback_gpu_shader_binder.cc new file mode 100644 index 00000000000..5af57f7f199 --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/fallback/fallback_gpu_shader_binder.cc @@ -0,0 +1,101 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "fallback_gpu_shader_binder.hh" + +#include + +#include "../gpu_shader_binder_internal.hh" + +namespace blender::ocio { + +namespace { + +static std::string generate_display_fragment_source( + const internal::GPUDisplayShader &display_shader) +{ + std::string source; + + /* Generate OCIO_to_scene_linear(). */ + if (display_shader.from_colorspace == "sRGB") { + /* Use Blender's default sRGB->Linear conversion. + * Expect that the alpha association is handled in the caller. */ + source += + "vec4 OCIO_to_scene_linear(vec4 pixel) {\n" + " return vec4(srgb_to_linear_rgb(pixel.rgb), pixel.a);\n" + "}\n"; + } + else { + /* Linear or Non-Color: no need to perform any conversion. */ + source += "vec4 OCIO_to_scene_linear(vec4 pixel) { return pixel; }\n"; + } + + /* Generate OCIO_to_display(). */ + if (display_shader.display == "sRGB") { + source += + "vec4 OCIO_to_display(vec4 pixel) {\n" + " return vec4(linear_rgb_to_srgb(pixel.rgb), pixel.a);\n" + "}\n"; + } + else { + /* Linear or Non-Color: no need to perform any conversion. */ + source += "vec4 OCIO_to_display(vec4 pixel) { return pixel; }\n"; + } + + return source; +} + +static std::string generate_scene_linear_fragment_source( + const internal::GPUDisplayShader &display_shader) +{ + std::string source; + + /* Generate OCIO_to_scene_linear(). */ + if (display_shader.from_colorspace == "sRGB") { + /* Use Blender's default sRGB->Linear conversion. + * Expect that the alpha association is handled in the caller. */ + source += + "vec4 OCIO_to_scene_linear(vec4 pixel) {\n" + " return vec4(srgb_to_linear_rgb(pixel.rgb), pixel.a);\n" + "}\n"; + } + else { + /* Linear or Non-Color: no need to perform any conversion. */ + source += "vec4 OCIO_to_scene_linear(vec4 pixel) { return pixel; }\n"; + } + + /* Generate OCIO_to_display(). */ + source += "vec4 OCIO_to_display(vec4 pixel) { return pixel; }\n"; + + return source; +} + +} // namespace + +void FallbackGPUShaderBinder::construct_display_shader( + internal::GPUDisplayShader &display_shader) const +{ + const std::string fragment_source = generate_display_fragment_source(display_shader); + + if (!create_gpu_shader(display_shader, fragment_source)) { + display_shader.is_valid = false; + return; + } + + display_shader.is_valid = true; +} + +void FallbackGPUShaderBinder::construct_scene_linear_shader( + internal::GPUDisplayShader &display_shader) const +{ + display_shader.is_valid = true; + + const std::string fragment_source = generate_scene_linear_fragment_source(display_shader); + + if (!create_gpu_shader(display_shader, fragment_source)) { + display_shader.is_valid = false; + } +} + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/intern/fallback/fallback_gpu_shader_binder.hh b/source/blender/imbuf/opencolorio/intern/fallback/fallback_gpu_shader_binder.hh new file mode 100644 index 00000000000..93f64064dcf --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/fallback/fallback_gpu_shader_binder.hh @@ -0,0 +1,19 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "OCIO_gpu_shader_binder.hh" + +namespace blender::ocio { + +class FallbackGPUShaderBinder : public GPUShaderBinder { + protected: + using GPUShaderBinder::GPUShaderBinder; + + void construct_display_shader(internal::GPUDisplayShader &display_shader) const override; + void construct_scene_linear_shader(internal::GPUDisplayShader &display_shader) const override; +}; + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/intern/fallback/fallback_processor_cache.cc b/source/blender/imbuf/opencolorio/intern/fallback/fallback_processor_cache.cc new file mode 100644 index 00000000000..840966f1d8f --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/fallback/fallback_processor_cache.cc @@ -0,0 +1,34 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "fallback_processor_cache.hh" + +#include "fallback_cpu_processor.hh" + +namespace blender::ocio { + +std::shared_ptr FallbackProcessorCache::get( + const StringRefNull from_colorspace, const StringRefNull to_colorspace) const +{ + if (from_colorspace == to_colorspace) { + static auto noop_cpu_processor = std::make_shared(); + return noop_cpu_processor; + } + + if (from_colorspace == "sRGB" && to_colorspace == "Linear") { + static auto srgb_to_linear_cpu_processor = + std::make_shared(); + return srgb_to_linear_cpu_processor; + } + + if (from_colorspace == "Linear" && to_colorspace == "sRGB") { + static auto linear_to_srgb_cpu_processor = + std::make_shared(); + return linear_to_srgb_cpu_processor; + } + + return nullptr; +} + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/intern/fallback/fallback_processor_cache.hh b/source/blender/imbuf/opencolorio/intern/fallback/fallback_processor_cache.hh new file mode 100644 index 00000000000..b7438fa391a --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/fallback/fallback_processor_cache.hh @@ -0,0 +1,24 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include + +#include "BLI_string_ref.hh" + +namespace blender::ocio { + +class CPUProcessor; + +class FallbackProcessorCache { + public: + /** + * Get processor to convert color space. + */ + std::shared_ptr get(StringRefNull from_colorspace, + StringRefNull to_colorspace) const; +}; + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/intern/gpu_shader_binder.cc b/source/blender/imbuf/opencolorio/intern/gpu_shader_binder.cc new file mode 100644 index 00000000000..b374961267f --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/gpu_shader_binder.cc @@ -0,0 +1,581 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "OCIO_gpu_shader_binder.hh" + +#include + +#include "MEM_guardedalloc.h" + +#include "BLI_assert.h" +#include "BLI_build_config.h" +#include "BLI_string_utils.hh" +#include "BLI_vector.hh" + +#include "BKE_colortools.hh" + +#include "GPU_immediate.hh" +#include "GPU_shader.hh" +#include "GPU_texture.hh" +#include "GPU_uniform_buffer.hh" + +#include "gpu_shader_create_info.hh" + +#include "OCIO_config.hh" + +#include "gpu_shader_binder_internal.hh" +#include "source_processor.hh" +#include "white_point.hh" + +namespace blender::ocio { +namespace internal { +/* -------------------------------------------------------------------- */ +/** \name GPUTextures + * \{ */ + +GPUTextures::~GPUTextures() +{ + for (GPULutTexture &lut : luts) { + GPU_texture_free(lut.texture); + } + if (dummy) { + GPU_texture_free(dummy); + } + if (uniforms_buffer) { + GPU_uniformbuf_free(uniforms_buffer); + } +} + +bool GPUTextures::initialize_common() +{ + dummy = GPU_texture_create_error(2, false); + + luts.clear(); + uniforms.clear(); + + return (dummy != nullptr); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name GPUTextures + * \{ */ + +GPUCurveMappping::~GPUCurveMappping() +{ + if (lut) { + MEM_freeN(lut); + } + + if (texture) { + GPU_texture_free(texture); + } + if (buffer) { + GPU_uniformbuf_free(buffer); + } +} + +void GPUCurveMappping::rasterize(CurveMapping &curve_mapping) +{ + if (lut) { + MEM_freeN(lut); + } + + BKE_curvemapping_init(&curve_mapping); + BKE_curvemapping_premultiply(&curve_mapping, false); + BKE_curvemapping_table_RGBA(&curve_mapping, &lut, &lut_size); +} + +bool GPUCurveMappping::initialize_common(const bool use_curve_mapping) +{ + if (!use_curve_mapping) { + return true; + } + + texture = GPU_texture_create_1d( + "OCIOCurveMap", lut_size, 1, GPU_RGBA16F, GPU_TEXTURE_USAGE_SHADER_READ, nullptr); + GPU_texture_filter_mode(texture, false); + GPU_texture_extend_mode(texture, GPU_SAMPLER_EXTEND_MODE_EXTEND); + + buffer = GPU_uniformbuf_create(sizeof(OCIO_GPUCurveMappingParameters)); + + if (texture == nullptr || buffer == nullptr) { + return false; + } + + return true; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name GPUDisplayShader + * \{ */ + +GPUDisplayShader::~GPUDisplayShader() +{ + if (shader) { + GPU_shader_free(shader); + } + if (parameters_buffer) { + GPU_uniformbuf_free(parameters_buffer); + } +} + +bool GPUDisplayShader::matches(const GPUDisplayParameters &display_parameters) const +{ + const bool use_curve_mapping = (display_parameters.curve_mapping != nullptr); + return (this->from_colorspace == display_parameters.from_colorspace && + this->view == display_parameters.view && this->display == display_parameters.display && + this->look == display_parameters.look && this->use_curve_mapping == use_curve_mapping); +} + +bool GPUDisplayShader::initialize_common() +{ + if (!textures.initialize_common()) { + is_valid = false; + return false; + } + + if (!curve_mapping.initialize_common(use_curve_mapping)) { + is_valid = false; + return false; + } + + return true; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name GPUShaderCache + * \{ */ + +GPUShaderCache::~GPUShaderCache() +{ + clear(); +} + +GPUDisplayShader *GPUShaderCache::get(const GPUDisplayParameters &display_parameters) +{ + for (std::list::iterator it = cache_.begin(); it != cache_.end(); it++) { + if (it->matches(display_parameters)) { + /* Move to front of the cache to mark as most recently used. */ + if (it != cache_.begin()) { + cache_.splice(cache_.begin(), cache_, it); + } + return &(*it); + } + } + return nullptr; +} + +GPUDisplayShader &GPUShaderCache::create_default() +{ + /* Remove least recently used element from cache. */ + while (cache_.size() >= MAX_SIZE) { + cache_.pop_back(); + } + + /* Create GPU shader. */ + cache_.emplace_front(); + + return cache_.front(); +} + +void GPUShaderCache::clear() +{ + cache_.clear(); +} + +/** \} */ + +} // namespace internal + +namespace { + +/* -------------------------------------------------------------------- */ +/** \name Internal implementation + * \{ */ + +/** + * Process the generated source code, doing necessary tweaks to get it compiled on the current + * backend. + * This function will do some code-level adjustments on the code generated by the OpenColorIO and + * solve all known compatibility issues. + */ +static void process_source(std::string &source) +{ + source_comment_out_uniforms(source); + source = GPU_shader_preprocess_source(source); + + /* Comparison operator in Metal returns per-element comparison and returns a vector of booleans. + * Need a special syntax to see if two vec3 are matched. + * + * NOTE: The replacement is optimized for transforming code generated by + * GradingPrimaryTransform. A more general approach is possible, but for now prefer processing + * speed. + * + * NOTE: The syntax works for all backends Blender supports. */ + BLI_string_replace( + source, "if ( gamma != vec3(1., 1., 1.) )", "if (! all(equal(gamma, vec3(1., 1., 1.))) )"); +} + +static void gpu_curve_mapping_update(internal::GPUCurveMappping &gpu_curve_mapping, + CurveMapping &curve_mapping) +{ + /* Test if we need to update. */ + /* TODO(sergey): Use more reliable cache identifier. + * Something like monotonously incrementing change counter feels to have less collisions. */ + const size_t cache_id = size_t(&curve_mapping) + curve_mapping.changed_timestamp; + if (gpu_curve_mapping.cache_id == cache_id) { + return; + } + gpu_curve_mapping.cache_id = cache_id; + + /* Update texture. */ + const int offset[3] = {0, 0, 0}; + const int extent[3] = {gpu_curve_mapping.lut_size, 0, 0}; + GPU_texture_update_sub(gpu_curve_mapping.texture, + GPU_DATA_FLOAT, + gpu_curve_mapping.lut, + UNPACK3(offset), + UNPACK3(extent)); + + /* Update uniforms. */ + OCIO_GPUCurveMappingParameters data; + for (int i = 0; i < 4; i++) { + const CurveMap &curve_map = curve_mapping.cm[i]; + data.range[i] = curve_map.range; + data.mintable[i] = curve_map.mintable; + data.ext_in_x[i] = curve_map.ext_in[0]; + data.ext_in_y[i] = curve_map.ext_in[1]; + data.ext_out_x[i] = curve_map.ext_out[0]; + data.ext_out_y[i] = curve_map.ext_out[1]; + data.first_x[i] = curve_map.table[0].x; + data.first_y[i] = curve_map.table[0].y; + data.last_x[i] = curve_map.table[CM_TABLE].x; + data.last_y[i] = curve_map.table[CM_TABLE].y; + } + for (int i = 0; i < 3; i++) { + data.black[i] = curve_mapping.black[i]; + data.bwmul[i] = curve_mapping.bwmul[i]; + } + data.lut_size = gpu_curve_mapping.lut_size; + data.use_extend_extrapolate = (curve_mapping.flag & CUMA_EXTEND_EXTRAPOLATE) != 0; + + GPU_uniformbuf_update(gpu_curve_mapping.buffer, &data); +} + +static void gpu_display_shader_parameters_update(internal::GPUDisplayShader &display_shader, + const GPUDisplayParameters &display_parameters, + float4x4 scene_linear_matrix) +{ + bool do_update = false; + if (display_shader.parameters_buffer == nullptr) { + display_shader.parameters_buffer = GPU_uniformbuf_create(sizeof(OCIO_GPUParameters)); + do_update = true; + } + + OCIO_GPUParameters &data = display_shader.parameters; + if (data.scene_linear_matrix != scene_linear_matrix) { + data.scene_linear_matrix = scene_linear_matrix; + do_update = true; + } + if (data.exponent != display_parameters.exponent) { + data.exponent = display_parameters.exponent; + do_update = true; + } + if (data.dither != display_parameters.dither) { + data.dither = display_parameters.dither; + do_update = true; + } + if (bool(data.use_predivide) != display_parameters.use_predivide) { + data.use_predivide = display_parameters.use_predivide; + do_update = true; + } + if (bool(data.do_overlay_merge) != display_parameters.do_overlay_merge) { + data.do_overlay_merge = display_parameters.do_overlay_merge; + do_update = true; + } + if (bool(data.use_hdr) != display_parameters.use_hdr) { + data.use_hdr = display_parameters.use_hdr; + do_update = true; + } + + if (do_update) { + GPU_uniformbuf_update(display_shader.parameters_buffer, &data); + } +} + +/* Bind the shader and update parameters and uniforms. */ +static bool gpu_shader_bind(const Config &config, + internal::GPUDisplayShader &display_shader, + const GPUDisplayParameters &display_parameters) +{ + using internal::TextureSlot; + using internal::UniformBufferSlot; + + /* Verify the shader is valid. */ + if (!display_shader.is_valid) { + return false; + } + + /* Update and bind curve mapping data. */ + if (display_parameters.curve_mapping) { + gpu_curve_mapping_update(display_shader.curve_mapping, *display_parameters.curve_mapping); + GPU_uniformbuf_bind(display_shader.curve_mapping.buffer, UniformBufferSlot::CURVE_MAPPING); + GPU_texture_bind(display_shader.curve_mapping.texture, TextureSlot::CURVE_MAPPING); + /* TODO(sergey): Can free the curve mapping's lookup table. + * Seems minor, maybe not that important. */ + } + + /* Bind textures to sampler units. Texture 0 is set by caller. + * Uniforms have already been set for texture bind points. */ + if (!display_parameters.do_overlay_merge) { + /* Avoid missing binds. */ + GPU_texture_bind(display_shader.textures.dummy, TextureSlot::OVERLAY); + } + for (int i = 0; i < display_shader.textures.luts.size(); i++) { + GPU_texture_bind(display_shader.textures.luts[i].texture, TextureSlot::LUTS_OFFSET + i); + } + + if (display_shader.textures.uniforms_buffer) { + GPU_uniformbuf_bind(display_shader.textures.uniforms_buffer, UniformBufferSlot::LUTS); + } + + float3x3 matrix = float3x3::identity() * display_parameters.scale; + if (display_parameters.use_white_balance) { + matrix *= calculate_white_point_matrix( + config, display_parameters.temperature, display_parameters.tint); + } + + gpu_display_shader_parameters_update(display_shader, display_parameters, float4x4(matrix)); + GPU_uniformbuf_bind(display_shader.parameters_buffer, UniformBufferSlot::DISPLAY); + + /* TODO(fclem): remove remains of IMM. */ + immBindShader(display_shader.shader); + + return true; +} + +/** \} */ + +} // namespace + +GPUShaderBinder::GPUShaderBinder(const Config &config) + : display_cache_(std::make_unique()), + scene_linear_cache_(std::make_unique()), + config_(config) +{ +} + +/* Keep private to the translation unit to allow proper destruction of smart pointers to internal + * data. */ +GPUShaderBinder::~GPUShaderBinder() = default; + +bool GPUShaderBinder::display_bind(const GPUDisplayParameters &display_parameters) const +{ + /* Attempt to get shader from the cache. */ + internal::GPUDisplayShader *display_shader = display_cache_->get(display_parameters); + + if (!display_shader) { + display_shader = &display_cache_->create_default(); + BLI_assert(display_shader); + + display_shader->from_colorspace = display_parameters.from_colorspace; + display_shader->view = display_parameters.view; + display_shader->display = display_parameters.display; + display_shader->look = display_parameters.look; + display_shader->use_curve_mapping = (display_parameters.curve_mapping != nullptr); + display_shader->is_valid = false; + + if (display_parameters.curve_mapping) { + /* Rasterize curve mapping early so that texture allocation can know the size of the lookup + * table. */ + display_shader->curve_mapping.rasterize(*display_parameters.curve_mapping); + } + + if (display_shader->initialize_common()) { + construct_display_shader(*display_shader); + } + } + else if (display_parameters.curve_mapping) { + /* Update curve mapping's lookup table. */ + display_shader->curve_mapping.rasterize(*display_parameters.curve_mapping); + } + + return gpu_shader_bind(config_, *display_shader, display_parameters); +} + +bool GPUShaderBinder::to_scene_linear_bind(const StringRefNull from_colorspace, + const bool use_predivide) const +{ + /* Re-use code and logic with the conversion to the display space. This assumes that empty names + * for display, view, and look are not valid for the OpenColorIO configuration, and so they can + * be used to indicate that the processor is used to convert from the given space to the linear. + */ + + GPUDisplayParameters display_parameters; + display_parameters.from_colorspace = from_colorspace; + display_parameters.use_predivide = use_predivide; + + /* Attempt to get shader from the cache. */ + internal::GPUDisplayShader *display_shader = scene_linear_cache_->get(display_parameters); + + if (!display_shader) { + display_shader = &display_cache_->create_default(); + BLI_assert(display_shader); + + display_shader->from_colorspace = display_parameters.from_colorspace; + + if (display_shader->initialize_common()) { + construct_scene_linear_shader(*display_shader); + } + } + + return gpu_shader_bind(config_, *display_shader, display_parameters); +} + +void GPUShaderBinder::unbind() const +{ + immUnbindProgram(); +} + +bool GPUShaderBinder::create_gpu_shader(internal::GPUDisplayShader &display_shader, + StringRefNull fragment_source) +{ + using namespace blender::gpu::shader; + + StageInterfaceInfo iface("OCIO_Interface", ""); + iface.smooth(Type::float2_t, "texCoord_interp"); + + ShaderCreateInfo info("OCIO_Display"); + + /* Work around OpenColorIO not supporting latest GLSL yet. */ + info.define("texture1D", "texture"); + info.define("texture2D", "texture"); + info.define("texture3D", "texture"); + + /* Work around unsupported in keyword in Metal GLSL emulation. */ +#if OS_MAC + info.define("in", ""); +#endif + + info.typedef_source("ocio_shader_shared.hh"); + info.sampler(internal::TextureSlot::IMAGE, ImageType::Float2D, "image_texture"); + info.sampler(internal::TextureSlot::OVERLAY, ImageType::Float2D, "overlay_texture"); + info.uniform_buf(internal::UniformBufferSlot::DISPLAY, "OCIO_GPUParameters", "parameters"); + info.push_constant(Type::float4x4_t, "ModelViewProjectionMatrix"); + info.vertex_in(0, Type::float2_t, "pos"); + info.vertex_in(1, Type::float2_t, "texCoord"); + info.vertex_out(iface); + info.fragment_out(0, Type::float4_t, "fragColor"); + info.vertex_source("gpu_shader_display_transform_vert.glsl"); + info.fragment_source("gpu_shader_display_transform_frag.glsl"); + + info.fragment_source_generated = fragment_source; + process_source(info.fragment_source_generated); + + /* #96502: Work around for incorrect OCIO GLSL code generation when using + * GradingPrimaryTransform. Should be reevaluated when changing to a next version of OCIO. + * (currently v2.1.1). */ + info.define("inf 1e32"); + + if (display_shader.use_curve_mapping) { + info.define("USE_CURVE_MAPPING"); + info.uniform_buf(internal::UniformBufferSlot::CURVE_MAPPING, + "OCIO_GPUCurveMappingParameters", + "curve_mapping"); + info.sampler( + internal::TextureSlot::CURVE_MAPPING, ImageType::Float1D, "curve_mapping_texture"); + } + + /* Set LUT textures. */ + int slot = internal::TextureSlot::LUTS_OFFSET; + for (const internal::GPULutTexture &texture : display_shader.textures.luts) { + const int dimensions = GPU_texture_dimensions(texture.texture); + const ImageType type = (dimensions == 1) ? ImageType::Float1D : + (dimensions == 2) ? ImageType::Float2D : + ImageType::Float3D; + info.sampler(slot++, type, texture.sampler_name.c_str()); + } + + /* Set LUT uniforms. */ +#if defined(WITH_OPENCOLORIO) + if (!display_shader.textures.uniforms.is_empty()) { + /* NOTE: For simplicity, we pad everything to size of vec4 avoiding sorting and alignment + * issues. It is unlikely that this becomes a real issue. */ + const size_t ubo_size = display_shader.textures.uniforms.size() * sizeof(float) * 4; + Vector ubo_data_buf(ubo_size); + + uint32_t *ubo_data = reinterpret_cast(ubo_data_buf.data()); + + std::stringstream ss; + ss << "struct OCIO_GPULutParameters {\n"; + + int index = 0; + for (internal::GPUUniform &uniform : display_shader.textures.uniforms) { + index += 1; + const OCIO_NAMESPACE::GpuShaderDesc::UniformData &data = uniform.data; + const char *name = uniform.name.c_str(); + char prefix = ' '; + int vec_len; + switch (data.m_type) { + case OCIO_NAMESPACE::UNIFORM_DOUBLE: { + vec_len = 1; + float value = float(data.m_getDouble()); + memcpy(ubo_data, &value, sizeof(float)); + break; + } + case OCIO_NAMESPACE::UNIFORM_BOOL: { + prefix = 'b'; + vec_len = 1; + int value = int(data.m_getBool()); + memcpy(ubo_data, &value, sizeof(int)); + break; + } + case OCIO_NAMESPACE::UNIFORM_FLOAT3: + vec_len = 3; + memcpy(ubo_data, data.m_getFloat3().data(), sizeof(float) * 3); + break; + case OCIO_NAMESPACE::UNIFORM_VECTOR_FLOAT: + vec_len = data.m_vectorFloat.m_getSize(); + memcpy(ubo_data, data.m_vectorFloat.m_getVector(), sizeof(float) * vec_len); + break; + case OCIO_NAMESPACE::UNIFORM_VECTOR_INT: + prefix = 'i'; + vec_len = data.m_vectorInt.m_getSize(); + memcpy(ubo_data, data.m_vectorInt.m_getVector(), sizeof(int) * vec_len); + break; + default: + continue; + } + /* Align every member to 16 bytes. */ + ubo_data += 4; + /* Use a generic variable name because some GLSL compilers can interpret the preprocessor + * define as recursive. */ + ss << " " << prefix << "vec4 var" << index << ";\n"; + /* Use a define to keep the generated code working. */ + StringRef suffix = StringRefNull("xyzw").substr(0, vec_len); + ss << "#define " << name << " lut_parameters.var" << index << "." << suffix << "\n"; + } + ss << "};\n"; + info.typedef_source_generated = ss.str(); + + info.uniform_buf(internal::UniformBufferSlot::LUTS, "OCIO_GPULutParameters", "lut_parameters"); + + display_shader.textures.uniforms_buffer = GPU_uniformbuf_create_ex( + ubo_size, ubo_data_buf.data(), "OCIO_LutParameters"); + } +#endif + + display_shader.shader = GPU_shader_create_from_info( + reinterpret_cast(&info)); + + return (display_shader.shader != nullptr); +} + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/intern/gpu_shader_binder_internal.hh b/source/blender/imbuf/opencolorio/intern/gpu_shader_binder_internal.hh new file mode 100644 index 00000000000..b6f956e9b33 --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/gpu_shader_binder_internal.hh @@ -0,0 +1,190 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** + * Functionality internal to the GPU binder implementations. It is not to be used outside of this + * module. Subclasses of the GPUShaderBinder are allowed to access these types. + * + * Implementation is in gpu_shader_binder.cc (implementation file of the public + * OCIO_gpu_shader_binder.hh). + */ + +#pragma once + +#include + +#include "BLI_string_ref.hh" +#include "BLI_utility_mixins.hh" +#include "BLI_vector.hh" + +#include "ocio_shader_shared.hh" + +#if defined(WITH_OPENCOLORIO) +# include "opencolorio.hh" +#endif + +struct CurveMapping; +struct GPUUniformBuf; +struct GPUShader; +struct GPUTexture; + +namespace blender::ocio { + +struct GPUDisplayParameters; + +namespace internal { + +/* Namespaces mnemonic index for texture slot that can be passed as integer argument. */ +struct TextureSlot { + enum { + IMAGE = 0, + OVERLAY = 1, + CURVE_MAPPING = 2, + LUTS_OFFSET = 3, + }; +}; + +/* Namespaces mnemonic index for uniform buffer slot that can be passed as integer argument. */ +struct UniformBufferSlot { + enum { + DISPLAY = 0, + CURVE_MAPPING = 1, + LUTS = 2, + }; +}; + +struct GPULutTexture { + GPUTexture *texture = nullptr; + std::string sampler_name; +}; + +struct GPUUniform { + std::string name; + + /* They are only required for processors generated by the OpenColorIO. For the simplicity of the + * internal API use OpenColorIO type (avoiding making extra copies of the data coming from the + * OpenColorIO library). */ +#if defined(WITH_OPENCOLORIO) + OCIO_NAMESPACE::GpuShaderDesc::UniformData data; +#endif +}; + +class GPUTextures : NonCopyable, NonMovable { + public: + Vector luts; + + /* Dummy in case of no overlay. */ + GPUTexture *dummy = nullptr; + + /* Uniforms */ + Vector uniforms; + GPUUniformBuf *uniforms_buffer = nullptr; + + ~GPUTextures(); + + /** + * Initialize common parts of this object: resources needed for both fall-back and OpenColorIO + * implementations. + * + * Returns true if the resources have been successfully allocated. + */ + bool initialize_common(); +}; + +class GPUCurveMappping : NonCopyable, NonMovable { + public: + int lut_size = 0; + float *lut = nullptr; + + GPUUniformBuf *buffer = nullptr; + GPUTexture *texture = nullptr; + size_t cache_id = 0; + + ~GPUCurveMappping(); + + /** + * Rasterize given curve mapping into a lookup table. + */ + void rasterize(CurveMapping &curve_mapping); + + /** + * Initialize common parts of this object: resources needed for both fall-back and OpenColorIO + * implementations. + * + * Requires rasterization to happen prior to this call. + * + * Returns true if the resources have been successfully allocated. + */ + bool initialize_common(bool use_curve_mapping); +}; + +class GPUDisplayShader : NonCopyable, NonMovable { + public: + /* Cache variables. */ + std::string from_colorspace; + std::string view; + std::string display; + std::string look; + bool use_curve_mapping = false; + + /* The shader is valid and can be bound. + * Note that the cache might contain invalid shaders to prevent Blender from attempting to keep + * re-trying to build the same failing shader. */ + bool is_valid = false; + + GPUShader *shader = nullptr; + + /* Uniform parameters. */ + OCIO_GPUParameters parameters = {}; + GPUUniformBuf *parameters_buffer = nullptr; + + GPUTextures textures; + GPUCurveMappping curve_mapping; + + ~GPUDisplayShader(); + + bool matches(const GPUDisplayParameters &display_parameters) const; + + /** + * Initialize common parts of this object: resources needed for both fall-back and OpenColorIO + * implementations. + * + * If the resources creation has failed sets is_valid to false, otherwise keeps is_valid + * unchanged. + * + * Returns true if the resources have been successfully allocated. + */ + bool initialize_common(); +}; + +class GPUShaderCache { + /* The maximum number of cached shaders. */ + static constexpr int MAX_SIZE = 4; + + public: + ~GPUShaderCache(); + + /** + * Get shader from the cache if exists, tag it as the most recently used for a faster lookup in + * the future. + */ + GPUDisplayShader *get(const GPUDisplayParameters &display_parameters); + + /** + * Create default-initialized GPUDisplayShader and put it to cache. + * The function ensures the cache has up to SHADER_CACHE_MAX_SIZE entries. + */ + GPUDisplayShader &create_default(); + + /** + * Remove all elements from the cache. + */ + void clear(); + + private: + std::list cache_; +}; + +} // namespace internal +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/intern/libocio/error_handling.cc b/source/blender/imbuf/opencolorio/intern/libocio/error_handling.cc new file mode 100644 index 00000000000..9bc16b805a3 --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/libocio/error_handling.cc @@ -0,0 +1,29 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "error_handling.hh" + +#if defined(WITH_OPENCOLORIO) + +# include "CLG_log.h" + +# include "../opencolorio.hh" + +namespace blender::ocio { + +static CLG_LogRef LOG = {"imbuf.color_management"}; + +void report_exception(const OCIO_NAMESPACE::Exception &exception) +{ + CLOG_ERROR(&LOG, "OpenColorIO Error: %s", exception.what()); +} + +void report_error(const StringRefNull error) +{ + CLOG_ERROR(&LOG, "OpenColorIO Error: %s", error.c_str()); +} + +} // namespace blender::ocio + +#endif diff --git a/source/blender/imbuf/opencolorio/intern/libocio/error_handling.hh b/source/blender/imbuf/opencolorio/intern/libocio/error_handling.hh new file mode 100644 index 00000000000..5e4c2f75000 --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/libocio/error_handling.hh @@ -0,0 +1,20 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#if defined(WITH_OPENCOLORIO) + +# include "BLI_string_ref.hh" + +# include "../opencolorio.hh" + +namespace blender::ocio { + +void report_exception(const OCIO_NAMESPACE::Exception &exception); +void report_error(StringRefNull error); + +} // namespace blender::ocio + +#endif diff --git a/source/blender/imbuf/opencolorio/intern/libocio/libocio_colorspace.cc b/source/blender/imbuf/opencolorio/intern/libocio/libocio_colorspace.cc new file mode 100644 index 00000000000..c0d27f7c154 --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/libocio/libocio_colorspace.cc @@ -0,0 +1,163 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "libocio_colorspace.hh" + +#if defined(WITH_OPENCOLORIO) + +# include + +# include "BLI_math_color.h" + +# include "../description.hh" +# include "error_handling.hh" +# include "libocio_cpu_processor.hh" +# include "libocio_processor.hh" + +namespace blender::ocio { + +static bool compare_floats(float a, float b, float abs_diff, int ulp_diff) +{ + /* Returns true if the absolute difference is smaller than abs_diff (for numbers near zero) + * or their relative difference is less than ulp_diff ULPs. Based on: + * https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ + */ + if (fabsf(a - b) < abs_diff) { + return true; + } + + if ((a < 0.0f) != (b < 0.0f)) { + return false; + } + + return (abs((*(int *)&a) - (*(int *)&b)) < ulp_diff); +} + +static bool color_space_is_invertible(const OCIO_NAMESPACE::ConstColorSpaceRcPtr &ocio_color_space) +{ + const StringRefNull family = ocio_color_space->getFamily(); + + if (family == "rrt" || family == "display") { + /* assume display and rrt transformations are not invertible in fact some of them could be, + * but it doesn't make much sense to allow use them as invertible. */ + return false; + } + + if (ocio_color_space->isData()) { + /* Data color spaces don't have transformation at all. */ + return true; + } + + if (ocio_color_space->getTransform(OCIO_NAMESPACE::COLORSPACE_DIR_TO_REFERENCE)) { + /* if there's defined transform to reference space, color space could be converted to scene + * linear. */ + return true; + } + + return true; +} + +static void color_space_is_builtin(const OCIO_NAMESPACE::ConstConfigRcPtr &ocio_config, + const OCIO_NAMESPACE::ConstColorSpaceRcPtr &ocio_color_space, + bool &is_scene_linear, + bool &is_srgb) +{ + OCIO_NAMESPACE::ConstProcessorRcPtr processor = create_ocio_processor_silent( + ocio_config, ocio_color_space->getName(), OCIO_NAMESPACE::ROLE_SCENE_LINEAR); + if (!processor) { + /* Silently ignore if no conversion possible, then it's not scene linear or sRGB. */ + is_scene_linear = false; + is_srgb = false; + return; + } + + OCIO_NAMESPACE::ConstCPUProcessorRcPtr cpu_processor = processor->getDefaultCPUProcessor(); + + is_scene_linear = true; + is_srgb = true; + for (int i = 0; i < 256; i++) { + float v = i / 255.0f; + + float cR[3] = {v, 0, 0}; + float cG[3] = {0, v, 0}; + float cB[3] = {0, 0, v}; + float cW[3] = {v, v, v}; + cpu_processor->applyRGB(cR); + cpu_processor->applyRGB(cG); + cpu_processor->applyRGB(cB); + cpu_processor->applyRGB(cW); + + /* Make sure that there is no channel crosstalk. */ + if (fabsf(cR[1]) > 1e-5f || fabsf(cR[2]) > 1e-5f || fabsf(cG[0]) > 1e-5f || + fabsf(cG[2]) > 1e-5f || fabsf(cB[0]) > 1e-5f || fabsf(cB[1]) > 1e-5f) + { + is_scene_linear = false; + is_srgb = false; + break; + } + /* Make sure that the three primaries combine linearly. */ + if (!compare_floats(cR[0], cW[0], 1e-6f, 64) || !compare_floats(cG[1], cW[1], 1e-6f, 64) || + !compare_floats(cB[2], cW[2], 1e-6f, 64)) + { + is_scene_linear = false; + is_srgb = false; + break; + } + /* Make sure that the three channels behave identically. */ + if (!compare_floats(cW[0], cW[1], 1e-6f, 64) || !compare_floats(cW[1], cW[2], 1e-6f, 64)) { + is_scene_linear = false; + is_srgb = false; + break; + } + + float out_v = (cW[0] + cW[1] + cW[2]) * (1.0f / 3.0f); + if (!compare_floats(v, out_v, 1e-6f, 64)) { + is_scene_linear = false; + } + if (!compare_floats(srgb_to_linearrgb(v), out_v, 1e-4f, 64)) { + is_srgb = false; + } + } +} + +LibOCIOColorSpace::LibOCIOColorSpace(const int index, + const OCIO_NAMESPACE::ConstConfigRcPtr &ocio_config, + const OCIO_NAMESPACE::ConstColorSpaceRcPtr &ocio_color_space) + : ocio_config_(ocio_config), + ocio_color_space_(ocio_color_space), + clean_description_(cleanup_description(ocio_color_space->getDescription())) +{ + this->index = index; + + is_inveetible_ = color_space_is_invertible(ocio_color_space); + color_space_is_builtin(ocio_config, ocio_color_space, is_scene_linear_, is_srgb_); +} + +const CPUProcessor *LibOCIOColorSpace::get_to_scene_linear_cpu_processor() const +{ + return to_scene_linear_cpu_processor_.get([&]() -> std::unique_ptr { + OCIO_NAMESPACE::ConstProcessorRcPtr ocio_processor = create_ocio_processor( + ocio_config_, ocio_color_space_->getName(), OCIO_NAMESPACE::ROLE_SCENE_LINEAR); + if (!ocio_processor) { + return nullptr; + } + return std::make_unique(ocio_processor->getDefaultCPUProcessor()); + }); +} + +const CPUProcessor *LibOCIOColorSpace::get_from_scene_linear_cpu_processor() const +{ + return from_scene_linear_cpu_processor_.get([&]() -> std::unique_ptr { + OCIO_NAMESPACE::ConstProcessorRcPtr ocio_processor = create_ocio_processor( + ocio_config_, OCIO_NAMESPACE::ROLE_SCENE_LINEAR, ocio_color_space_->getName()); + if (!ocio_processor) { + return nullptr; + } + return std::make_unique(ocio_processor->getDefaultCPUProcessor()); + }); +} + +} // namespace blender::ocio + +#endif diff --git a/source/blender/imbuf/opencolorio/intern/libocio/libocio_colorspace.hh b/source/blender/imbuf/opencolorio/intern/libocio/libocio_colorspace.hh new file mode 100644 index 00000000000..82765a1135a --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/libocio/libocio_colorspace.hh @@ -0,0 +1,75 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#if defined(WITH_OPENCOLORIO) + +# include + +# include "MEM_guardedalloc.h" + +# include "OCIO_colorspace.hh" + +# include "../cpu_processor_cache.hh" +# include "../opencolorio.hh" + +namespace blender::ocio { + +class LibOCIOColorSpace : public ColorSpace { + OCIO_NAMESPACE::ConstConfigRcPtr ocio_config_; + OCIO_NAMESPACE::ConstColorSpaceRcPtr ocio_color_space_; + + std::string clean_description_; + bool is_inveetible_ = false; + + bool is_scene_linear_ = false; + bool is_srgb_ = false; + + CPUProcessorCache to_scene_linear_cpu_processor_; + CPUProcessorCache from_scene_linear_cpu_processor_; + + public: + LibOCIOColorSpace(int index, + const OCIO_NAMESPACE::ConstConfigRcPtr &ocio_config, + const OCIO_NAMESPACE::ConstColorSpaceRcPtr &ocio_color_space); + + StringRefNull name() const override + { + /* TODO(sergey): Avoid construction StringRefNull on every call? */ + return ocio_color_space_->getName(); + } + StringRefNull description() const override + { + return clean_description_; + } + + bool is_invertible() const override + { + return is_inveetible_; + } + + bool is_scene_linear() const override + { + return is_scene_linear_; + } + bool is_srgb() const override + { + return is_srgb_; + } + + bool is_data() const override + { + return ocio_color_space_->isData(); + } + + const CPUProcessor *get_to_scene_linear_cpu_processor() const override; + const CPUProcessor *get_from_scene_linear_cpu_processor() const override; + + MEM_CXX_CLASS_ALLOC_FUNCS("LibOCIOColorSpace"); +}; + +} // namespace blender::ocio + +#endif diff --git a/source/blender/imbuf/opencolorio/intern/libocio/libocio_config.cc b/source/blender/imbuf/opencolorio/intern/libocio/libocio_config.cc new file mode 100644 index 00000000000..37996cc6bb1 --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/libocio/libocio_config.cc @@ -0,0 +1,450 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "libocio_config.hh" + +#if defined(WITH_OPENCOLORIO) + +# include +# include + +# include + +# include "BLI_array.hh" +# include "BLI_assert.h" +# include "BLI_index_range.hh" +# include "BLI_math_matrix.hh" + +# include "OCIO_matrix.hh" +# include "OCIO_role_names.hh" + +# include "error_handling.hh" +# include "libocio_colorspace.hh" +# include "libocio_cpu_processor.hh" +# include "libocio_display_processor.hh" +# include "libocio_processor.hh" + +namespace blender::ocio { + +/* -------------------------------------------------------------------- */ +/** \name Construction + * \{ */ + +std::unique_ptr LibOCIOConfig::create_from_environment() +{ + try { + OCIO_NAMESPACE::ConstConfigRcPtr ocio_config = OCIO_NAMESPACE::Config::CreateFromEnv(); + if (!ocio_config) { + return nullptr; + } + + return std::unique_ptr(new LibOCIOConfig(ocio_config)); + } + catch (OCIO_NAMESPACE::Exception &exception) { + report_exception(exception); + } + + return nullptr; +} + +std::unique_ptr LibOCIOConfig::create_from_file(const StringRefNull filename) +{ + try { + OCIO_NAMESPACE::ConstConfigRcPtr ocio_config = OCIO_NAMESPACE::Config::CreateFromFile( + filename.c_str()); + if (!ocio_config) { + return nullptr; + } + + return std::unique_ptr(new LibOCIOConfig(ocio_config)); + } + catch (OCIO_NAMESPACE::Exception &exception) { + report_exception(exception); + } + + return nullptr; +} + +LibOCIOConfig::LibOCIOConfig(const OCIO_NAMESPACE::ConstConfigRcPtr &ocio_config) +{ + BLI_assert(ocio_config); + + /* Set the global OpenColorIO configuration so that other parts of Blender can access it. For + * example, Cycles uses for own color management of textures. + * + * Acquire a pointer to the configuration and pass it around explicitly to avoid unneeded shared + * pointer acquisition. */ + OCIO_NAMESPACE::SetCurrentConfig(ocio_config); + ocio_config_ = OCIO_NAMESPACE::GetCurrentConfig(); + + initialize_color_spaces(); + initialize_looks(); + initialize_displays(); +} + +LibOCIOConfig::~LibOCIOConfig() {} + +void LibOCIOConfig::initialize_color_spaces() +{ + OCIO_NAMESPACE::ColorSpaceSetRcPtr ocio_color_spaces; + + try { + ocio_color_spaces = ocio_config_->getColorSpaces(nullptr); + } + catch (OCIO_NAMESPACE::Exception &exception) { + report_exception(exception); + return; + } + + if (!ocio_color_spaces) { + report_error("Invalid OpenColorIO configuration: color spaces set is nullptr"); + return; + } + + const int num_color_spaces = ocio_color_spaces->getNumColorSpaces(); + if (num_color_spaces < 0) { + report_error(fmt::format( + "Invalid OpenColorIO configuration: invalid number of color spaces {}", num_color_spaces)); + return; + } + + color_spaces_.reserve(num_color_spaces); + + for (const int i : IndexRange(num_color_spaces)) { + const OCIO_NAMESPACE::ConstColorSpaceRcPtr ocio_color_space = + ocio_color_spaces->getColorSpaceByIndex(i); + color_spaces_.append_as(i, ocio_config_, ocio_color_space); + } + + /* Create index array for access to the color space in alphabetic order. */ + sorted_color_space_index_.resize(num_color_spaces); + std::iota(sorted_color_space_index_.begin(), sorted_color_space_index_.end(), 0); + std::sort(sorted_color_space_index_.begin(), sorted_color_space_index_.end(), [&](int a, int b) { + return color_spaces_[a].name() < color_spaces_[b].name(); + }); +} + +void LibOCIOConfig::initialize_looks() +{ + const int num_looks = ocio_config_->getNumLooks(); + + looks_.reserve(num_looks + 1); + + /* Add entry for look None. */ + looks_.append_as(0, nullptr); + + for (const int i : IndexRange(num_looks)) { + const StringRefNull view_name = ocio_config_->getLookNameByIndex(i); + + /* Look None is built-in and always exists. Skip it from the configuration. */ + if (view_name == "None") { + continue; + } + + const OCIO_NAMESPACE::ConstLookRcPtr ocio_look = ocio_config_->getLook(view_name.c_str()); + looks_.append_as(i + 1, ocio_look); + } +} + +void LibOCIOConfig::initialize_displays() +{ + const int num_displays = ocio_config_->getNumDisplays(); + if (num_displays < 0) { + report_error(fmt::format("Invalid OpenColorIO configuration: invalid number of displays {}", + num_displays)); + return; + } + + displays_.reserve(num_displays); + + for (const int i : IndexRange(num_displays)) { + displays_.append_as(i, *this); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color space information + * \{ */ + +float3 LibOCIOConfig::get_default_luma_coefs() const +{ + try { + double rgb_double[3]; + ocio_config_->getDefaultLumaCoefs(rgb_double); + + return float3(rgb_double[0], rgb_double[1], rgb_double[2]); + } + catch (OCIO_NAMESPACE::Exception &exception) { + report_exception(exception); + } + + /* Fallback to the older Blender assumed primaries of ITU-BT.709 / sRGB, matching the + * coefficients used in the fallback implementation. */ + return float3(0.2126f, 0.7152f, 0.0722f); +} + +static bool to_scene_linear_matrix(const OCIO_NAMESPACE::ConstConfigRcPtr &ocio_config, + const StringRefNull colorspace, + float3x3 &to_scene_linear) +{ + const OCIO_NAMESPACE::ConstProcessorRcPtr processor = create_ocio_processor( + ocio_config, colorspace.c_str(), OCIO_NAMESPACE::ROLE_SCENE_LINEAR); + if (!processor) { + return false; + } + + const OCIO_NAMESPACE::ConstCPUProcessorRcPtr cpu_processor = processor->getDefaultCPUProcessor(); + to_scene_linear = float3x3::identity(); + cpu_processor->applyRGB(to_scene_linear[0]); + cpu_processor->applyRGB(to_scene_linear[1]); + cpu_processor->applyRGB(to_scene_linear[2]); + + return true; +} + +float3x3 LibOCIOConfig::get_xyz_to_scene_linear_matrix() const +{ + /* Default to ITU-BT.709 in case no appropriate transform found. + * Note XYZ is defined here as having a D65 white point. */ + float3x3 xyz_to_scene_linear = XYZ_TO_REC709; + + /* Get from OpenColorO config if it has the required roles. */ + if (!ocio_config_->hasRole(OCIO_NAMESPACE::ROLE_SCENE_LINEAR)) { + return xyz_to_scene_linear; + } + + if (ocio_config_->hasRole("aces_interchange")) { + /* Standard OpenColorIO role, defined as ACES AP0 (ACES2065-1). */ + float3x3 aces_to_scene_linear; + if (to_scene_linear_matrix(ocio_config_, "aces_interchange", aces_to_scene_linear)) { + float3x3 xyz_to_aces = math::invert(ACES_TO_XYZ); + xyz_to_scene_linear = aces_to_scene_linear * xyz_to_aces; + } + } + else if (ocio_config_->hasRole("XYZ")) { + /* Custom role used before the standard existed. */ + to_scene_linear_matrix(ocio_config_, "XYZ", xyz_to_scene_linear); + } + + return xyz_to_scene_linear; +} + +const char *LibOCIOConfig::get_color_space_from_filepath(const char *filepath) const +{ + /* If Blender specific default_byte or default_float roles exist, don't use the default rule + * which can't distinguish between these two cases automatically. */ + if (ocio_config_->filepathOnlyMatchesDefaultRule(filepath) && + (ocio_config_->hasRole(OCIO_ROLE_DEFAULT_BYTE) || + ocio_config_->hasRole(OCIO_ROLE_DEFAULT_FLOAT))) + { + return nullptr; + } + + return ocio_config_->getColorSpaceFromFilepath(filepath); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color space API + * \{ */ + +const ColorSpace *LibOCIOConfig::get_color_space(const StringRefNull name) const +{ + OCIO_NAMESPACE::ConstColorSpaceRcPtr ocio_color_space; + + try { + /* Lookup color space in the OpenColorIO, letting it resolve role name or an alias. */ + ocio_color_space = ocio_config_->getColorSpace(name.c_str()); + } + catch (OCIO_NAMESPACE::Exception &exception) { + report_exception(exception); + return nullptr; + } + + if (!ocio_color_space) { + return nullptr; + } + + for (const LibOCIOColorSpace &color_space : color_spaces_) { + /* TODO(sergey): Is there faster way to lookup Blender-side color space? + * It does not seem that pointer in ConstColorSpaceRcPtr is unique enough to use for + * comparison. */ + if (color_space.name() == ocio_color_space->getName()) { + return &color_space; + } + } + + report_error( + fmt::format("Invalid OpenColorIO configuration: color space {} not found on Blender side", + ocio_color_space->getName())); + + return nullptr; +} + +int LibOCIOConfig::get_num_color_spaces() const +{ + return color_spaces_.size(); +} + +const ColorSpace *LibOCIOConfig::get_color_space_by_index(int const index) const +{ + if (index < 0 || index >= color_spaces_.size()) { + return nullptr; + } + return &color_spaces_[index]; +} + +const ColorSpace *LibOCIOConfig::get_sorted_color_space_by_index(const int index) const +{ + BLI_assert(color_spaces_.size() == sorted_color_space_index_.size()); + if (index < 0 || index >= color_spaces_.size()) { + return nullptr; + } + return get_color_space_by_index(sorted_color_space_index_[index]); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Display API + * \{ */ + +const Display *LibOCIOConfig::get_default_display() const +{ + if (displays_.is_empty()) { + return nullptr; + } + /* Matches the behavior of OpenColorIO, but avoids using API which potentially throws exception + * and requires string lookups. */ + return &displays_[0]; +} + +const Display *LibOCIOConfig::get_display_by_name(const StringRefNull name) const +{ + //* TODO(sergey): Is there faster way to lookup Blender-side display? + for (const LibOCIODisplay &display : displays_) { + if (display.name() == name) { + return &display; + } + } + return nullptr; +} + +int LibOCIOConfig::get_num_displays() const +{ + return displays_.size(); +} + +const Display *LibOCIOConfig::get_display_by_index(int index) const +{ + if (index < 0 || index >= displays_.size()) { + return nullptr; + } + return &displays_[index]; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Display colorspace API + * \{ */ + +const ColorSpace *LibOCIOConfig::get_display_view_color_space(const StringRefNull display, + const StringRefNull view) const +{ + StringRefNull display_color_space; + + try { + display_color_space = ocio_config_->getDisplayViewColorSpaceName(display.c_str(), + view.c_str()); + /* OpenColorIO does not resolve this token for us, so do it ourselves. */ + if (strcasecmp(display_color_space.c_str(), "") == 0) { + display_color_space = display; + } + } + catch (OCIO_NAMESPACE::Exception &exception) { + report_exception(exception); + display_color_space = display; + } + + return get_color_space(display_color_space); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Look API + * \{ */ + +const Look *LibOCIOConfig::get_look_by_name(const StringRefNull name) const +{ + /* TODO(sergey): Is there faster way to lookup Blender-side look? */ + for (const LibOCIOLook &look : looks_) { + if (look.name() == name) { + return &look; + } + } + return nullptr; +} + +int LibOCIOConfig::get_num_looks() const +{ + return looks_.size(); +} + +const Look *LibOCIOConfig::get_look_by_index(const int index) const +{ + if (index < 0 || index >= looks_.size()) { + return nullptr; + } + return &looks_[index]; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Processor API + * \{ */ + +std::shared_ptr LibOCIOConfig::get_display_cpu_processor( + const DisplayParameters &display_parameters) const +{ + OCIO_NAMESPACE::ConstProcessorRcPtr processor = create_ocio_display_processor( + *this, display_parameters); + if (!processor) { + return nullptr; + } + return std::make_shared(processor->getDefaultCPUProcessor()); +} + +std::shared_ptr LibOCIOConfig::get_cpu_processor( + const StringRefNull from_colorspace, const StringRefNull to_colorspace) const +{ + const OCIO_NAMESPACE::ConstProcessorRcPtr processor = create_ocio_processor( + ocio_config_, from_colorspace.c_str(), to_colorspace.c_str()); + if (!processor) { + return nullptr; + } + return std::make_shared(processor->getDefaultCPUProcessor()); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Processor API + * \{ */ + +const GPUShaderBinder &LibOCIOConfig::get_gpu_shader_binder() const +{ + return gpu_shader_binder_; +} + +/** \} */ + +} // namespace blender::ocio + +#endif diff --git a/source/blender/imbuf/opencolorio/intern/libocio/libocio_config.hh b/source/blender/imbuf/opencolorio/intern/libocio/libocio_config.hh new file mode 100644 index 00000000000..e929780d9e1 --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/libocio/libocio_config.hh @@ -0,0 +1,106 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#if defined(WITH_OPENCOLORIO) + +# include "MEM_guardedalloc.h" + +# include "BLI_vector.hh" + +# include "OCIO_config.hh" + +# include "libocio_colorspace.hh" +# include "libocio_display.hh" +# include "libocio_gpu_shader_binder.hh" +# include "libocio_look.hh" + +# include "../opencolorio.hh" + +namespace blender::ocio { + +class ColorSpace; +class Display; +class View; + +class LibOCIOConfig : public Config { + OCIO_NAMESPACE::ConstConfigRcPtr ocio_config_; + + /* Storage of for Blender-side representation of OpenColorIO configuration. + * Note that the color spaces correspond to color spaces from OpenColorIO configuration: this + * array does not contain aliases or roles. If role or alias is to be resolved OpenColorIO is to + * be used first to provide color space name which then can be looked up in this array. */ + Vector color_spaces_; + Vector looks_; + Vector displays_; + + /* Array with indices into color_spaces_. + * color_spaces_[sorted_color_space_index_[i]] provides alphabetically sorted access. */ + Vector sorted_color_space_index_; + + LibOCIOGPUShaderBinder gpu_shader_binder_{*this}; + + public: + ~LibOCIOConfig(); + + static std::unique_ptr create_from_environment(); + static std::unique_ptr create_from_file(StringRefNull filename); + + /* Color space information. */ + float3 get_default_luma_coefs() const override; + float3x3 get_xyz_to_scene_linear_matrix() const override; + const char *get_color_space_from_filepath(const char *filepath) const override; + + /* Color space API. */ + const ColorSpace *get_color_space(StringRefNull name) const override; + int get_num_color_spaces() const override; + const ColorSpace *get_color_space_by_index(int index) const override; + const ColorSpace *get_sorted_color_space_by_index(int index) const override; + + /* Display API. */ + const Display *get_default_display() const override; + const Display *get_display_by_name(StringRefNull name) const override; + int get_num_displays() const override; + const Display *get_display_by_index(int index) const override; + + /* Display colorspace API. */ + const ColorSpace *get_display_view_color_space(StringRefNull display, + StringRefNull view) const override; + + /* Look API. */ + const Look *get_look_by_name(StringRefNull name) const override; + int get_num_looks() const override; + const Look *get_look_by_index(int index) const override; + + /* Processor API. */ + std::shared_ptr get_display_cpu_processor( + const DisplayParameters &display_parameters) const override; + std::shared_ptr get_cpu_processor( + StringRefNull from_colorspace, StringRefNull to_colorspace) const override; + + /* Processor API. */ + const GPUShaderBinder &get_gpu_shader_binder() const override; + + /* Integration with the OpenColorIO specific routines. */ + const OCIO_NAMESPACE::ConstConfigRcPtr &get_ocio_config() const + { + return ocio_config_; + } + + MEM_CXX_CLASS_ALLOC_FUNCS("LibOCIOConfig"); + + private: + explicit LibOCIOConfig(const OCIO_NAMESPACE::ConstConfigRcPtr &ocio_config); + + /* Initialize BLender-side representation of color spaces, displays, etc. from the current + * OpenColorIO configuration. */ + void initialize_color_spaces(); + void initialize_looks(); + void initialize_displays(); +}; + +} // namespace blender::ocio + +#endif diff --git a/source/blender/imbuf/opencolorio/intern/libocio/libocio_cpu_processor.cc b/source/blender/imbuf/opencolorio/intern/libocio/libocio_cpu_processor.cc new file mode 100644 index 00000000000..fb5e1ff48dc --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/libocio/libocio_cpu_processor.cc @@ -0,0 +1,111 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "libocio_cpu_processor.hh" + +#if defined(WITH_OPENCOLORIO) + +# include "OCIO_packed_image.hh" + +# include "BLI_assert.h" + +# include "error_handling.hh" + +namespace blender::ocio { + +LibOCIOCPUProcessor::LibOCIOCPUProcessor( + const OCIO_NAMESPACE::ConstCPUProcessorRcPtr &ocio_cpu_processor) + : ocio_cpu_processor_(ocio_cpu_processor) +{ + BLI_assert(ocio_cpu_processor_); +} + +void LibOCIOCPUProcessor::apply_rgb(float rgb[3]) const +{ + ocio_cpu_processor_->applyRGB(rgb); +} + +void LibOCIOCPUProcessor::apply_rgba(float rgba[4]) const +{ + ocio_cpu_processor_->applyRGBA(rgba); +} + +void LibOCIOCPUProcessor::apply_rgba_predivide(float rgba[4]) const +{ + if (rgba[3] == 1.0f || rgba[3] == 0.0f) { + apply_rgba(rgba); + return; + } + + const float alpha = rgba[3]; + const float inv_alpha = 1.0f / alpha; + + rgba[0] *= inv_alpha; + rgba[1] *= inv_alpha; + rgba[2] *= inv_alpha; + + apply_rgba(rgba); + + rgba[0] *= alpha; + rgba[1] *= alpha; + rgba[2] *= alpha; +} + +void LibOCIOCPUProcessor::apply(const PackedImage &image) const +{ + /* TODO(sergey): Support other bit depths. */ + if (image.get_bit_depth() != BitDepth::BIT_DEPTH_F32) { + return; + } + + try { + ocio_cpu_processor_->apply(image); + } + catch (OCIO_NAMESPACE::Exception &exception) { + report_exception(exception); + } +} + +void LibOCIOCPUProcessor::apply_predivide(const PackedImage &image) const +{ + /* TODO(sergey): Support other bit depths. */ + if (image.get_bit_depth() != BitDepth::BIT_DEPTH_F32) { + return; + } + + if (image.get_num_channels() == 4) { + /* Convert from premultiplied alpha to straight alpha. */ + float *pixel = reinterpret_cast(image.get_data()); + const size_t pixel_count = image.get_width() * image.get_height(); + for (size_t i = 0; i < pixel_count; i++, pixel += 4) { + const float alpha = pixel[3]; + if (alpha != 0.0f && alpha != 1.0f) { + const float inv_alpha = 1.0f / alpha; + pixel[0] *= inv_alpha; + pixel[1] *= inv_alpha; + pixel[2] *= inv_alpha; + } + } + } + + apply(image); + + if (image.get_num_channels() == 4) { + /* Back to premultiplied alpha. */ + float *pixel = reinterpret_cast(image.get_data()); + const size_t pixel_count = image.get_width() * image.get_height(); + for (size_t i = 0; i < pixel_count; i++, pixel += 4) { + const float alpha = pixel[3]; + if (alpha != 0.0f && alpha != 1.0f) { + pixel[0] *= alpha; + pixel[1] *= alpha; + pixel[2] *= alpha; + } + } + } +} + +} // namespace blender::ocio + +#endif diff --git a/source/blender/imbuf/opencolorio/intern/libocio/libocio_cpu_processor.hh b/source/blender/imbuf/opencolorio/intern/libocio/libocio_cpu_processor.hh new file mode 100644 index 00000000000..2f4071e2c4f --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/libocio/libocio_cpu_processor.hh @@ -0,0 +1,41 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#if defined(WITH_OPENCOLORIO) + +# include "MEM_guardedalloc.h" + +# include "OCIO_cpu_processor.hh" + +# include "../opencolorio.hh" + +namespace blender::ocio { + +class LibOCIOCPUProcessor : public CPUProcessor { + OCIO_NAMESPACE::ConstCPUProcessorRcPtr ocio_cpu_processor_; + + public: + explicit LibOCIOCPUProcessor(const OCIO_NAMESPACE::ConstCPUProcessorRcPtr &ocio_cpu_processor); + + bool is_noop() const override + { + return false; + } + + void apply_rgb(float rgb[3]) const override; + void apply_rgba(float rgba[4]) const override; + + void apply_rgba_predivide(float rgba[4]) const override; + + void apply(const PackedImage &image) const override; + void apply_predivide(const PackedImage &image) const override; + + MEM_CXX_CLASS_ALLOC_FUNCS("LibOCIOCPUProcessor"); +}; + +} // namespace blender::ocio + +#endif diff --git a/source/blender/imbuf/opencolorio/intern/libocio/libocio_display.cc b/source/blender/imbuf/opencolorio/intern/libocio/libocio_display.cc new file mode 100644 index 00000000000..cc888d24912 --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/libocio/libocio_display.cc @@ -0,0 +1,116 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "libocio_display.hh" + +#if defined(WITH_OPENCOLORIO) + +# include "BLI_index_range.hh" + +# include "OCIO_config.hh" + +# include "../opencolorio.hh" + +# include "error_handling.hh" +# include "libocio_config.hh" +# include "libocio_cpu_processor.hh" +# include "libocio_display_processor.hh" + +namespace blender::ocio { + +LibOCIODisplay::LibOCIODisplay(const int index, const LibOCIOConfig &config) : config_(&config) +{ + const OCIO_NAMESPACE::ConstConfigRcPtr &ocio_config = config.get_ocio_config(); + + this->index = index; + + name_ = ocio_config->getDisplay(index); + + /* Initialize views. */ + const int num_views = ocio_config->getNumViews(name_.c_str()); + if (num_views < 0) { + report_error("Invalid OpenColorIO configuration: negative number of views"); + return; + } + views_.reserve(num_views); + for (const int view_index : IndexRange(num_views)) { + const char *view_name = ocio_config->getView(name_.c_str(), view_index); + views_.append_as(view_index, view_name); + } +} + +const View *LibOCIODisplay::get_view_by_name(const StringRefNull name) const +{ + /* TODO(sergey): Is there faster way to lookup Blender-side view? */ + for (const LibOCIOView &view : views_) { + if (view.name() == name) { + return &view; + } + } + return nullptr; +} + +int LibOCIODisplay::get_num_views() const +{ + return views_.size(); +} + +const View *LibOCIODisplay::get_view_by_index(const int index) const +{ + if (index < 0 || index >= views_.size()) { + return nullptr; + } + return &views_[index]; +} + +const CPUProcessor *LibOCIODisplay::get_to_scene_linear_cpu_processor() const +{ + return to_scene_linear_cpu_processor_.get([&]() -> std::unique_ptr { + DisplayParameters display_parameters; + display_parameters.from_colorspace = OCIO_NAMESPACE::ROLE_SCENE_LINEAR; + display_parameters.view = get_default_view()->name(); + display_parameters.display = name_; + display_parameters.inverse = true; + OCIO_NAMESPACE::ConstProcessorRcPtr ocio_processor = create_ocio_display_processor( + *config_, display_parameters); + if (!ocio_processor) { + return nullptr; + } + + OCIO_NAMESPACE::ConstCPUProcessorRcPtr ocio_cpu_processor = + ocio_processor->getDefaultCPUProcessor(); + if (!ocio_cpu_processor) { + return nullptr; + } + + return std::make_unique(ocio_cpu_processor); + }); +} + +const CPUProcessor *LibOCIODisplay::get_from_scene_linear_cpu_processor() const +{ + return from_scene_linear_cpu_processor_.get([&]() -> std::unique_ptr { + DisplayParameters display_parameters; + display_parameters.from_colorspace = OCIO_NAMESPACE::ROLE_SCENE_LINEAR; + display_parameters.view = get_default_view()->name(); + display_parameters.display = name_; + OCIO_NAMESPACE::ConstProcessorRcPtr ocio_processor = create_ocio_display_processor( + *config_, display_parameters); + if (!ocio_processor) { + return nullptr; + } + + OCIO_NAMESPACE::ConstCPUProcessorRcPtr ocio_cpu_processor = + ocio_processor->getDefaultCPUProcessor(); + if (!ocio_cpu_processor) { + return nullptr; + } + + return std::make_unique(ocio_cpu_processor); + }); +} + +} // namespace blender::ocio + +#endif diff --git a/source/blender/imbuf/opencolorio/intern/libocio/libocio_display.hh b/source/blender/imbuf/opencolorio/intern/libocio/libocio_display.hh new file mode 100644 index 00000000000..20ecaebc58d --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/libocio/libocio_display.hh @@ -0,0 +1,70 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#if defined(WITH_OPENCOLORIO) + +# include + +# include "MEM_guardedalloc.h" + +# include "BLI_vector.hh" + +# include "OCIO_display.hh" + +# include "../cpu_processor_cache.hh" + +# include "libocio_view.hh" + +namespace blender::ocio { + +class LibOCIOConfig; + +class LibOCIODisplay : public Display { + /* Store by pointer to allow move semantic. + * In practice this must never be nullable. */ + const LibOCIOConfig *config_ = nullptr; + + StringRefNull name_; + Vector views_; + + CPUProcessorCache to_scene_linear_cpu_processor_; + CPUProcessorCache from_scene_linear_cpu_processor_; + + public: + LibOCIODisplay(int index, const LibOCIOConfig &config); + LibOCIODisplay(const LibOCIODisplay &other) = delete; + LibOCIODisplay(LibOCIODisplay &&other) noexcept = default; + + ~LibOCIODisplay() = default; + + LibOCIODisplay &operator=(const LibOCIODisplay &other) = delete; + LibOCIODisplay &operator=(LibOCIODisplay &&other) = default; + + StringRefNull name() const override + { + return name_; + } + + const View *get_default_view() const override + { + /* Matches the behavior of OpenColorIO, but avoids using API which potentially throws exception + * and requires string lookups. */ + return get_view_by_index(0); + } + + const View *get_view_by_name(StringRefNull name) const override; + int get_num_views() const override; + const View *get_view_by_index(int index) const override; + + const CPUProcessor *get_to_scene_linear_cpu_processor() const override; + const CPUProcessor *get_from_scene_linear_cpu_processor() const override; + + MEM_CXX_CLASS_ALLOC_FUNCS("LibOCIOConfig"); +}; + +} // namespace blender::ocio + +#endif diff --git a/source/blender/imbuf/opencolorio/intern/libocio/libocio_display_processor.cc b/source/blender/imbuf/opencolorio/intern/libocio/libocio_display_processor.cc new file mode 100644 index 00000000000..fa44f5b573c --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/libocio/libocio_display_processor.cc @@ -0,0 +1,117 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "libocio_display_processor.hh" + +#if defined(WITH_OPENCOLORIO) + +# include "BLI_math_matrix.hh" + +# include "OCIO_config.hh" + +# include "error_handling.hh" +# include "libocio_config.hh" + +# include "../white_point.hh" + +namespace blender::ocio { + +OCIO_NAMESPACE::ConstProcessorRcPtr create_ocio_display_processor( + const LibOCIOConfig &config, const DisplayParameters &display_parameters) +{ + using namespace OCIO_NAMESPACE; + + const ConstConfigRcPtr &ocio_config = config.get_ocio_config(); + + GroupTransformRcPtr group = GroupTransform::Create(); + + const char *from_colorspace = display_parameters.from_colorspace.c_str(); + + /* Linear transforms. */ + if (display_parameters.scale != 1.0f || display_parameters.use_white_balance) { + /* Always apply exposure and/or white balance in scene linear. */ + ColorSpaceTransformRcPtr ct = ColorSpaceTransform::Create(); + ct->setSrc(from_colorspace); + ct->setDst(ROLE_SCENE_LINEAR); + group->appendTransform(ct); + + /* Make further transforms aware of the color space change. */ + from_colorspace = ROLE_SCENE_LINEAR; + + /* Apply scale. */ + MatrixTransformRcPtr mt = MatrixTransform::Create(); + float3x3 matrix = float3x3::identity() * display_parameters.scale; + + /* Apply white balance. */ + if (display_parameters.use_white_balance) { + matrix *= calculate_white_point_matrix( + config, display_parameters.temperature, display_parameters.tint); + } + + mt->setMatrix(double4x4(math::transpose(matrix)).base_ptr()); + group->appendTransform(mt); + } + + /* Add look transform. */ + bool use_look = (display_parameters.look != nullptr && display_parameters.look[0] != '\0'); + if (use_look) { + const char *look_output = LookTransform::GetLooksResultColorSpace( + ocio_config, ocio_config->getCurrentContext(), display_parameters.look.c_str()); + + if (look_output != nullptr && look_output[0] != 0) { + LookTransformRcPtr lt = LookTransform::Create(); + lt->setSrc(from_colorspace); + lt->setDst(look_output); + lt->setLooks(display_parameters.look.c_str()); + group->appendTransform(lt); + + /* Make further transforms aware of the color space change. */ + from_colorspace = look_output; + } + else { + /* For empty looks, no output color space is returned. */ + use_look = false; + } + } + + /* Add view and display transform. */ + DisplayViewTransformRcPtr dvt = DisplayViewTransform::Create(); + dvt->setSrc(from_colorspace); + dvt->setLooksBypass(use_look); + dvt->setView(display_parameters.view.c_str()); + dvt->setDisplay(display_parameters.display.c_str()); + group->appendTransform(dvt); + + /* Gamma. */ + if (display_parameters.exponent != 1.0f) { + ExponentTransformRcPtr et = ExponentTransform::Create(); + const double value[4] = {display_parameters.exponent, + display_parameters.exponent, + display_parameters.exponent, + 1.0}; + et->setValue(value); + group->appendTransform(et); + } + + if (display_parameters.inverse) { + group->setDirection(TRANSFORM_DIR_INVERSE); + } + + /* Create processor from transform. This is the moment were OCIO validates the entire transform, + * no need to check for the validity of inputs above. */ + ConstProcessorRcPtr p; + try { + p = ocio_config->getProcessor(group); + } + catch (Exception &exception) { + report_exception(exception); + return nullptr; + } + + return p; +} + +} // namespace blender::ocio + +#endif diff --git a/source/blender/imbuf/opencolorio/intern/libocio/libocio_display_processor.hh b/source/blender/imbuf/opencolorio/intern/libocio/libocio_display_processor.hh new file mode 100644 index 00000000000..01688bd840c --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/libocio/libocio_display_processor.hh @@ -0,0 +1,21 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#if defined(WITH_OPENCOLORIO) + +# include "../opencolorio.hh" + +namespace blender::ocio { + +class LibOCIOConfig; +struct DisplayParameters; + +OCIO_NAMESPACE::ConstProcessorRcPtr create_ocio_display_processor( + const LibOCIOConfig &config, const DisplayParameters &display_parameters); + +} // namespace blender::ocio + +#endif diff --git a/source/blender/imbuf/opencolorio/intern/libocio/libocio_gpu_shader_binder.cc b/source/blender/imbuf/opencolorio/intern/libocio/libocio_gpu_shader_binder.cc new file mode 100644 index 00000000000..eb6e0d509b7 --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/libocio/libocio_gpu_shader_binder.cc @@ -0,0 +1,249 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "libocio_gpu_shader_binder.hh" + +#if defined(WITH_OPENCOLORIO) + +# include "GPU_texture.hh" + +# include "error_handling.hh" +# include "libocio_config.hh" +# include "libocio_display_processor.hh" +# include "libocio_processor.hh" + +# include "../gpu_shader_binder_internal.hh" + +namespace blender::ocio { + +namespace { + +using namespace OCIO_NAMESPACE; + +static ConstProcessorRcPtr create_to_scene_linear_processor( + const ConstConfigRcPtr &ocio_config, const internal::GPUDisplayShader &display_shader) +{ + return create_ocio_processor( + ocio_config, display_shader.from_colorspace.c_str(), ROLE_SCENE_LINEAR); +} + +static ConstProcessorRcPtr create_to_display_processor( + const LibOCIOConfig &config, const internal::GPUDisplayShader &display_shader) +{ + DisplayParameters display_parameters; + display_parameters.from_colorspace = ROLE_SCENE_LINEAR; + display_parameters.view = display_shader.view; + display_parameters.display = display_shader.display; + display_parameters.look = display_shader.look; + return create_ocio_display_processor(config, display_parameters); +} + +static ConstProcessorRcPtr create_noop_processor(const ConstConfigRcPtr &ocio_config) +{ + return create_ocio_processor(ocio_config, ROLE_SCENE_LINEAR, ROLE_SCENE_LINEAR); +} + +static bool add_gpu_uniform(internal::GPUTextures &textures, + const GpuShaderDescRcPtr &shader_desc, + const int index) +{ + internal::GPUUniform uniform; + uniform.name = shader_desc->getUniform(index, uniform.data); + if (uniform.data.m_type == UNIFORM_UNKNOWN) { + return false; + } + + textures.uniforms.append(uniform); + return true; +} + +static bool add_gpu_lut_1D2D(internal::GPUTextures &textures, + const GpuShaderDescRcPtr &shader_desc, + const int index) +{ + const char *texture_name = nullptr; + const char *sampler_name = nullptr; + unsigned int width = 0; + unsigned int height = 0; + + GpuShaderCreator::TextureType channel = GpuShaderCreator::TEXTURE_RGB_CHANNEL; + Interpolation interpolation = INTERP_LINEAR; + + /* Always use 2D textures in OpenColorIO 2.3, simpler and same performance. */ + static_assert(OCIO_VERSION_HEX >= 0x02030000); + GpuShaderDesc::TextureDimensions dimensions = GpuShaderDesc::TEXTURE_2D; + shader_desc->getTexture( + index, texture_name, sampler_name, width, height, channel, dimensions, interpolation); + + const float *values; + shader_desc->getTextureValues(index, values); + if (texture_name == nullptr || sampler_name == nullptr || width == 0 || height == 0 || + values == nullptr) + { + return false; + } + + eGPUTextureFormat format = (channel == GpuShaderCreator::TEXTURE_RGB_CHANNEL) ? GPU_RGB16F : + GPU_R16F; + + internal::GPULutTexture lut; + lut.texture = GPU_texture_create_2d( + texture_name, width, height, 1, format, GPU_TEXTURE_USAGE_SHADER_READ, values); + if (lut.texture == nullptr) { + return false; + } + + GPU_texture_filter_mode(lut.texture, interpolation != INTERP_NEAREST); + GPU_texture_extend_mode(lut.texture, GPU_SAMPLER_EXTEND_MODE_EXTEND); + + lut.sampler_name = sampler_name; + + textures.luts.append(lut); + + return true; +} + +static bool add_gpu_lut_3D(internal::GPUTextures &textures, + const GpuShaderDescRcPtr &shader_desc, + const int index) +{ + const char *texture_name = nullptr; + const char *sampler_name = nullptr; + unsigned int edgelen = 0; + Interpolation interpolation = INTERP_LINEAR; + shader_desc->get3DTexture(index, texture_name, sampler_name, edgelen, interpolation); + + const float *values; + shader_desc->get3DTextureValues(index, values); + if (texture_name == nullptr || sampler_name == nullptr || edgelen == 0 || values == nullptr) { + return false; + } + + internal::GPULutTexture lut; + lut.texture = GPU_texture_create_3d(texture_name, + edgelen, + edgelen, + edgelen, + 1, + GPU_RGB16F, + GPU_TEXTURE_USAGE_SHADER_READ, + values); + if (lut.texture == nullptr) { + return false; + } + + GPU_texture_filter_mode(lut.texture, interpolation != INTERP_NEAREST); + GPU_texture_extend_mode(lut.texture, GPU_SAMPLER_EXTEND_MODE_EXTEND); + + lut.sampler_name = sampler_name; + + textures.luts.append(lut); + return true; +} + +static bool create_gpu_textures(internal::GPUTextures &textures, + const GpuShaderDescRcPtr &shader_desc) +{ + for (int index = 0; index < shader_desc->getNumUniforms(); index++) { + if (!add_gpu_uniform(textures, shader_desc, index)) { + return false; + } + } + for (int index = 0; index < shader_desc->getNumTextures(); index++) { + if (!add_gpu_lut_1D2D(textures, shader_desc, index)) { + return false; + } + } + for (int index = 0; index < shader_desc->getNum3DTextures(); index++) { + if (!add_gpu_lut_3D(textures, shader_desc, index)) { + return false; + } + } + + return true; +} + +} // namespace + +void LibOCIOGPUShaderBinder::construct_shader_for_processors( + internal::GPUDisplayShader &display_shader, + ConstProcessorRcPtr &processor_to_scene_linear, + ConstProcessorRcPtr processor_to_display) const +{ + GpuShaderDescRcPtr shaderdesc_to_scene_linear = GpuShaderDesc::CreateShaderDesc(); + shaderdesc_to_scene_linear->setLanguage(GPU_LANGUAGE_GLSL_1_3); + shaderdesc_to_scene_linear->setFunctionName("OCIO_to_scene_linear"); + shaderdesc_to_scene_linear->setResourcePrefix("to_scene"); + processor_to_scene_linear->getDefaultGPUProcessor()->extractGpuShaderInfo( + shaderdesc_to_scene_linear); + shaderdesc_to_scene_linear->finalize(); + + GpuShaderDescRcPtr shaderdesc_to_display = GpuShaderDesc::CreateShaderDesc(); + shaderdesc_to_display->setLanguage(GPU_LANGUAGE_GLSL_1_3); + shaderdesc_to_display->setFunctionName("OCIO_to_display"); + shaderdesc_to_display->setResourcePrefix("to_display"); + processor_to_display->getDefaultGPUProcessor()->extractGpuShaderInfo(shaderdesc_to_display); + shaderdesc_to_display->finalize(); + + /* Create GPU textures. */ + if (!create_gpu_textures(display_shader.textures, shaderdesc_to_scene_linear) || + !create_gpu_textures(display_shader.textures, shaderdesc_to_display)) + { + display_shader.is_valid = false; + return; + } + + std::string fragment_source; + fragment_source += shaderdesc_to_scene_linear->getShaderText(); + fragment_source += "\n"; + fragment_source += shaderdesc_to_display->getShaderText(); + fragment_source += "\n"; + + if (!create_gpu_shader(display_shader, fragment_source)) { + display_shader.is_valid = false; + return; + } + + display_shader.is_valid = true; +} + +void LibOCIOGPUShaderBinder::construct_display_shader( + internal::GPUDisplayShader &display_shader) const +{ + const LibOCIOConfig &config = static_cast(config_); + const OCIO_NAMESPACE::ConstConfigRcPtr &ocio_config = config.get_ocio_config(); + + ConstProcessorRcPtr processor_to_scene_linear = create_to_scene_linear_processor(ocio_config, + display_shader); + ConstProcessorRcPtr processor_to_display = create_to_display_processor(config, display_shader); + + if (!processor_to_scene_linear || !processor_to_display) { + display_shader.is_valid = false; + return; + } + + construct_shader_for_processors(display_shader, processor_to_scene_linear, processor_to_display); +} + +void LibOCIOGPUShaderBinder::construct_scene_linear_shader( + internal::GPUDisplayShader &display_shader) const +{ + const LibOCIOConfig &config = static_cast(config_); + const OCIO_NAMESPACE::ConstConfigRcPtr &ocio_config = config.get_ocio_config(); + + ConstProcessorRcPtr processor_to_scene_linear = create_to_scene_linear_processor(ocio_config, + display_shader); + ConstProcessorRcPtr processor_to_display = create_noop_processor(ocio_config); + + if (!processor_to_scene_linear || !processor_to_display) { + display_shader.is_valid = false; + return; + } + + construct_shader_for_processors(display_shader, processor_to_scene_linear, processor_to_display); +} + +} // namespace blender::ocio + +#endif diff --git a/source/blender/imbuf/opencolorio/intern/libocio/libocio_gpu_shader_binder.hh b/source/blender/imbuf/opencolorio/intern/libocio/libocio_gpu_shader_binder.hh new file mode 100644 index 00000000000..ed47fa8ac8d --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/libocio/libocio_gpu_shader_binder.hh @@ -0,0 +1,36 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#if defined(WITH_OPENCOLORIO) + +# include "MEM_guardedalloc.h" + +# include "OCIO_gpu_shader_binder.hh" + +# include "../opencolorio.hh" + +namespace blender::ocio { + +class LibOCIOGPUShaderBinder : public GPUShaderBinder { + public: + using GPUShaderBinder::GPUShaderBinder; + + MEM_CXX_CLASS_ALLOC_FUNCS("LibOCIOGPUShaderBinder"); + + private: + void construct_shader_for_processors( + internal::GPUDisplayShader &display_shader, + OCIO_NAMESPACE::ConstProcessorRcPtr &processor_to_scene_linear, + OCIO_NAMESPACE::ConstProcessorRcPtr processor_to_display) const; + + protected: + void construct_display_shader(internal::GPUDisplayShader &display_shader) const override; + void construct_scene_linear_shader(internal::GPUDisplayShader &display_shader) const override; +}; + +} // namespace blender::ocio + +#endif diff --git a/source/blender/imbuf/opencolorio/intern/libocio/libocio_look.cc b/source/blender/imbuf/opencolorio/intern/libocio/libocio_look.cc new file mode 100644 index 00000000000..8f6a5f9bde9 --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/libocio/libocio_look.cc @@ -0,0 +1,32 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "libocio_look.hh" + +#if defined(WITH_OPENCOLORIO) + +# include "../view_specific_look.hh" + +namespace blender::ocio { + +LibOCIOLook::LibOCIOLook(const int index, const OCIO_NAMESPACE::ConstLookRcPtr &ocio_look) + : ocio_look_(ocio_look) +{ + this->index = index; + this->is_noop = (ocio_look == nullptr); + + if (ocio_look_) { + const StringRefNull look_name = ocio_look_->getName(); + + StringRef view, ui_name; + if (split_view_specific_look(look_name, view, ui_name)) { + view_ = view; + ui_name_ = ui_name; + } + } +} + +} // namespace blender::ocio + +#endif diff --git a/source/blender/imbuf/opencolorio/intern/libocio/libocio_look.hh b/source/blender/imbuf/opencolorio/intern/libocio/libocio_look.hh new file mode 100644 index 00000000000..aa5ec5edf40 --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/libocio/libocio_look.hh @@ -0,0 +1,64 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#if defined(WITH_OPENCOLORIO) + +# include "MEM_guardedalloc.h" + +# include + +# include "OCIO_look.hh" + +# include "../opencolorio.hh" + +namespace blender::ocio { + +class LibOCIOLook : public Look { + OCIO_NAMESPACE::ConstLookRcPtr ocio_look_; + + /* View and interface name for view specific look. */ + /* TODO(sergey): Use StringRef when all users supports non-null-terminated strings. */ + std::string view_; + std::string ui_name_; + + public: + LibOCIOLook(int index, const OCIO_NAMESPACE::ConstLookRcPtr &ocio_look); + + StringRefNull name() const override + { + if (ocio_look_) { + return ocio_look_->getName(); + } + return "None"; + } + + StringRefNull ui_name() const override + { + if (ui_name_.empty()) { + return name(); + } + return ui_name_; + } + + StringRefNull view() const override + { + return view_; + } + + StringRefNull process_space() const override + { + if (ocio_look_) { + return ocio_look_->getProcessSpace(); + } + return ""; + } + + MEM_CXX_CLASS_ALLOC_FUNCS("LibOCIOLook"); +}; + +} // namespace blender::ocio + +#endif diff --git a/source/blender/imbuf/opencolorio/intern/libocio/libocio_processor.cc b/source/blender/imbuf/opencolorio/intern/libocio/libocio_processor.cc new file mode 100644 index 00000000000..3d863c1c9c2 --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/libocio/libocio_processor.cc @@ -0,0 +1,46 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "libocio_processor.hh" + +#if defined(WITH_OPENCOLORIO) + +# include "error_handling.hh" + +namespace blender::ocio { + +OCIO_NAMESPACE::ConstProcessorRcPtr create_ocio_processor( + const OCIO_NAMESPACE::ConstConfigRcPtr &ocio_config, + const StringRefNull from_colorspace, + const StringRefNull to_colorspace) +{ + try { + OCIO_NAMESPACE::ConstProcessorRcPtr processor = ocio_config->getProcessor( + from_colorspace.c_str(), to_colorspace.c_str()); + return processor; + } + catch (OCIO_NAMESPACE::Exception &exception) { + report_exception(exception); + } + return nullptr; +} + +OCIO_NAMESPACE::ConstProcessorRcPtr create_ocio_processor_silent( + const OCIO_NAMESPACE::ConstConfigRcPtr &ocio_config, + const StringRefNull from_colorspace, + const StringRefNull to_colorspace) +{ + try { + OCIO_NAMESPACE::ConstProcessorRcPtr processor = ocio_config->getProcessor( + from_colorspace.c_str(), to_colorspace.c_str()); + return processor; + } + catch (OCIO_NAMESPACE::Exception &exception) { + } + return nullptr; +} + +} // namespace blender::ocio + +#endif diff --git a/source/blender/imbuf/opencolorio/intern/libocio/libocio_processor.hh b/source/blender/imbuf/opencolorio/intern/libocio/libocio_processor.hh new file mode 100644 index 00000000000..5f46406b5d6 --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/libocio/libocio_processor.hh @@ -0,0 +1,34 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#if defined(WITH_OPENCOLORIO) + +# include "BLI_string_ref.hh" + +# include "../opencolorio.hh" + +namespace blender::ocio { + +class LibOCIOConfig; + +/** + * Create OpenColorIO processor between frames. + * If the processor can not be created returns nullptr. + * + * The silent version does not print any errors if the processor creation has failed. + */ +OCIO_NAMESPACE::ConstProcessorRcPtr create_ocio_processor( + const OCIO_NAMESPACE::ConstConfigRcPtr &ocio_config, + StringRefNull from_colorspace, + StringRefNull to_colorspace); +OCIO_NAMESPACE::ConstProcessorRcPtr create_ocio_processor_silent( + const OCIO_NAMESPACE::ConstConfigRcPtr &ocio_config, + StringRefNull from_colorspace, + StringRefNull to_colorspace); + +} // namespace blender::ocio + +#endif diff --git a/source/blender/imbuf/opencolorio/intern/libocio/libocio_view.hh b/source/blender/imbuf/opencolorio/intern/libocio/libocio_view.hh new file mode 100644 index 00000000000..8c690a02b93 --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/libocio/libocio_view.hh @@ -0,0 +1,36 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#if defined(WITH_OPENCOLORIO) + +# include "MEM_guardedalloc.h" + +# include "OCIO_view.hh" + +# include "BLI_string_ref.hh" + +namespace blender::ocio { + +class LibOCIOView : public View { + StringRefNull name_; + + public: + LibOCIOView(const int index, const StringRefNull name) : name_(name) + { + this->index = index; + } + + StringRefNull name() const override + { + return name_; + } + + MEM_CXX_CLASS_ALLOC_FUNCS("LibOCIOView"); +}; + +} // namespace blender::ocio + +#endif diff --git a/intern/opencolorio/ocio_shader_shared.hh b/source/blender/imbuf/opencolorio/intern/ocio_shader_shared.hh similarity index 96% rename from intern/opencolorio/ocio_shader_shared.hh rename to source/blender/imbuf/opencolorio/intern/ocio_shader_shared.hh index d84edbb306f..331ed8f6d96 100644 --- a/intern/opencolorio/ocio_shader_shared.hh +++ b/source/blender/imbuf/opencolorio/intern/ocio_shader_shared.hh @@ -34,7 +34,7 @@ struct OCIO_GPUParameters { float dither; float exponent; bool32_t use_predivide; - bool32_t use_overlay; + bool32_t do_overlay_merge; bool32_t use_hdr; int _pad0; int _pad1; diff --git a/source/blender/imbuf/opencolorio/intern/opencolorio.hh b/source/blender/imbuf/opencolorio/intern/opencolorio.hh new file mode 100644 index 00000000000..9b613d4702c --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/opencolorio.hh @@ -0,0 +1,18 @@ +/* SPDX-FileCopyrightText: 2015 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4251 4275) +#endif + +#ifdef WITH_OPENCOLORIO +# include +#endif + +#ifdef _MSC_VER +# pragma warning(pop) +#endif diff --git a/source/blender/imbuf/opencolorio/intern/source_processor.cc b/source/blender/imbuf/opencolorio/intern/source_processor.cc new file mode 100644 index 00000000000..249f955eeba --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/source_processor.cc @@ -0,0 +1,22 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "source_processor.hh" + +namespace blender::ocio { + +void source_comment_out_uniforms(std::string &source) +{ + size_t index = 0; + while (true) { + index = source.find("uniform ", index); + if (index == -1) { + break; + } + source.replace(index, 2, "//"); + index += 2; + } +} + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/intern/source_processor.hh b/source/blender/imbuf/opencolorio/intern/source_processor.hh new file mode 100644 index 00000000000..9970e6aaaf6 --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/source_processor.hh @@ -0,0 +1,18 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include + +namespace blender::ocio { + +/** + * Comment out all uniform statements. This avoids double declarations from the backend. + * This function modifies source in-place without adding extra characters. This means that + * statement like `uniform vec3 pos;` becomes `//iform vec3 pos;`. + */ +void source_comment_out_uniforms(std::string &source); + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/intern/source_processor_test.cc b/source/blender/imbuf/opencolorio/intern/source_processor_test.cc new file mode 100644 index 00000000000..252a9b26d64 --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/source_processor_test.cc @@ -0,0 +1,26 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "source_processor.hh" + +#include "testing/testing.h" + +namespace blender::ocio { + +TEST(ocio_source_processor, source_comment_out_uniforms) +{ + { + std::string source = "int main() { return 0; }"; + source_comment_out_uniforms(source); + EXPECT_EQ(source, "int main() { return 0; }"); + } + + { + std::string source = "uniform vec3 pos;\nuniform vec4 color;\n"; + source_comment_out_uniforms(source); + EXPECT_EQ(source, "//iform vec3 pos;\n//iform vec4 color;\n"); + } +} + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/intern/version.cc b/source/blender/imbuf/opencolorio/intern/version.cc new file mode 100644 index 00000000000..a98e9cea3c0 --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/version.cc @@ -0,0 +1,21 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "OCIO_version.hh" + +#include "opencolorio.hh" + +namespace blender::ocio { + +Version get_version() +{ +#ifdef WITH_OPENCOLORIO + const int version_hex = OCIO_NAMESPACE::GetVersionHex(); +#else + const int version_hex = 0; +#endif + return {version_hex >> 24, (version_hex >> 16) & 0xff, (version_hex >> 8) & 0xff}; +} + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/intern/view_specific_look.cc b/source/blender/imbuf/opencolorio/intern/view_specific_look.cc new file mode 100644 index 00000000000..654099d8541 --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/view_specific_look.cc @@ -0,0 +1,24 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "view_specific_look.hh" + +namespace blender::ocio { + +bool split_view_specific_look(const StringRef look_name, StringRef &view, StringRef &ui_name) +{ + const int64_t separator_offset = look_name.find(" - "); + if (separator_offset == -1) { + view = {}; + ui_name = look_name; + return false; + } + + view = look_name.substr(0, separator_offset); + ui_name = look_name.substr(separator_offset + 3); + + return true; +} + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/intern/view_specific_look.hh b/source/blender/imbuf/opencolorio/intern/view_specific_look.hh new file mode 100644 index 00000000000..8a1922e1625 --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/view_specific_look.hh @@ -0,0 +1,23 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_string_ref.hh" + +namespace blender::ocio { + +/** + * Split the view-specific look name to a view name and look name for the interface. + * + * Look is considered to be view-specific when it contains dash in its name. In this case the part + * of the look name is considered to be the name of the view the look is specific to. + * + * If the look is not view-specific view is an empty string and ui name is the look name. + * + * Returns true if the look name is view-specific. + */ +bool split_view_specific_look(StringRef look_name, StringRef &view, StringRef &ui_name); + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/intern/view_specific_look_test.cc b/source/blender/imbuf/opencolorio/intern/view_specific_look_test.cc new file mode 100644 index 00000000000..78b58fa0bba --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/view_specific_look_test.cc @@ -0,0 +1,42 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "view_specific_look.hh" + +#include "testing/testing.h" + +namespace blender::ocio { + +TEST(ocio_view_look, split_view_specific_look) +{ + { + StringRef view, ui_name; + EXPECT_FALSE(split_view_specific_look("", view, ui_name)); + EXPECT_EQ(view, ""); + EXPECT_EQ(ui_name, ""); + } + + { + StringRef view, ui_name; + EXPECT_FALSE(split_view_specific_look("Very Low Contrast", view, ui_name)); + EXPECT_EQ(view, ""); + EXPECT_EQ(ui_name, "Very Low Contrast"); + } + + { + StringRef view, ui_name; + EXPECT_TRUE(split_view_specific_look("AgX - Punchy", view, ui_name)); + EXPECT_EQ(view, "AgX"); + EXPECT_EQ(ui_name, "Punchy"); + } + + { + StringRef view, ui_name; + EXPECT_TRUE(split_view_specific_look("AgX - Punchy - New", view, ui_name)); + EXPECT_EQ(view, "AgX"); + EXPECT_EQ(ui_name, "Punchy - New"); + } +} + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/intern/white_point.cc b/source/blender/imbuf/opencolorio/intern/white_point.cc new file mode 100644 index 00000000000..6ab1520f75d --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/white_point.cc @@ -0,0 +1,34 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "white_point.hh" + +#include "BLI_math_color.hh" +#include "BLI_math_matrix.hh" + +#include "OCIO_config.hh" + +namespace blender::ocio { + +float3x3 calculate_white_point_matrix(const Config &config, + const float temperature, + const float tint) +{ + /* Compute white point of the scene space in XYZ. */ + const float3x3 xyz_to_scene = config.get_xyz_to_scene_linear_matrix(); + const float3x3 scene_to_xyz = math::invert(xyz_to_scene); + const float3 target = scene_to_xyz * float3(1.0f); + + /* Add operations to the matrix. + * Note: Since we're multiplying from the right, the operations here will be performed in + * reverse list order (scene-to-XYZ, then adaption, then XYZ-to-scene, then exposure). */ + float3x3 matrix = xyz_to_scene; + matrix *= math::chromatic_adaption_matrix(math::whitepoint_from_temp_tint(temperature, tint), + target); + matrix *= scene_to_xyz; + + return matrix; +} + +} // namespace blender::ocio diff --git a/source/blender/imbuf/opencolorio/intern/white_point.hh b/source/blender/imbuf/opencolorio/intern/white_point.hh new file mode 100644 index 00000000000..a6f93bf5fb3 --- /dev/null +++ b/source/blender/imbuf/opencolorio/intern/white_point.hh @@ -0,0 +1,15 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_math_matrix_types.hh" + +namespace blender::ocio { + +class Config; + +float3x3 calculate_white_point_matrix(const Config &config, float temperature, float tint); + +} // namespace blender::ocio diff --git a/intern/opencolorio/gpu_shader_display_transform_frag.glsl b/source/blender/imbuf/opencolorio/shaders/gpu_shader_display_transform_frag.glsl similarity index 64% rename from intern/opencolorio/gpu_shader_display_transform_frag.glsl rename to source/blender/imbuf/opencolorio/shaders/gpu_shader_display_transform_frag.glsl index 2ac97aaa37e..9c05533a470 100644 --- a/intern/opencolorio/gpu_shader_display_transform_frag.glsl +++ b/source/blender/imbuf/opencolorio/shaders/gpu_shader_display_transform_frag.glsl @@ -4,6 +4,46 @@ /* Blender OpenColorIO implementation */ +/* -------------------------------------------------------------------- */ +/** \name Hardcoded color space conversion for fallback implementation + * + * NOTE: It is tempting to include gpu_shader_common_color_utils.glsl, but it should not be done + * here as that header is intended to be used from the node shaders, and the source processor does + * much more than simply including the file (it also follows some implicit dependencies that is + * undesired here, and might break since we do not use node shaders here. + * \{ */ + +float srgb_to_linear_rgb(float color) +{ + if (color < 0.04045f) { + return (color < 0.0f) ? 0.0f : color * (1.0f / 12.92f); + } + return pow((color + 0.055f) * (1.0f / 1.055f), 2.4f); +} + +float3 srgb_to_linear_rgb(float3 color) +{ + return float3( + srgb_to_linear_rgb(color.r), srgb_to_linear_rgb(color.g), srgb_to_linear_rgb(color.b)); +} + +float linear_rgb_to_srgb(float color) +{ + if (color < 0.0031308f) { + return (color < 0.0f) ? 0.0f : color * 12.92f; + } + + return 1.055f * pow(color, 1.0f / 2.4f) - 0.055f; +} + +float3 linear_rgb_to_srgb(float3 color) +{ + return float3( + linear_rgb_to_srgb(color.r), linear_rgb_to_srgb(color.g), linear_rgb_to_srgb(color.b)); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Curve Mapping Implementation * \{ */ @@ -15,7 +55,7 @@ float read_curve_mapping(int table, int index) return texelFetch(curve_mapping_texture, index, 0)[table]; } -float curvemap_calc_extend(int table, float x, vec2 first, vec2 last) +float curvemap_calc_extend(int table, float x, float2 first, float2 last) { if (x <= first[0]) { if (curve_mapping.use_extend_extrapolate == 0) { @@ -62,8 +102,8 @@ float curvemap_evaluateF(int table, float value) if (fi < 0.0 || fi > float(CM_TABLE)) { return curvemap_calc_extend(table, value, - vec2(curve_mapping.first_x[table], curve_mapping.first_y[table]), - vec2(curve_mapping.last_x[table], curve_mapping.last_y[table])); + float2(curve_mapping.first_x[table], curve_mapping.first_y[table]), + float2(curve_mapping.last_x[table], curve_mapping.last_y[table])); } else { if (i < 0) { @@ -79,11 +119,11 @@ float curvemap_evaluateF(int table, float value) } } -vec4 curvemapping_evaluate_premulRGBF(vec4 col) +float4 curvemapping_evaluate_premulRGBF(float4 col) { col.rgb = (col.rgb - curve_mapping.black.rgb) * curve_mapping.bwmul.rgb; - vec4 result; + float4 result; result.r = curvemap_evaluateF(0, col.r); result.g = curvemap_evaluateF(1, col.g); result.b = curvemap_evaluateF(2, col.b); @@ -101,9 +141,9 @@ vec4 curvemapping_evaluate_premulRGBF(vec4 col) /* 2D hash (iqint3) recommended from "Hash Functions for GPU Rendering" JCGT Vol. 9, No. 3, 2020 * https://jcgt.org/published/0009/03/02/ */ -float hash_iqint3_f(uvec2 x) +float hash_iqint3_f(uint2 x) { - uvec2 q = 1103515245u * ((x >> 1u) ^ (x.yx)); + uint2 q = 1103515245u * ((x >> 1u) ^ (x.yx)); uint n = 1103515245u * ((q.x) ^ (q.y >> 3u)); return float(n) * (1.0 / float(0xffffffffu)); } @@ -112,7 +152,7 @@ float hash_iqint3_f(uvec2 x) * Triangle distribution which gives a more final uniform noise, * see "Banding in Games: A Noisy Rant" by Mikkel Gjoel (slide 27) * https://loopit.dk/banding_in_games.pdf */ -float dither_random_value(uvec2 co) +float dither_random_value(uint2 co) { float v = hash_iqint3_f(co); /* Convert uniform distribution into triangle-shaped distribution. Based on @@ -122,13 +162,13 @@ float dither_random_value(uvec2 co) return v; } -uvec2 get_pixel_coord(sampler2D tex, vec2 uv) +uint2 get_pixel_coord(sampler2D tex, float2 uv) { - vec2 size = vec2(textureSize(tex, 0)); - return uvec2(uv * size); + float2 size = float2(textureSize(tex, 0)); + return uint2(uv * size); } -vec4 apply_dither(vec4 col, uvec2 uv) +float4 apply_dither(float4 col, uint2 uv) { col.rgb += dither_random_value(uv) * 0.0033 * parameters.dither; return col; @@ -142,11 +182,11 @@ vec4 apply_dither(vec4 col, uvec2 uv) /* Prototypes: Implementation is generated and defined after. */ #ifndef GPU_METAL /* Forward declaration invalid in MSL. */ -vec4 OCIO_to_scene_linear(vec4 pixel); -vec4 OCIO_to_display(vec4 pixel); +float4 OCIO_to_scene_linear(float4 pixel); +float4 OCIO_to_display(float4 pixel); #endif -vec4 OCIO_ProcessColor(vec4 col, vec4 col_overlay) +float4 OCIO_ProcessColor(float4 col, float4 col_overlay) { #ifdef USE_CURVE_MAPPING col = curvemapping_evaluate_premulRGBF(col); @@ -158,10 +198,8 @@ vec4 OCIO_ProcessColor(vec4 col, vec4 col_overlay) } } - /* NOTE: This is true we only do de-pre-multiply here and NO pre-multiply - * and the reason is simple -- opengl is always configured - * for straight alpha at this moment - */ + /* NOTE: This is true we only do de-pre-multiply here and NO pre-multiply and the reason is + * simple -- opengl is always configured for straight alpha at this moment. */ /* Convert to scene linear (usually a no-op). */ col = OCIO_to_scene_linear(col); @@ -174,12 +212,11 @@ vec4 OCIO_ProcessColor(vec4 col, vec4 col_overlay) /* Blend with overlay in UI color-space. * - * UI color-space here refers to the display linear color space, - * i.e: The linear color space w.r.t. display chromaticity and radiometry. - * We separate the color-management process into two steps to be able to - * merge UI using alpha blending in the correct color space. */ - if (parameters.use_overlay) { - col.rgb = pow(col.rgb, vec3(parameters.exponent * 2.2)); + * UI color-space here refers to the display linear color space, i.e: The linear color space + * w.r.t. display chromaticity and radiometry. We separate the color-management process into two + * steps to be able to merge UI using alpha blending in the correct color space. */ + if (parameters.do_overlay_merge) { + col.rgb = pow(col.rgb, float3(parameters.exponent * 2.2)); if (!parameters.use_hdr) { /* If we're not using an extended color space, clamp the color 0..1. */ @@ -192,14 +229,14 @@ vec4 OCIO_ProcessColor(vec4 col, vec4 col_overlay) } col *= 1.0 - col_overlay.a; col += col_overlay; /* Assumed unassociated alpha. */ - col.rgb = pow(col.rgb, vec3(1.0 / 2.2)); + col.rgb = pow(col.rgb, float3(1.0 / 2.2)); } else { - col.rgb = pow(col.rgb, vec3(parameters.exponent)); + col.rgb = pow(col.rgb, float3(parameters.exponent)); } if (parameters.dither > 0.0) { - uvec2 texel = get_pixel_coord(image_texture, texCoord_interp.st); + uint2 texel = get_pixel_coord(image_texture, texCoord_interp.st); col = apply_dither(col, texel); } @@ -210,8 +247,8 @@ vec4 OCIO_ProcessColor(vec4 col, vec4 col_overlay) void main() { - vec4 col = texture(image_texture, texCoord_interp.st); - vec4 col_overlay = texture(overlay_texture, texCoord_interp.st); + float4 col = texture(image_texture, texCoord_interp.st); + float4 col_overlay = texture(overlay_texture, texCoord_interp.st); fragColor = OCIO_ProcessColor(col, col_overlay); } diff --git a/intern/opencolorio/gpu_shader_display_transform_vert.glsl b/source/blender/imbuf/opencolorio/shaders/gpu_shader_display_transform_vert.glsl similarity index 100% rename from intern/opencolorio/gpu_shader_display_transform_vert.glsl rename to source/blender/imbuf/opencolorio/shaders/gpu_shader_display_transform_vert.glsl diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 5c7ddc9a1ec..644b033f209 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -28,7 +28,6 @@ struct AnimData; struct Brush; struct Collection; -struct ColorSpace; struct CurveMapping; struct CurveProfile; struct CustomData_MeshMasks; @@ -43,12 +42,19 @@ struct bNodeTree; /** Workaround to forward-declare C++ type in C header. */ #ifdef __cplusplus -namespace blender::bke { +namespace blender { +namespace bke { class SceneRuntime; } +namespace ocio { +class ColorSpace; +} +} // namespace blender using SceneRuntimeHandle = blender::bke::SceneRuntime; +using ColorSpaceHandle = blender::ocio::ColorSpace; #else // __cplusplus typedef struct SceneRuntimeHandle SceneRuntimeHandle; +typedef struct ColorSpaceHandle ColorSpaceHandle; #endif // __cplusplus /* -------------------------------------------------------------------- */ @@ -1489,7 +1495,7 @@ typedef struct UnifiedPaintSettings { float mask_tex_mouse[2]; /** ColorSpace cache to avoid locking up during sampling. */ - struct ColorSpace *colorspace; + const ColorSpaceHandle *colorspace; } UnifiedPaintSettings; /** #UnifiedPaintSettings::flag */ diff --git a/source/blender/makesrna/intern/rna_color.cc b/source/blender/makesrna/intern/rna_color.cc index bf19ff8d7ef..e80234ce9d2 100644 --- a/source/blender/makesrna/intern/rna_color.cc +++ b/source/blender/makesrna/intern/rna_color.cc @@ -495,16 +495,28 @@ static void rna_ColorManagedDisplaySettings_display_device_update(Main *bmain, static int rna_ColorManagedViewSettings_view_transform_get(PointerRNA *ptr) { + const ID *id = ptr->owner_id; + BLI_assert(GS(id->name) == ID_SCE); + + const Scene *scene = reinterpret_cast(id); + ColorManagedViewSettings *view = (ColorManagedViewSettings *)ptr->data; - return IMB_colormanagement_view_get_named_index(view->view_transform); + return IMB_colormanagement_view_get_named_index(scene->display_settings.display_device, + view->view_transform); } static void rna_ColorManagedViewSettings_view_transform_set(PointerRNA *ptr, int value) { + const ID *id = ptr->owner_id; + BLI_assert(GS(id->name) == ID_SCE); + + const Scene *scene = reinterpret_cast(id); + ColorManagedViewSettings *view = (ColorManagedViewSettings *)ptr->data; - const char *view_name = IMB_colormanagement_view_get_indexed_name(value); + const char *view_name = IMB_colormanagement_view_get_indexed_name( + scene->display_settings.display_device, value); if (!view_name) { return; } diff --git a/source/blender/makesrna/intern/rna_render.cc b/source/blender/makesrna/intern/rna_render.cc index c53988139d9..5ec02040faf 100644 --- a/source/blender/makesrna/intern/rna_render.cc +++ b/source/blender/makesrna/intern/rna_render.cc @@ -103,9 +103,9 @@ static void engine_tag_update(RenderEngine *engine) engine->flag |= RE_ENGINE_DO_UPDATE; } -static bool engine_support_display_space_shader(RenderEngine * /*engine*/, Scene *scene) +static bool engine_support_display_space_shader(RenderEngine * /*engine*/, Scene * /*scene*/) { - return IMB_colormanagement_support_glsl_draw(&scene->view_settings); + return true; } static int engine_get_preview_pixel_size(RenderEngine * /*engine*/, Scene *scene) diff --git a/source/blender/nodes/composite/CMakeLists.txt b/source/blender/nodes/composite/CMakeLists.txt index 1c8c4776979..07a9ae60ab6 100644 --- a/source/blender/nodes/composite/CMakeLists.txt +++ b/source/blender/nodes/composite/CMakeLists.txt @@ -135,12 +135,9 @@ set(LIB bf_compositor PRIVATE bf::render PRIVATE bf::windowmanager + PRIVATE bf::dependencies::optional::opencolorio ) -if(WITH_OPENCOLORIO) - add_definitions(-DWITH_OCIO) -endif() - if(WITH_IMAGE_OPENEXR) add_definitions(-DWITH_IMAGE_OPENEXR) endif() diff --git a/source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc b/source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc index 9380a25c1ea..e46211a94a1 100644 --- a/source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc +++ b/source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc @@ -53,7 +53,7 @@ static void node_composit_buts_convert_colorspace(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { -#ifndef WITH_OCIO +#ifndef WITH_OPENCOLORIO layout->label(RPT_("Disabled, built without OpenColorIO"), ICON_ERROR); #endif diff --git a/source/blender/python/generic/blf_py_api.cc b/source/blender/python/generic/blf_py_api.cc index d02c9edd680..d56ef1549d4 100644 --- a/source/blender/python/generic/blf_py_api.cc +++ b/source/blender/python/generic/blf_py_api.cc @@ -33,7 +33,7 @@ struct BPyBLFImBufContext { PyObject_HEAD /* Required Python macro. */ PyObject *py_imbuf; - ColorManagedDisplay *display; + const ColorManagedDisplay *display; int fontid; BLFBufferState *buffer_state; @@ -696,7 +696,7 @@ static PyObject *py_blf_bind_imbuf(PyObject * /*self*/, PyObject *args, PyObject return nullptr; } - ColorManagedDisplay *display = nullptr; + const ColorManagedDisplay *display = nullptr; if (display_name) { display = IMB_colormanagement_display_get_named(display_name); if (UNLIKELY(display == nullptr)) { diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt index dc41523be57..8920d334826 100644 --- a/source/blender/python/intern/CMakeLists.txt +++ b/source/blender/python/intern/CMakeLists.txt @@ -8,7 +8,6 @@ set(INC ../../imbuf/intern/oiio ../../makesrna ../../../../intern/mantaflow/extern - ../../../../intern/opencolorio # RNA_prototypes.hh ${CMAKE_BINARY_DIR}/source/blender/makesrna ) @@ -130,6 +129,7 @@ set(LIB PRIVATE bf::intern::guardedalloc PRIVATE bf::animrig bf_python_gpu + PRIVATE bf::imbuf::opencolorio ${PYTHON_LINKFLAGS} ${PYTHON_LIBRARIES} @@ -328,10 +328,6 @@ if(WITH_ALEMBIC) add_definitions(-DWITH_ALEMBIC) endif() -if(WITH_OPENCOLORIO) - add_definitions(-DWITH_OCIO) -endif() - if(WITH_OPENVDB) add_definitions(-DWITH_OPENVDB) list(APPEND INC diff --git a/source/blender/python/intern/bpy_app_build_options.cc b/source/blender/python/intern/bpy_app_build_options.cc index 25c44225d69..4931b86c3c9 100644 --- a/source/blender/python/intern/bpy_app_build_options.cc +++ b/source/blender/python/intern/bpy_app_build_options.cc @@ -273,7 +273,7 @@ static PyObject *make_builtopts_info() SetObjIncref(Py_False); #endif -#ifdef WITH_OCIO +#ifdef WITH_OPENCOLORIO SetObjIncref(Py_True); #else SetObjIncref(Py_False); diff --git a/source/blender/python/intern/bpy_app_ocio.cc b/source/blender/python/intern/bpy_app_ocio.cc index 39cc0f2c492..95d9e872189 100644 --- a/source/blender/python/intern/bpy_app_ocio.cc +++ b/source/blender/python/intern/bpy_app_ocio.cc @@ -13,9 +13,9 @@ #include "../generic/py_capi_utils.hh" -#ifdef WITH_OCIO -# include "ocio_capi.h" -#endif +#include "OCIO_version.hh" + +namespace ocio = blender::ocio; static PyTypeObject BlenderAppOCIOType; @@ -38,28 +38,23 @@ static PyObject *make_ocio_info() PyObject *ocio_info; int pos = 0; -#ifdef WITH_OCIO - int curversion; -#endif - ocio_info = PyStructSequence_New(&BlenderAppOCIOType); if (ocio_info == nullptr) { return nullptr; } -#ifndef WITH_OCIO +#ifndef WITH_OPENCOLORIO # define SetStrItem(str) PyStructSequence_SET_ITEM(ocio_info, pos++, PyUnicode_FromString(str)) #endif #define SetObjItem(obj) PyStructSequence_SET_ITEM(ocio_info, pos++, obj) -#ifdef WITH_OCIO - curversion = OCIO_getVersionHex(); +#ifdef WITH_OPENCOLORIO + const ocio::Version ocio_version = ocio::get_version(); SetObjItem(PyBool_FromLong(1)); - SetObjItem( - PyC_Tuple_Pack_I32({curversion >> 24, (curversion >> 16) % 256, (curversion >> 8) % 256})); + SetObjItem(PyC_Tuple_Pack_I32({ocio_version.major, ocio_version.minor, ocio_version.patch})); SetObjItem(PyUnicode_FromFormat( - "%2d, %2d, %2d", curversion >> 24, (curversion >> 16) % 256, (curversion >> 8) % 256)); + "%2d, %2d, %2d", ocio_version.major, ocio_version.minor, ocio_version.patch)); #else SetObjItem(PyBool_FromLong(0)); SetObjItem(PyC_Tuple_Pack_I32({0, 0, 0})); diff --git a/source/blender/sequencer/intern/effects/vse_effect_text.cc b/source/blender/sequencer/intern/effects/vse_effect_text.cc index 3b23282f988..c4466e0cd0b 100644 --- a/source/blender/sequencer/intern/effects/vse_effect_text.cc +++ b/source/blender/sequencer/intern/effects/vse_effect_text.cc @@ -572,7 +572,7 @@ static void text_draw(const TextVarsRuntime *runtime, float color[4]) static rcti draw_text_outline(const RenderData *context, const TextVars *data, const TextVarsRuntime *runtime, - ColorManagedDisplay *display, + const ColorManagedDisplay *display, ImBuf *out) { /* Outline width of 1.0 maps to half of text line height. */ @@ -1026,7 +1026,7 @@ static ImBuf *do_text_effect(const RenderData *context, TextVars *data = static_cast(strip->effectdata); const char *display_device = context->scene->display_settings.display_device; - ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device); + const ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device); const int font_flags = ((data->flag & SEQ_TEXT_BOLD) ? BLF_BOLD : 0) | ((data->flag & SEQ_TEXT_ITALIC) ? BLF_ITALIC : 0); diff --git a/source/blender/sequencer/intern/modifier.cc b/source/blender/sequencer/intern/modifier.cc index 43b0c98b4f4..df757a57ba9 100644 --- a/source/blender/sequencer/intern/modifier.cc +++ b/source/blender/sequencer/intern/modifier.cc @@ -818,14 +818,16 @@ static void tonemapmodifier_init_data(StripModifierData *smd) } /* Convert chunk of float image pixels to scene linear space, in-place. */ -static void pixels_to_scene_linear_float(ColorSpace *colorspace, float4 *pixels, int64_t count) +static void pixels_to_scene_linear_float(const ColorSpace *colorspace, + float4 *pixels, + int64_t count) { IMB_colormanagement_colorspace_to_scene_linear( (float *)(pixels), int(count), 1, 4, colorspace, false); } /* Convert chunk of byte image pixels to scene linear space, into a destination array. */ -static void pixels_to_scene_linear_byte(ColorSpace *colorspace, +static void pixels_to_scene_linear_byte(const ColorSpace *colorspace, const uchar *pixels, float4 *dst, int64_t count) @@ -843,7 +845,7 @@ static void pixels_to_scene_linear_byte(ColorSpace *colorspace, static void scene_linear_to_image_chunk_float(ImBuf *ibuf, IndexRange range) { - ColorSpace *colorspace = ibuf->float_buffer.colorspace; + const ColorSpace *colorspace = ibuf->float_buffer.colorspace; float4 *fptr = reinterpret_cast(ibuf->float_buffer.data); IMB_colormanagement_scene_linear_to_colorspace( (float *)(fptr + range.first()), int(range.size()), 1, 4, colorspace); @@ -851,7 +853,7 @@ static void scene_linear_to_image_chunk_float(ImBuf *ibuf, IndexRange range) static void scene_linear_to_image_chunk_byte(float4 *src, ImBuf *ibuf, IndexRange range) { - ColorSpace *colorspace = ibuf->byte_buffer.colorspace; + const ColorSpace *colorspace = ibuf->byte_buffer.colorspace; IMB_colormanagement_scene_linear_to_colorspace( (float *)src, int(range.size()), 1, 4, colorspace); const float4 *src_ptr = src; diff --git a/source/blender/sequencer/intern/strip_add.cc b/source/blender/sequencer/intern/strip_add.cc index e30777eb37b..154e0c6ffad 100644 --- a/source/blender/sequencer/intern/strip_add.cc +++ b/source/blender/sequencer/intern/strip_add.cc @@ -117,7 +117,7 @@ static void strip_add_set_view_transform(Scene *scene, Strip *strip, LoadData *l role_colorspace_byte = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DEFAULT_BYTE); if (STREQ(strip_colorspace, role_colorspace_byte)) { - ColorManagedDisplay *display = IMB_colormanagement_display_get_named( + const ColorManagedDisplay *display = IMB_colormanagement_display_get_named( scene->display_settings.display_device); const char *default_view_transform = IMB_colormanagement_display_get_default_view_transform_name(display); diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 47c80855249..296fb6b779e 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -21,6 +21,7 @@ set(LIB PRIVATE bf::intern::guardedalloc PRIVATE bf::render PRIVATE bf::windowmanager + PRIVATE bf::dependencies::optional::opencolorio ) if(HAVE_FEENABLEEXCEPT) @@ -135,10 +136,6 @@ if(WITH_GMP) add_definitions(-DWITH_GMP) endif() -if(WITH_OPENCOLORIO) - add_definitions(-DWITH_OCIO) -endif() - # Setup the EXE sources and `buildinfo`. set(SRC creator.cc diff --git a/source/creator/creator_args.cc b/source/creator/creator_args.cc index f15f8f8480f..3cea195ec97 100644 --- a/source/creator/creator_args.cc +++ b/source/creator/creator_args.cc @@ -93,7 +93,7 @@ struct BuildDefs { bool with_ffmpeg; bool with_freestyle; bool with_libmv; - bool with_ocio; + bool with_opencolorio; bool with_renderdoc; bool with_xr_openxr; }; @@ -128,8 +128,8 @@ static void build_defs_init(BuildDefs *build_defs, bool force_all) # ifdef WITH_LIBMV build_defs->with_libmv = true; # endif -# ifdef WITH_OCIO - build_defs->with_ocio = true; +# ifdef WITH_OPENCOLORIO + build_defs->with_opencolorio = true; # endif # ifdef WITH_RENDERDOC build_defs->with_renderdoc = true; @@ -870,7 +870,7 @@ static void print_help(bArgs *ba, bool all) PRINT( " $BLENDER_CUSTOM_SPLASH_BANNER Full path to an image to overlay on the splash screen.\n"); - if (defs.with_ocio) { + if (defs.with_opencolorio) { PRINT(" $OCIO Path to override the OpenColorIO configuration file.\n"); } if (defs.win32 || all) {