forked from bartvdbraak/blender
Python API: add mathutils.Color functions to convert color spaces
Between scene linear and sRGB, XYZ, linear Rec.709 and ACES2065-1. And add some clarifications about color spaces in the docs. Fixes T98267 Ref T68926 Differential Revision: https://developer.blender.org/D14989
This commit is contained in:
parent
2f2d13b8c6
commit
469ee7ff15
@ -244,7 +244,7 @@ void FallbackImpl::configGetDefaultLumaCoefs(OCIO_ConstConfigRcPtr * /*config*/,
|
||||
void FallbackImpl::configGetXYZtoRGB(OCIO_ConstConfigRcPtr * /*config*/, float xyz_to_rgb[3][3])
|
||||
{
|
||||
/* Default to ITU-BT.709. */
|
||||
memcpy(xyz_to_rgb, OCIO_XYZ_TO_LINEAR_SRGB, sizeof(OCIO_XYZ_TO_LINEAR_SRGB));
|
||||
memcpy(xyz_to_rgb, OCIO_XYZ_TO_REC709, sizeof(OCIO_XYZ_TO_REC709));
|
||||
}
|
||||
|
||||
int FallbackImpl::configGetNumLooks(OCIO_ConstConfigRcPtr * /*config*/)
|
||||
|
@ -31,10 +31,15 @@ OCIO_DECLARE_HANDLE(OCIO_ConstContextRcPtr);
|
||||
OCIO_DECLARE_HANDLE(OCIO_PackedImageDesc);
|
||||
OCIO_DECLARE_HANDLE(OCIO_ConstLookRcPtr);
|
||||
|
||||
/* Standard XYZ to linear sRGB transform, for fallback. */
|
||||
static const float OCIO_XYZ_TO_LINEAR_SRGB[3][3] = {{3.2404542f, -0.9692660f, 0.0556434f},
|
||||
{-1.5371385f, 1.8760108f, -0.2040259f},
|
||||
{-0.4985314f, 0.0415560f, 1.0572252f}};
|
||||
/* 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
|
||||
|
@ -317,7 +317,7 @@ void OCIOImpl::configGetXYZtoRGB(OCIO_ConstConfigRcPtr *config_, float xyz_to_rg
|
||||
|
||||
/* 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_rgb, OCIO_XYZ_TO_LINEAR_SRGB, sizeof(OCIO_XYZ_TO_LINEAR_SRGB));
|
||||
memcpy(xyz_to_rgb, 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)) {
|
||||
@ -328,13 +328,8 @@ void OCIOImpl::configGetXYZtoRGB(OCIO_ConstConfigRcPtr *config_, float xyz_to_rg
|
||||
/* Standard OpenColorIO role, defined as ACES AP0 (ACES2065-1). */
|
||||
float aces_to_rgb[3][3];
|
||||
if (to_scene_linear_matrix(config, "aces_interchange", aces_to_rgb)) {
|
||||
/* This is the OpenColorIO builtin transform:
|
||||
* UTILITY - ACES-AP0_to_CIE-XYZ-D65_BFD. */
|
||||
const float ACES_AP0_to_xyz_D65[3][3] = {{0.938280f, 0.337369f, 0.001174f},
|
||||
{-0.004451f, 0.729522f, -0.003711f},
|
||||
{0.016628f, -0.066890f, 1.091595f}};
|
||||
float xyz_to_aces[3][3];
|
||||
invert_m3_m3(xyz_to_aces, ACES_AP0_to_xyz_D65);
|
||||
invert_m3_m3(xyz_to_aces, OCIO_ACES_TO_XYZ);
|
||||
|
||||
mul_m3_m3m3(xyz_to_rgb, aces_to_rgb, xyz_to_aces);
|
||||
}
|
||||
|
@ -72,8 +72,16 @@ BLI_INLINE float IMB_colormanagement_get_luminance(const float rgb[3]);
|
||||
* Byte equivalent of #IMB_colormanagement_get_luminance().
|
||||
*/
|
||||
BLI_INLINE unsigned char IMB_colormanagement_get_luminance_byte(const unsigned char[3]);
|
||||
|
||||
/**
|
||||
* Conversion between scene linear and other color spaces.
|
||||
*/
|
||||
BLI_INLINE void IMB_colormanagement_xyz_to_rgb(float rgb[3], const float xyz[3]);
|
||||
BLI_INLINE void IMB_colormanagement_rgb_to_xyz(float xyz[3], const float rgb[3]);
|
||||
BLI_INLINE void IMB_colormanagement_rec709_to_rgb(float rgb[3], const float rec709[3]);
|
||||
BLI_INLINE void IMB_colormanagement_rgb_to_rec709(float rec709[3], const float rgb[3]);
|
||||
BLI_INLINE void IMB_colormanagement_aces_to_rgb(float rgb[3], const float aces[3]);
|
||||
BLI_INLINE void IMB_colormanagement_rgb_to_aces(float aces[3], const float rgb[3]);
|
||||
const float *IMB_colormanagement_get_xyz_to_rgb(void);
|
||||
|
||||
/** \} */
|
||||
|
@ -20,6 +20,10 @@ struct OCIO_ConstCPUProcessorRcPtr;
|
||||
extern float imbuf_luma_coefficients[3];
|
||||
extern float imbuf_xyz_to_rgb[3][3];
|
||||
extern float imbuf_rgb_to_xyz[3][3];
|
||||
extern float imbuf_xyz_to_aces[3][3];
|
||||
extern float imbuf_aces_to_xyz[3][3];
|
||||
extern float imbuf_xyz_to_rec709[3][3];
|
||||
extern float imbuf_rec709_to_xyz[3][3];
|
||||
|
||||
#define MAX_COLORSPACE_NAME 64
|
||||
#define MAX_COLORSPACE_DESCRIPTION 512
|
||||
|
@ -75,8 +75,10 @@ static int global_tot_looks = 0;
|
||||
float imbuf_luma_coefficients[3] = {0.0f};
|
||||
float imbuf_xyz_to_rgb[3][3] = {{0.0f}};
|
||||
float imbuf_rgb_to_xyz[3][3] = {{0.0f}};
|
||||
static float imbuf_xyz_to_linear_srgb[3][3] = {{0.0f}};
|
||||
static float imbuf_linear_srgb_to_xyz[3][3] = {{0.0f}};
|
||||
float imbuf_xyz_to_rec709[3][3] = {{0.0f}};
|
||||
float imbuf_rec709_to_xyz[3][3] = {{0.0f}};
|
||||
float imbuf_xyz_to_aces[3][3] = {{0.0f}};
|
||||
float imbuf_aces_to_xyz[3][3] = {{0.0f}};
|
||||
|
||||
/* lock used by pre-cached processors getters, so processor wouldn't
|
||||
* be created several times
|
||||
@ -573,10 +575,14 @@ static void colormanage_load_config(OCIO_ConstConfigRcPtr *config)
|
||||
|
||||
/* Load luminance coefficients. */
|
||||
OCIO_configGetDefaultLumaCoefs(config, imbuf_luma_coefficients);
|
||||
|
||||
/* Load standard color spaces. */
|
||||
OCIO_configGetXYZtoRGB(config, imbuf_xyz_to_rgb);
|
||||
invert_m3_m3(imbuf_rgb_to_xyz, imbuf_xyz_to_rgb);
|
||||
copy_m3_m3(imbuf_xyz_to_linear_srgb, OCIO_XYZ_TO_LINEAR_SRGB);
|
||||
invert_m3_m3(imbuf_linear_srgb_to_xyz, imbuf_xyz_to_linear_srgb);
|
||||
copy_m3_m3(imbuf_xyz_to_rec709, OCIO_XYZ_TO_REC709);
|
||||
invert_m3_m3(imbuf_rec709_to_xyz, imbuf_xyz_to_rec709);
|
||||
copy_m3_m3(imbuf_aces_to_xyz, OCIO_ACES_TO_XYZ);
|
||||
invert_m3_m3(imbuf_xyz_to_aces, imbuf_aces_to_xyz);
|
||||
}
|
||||
|
||||
static void colormanage_free_config(void)
|
||||
@ -2370,14 +2376,14 @@ void IMB_colormanagement_color_picking_to_scene_linear_v3(float pixel[3])
|
||||
void IMB_colormanagement_scene_linear_to_srgb_v3(float pixel[3])
|
||||
{
|
||||
mul_m3_v3(imbuf_rgb_to_xyz, pixel);
|
||||
mul_m3_v3(imbuf_xyz_to_linear_srgb, pixel);
|
||||
mul_m3_v3(imbuf_xyz_to_rec709, pixel);
|
||||
linearrgb_to_srgb_v3_v3(pixel, pixel);
|
||||
}
|
||||
|
||||
void IMB_colormanagement_srgb_to_scene_linear_v3(float pixel[3])
|
||||
{
|
||||
srgb_to_linearrgb_v3_v3(pixel, pixel);
|
||||
mul_m3_v3(imbuf_linear_srgb_to_xyz, pixel);
|
||||
mul_m3_v3(imbuf_rec709_to_xyz, pixel);
|
||||
mul_m3_v3(imbuf_xyz_to_rgb, pixel);
|
||||
}
|
||||
|
||||
|
@ -37,4 +37,28 @@ void IMB_colormanagement_rgb_to_xyz(float xyz[3], const float rgb[3])
|
||||
mul_v3_m3v3(xyz, imbuf_rgb_to_xyz, rgb);
|
||||
}
|
||||
|
||||
void IMB_colormanagement_rec709_to_rgb(float rgb[3], const float rec709[3])
|
||||
{
|
||||
mul_v3_m3v3(rgb, imbuf_rec709_to_xyz, rec709);
|
||||
mul_v3_m3v3(rgb, imbuf_xyz_to_rgb, rgb);
|
||||
}
|
||||
|
||||
void IMB_colormanagement_rgb_to_rec709(float rec709[3], const float rgb[3])
|
||||
{
|
||||
mul_v3_m3v3(rec709, imbuf_rgb_to_xyz, rgb);
|
||||
mul_v3_m3v3(rec709, imbuf_xyz_to_rec709, rec709);
|
||||
}
|
||||
|
||||
void IMB_colormanagement_aces_to_rgb(float rgb[3], const float aces[3])
|
||||
{
|
||||
mul_v3_m3v3(rgb, imbuf_aces_to_xyz, aces);
|
||||
mul_v3_m3v3(rgb, imbuf_xyz_to_rgb, rgb);
|
||||
}
|
||||
|
||||
void IMB_colormanagement_rgb_to_aces(float aces[3], const float rgb[3])
|
||||
{
|
||||
mul_v3_m3v3(aces, imbuf_rgb_to_xyz, rgb);
|
||||
mul_v3_m3v3(aces, imbuf_xyz_to_aces, aces);
|
||||
}
|
||||
|
||||
#endif /* __IMB_COLORMANAGEMENT_INLINE_H__ */
|
||||
|
@ -2195,7 +2195,7 @@ static void rna_def_mloopcol(BlenderRNA *brna)
|
||||
RNA_def_property_range(prop, 0.0f, 1.0f);
|
||||
RNA_def_property_float_funcs(
|
||||
prop, "rna_MeshLoopColor_color_get", "rna_MeshLoopColor_color_set", NULL);
|
||||
RNA_def_property_ui_text(prop, "Color", "");
|
||||
RNA_def_property_ui_text(prop, "Color", "Color in sRGB color space");
|
||||
RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all");
|
||||
}
|
||||
|
||||
@ -3210,7 +3210,9 @@ static void rna_def_mesh(BlenderRNA *brna)
|
||||
NULL);
|
||||
RNA_def_property_struct_type(prop, "MeshLoopColorLayer");
|
||||
RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
|
||||
RNA_def_property_ui_text(prop, "Vertex Colors", "All vertex colors");
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Vertex Colors",
|
||||
"Legacy vertex color layers. Deprecated, use color attributes instead");
|
||||
rna_def_loop_colors(brna, prop);
|
||||
|
||||
/* Sculpt Vertex colors */
|
||||
@ -3228,7 +3230,9 @@ static void rna_def_mesh(BlenderRNA *brna)
|
||||
NULL);
|
||||
RNA_def_property_struct_type(prop, "MeshVertColorLayer");
|
||||
RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
|
||||
RNA_def_property_ui_text(prop, "Sculpt Vertex Colors", "All vertex colors");
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Sculpt Vertex Colors",
|
||||
"Sculpt vertex color layers. Deprecated, use color attributes instead");
|
||||
rna_def_vert_colors(brna, prop);
|
||||
|
||||
/* TODO: edge customdata layers (bmesh py api can access already). */
|
||||
|
@ -4,6 +4,7 @@ set(INC
|
||||
.
|
||||
../../blenkernel
|
||||
../../blenlib
|
||||
../../imbuf
|
||||
../../bmesh
|
||||
../../depsgraph
|
||||
../../makesdna
|
||||
@ -42,6 +43,7 @@ set(SRC
|
||||
|
||||
set(LIB
|
||||
bf_blenlib
|
||||
bf_imbuf
|
||||
bf_python_ext
|
||||
|
||||
${PYTHON_LINKFLAGS}
|
||||
|
@ -14,6 +14,8 @@
|
||||
#include "../generic/py_capi_utils.h"
|
||||
#include "../generic/python_utildefines.h"
|
||||
|
||||
#include "IMB_colormanagement.h"
|
||||
|
||||
#ifndef MATH_STANDALONE
|
||||
# include "BLI_dynstr.h"
|
||||
#endif
|
||||
@ -76,6 +78,120 @@ static PyObject *Color_ToTupleExt(ColorObject *self, int ndigits)
|
||||
return ret;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(Color_from_scene_linear_to_srgb_doc,
|
||||
".. function:: from_scene_linear_to_srgb()\n"
|
||||
"\n"
|
||||
" Convert from scene linear to sRGB color space.\n"
|
||||
"\n"
|
||||
" :return: A color in sRGB color space.\n"
|
||||
" :rtype: :class:`Color`\n");
|
||||
static PyObject *Color_from_scene_linear_to_srgb(ColorObject *self)
|
||||
{
|
||||
float col[3] = {self->col[0], self->col[1], self->col[2]};
|
||||
IMB_colormanagement_scene_linear_to_srgb_v3(col);
|
||||
return Color_CreatePyObject(col, Py_TYPE(self));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(Color_from_srgb_to_scene_linear_doc,
|
||||
".. function:: from_srgb_to_scene_linear()\n"
|
||||
"\n"
|
||||
" Convert from sRGB to scene linear color space.\n"
|
||||
"\n"
|
||||
" :return: A color in scene linear color space.\n"
|
||||
" :rtype: :class:`Color`\n");
|
||||
static PyObject *Color_from_srgb_to_scene_linear(ColorObject *self)
|
||||
{
|
||||
float col[3] = {self->col[0], self->col[1], self->col[2]};
|
||||
IMB_colormanagement_srgb_to_scene_linear_v3(col);
|
||||
return Color_CreatePyObject(col, Py_TYPE(self));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(Color_from_scene_linear_to_xyz_d65_doc,
|
||||
".. function:: from_scene_linear_to_xyz_d65()\n"
|
||||
"\n"
|
||||
" Convert from scene linear to CIE XYZ (Illuminant D65) color space.\n"
|
||||
"\n"
|
||||
" :return: A color in XYZ color space.\n"
|
||||
" :rtype: :class:`Color`\n");
|
||||
static PyObject *Color_from_scene_linear_to_xyz_d65(ColorObject *self)
|
||||
{
|
||||
float col[3];
|
||||
IMB_colormanagement_rgb_to_xyz(col, self->col);
|
||||
return Color_CreatePyObject(col, Py_TYPE(self));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(Color_from_xyz_d65_to_scene_linear_doc,
|
||||
".. function:: from_xyz_d65_to_scene_linear()\n"
|
||||
"\n"
|
||||
" Convert from CIE XYZ (Illuminant D65) to scene linear color space.\n"
|
||||
"\n"
|
||||
" :return: A color in scene linear color space.\n"
|
||||
" :rtype: :class:`Color`\n");
|
||||
static PyObject *Color_from_xyz_d65_to_scene_linear(ColorObject *self)
|
||||
{
|
||||
float col[3];
|
||||
IMB_colormanagement_xyz_to_rgb(col, self->col);
|
||||
return Color_CreatePyObject(col, Py_TYPE(self));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(Color_from_scene_linear_to_aces_doc,
|
||||
".. function:: from_scene_linear_to_aces()\n"
|
||||
"\n"
|
||||
" Convert from scene linear to ACES2065-1 linear color space.\n"
|
||||
"\n"
|
||||
" :return: A color in ACES2065-1 linear color space.\n"
|
||||
" :rtype: :class:`Color`\n");
|
||||
static PyObject *Color_from_scene_linear_to_aces(ColorObject *self)
|
||||
{
|
||||
float col[3];
|
||||
IMB_colormanagement_rgb_to_aces(col, self->col);
|
||||
return Color_CreatePyObject(col, Py_TYPE(self));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(Color_from_aces_to_scene_linear_doc,
|
||||
".. function:: from_aces_to_scene_linear()\n"
|
||||
"\n"
|
||||
" Convert from ACES2065-1 linear to scene linear color space.\n"
|
||||
"\n"
|
||||
" :return: A color in scene linear color space.\n"
|
||||
" :rtype: :class:`Color`\n");
|
||||
static PyObject *Color_from_aces_to_scene_linear(ColorObject *self)
|
||||
{
|
||||
float col[3];
|
||||
IMB_colormanagement_aces_to_rgb(col, self->col);
|
||||
return Color_CreatePyObject(col, Py_TYPE(self));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(Color_from_scene_linear_to_rec709_linear_doc,
|
||||
".. function:: from_scene_linear_to_rec709_linear()\n"
|
||||
"\n"
|
||||
" Convert from scene linear to Rec.709 linear color space.\n"
|
||||
"\n"
|
||||
" :return: A color in Rec.709 linear color space.\n"
|
||||
" :rtype: :class:`Color`\n");
|
||||
static PyObject *Color_from_scene_linear_to_rec709_linear(ColorObject *self)
|
||||
{
|
||||
float col[3];
|
||||
IMB_colormanagement_rgb_to_rec709(col, self->col);
|
||||
return Color_CreatePyObject(col, Py_TYPE(self));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(Color_from_rec709_linear_to_scene_linear_doc,
|
||||
".. function:: from_rec709_linear_to_scene_linear()\n"
|
||||
"\n"
|
||||
" Convert from Rec.709 linear color space to scene linear color space.\n"
|
||||
"\n"
|
||||
" :return: A color in scene linear color space.\n"
|
||||
" :rtype: :class:`Color`\n");
|
||||
static PyObject *Color_from_rec709_linear_to_scene_linear(ColorObject *self)
|
||||
{
|
||||
float col[3];
|
||||
IMB_colormanagement_rec709_to_rgb(col, self->col);
|
||||
return Color_CreatePyObject(col, Py_TYPE(self));
|
||||
}
|
||||
|
||||
/* ---------------------------- Colorspace conversion -------------- */
|
||||
|
||||
PyDoc_STRVAR(Color_copy_doc,
|
||||
".. function:: copy()\n"
|
||||
"\n"
|
||||
@ -869,17 +985,56 @@ static struct PyMethodDef Color_methods[] = {
|
||||
|
||||
/* base-math methods */
|
||||
{"freeze", (PyCFunction)BaseMathObject_freeze, METH_NOARGS, BaseMathObject_freeze_doc},
|
||||
|
||||
/* Colorspace methods. */
|
||||
{"from_scene_linear_to_srgb",
|
||||
(PyCFunction)Color_from_scene_linear_to_srgb,
|
||||
METH_NOARGS,
|
||||
Color_from_scene_linear_to_srgb_doc},
|
||||
{"from_srgb_to_scene_linear",
|
||||
(PyCFunction)Color_from_srgb_to_scene_linear,
|
||||
METH_NOARGS,
|
||||
Color_from_srgb_to_scene_linear_doc},
|
||||
{"from_scene_linear_to_xyz_d65",
|
||||
(PyCFunction)Color_from_scene_linear_to_xyz_d65,
|
||||
METH_NOARGS,
|
||||
Color_from_scene_linear_to_xyz_d65_doc},
|
||||
{"from_xyz_d65_to_scene_linear",
|
||||
(PyCFunction)Color_from_xyz_d65_to_scene_linear,
|
||||
METH_NOARGS,
|
||||
Color_from_xyz_d65_to_scene_linear_doc},
|
||||
{"from_scene_linear_to_aces",
|
||||
(PyCFunction)Color_from_scene_linear_to_aces,
|
||||
METH_NOARGS,
|
||||
Color_from_scene_linear_to_aces_doc},
|
||||
{"from_aces_to_scene_linear",
|
||||
(PyCFunction)Color_from_aces_to_scene_linear,
|
||||
METH_NOARGS,
|
||||
Color_from_aces_to_scene_linear_doc},
|
||||
{"from_scene_linear_to_rec709_linear",
|
||||
(PyCFunction)Color_from_scene_linear_to_rec709_linear,
|
||||
METH_NOARGS,
|
||||
Color_from_scene_linear_to_rec709_linear_doc},
|
||||
{"from_rec709_linear_to_scene_linear",
|
||||
(PyCFunction)Color_from_rec709_linear_to_scene_linear,
|
||||
METH_NOARGS,
|
||||
Color_from_rec709_linear_to_scene_linear_doc},
|
||||
{NULL, NULL, 0, NULL},
|
||||
};
|
||||
|
||||
/* ------------------PY_OBECT DEFINITION-------------------------- */
|
||||
PyDoc_STRVAR(color_doc,
|
||||
".. class:: Color(rgb)\n"
|
||||
"\n"
|
||||
" This object gives access to Colors in Blender.\n"
|
||||
"\n"
|
||||
" :param rgb: (r, g, b) color values\n"
|
||||
" :type rgb: 3d vector\n");
|
||||
PyDoc_STRVAR(
|
||||
color_doc,
|
||||
".. class:: Color(rgb)\n"
|
||||
"\n"
|
||||
" This object gives access to Colors in Blender.\n"
|
||||
"\n"
|
||||
" Most colors returned by Blender APIs are in scene linear color space, as defined by "
|
||||
" the OpenColorIO configuration. The notable exception is user interface theming colors, "
|
||||
" which are in sRGB color space.\n"
|
||||
"\n"
|
||||
" :param rgb: (r, g, b) color values\n"
|
||||
" :type rgb: 3d vector\n");
|
||||
PyTypeObject color_Type = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0) "Color", /* tp_name */
|
||||
sizeof(ColorObject), /* tp_basicsize */
|
||||
|
Loading…
Reference in New Issue
Block a user