/*
* Matrixes for conversion between CIE XYZ values to device linear RGB,
* with values normalized so that Y = 1.0 and R = G = B = 255.0
* represent the maximum possible values.
*
* If the device primaries are nonlinear, or if there are more then
* three primaries, additional code could be added to these functions,
* possibly replacing the type of "struct RGB". Either way, such code
* would involve more than a simple matrix multiplication;
*/
#include "color.h"
#include
#include
/*
* Conversion matrices, calculated by RGBXYZ_init()
*/
static double RGBXYZ[3][3], XYZRGB[3][3];
/*
* This is the inverse of the RGB-to-XYZ matrix
*/
struct RGB const_func XYZ_to_RGB(struct cie_XYZ XYZ)
{
struct RGB RGB;
color_mat_multiply((double *)&RGB, XYZRGB, (const double *)&XYZ);
return RGB;
}
/*
* These are the values from the XYZ table at the RGB primaries from the
* data sheet, normalized by their brightness (both taken from the
* center points of the data sheet):
*
* R = 622.5 nm @ 405 mcd
* G = 523.5 nm @ 690 mcd
* B = 466.0 nm @ 190 mcd
* Total: 1285 mcd
*
* Normalized values are thus given by:
* = * /1285 mcd /
*/
struct cie_XYZ const_func RGB_to_XYZ(struct RGB RGB)
{
struct cie_XYZ XYZ;
color_mat_multiply((double *)&XYZ, RGBXYZ, (const double *)&RGB);
return XYZ;
}
/*
* Compute the RGB <-> XYZ transformation matrices. For pure spectral
* primaries (as one can expect from LEDs or lasers) this code should
* be possible to use as-is with modified constants; for nonspectral
* primaries the XYZ or xyY values will have to be obtained directly
* some other way.
*/
void RGBXYZ_init(void)
{
/*
* This code assumes the available data specifies a luminous
* (photometric) intensity, normally given in candela (cd).
* If what we have is a bolometric intensity (W/sr) then
* the values need to be scaled using the photometric_factor()
* for the given wavelength.
*/
struct primary {
double nm; /* Wavelength in nm */
double cd; /* Intensity in cd */
};
/* From the WS2812B data sheet (average of ranges given) */
const struct primary primary[3] = {
{ 622.5, 0.405 }, /* Red */
{ 523.5, 0.690 }, /* Green */
{ 466.0, 0.190 } /* Blue */
};
double peakcd; /* Peak (total) intensity in cd */
double scale;
int i;
peakcd = 0.0;
for (i = 0; i < 3; i++)
peakcd += primary[i].cd;
/*
* Compute the RGBXYZ matrix. Note that we already have a
* photometric intensity (in cd). If we have a *bolometric*
* intensity (in W/sr) then we need to convert it to a photometric
* intensity using the photometric_scale() conversion factor.
*
* This matrix represents the contribution to the total XYZ
* value for *each scale point* of each primary.
*/
scale = 1.0/(255.0*peakcd);
printf("rgbxyz =\n\n");
for (i = 0; i < 3; i++) {
struct cie_xy xy = spectral(primary[i].nm);
struct cie_XYZ XYZ = xyY_to_XYZ(xy, primary[i].cd*scale);
RGBXYZ[0][i] = XYZ.X;
RGBXYZ[1][i] = XYZ.Y;
RGBXYZ[2][i] = XYZ.Z;
}
for (i = 0; i < 3; i++) {
printf(" %22.14e %22.14e %22.14e\n",
RGBXYZ[i][0], RGBXYZ[i][1], RGBXYZ[i][2]);
}
/*
* The inverse matrix provides for XYZ -> RGB conversion
*/
color_mat_inverse(XYZRGB, RGBXYZ);
printf("\nxyzrgb =\n\n");
for (i = 0; i < 3; i++) {
printf(" %22.14e %22.14e %22.14e\n",
XYZRGB[i][0], XYZRGB[i][1], XYZRGB[i][2]);
}
putchar('\n');
}