16# include <OpenColorIO/OpenColorIO.h>
17namespace OCIO = OCIO_NAMESPACE;
31static unordered_map<ustring, ustring> cached_colorspaces;
32static unordered_map<ustring, OCIO::ConstProcessorRcPtr> cached_processors;
45 OCIO::ConstConfigRcPtr config =
nullptr;
47 config = OCIO::GetCurrentConfig();
49 catch (
const OCIO::Exception &exception) {
50 VLOG_WARNING <<
"OCIO config error: " << exception.what();
61 if (cached_processors.find(colorspace) == cached_processors.end()) {
63 cached_processors[colorspace] = config->getProcessor(colorspace.c_str(),
"scene_linear");
65 catch (
const OCIO::Exception &exception) {
66 cached_processors[colorspace] = OCIO::ConstProcessorRcPtr();
68 <<
" can't be converted to scene_linear: " << exception.what();
72 const OCIO::Processor *processor = cached_processors[colorspace].get();
73 return (ColorSpaceProcessor *)processor;
90 OCIO::ConstConfigRcPtr config =
nullptr;
92 config = OCIO::GetCurrentConfig();
94 catch (
const OCIO::Exception &exception) {
95 VLOG_WARNING <<
"OCIO config error: " << exception.what();
104 const OCIO::ConstColorSpaceRcPtr space = config->getColorSpace(colorspace.c_str());
105 return space && space->isData();
107 catch (
const OCIO::Exception &) {
116 const char *file_colorspace,
117 const char *file_format,
123 const bool srgb = (strcmp(file_colorspace,
"sRGB") == 0 ||
124 strcmp(file_colorspace,
"GammaCorrected") == 0 ||
125 (file_colorspace[0] ==
'\0' &&
126 (strcmp(file_format,
"png") == 0 || strcmp(file_format,
"jpeg") == 0 ||
127 strcmp(file_format,
"tiff") == 0 || strcmp(file_format,
"dpx") == 0 ||
128 strcmp(file_format,
"jpeg2000") == 0)));
144 if (cached_colorspaces.find(colorspace) != cached_colorspaces.end()) {
145 return cached_colorspaces[colorspace];
150 bool is_scene_linear;
152 is_builtin_colorspace(colorspace, is_scene_linear, is_srgb);
155 if (is_scene_linear) {
156 VLOG_INFO <<
"Colorspace " << colorspace.string() <<
" is no-op";
161 VLOG_INFO <<
"Colorspace " << colorspace.string() <<
" is sRGB";
168 OCIO::ConstConfigRcPtr config =
nullptr;
170 config = OCIO::GetCurrentConfig();
172 catch (
const OCIO::Exception &exception) {
173 VLOG_WARNING <<
"OCIO config error: " << exception.what();
177 if (!config || !config->getColorSpace(colorspace.c_str())) {
178 VLOG_WARNING <<
"Colorspace " << colorspace.c_str() <<
" not found, using raw instead";
182 <<
" can't be converted to scene_linear, using raw instead";
189 VLOG_INFO <<
"Colorspace " << colorspace.string() <<
" handled through OpenColorIO";
190 cached_colorspaces[colorspace] = colorspace;
194 <<
" not available, built without OpenColorIO";
199void ColorSpaceManager::is_builtin_colorspace(ustring colorspace,
200 bool &is_scene_linear,
204 const OCIO::Processor *processor = (
const OCIO::Processor *)
get_processor(colorspace);
206 is_scene_linear =
false;
211 const OCIO::ConstCPUProcessorRcPtr device_processor = processor->getDefaultCPUProcessor();
212 is_scene_linear =
true;
214 for (
int i = 0;
i < 256;
i++) {
215 const float v =
i / 255.0f;
217 float cR[3] = {
v, 0, 0};
218 float cG[3] = {0,
v, 0};
219 float cB[3] = {0, 0,
v};
220 float cW[3] = {
v,
v,
v};
221 device_processor->applyRGB(cR);
222 device_processor->applyRGB(cG);
223 device_processor->applyRGB(cB);
224 device_processor->applyRGB(cW);
227 if (
fabsf(cR[1]) > 1e-5f ||
fabsf(cR[2]) > 1e-5f ||
fabsf(cG[0]) > 1e-5f ||
230 is_scene_linear =
false;
238 is_scene_linear =
false;
244 is_scene_linear =
false;
251 is_scene_linear =
false;
259 is_scene_linear =
false;
266template<
typename T>
inline float4 cast_to_float4(
T *
data)
274template<
typename T>
inline void cast_from_float4(
T *
data,
const float4 value)
283template<
typename T,
bool compress_as_srgb = false>
284inline void processor_apply_pixels_rgba(
const OCIO::Processor *processor,
286 const size_t num_pixels)
291 const OCIO::ConstCPUProcessorRcPtr device_processor = processor->getDefaultCPUProcessor();
294 const size_t chunk_size = std::min((
size_t)(16 * 1024 * 1024), num_pixels);
297 for (
size_t j = 0; j < num_pixels; j += chunk_size) {
298 const size_t width = std::min(chunk_size, num_pixels - j);
300 for (
size_t i = 0;
i < width;
i++) {
301 float4 value = cast_to_float4(pixels + 4 * (j +
i));
303 if (!(value.w <= 0.0f || value.w == 1.0f)) {
304 const float inv_alpha = 1.0f / value.w;
305 value.x *= inv_alpha;
306 value.y *= inv_alpha;
307 value.z *= inv_alpha;
310 float_pixels[
i] = value;
313 const OCIO::PackedImageDesc desc((
float *)float_pixels.data(), width, 1, 4);
314 device_processor->apply(desc);
316 for (
size_t i = 0;
i < width;
i++) {
317 float4 value = float_pixels[
i];
319 if (compress_as_srgb) {
323 if (!(value.w <= 0.0f || value.w == 1.0f)) {
329 cast_from_float4(pixels + 4 * (j +
i), value);
334template<
typename T,
bool compress_as_srgb = false>
335inline void processor_apply_pixels_grayscale(
const OCIO::Processor *processor,
337 const size_t num_pixels)
339 const OCIO::ConstCPUProcessorRcPtr device_processor = processor->getDefaultCPUProcessor();
342 const size_t chunk_size = std::min((
size_t)(16 * 1024 * 1024), num_pixels);
345 for (
size_t j = 0; j < num_pixels; j += chunk_size) {
346 const size_t width = std::min(chunk_size, num_pixels - j);
350 const T *pixel = pixels + j;
351 float *fpixel = float_pixels.data();
352 for (
size_t i = 0;
i < width;
i++, pixel++, fpixel += 3) {
360 const OCIO::PackedImageDesc desc((
float *)float_pixels.data(), width, 1, 3);
361 device_processor->apply(desc);
364 T *pixel = pixels + j;
365 const float *fpixel = float_pixels.data();
366 for (
size_t i = 0;
i < width;
i++, pixel++, fpixel += 3) {
368 if (compress_as_srgb) {
381 ustring colorspace,
T *pixels,
const size_t num_pixels,
bool is_rgba,
bool compress_as_srgb)
384 const OCIO::Processor *processor = (
const OCIO::Processor *)
get_processor(colorspace);
388 if (compress_as_srgb) {
390 processor_apply_pixels_rgba<T, true>(processor, pixels, num_pixels);
394 processor_apply_pixels_rgba<T>(processor, pixels, num_pixels);
398 if (compress_as_srgb) {
400 processor_apply_pixels_grayscale<T, true>(processor, pixels, num_pixels);
404 processor_apply_pixels_grayscale<T>(processor, pixels, num_pixels);
413 (void)compress_as_srgb;
422 const OCIO::Processor *processor = (
const OCIO::Processor *)processor_;
425 const OCIO::ConstCPUProcessorRcPtr device_processor = processor->getDefaultCPUProcessor();
428 device_processor->applyRGB(&rgb.
x);
432 device_processor->applyRGB(pixel);
434 else if (channels == 4) {
435 if (pixel[3] == 1.0f || pixel[3] == 0.0f) {
437 device_processor->applyRGB(pixel);
442 const float alpha = pixel[3];
443 const float inv_alpha = 1.0f / alpha;
445 pixel[0] *= inv_alpha;
446 pixel[1] *= inv_alpha;
447 pixel[2] *= inv_alpha;
449 device_processor->applyRGB(pixel);
475 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 free_memory()
static ColorSpaceProcessor * get_processor(ustring colorspace)
static void init_fallback_config()
static void to_scene_linear(ustring colorspace, T *pixels, const size_t num_pixels, bool is_rgba, bool compress_as_srgb)
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
VecBase< float, 4 > float4
#define assert(assertion)
ccl_device_inline bool compare_floats(const float a, const float b, float abs_diff, const int ulp_diff)
ccl_device_inline float average(const float2 a)
ustring u_colorspace_srgb
CCL_NAMESPACE_BEGIN ustring u_colorspace_auto
std::unique_lock< std::mutex > thread_scoped_lock