Refactor: OpenColorIO integration
Briefly about this change: - OpenColorIO C-API is removed. - The information about color spaces in ImBuf module is removed. It was stored in global ListBase in colormanagement.cc. - Both OpenColorIO and fallback implementation supports GPU drawing. - Fallback implementation supports white point, RGB curves, etc. - Removed check for support of GPU drawing in IMB. Historically it was implemented in a separate library with C-API, this is because way back C++ code needed to stay in intern. This causes all sort of overheads, and even calls that are strictly considered bad level. This change moves OpenColorIO integration into a module within imbuf, next to movie, and next to IMB_colormanagement which is the main user of it. This allows to avoid copy of color spaces, displays, views etc in the ImBuf: they were used to help quickly querying information to be shown on the interface. With this change it can be stored in the same data structures as what is used by the OpenColorIO integration. While it might not be fully avoiding duplication it is now less, and there is no need in the user code to maintain the copies. In a lot of cases this change also avoids allocations done per access to the OpenColorIO. For example, it is not needed anymore to allocate image descriptor in a heap. The bigger user-visible change is that the fallback implementation now supports GLSL drawing, with the whole list of supported features, such as curve mapping and white point. This should help simplifying code which relies on color space conversion on GPU: there is no need to figure out fallback solution in such cases. The only case when drawing will not work is when there is some actual bug, or driver issue, and shader has failed to compile. The change avoids having an opaque type for color space, and instead uses forward declaration. It is a bit verbose on declaration, but helps avoiding unsafe type-casts. There are ways to solve this in the future, like having a header for forward declaration, or to flatten the name space a bit. There should be no user-level changes under normal operation. When building without OpenColorIO or the configuration has a typo or is missing a fuller set of color management tools is applies (such as the white point correction). Pull Request: https://projects.blender.org/blender/blender/pulls/138433
This commit is contained in:
parent
40af2b7be4
commit
7ceb4495c5
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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}")
|
@ -1,546 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#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>(
|
||||
"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<OCIO_PackedImageDescription *>(id));
|
||||
}
|
||||
|
||||
const char *FallbackImpl::getVersionString()
|
||||
{
|
||||
return "fallback";
|
||||
}
|
||||
|
||||
int FallbackImpl::getVersionHex()
|
||||
{
|
||||
return 0;
|
||||
}
|
@ -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();
|
||||
}
|
@ -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 <cstddef>
|
||||
|
||||
#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 */
|
@ -1,844 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable : 4251 4275)
|
||||
#endif
|
||||
#include <OpenColorIO/OpenColorIO.h>
|
||||
#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<ConstConfigRcPtr>(__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<ConstConfigRcPtr>(__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<ConstConfigRcPtr>(__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<ConstConfigRcPtr *>(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<ConstColorSpaceRcPtr>(__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, "<USE_DISPLAY_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<ConstLookRcPtr>(__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<ConstLookRcPtr *>(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<ConstColorSpaceRcPtr *>(cs));
|
||||
}
|
||||
|
||||
OCIO_ConstProcessorRcPtr *OCIOImpl::configGetProcessorWithNames(OCIO_ConstConfigRcPtr *config,
|
||||
const char *srcName,
|
||||
const char *dstName)
|
||||
{
|
||||
ConstProcessorRcPtr *processor = MEM_new<ConstProcessorRcPtr>(__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<ConstProcessorRcPtr *>(processor));
|
||||
}
|
||||
|
||||
OCIO_ConstCPUProcessorRcPtr *OCIOImpl::processorGetCPUProcessor(
|
||||
OCIO_ConstProcessorRcPtr *processor)
|
||||
{
|
||||
ConstCPUProcessorRcPtr *cpu_processor = MEM_new<ConstCPUProcessorRcPtr>(__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<ConstCPUProcessorRcPtr *>(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<ConstProcessorRcPtr>(__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<PackedImageDesc>(__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<PackedImageDesc *>(id));
|
||||
}
|
||||
|
||||
const char *OCIOImpl::getVersionString()
|
||||
{
|
||||
return GetVersion();
|
||||
}
|
||||
|
||||
int OCIOImpl::getVersionHex()
|
||||
{
|
||||
return GetVersionHex();
|
||||
}
|
@ -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 */
|
@ -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 <cstring>
|
||||
#include <list>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable : 4251 4275)
|
||||
#endif
|
||||
#include <OpenColorIO/OpenColorIO.h>
|
||||
#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<OCIO_GPULutTexture> luts;
|
||||
|
||||
/* Dummy in case of no overlay. */
|
||||
GPUTexture *dummy = nullptr;
|
||||
|
||||
/* Uniforms */
|
||||
std::vector<OCIO_GPUUniform> 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<OCIO_GPUDisplayShader> 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<uint32_t *>(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<GPUShaderCreateInfo *>(&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<OCIO_GPUDisplayShader>::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();
|
||||
}
|
||||
|
||||
/** \} */
|
@ -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.
|
||||
|
@ -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);
|
||||
|
||||
|
@ -24,7 +24,6 @@
|
||||
|
||||
#include <ft2build.h>
|
||||
|
||||
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];
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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) {
|
||||
|
@ -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<std::string> current_names{"Foo", "Bar", "Bar.003", "Baz.001", "Big.999"};
|
||||
|
@ -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}
|
||||
|
@ -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 <OpenColorIO/OpenColorIO.h>
|
||||
#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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 = {};
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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` */
|
||||
|
@ -1205,7 +1205,7 @@ static std::unique_ptr<uiTooltipData> 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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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__ */
|
||||
|
@ -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<float *>(MEM_dupallocN(ibuf->float_buffer.data));
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
135
source/blender/imbuf/opencolorio/CMakeLists.txt
Normal file
135
source/blender/imbuf/opencolorio/CMakeLists.txt
Normal file
@ -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()
|
16
source/blender/imbuf/opencolorio/OCIO_api.hh
Normal file
16
source/blender/imbuf/opencolorio/OCIO_api.hh
Normal file
@ -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"
|
61
source/blender/imbuf/opencolorio/OCIO_colorspace.hh
Normal file
61
source/blender/imbuf/opencolorio/OCIO_colorspace.hh
Normal file
@ -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
|
217
source/blender/imbuf/opencolorio/OCIO_config.hh
Normal file
217
source/blender/imbuf/opencolorio/OCIO_config.hh
Normal file
@ -0,0 +1,217 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#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<Config> 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<Config> 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<Config> 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<const CPUProcessor> get_display_cpu_processor(
|
||||
const DisplayParameters &display_parameters) const = 0;
|
||||
|
||||
/**
|
||||
* Get processor which converts color between given color spaces.
|
||||
*/
|
||||
virtual std::shared_ptr<const CPUProcessor> 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
|
49
source/blender/imbuf/opencolorio/OCIO_cpu_processor.hh
Normal file
49
source/blender/imbuf/opencolorio/OCIO_cpu_processor.hh
Normal file
@ -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
|
59
source/blender/imbuf/opencolorio/OCIO_display.hh
Normal file
59
source/blender/imbuf/opencolorio/OCIO_display.hh
Normal file
@ -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
|
110
source/blender/imbuf/opencolorio/OCIO_gpu_shader_binder.hh
Normal file
110
source/blender/imbuf/opencolorio/OCIO_gpu_shader_binder.hh
Normal file
@ -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 <memory>
|
||||
#include <string>
|
||||
|
||||
#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<internal::GPUShaderCache> display_cache_;
|
||||
|
||||
/* Cache of shaders used for conversion to scene linear space. */
|
||||
std::unique_ptr<internal::GPUShaderCache> 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
|
53
source/blender/imbuf/opencolorio/OCIO_look.hh
Normal file
53
source/blender/imbuf/opencolorio/OCIO_look.hh
Normal file
@ -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
|
22
source/blender/imbuf/opencolorio/OCIO_matrix.hh
Normal file
22
source/blender/imbuf/opencolorio/OCIO_matrix.hh
Normal file
@ -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
|
175
source/blender/imbuf/opencolorio/OCIO_packed_image.hh
Normal file
175
source/blender/imbuf/opencolorio/OCIO_packed_image.hh
Normal file
@ -0,0 +1,175 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#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
|
14
source/blender/imbuf/opencolorio/OCIO_role_names.hh
Normal file
14
source/blender/imbuf/opencolorio/OCIO_role_names.hh
Normal file
@ -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"
|
21
source/blender/imbuf/opencolorio/OCIO_version.hh
Normal file
21
source/blender/imbuf/opencolorio/OCIO_version.hh
Normal file
@ -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
|
28
source/blender/imbuf/opencolorio/OCIO_view.hh
Normal file
28
source/blender/imbuf/opencolorio/OCIO_view.hh
Normal file
@ -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
|
40
source/blender/imbuf/opencolorio/intern/config.cc
Normal file
40
source/blender/imbuf/opencolorio/intern/config.cc
Normal file
@ -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> Config::create_from_environment()
|
||||
{
|
||||
#if defined(WITH_OPENCOLORIO)
|
||||
return LibOCIOConfig::create_from_environment();
|
||||
#endif
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<Config> 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> Config::create_fallback()
|
||||
{
|
||||
return std::make_unique<FallbackConfig>();
|
||||
}
|
||||
|
||||
} // namespace blender::ocio
|
@ -0,0 +1,57 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#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<CPUProcessorCache>.
|
||||
*
|
||||
* 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<const CPUProcessor> 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<std::unique_ptr<CPUProcessor>()> &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
|
28
source/blender/imbuf/opencolorio/intern/description.cc
Normal file
28
source/blender/imbuf/opencolorio/intern/description.cc
Normal file
@ -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
|
22
source/blender/imbuf/opencolorio/intern/description.hh
Normal file
22
source/blender/imbuf/opencolorio/intern/description.hh
Normal file
@ -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 <string>
|
||||
|
||||
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
|
18
source/blender/imbuf/opencolorio/intern/description_test.cc
Normal file
18
source/blender/imbuf/opencolorio/intern/description_test.cc
Normal file
@ -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
|
@ -0,0 +1,85 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#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
|
@ -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<const CPUProcessor> FallbackConfig::get_display_cpu_processor(
|
||||
const DisplayParameters &display_parameters) const
|
||||
{
|
||||
return create_fallback_display_cpu_processor(*this, display_parameters);
|
||||
}
|
||||
|
||||
std::shared_ptr<const CPUProcessor> 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
|
@ -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<const ColorSpace *> 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<const CPUProcessor> get_display_cpu_processor(
|
||||
const DisplayParameters &display_parameters) const override;
|
||||
std::shared_ptr<const CPUProcessor> 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
|
@ -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<void (*pixel_processor)(float dst[3], const float src[3])>
|
||||
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<float *>(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<float *>(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<linearrgb_to_srgb_v3_v3>;
|
||||
using FallbackSRGBToLinearRGBCPUProcessor = FallbackCustomCPUProcessor<srgb_to_linearrgb_v3_v3>;
|
||||
|
||||
} // namespace blender::ocio
|
@ -0,0 +1,71 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#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
|
@ -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
|
@ -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
|
@ -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<const CPUProcessor> get()
|
||||
{
|
||||
static auto processor = std::make_shared<NOOPDisplayCPUProcessor>();
|
||||
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<PixelSpaceProcessor3 pixel_space_processor, bool is_inverse>
|
||||
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<float *>(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<float *>(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<const CPUProcessor> 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<BaseDisplayCPUProcessor> processor;
|
||||
|
||||
if (display_parameters.from_colorspace == "Linear") {
|
||||
if (display_parameters.inverse) {
|
||||
processor = std::make_shared<DisplayCPUProcessor<srgb_to_linearrgb_v3_v3, true>>();
|
||||
}
|
||||
else {
|
||||
processor = std::make_shared<DisplayCPUProcessor<linearrgb_to_srgb_v3_v3, false>>();
|
||||
}
|
||||
}
|
||||
else if (display_parameters.from_colorspace == "sRGB") {
|
||||
if (display_parameters.inverse) {
|
||||
processor = std::make_shared<DisplayCPUProcessor<linearrgb_to_srgb_v3_v3, true>>();
|
||||
}
|
||||
else {
|
||||
processor = std::make_shared<DisplayCPUProcessor<srgb_to_linearrgb_v3_v3, false>>();
|
||||
}
|
||||
}
|
||||
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
|
@ -0,0 +1,18 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace blender::ocio {
|
||||
|
||||
class Config;
|
||||
class CPUProcessor;
|
||||
struct DisplayParameters;
|
||||
|
||||
std::shared_ptr<const CPUProcessor> create_fallback_display_cpu_processor(
|
||||
const Config &config, const DisplayParameters &display_parameters);
|
||||
|
||||
} // namespace blender::ocio
|
@ -0,0 +1,101 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "fallback_gpu_shader_binder.hh"
|
||||
|
||||
#include <string>
|
||||
|
||||
#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
|
@ -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
|
@ -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<const CPUProcessor> FallbackProcessorCache::get(
|
||||
const StringRefNull from_colorspace, const StringRefNull to_colorspace) const
|
||||
{
|
||||
if (from_colorspace == to_colorspace) {
|
||||
static auto noop_cpu_processor = std::make_shared<FallbackNOOPCPUProcessor>();
|
||||
return noop_cpu_processor;
|
||||
}
|
||||
|
||||
if (from_colorspace == "sRGB" && to_colorspace == "Linear") {
|
||||
static auto srgb_to_linear_cpu_processor =
|
||||
std::make_shared<FallbackSRGBToLinearRGBCPUProcessor>();
|
||||
return srgb_to_linear_cpu_processor;
|
||||
}
|
||||
|
||||
if (from_colorspace == "Linear" && to_colorspace == "sRGB") {
|
||||
static auto linear_to_srgb_cpu_processor =
|
||||
std::make_shared<FallbackLinearRGBToSRGBCPUProcessor>();
|
||||
return linear_to_srgb_cpu_processor;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace blender::ocio
|
@ -0,0 +1,24 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
namespace blender::ocio {
|
||||
|
||||
class CPUProcessor;
|
||||
|
||||
class FallbackProcessorCache {
|
||||
public:
|
||||
/**
|
||||
* Get processor to convert color space.
|
||||
*/
|
||||
std::shared_ptr<const CPUProcessor> get(StringRefNull from_colorspace,
|
||||
StringRefNull to_colorspace) const;
|
||||
};
|
||||
|
||||
} // namespace blender::ocio
|
581
source/blender/imbuf/opencolorio/intern/gpu_shader_binder.cc
Normal file
581
source/blender/imbuf/opencolorio/intern/gpu_shader_binder.cc
Normal file
@ -0,0 +1,581 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "OCIO_gpu_shader_binder.hh"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#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<GPUDisplayShader>::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<internal::GPUShaderCache>()),
|
||||
scene_linear_cache_(std::make_unique<internal::GPUShaderCache>()),
|
||||
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<uint8_t> ubo_data_buf(ubo_size);
|
||||
|
||||
uint32_t *ubo_data = reinterpret_cast<uint32_t *>(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<GPUShaderCreateInfo *>(&info));
|
||||
|
||||
return (display_shader.shader != nullptr);
|
||||
}
|
||||
|
||||
} // namespace blender::ocio
|
@ -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 <list>
|
||||
|
||||
#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<GPULutTexture> luts;
|
||||
|
||||
/* Dummy in case of no overlay. */
|
||||
GPUTexture *dummy = nullptr;
|
||||
|
||||
/* Uniforms */
|
||||
Vector<GPUUniform> 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<GPUDisplayShader> cache_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace blender::ocio
|
@ -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
|
@ -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
|
@ -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 <cmath>
|
||||
|
||||
# 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<CPUProcessor> {
|
||||
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<LibOCIOCPUProcessor>(ocio_processor->getDefaultCPUProcessor());
|
||||
});
|
||||
}
|
||||
|
||||
const CPUProcessor *LibOCIOColorSpace::get_from_scene_linear_cpu_processor() const
|
||||
{
|
||||
return from_scene_linear_cpu_processor_.get([&]() -> std::unique_ptr<CPUProcessor> {
|
||||
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<LibOCIOCPUProcessor>(ocio_processor->getDefaultCPUProcessor());
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace blender::ocio
|
||||
|
||||
#endif
|
@ -0,0 +1,75 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(WITH_OPENCOLORIO)
|
||||
|
||||
# include <string>
|
||||
|
||||
# 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
|
@ -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 <algorithm>
|
||||
# include <numeric>
|
||||
|
||||
# include <fmt/format.h>
|
||||
|
||||
# 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<Config> LibOCIOConfig::create_from_environment()
|
||||
{
|
||||
try {
|
||||
OCIO_NAMESPACE::ConstConfigRcPtr ocio_config = OCIO_NAMESPACE::Config::CreateFromEnv();
|
||||
if (!ocio_config) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::unique_ptr<LibOCIOConfig>(new LibOCIOConfig(ocio_config));
|
||||
}
|
||||
catch (OCIO_NAMESPACE::Exception &exception) {
|
||||
report_exception(exception);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<Config> 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<LibOCIOConfig>(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(), "<USE_DISPLAY_NAME>") == 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<const CPUProcessor> 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<LibOCIOCPUProcessor>(processor->getDefaultCPUProcessor());
|
||||
}
|
||||
|
||||
std::shared_ptr<const CPUProcessor> 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<LibOCIOCPUProcessor>(processor->getDefaultCPUProcessor());
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Processor API
|
||||
* \{ */
|
||||
|
||||
const GPUShaderBinder &LibOCIOConfig::get_gpu_shader_binder() const
|
||||
{
|
||||
return gpu_shader_binder_;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::ocio
|
||||
|
||||
#endif
|
@ -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<LibOCIOColorSpace> color_spaces_;
|
||||
Vector<LibOCIOLook> looks_;
|
||||
Vector<LibOCIODisplay> displays_;
|
||||
|
||||
/* Array with indices into color_spaces_.
|
||||
* color_spaces_[sorted_color_space_index_[i]] provides alphabetically sorted access. */
|
||||
Vector<int> sorted_color_space_index_;
|
||||
|
||||
LibOCIOGPUShaderBinder gpu_shader_binder_{*this};
|
||||
|
||||
public:
|
||||
~LibOCIOConfig();
|
||||
|
||||
static std::unique_ptr<Config> create_from_environment();
|
||||
static std::unique_ptr<Config> 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<const CPUProcessor> get_display_cpu_processor(
|
||||
const DisplayParameters &display_parameters) const override;
|
||||
std::shared_ptr<const CPUProcessor> 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
|
@ -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<float *>(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<float *>(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
|
@ -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
|
@ -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<CPUProcessor> {
|
||||
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<LibOCIOCPUProcessor>(ocio_cpu_processor);
|
||||
});
|
||||
}
|
||||
|
||||
const CPUProcessor *LibOCIODisplay::get_from_scene_linear_cpu_processor() const
|
||||
{
|
||||
return from_scene_linear_cpu_processor_.get([&]() -> std::unique_ptr<CPUProcessor> {
|
||||
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<LibOCIOCPUProcessor>(ocio_cpu_processor);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace blender::ocio
|
||||
|
||||
#endif
|
@ -0,0 +1,70 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(WITH_OPENCOLORIO)
|
||||
|
||||
# include <memory>
|
||||
|
||||
# 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<LibOCIOView> 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
|
@ -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
|
@ -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
|
@ -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<const LibOCIOConfig &>(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<const LibOCIOConfig &>(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
|
@ -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
|
@ -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
|
@ -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 <string>
|
||||
|
||||
# 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
|
@ -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
|
@ -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
|
@ -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
|
@ -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;
|
18
source/blender/imbuf/opencolorio/intern/opencolorio.hh
Normal file
18
source/blender/imbuf/opencolorio/intern/opencolorio.hh
Normal file
@ -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 <OpenColorIO/OpenColorIO.h>
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(pop)
|
||||
#endif
|
22
source/blender/imbuf/opencolorio/intern/source_processor.cc
Normal file
22
source/blender/imbuf/opencolorio/intern/source_processor.cc
Normal file
@ -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
|
18
source/blender/imbuf/opencolorio/intern/source_processor.hh
Normal file
18
source/blender/imbuf/opencolorio/intern/source_processor.hh
Normal file
@ -0,0 +1,18 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
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
|
@ -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
|
21
source/blender/imbuf/opencolorio/intern/version.cc
Normal file
21
source/blender/imbuf/opencolorio/intern/version.cc
Normal file
@ -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
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user