/* * 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'); }