16# include <OpenColorIO/OpenColorIO.h>
17namespace OCIO = OCIO_NAMESPACE;
30static unordered_map<ustring, ustring> cached_colorspaces;
33static unordered_map<ustring, OCIO::ConstProcessorRcPtr> cached_processors;
36static string cache_scene_linear_name;
38static void check_invalidate_caches()
44 OCIO::ConstConfigRcPtr config =
nullptr;
46 config = OCIO::GetCurrentConfig();
48 catch (
const OCIO::Exception &exception) {
49 LOG_ERROR <<
"OCIO config error: " << exception.what();
53 const OCIO::ConstColorSpaceRcPtr scene_linear_colorspace = config->getColorSpace(
"scene_linear");
54 if (scene_linear_colorspace && cache_scene_linear_name != scene_linear_colorspace->getName()) {
55 cache_scene_linear_name = scene_linear_colorspace->getName();
58 cached_processors.clear();
62 cached_colorspaces.clear();
78 OCIO::ConstConfigRcPtr config =
nullptr;
80 config = OCIO::GetCurrentConfig();
82 catch (
const OCIO::Exception &exception) {
83 LOG_ERROR <<
"OCIO config error: " << exception.what();
91 check_invalidate_caches();
97 if (cached_processors.find(colorspace) == cached_processors.end()) {
99 cached_processors[colorspace] = config->getProcessor(colorspace.c_str(),
"scene_linear");
101 catch (
const OCIO::Exception &exception) {
102 cached_processors[colorspace] = OCIO::ConstProcessorRcPtr();
104 <<
" cannot be converted to scene_linear: " << exception.what();
108 const OCIO::Processor *processor = cached_processors[colorspace].get();
109 return (ColorSpaceProcessor *)processor;
126 OCIO::ConstConfigRcPtr config =
nullptr;
128 config = OCIO::GetCurrentConfig();
130 catch (
const OCIO::Exception &exception) {
131 LOG_ERROR <<
"OCIO config error: " << exception.what();
140 const OCIO::ConstColorSpaceRcPtr space = config->getColorSpace(colorspace.c_str());
141 return space && space->isData();
143 catch (
const OCIO::Exception &) {
152 const char *file_colorspace,
153 const char *file_format,
159 const bool srgb = (strcmp(file_colorspace,
"sRGB") == 0 ||
160 strcmp(file_colorspace,
"GammaCorrected") == 0 ||
161 (file_colorspace[0] ==
'\0' &&
162 (strcmp(file_format,
"png") == 0 || strcmp(file_format,
"jpeg") == 0 ||
163 strcmp(file_format,
"tiff") == 0 || strcmp(file_format,
"dpx") == 0 ||
164 strcmp(file_format,
"jpeg2000") == 0)));
177 check_invalidate_caches();
182 if (cached_colorspaces.find(colorspace) != cached_colorspaces.end()) {
183 return cached_colorspaces[colorspace];
188 bool is_scene_linear;
190 is_builtin_colorspace(colorspace, is_scene_linear, is_srgb);
193 if (is_scene_linear) {
194 LOG_INFO <<
"Colorspace " << colorspace.string() <<
" is no-op";
199 LOG_INFO <<
"Colorspace " << colorspace.string() <<
" is sRGB";
206 OCIO::ConstConfigRcPtr config =
nullptr;
208 config = OCIO::GetCurrentConfig();
210 catch (
const OCIO::Exception &exception) {
211 LOG_ERROR <<
"OCIO config error: " << exception.what();
215 if (!config || !config->getColorSpace(colorspace.c_str())) {
216 LOG_WARNING <<
"Colorspace " << colorspace.c_str() <<
" not found, using raw instead";
220 <<
" can't be converted to scene_linear, using raw instead";
227 LOG_INFO <<
"Colorspace " << colorspace.string() <<
" handled through OpenColorIO";
228 cached_colorspaces[colorspace] = colorspace;
232 <<
" not available, built without OpenColorIO";
237void ColorSpaceManager::is_builtin_colorspace(ustring colorspace,
238 bool &is_scene_linear,
242 const OCIO::Processor *processor = (
const OCIO::Processor *)
get_processor(colorspace);
244 is_scene_linear =
false;
249 const OCIO::ConstCPUProcessorRcPtr device_processor = processor->getDefaultCPUProcessor();
250 is_scene_linear =
true;
252 for (
int i = 0;
i < 256;
i++) {
253 const float v =
i / 255.0f;
255 float cR[3] = {
v, 0, 0};
256 float cG[3] = {0,
v, 0};
257 float cB[3] = {0, 0,
v};
258 float cW[3] = {
v,
v,
v};
259 device_processor->applyRGB(cR);
260 device_processor->applyRGB(cG);
261 device_processor->applyRGB(cB);
262 device_processor->applyRGB(cW);
265 if (
fabsf(cR[1]) > 1e-5f ||
fabsf(cR[2]) > 1e-5f ||
fabsf(cG[0]) > 1e-5f ||
268 is_scene_linear =
false;
276 is_scene_linear =
false;
282 is_scene_linear =
false;
289 is_scene_linear =
false;
297 is_scene_linear =
false;
304template<
typename T>
inline float4 cast_to_float4(
T *
data)
312template<
typename T>
inline void cast_from_float4(
T *
data,
const float4 value)
321template<
typename T,
bool compress_as_srgb = false>
322inline void processor_apply_pixels_rgba(
const OCIO::Processor *processor,
324 const size_t num_pixels,
325 const bool ignore_alpha)
330 const OCIO::ConstCPUProcessorRcPtr device_processor = processor->getDefaultCPUProcessor();
333 const size_t chunk_size = std::min((
size_t)(16 * 1024 * 1024), num_pixels);
336 for (
size_t j = 0; j < num_pixels; j += chunk_size) {
337 const size_t width = std::min(chunk_size, num_pixels - j);
339 for (
size_t i = 0;
i < width;
i++) {
340 float4 value = cast_to_float4(pixels + 4 * (j +
i));
342 if (!ignore_alpha && !(value.
w <= 0.0f || value.
w == 1.0f)) {
343 const float inv_alpha = 1.0f / value.
w;
344 value.
x *= inv_alpha;
345 value.
y *= inv_alpha;
346 value.
z *= inv_alpha;
349 float_pixels[
i] = value;
352 const OCIO::PackedImageDesc desc((
float *)float_pixels.data(), width, 1, 4);
353 device_processor->apply(desc);
355 for (
size_t i = 0;
i < width;
i++) {
356 float4 value = float_pixels[
i];
358 if (compress_as_srgb) {
362 if (!ignore_alpha && !(value.
w <= 0.0f || value.
w == 1.0f)) {
368 cast_from_float4(pixels + 4 * (j +
i), value);
373template<
typename T,
bool compress_as_srgb = false>
374inline void processor_apply_pixels_grayscale(
const OCIO::Processor *processor,
376 const size_t num_pixels)
378 const OCIO::ConstCPUProcessorRcPtr device_processor = processor->getDefaultCPUProcessor();
381 const size_t chunk_size = std::min((
size_t)(16 * 1024 * 1024), num_pixels);
384 for (
size_t j = 0; j < num_pixels; j += chunk_size) {
385 const size_t width = std::min(chunk_size, num_pixels - j);
389 const T *pixel = pixels + j;
390 float *fpixel = float_pixels.data();
391 for (
size_t i = 0;
i < width;
i++, pixel++, fpixel += 3) {
399 const OCIO::PackedImageDesc desc((
float *)float_pixels.data(), width, 1, 3);
400 device_processor->apply(desc);
403 T *pixel = pixels + j;
404 const float *fpixel = float_pixels.data();
405 for (
size_t i = 0;
i < width;
i++, pixel++, fpixel += 3) {
407 if (compress_as_srgb) {
421 const size_t num_pixels,
423 bool compress_as_srgb,
427 const OCIO::Processor *processor = (
const OCIO::Processor *)
get_processor(colorspace);
431 if (compress_as_srgb) {
433 processor_apply_pixels_rgba<T, true>(processor, pixels, num_pixels, ignore_alpha);
437 processor_apply_pixels_rgba<T>(processor, pixels, num_pixels, ignore_alpha);
441 if (compress_as_srgb) {
443 processor_apply_pixels_grayscale<T, true>(processor, pixels, num_pixels);
447 processor_apply_pixels_grayscale<T>(processor, pixels, num_pixels);
456 (void)compress_as_srgb;
465 const OCIO::Processor *processor = (
const OCIO::Processor *)processor_;
468 const OCIO::ConstCPUProcessorRcPtr device_processor = processor->getDefaultCPUProcessor();
471 device_processor->applyRGB(&rgb.
x);
475 device_processor->applyRGB(pixel);
477 else if (channels == 4) {
478 if (pixel[3] == 1.0f || pixel[3] == 0.0f) {
480 device_processor->applyRGB(pixel);
485 const float alpha = pixel[3];
486 const float inv_alpha = 1.0f / alpha;
488 pixel[0] *= inv_alpha;
489 pixel[1] *= inv_alpha;
490 pixel[2] *= inv_alpha;
492 device_processor->applyRGB(pixel);
518 OCIO::SetCurrentConfig(OCIO::Config::CreateRaw());
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const BMVert * v
static bool colorspace_is_data(ustring colorspace)
static ustring detect_known_colorspace(ustring colorspace, const char *file_colorspace, const char *file_format, bool is_float)
static void to_scene_linear(ustring colorspace, T *pixels, const size_t num_pixels, bool is_rgba, bool compress_as_srgb, bool ignore_alpha)
static void free_memory()
static ColorSpaceProcessor * get_processor(ustring colorspace)
static void init_fallback_config()
ccl_device float color_srgb_to_linear(const float c)
ccl_device float4 color_linear_to_srgb_v4(const float4 c)
ccl_device float color_linear_to_srgb(const float c)
CCL_NAMESPACE_BEGIN ustring u_colorspace_auto
T util_image_cast_from_float(const float value)
float util_image_cast_to_float(T value)
static void map_free_memory(T &data)
#define CCL_NAMESPACE_END
#define assert(assertion)
ccl_device_inline bool compare_floats(const float a, const float b, float abs_diff, const int ulp_diff)
ustring u_colorspace_srgb
CCL_NAMESPACE_BEGIN ustring u_colorspace_auto
std::unique_lock< std::mutex > thread_scoped_lock