/* * Compute color coordinates of locations of the spectral and Planck * (thermal) loci. The color space used is CIE xy; combine with * intensity (Y) for a total set of color coordinates. Colors need to * be converted to XYZ space (weighted by intensity) before mixing. */ #include "color.h" #include #include #include static inline double rgbmax(struct RGB RGB) { double m; m = (RGB.R < RGB.G) ? RGB.G : RGB.R; m = (m < RGB.B) ? RGB.B : m; return m; } static void thermal_table(FILE *f) { double mired; double T; /* Temperature (K) */ double scale, power; double scale2; struct cie_XYZ XYZ; struct RGB RGB; struct cie_xy xy; double m; bool capped; double lyr, lyw; /* log(Y) for red and white points */ double syr, syw; /* Scaled Y for red and white points */ /* Full level calculation - test */ RGB.R = 255.0; RGB.G = 255.0; RGB.B = 255.0; XYZ = RGB_to_XYZ(RGB); printf("X %11.8f Y %11.8f Z %11.8f\n", XYZ.X, XYZ.Y, XYZ.Z); RGB = XYZ_to_RGB(XYZ); printf("R %11.6f G %11.6f B %11.6f\n", RGB.R, RGB.G, RGB.B); xy = XYZ_to_xy(XYZ); printf("x %11.8f y %11.8f\n", xy.x, xy.y); putchar('\n'); printf("%-7s %-7s %-10s %-10s %-10s %-10s %-10s %-10s %-10s %-10s\n", "Mired", "Temp(K)", "X", "Y", "Z", "R", "G", "B", "x", "y"); /* * Compute a power scale so that the red point is at (255,0,0) * and the white point at the maximum available intensity, * == scale. */ /* Red point */ T = 1662.0; /* Peak red */ XYZ = planckian(T); printf("Y(r) = %g, ", XYZ.Y); lyr = log(XYZ.Y); RGB = XYZ_to_RGB(XYZ); m = XYZ.Y * 255.0/rgbmax(RGB); syr = log(m); printf("SY(r) = %g\n", m); /* White point */ T = 8743.7; XYZ = planckian(T); printf("Y(w) = %g, ", XYZ.Y); lyw = log(XYZ.Y); RGB = XYZ_to_RGB(XYZ); m = XYZ.Y * 255.0/rgbmax(RGB); syw = log(m); printf("SY(w) = %g\n", m); /* * Power law matching: * * y = A x^B * ln y = ln A + ln (x^B) * ln y = ln A + B ln x * ln y1 - ln y0 = B (ln x1 - ln x0) * B = (ln y1 - ln y0)/(ln x1 - ln x0) * ln y1 = ln A + B ln x1 * ln A = ln y1 - B ln x1 * A = exp(ln y1 - B ln x1) */ power = (syw - syr)/(lyw - lyr); scale = exp(syw - lyw*power); power = power - 1.0; /* divide the factor with Y */ printf("lyr = %g, syr = %g\n", lyr, syr); printf("lyw = %g, syw = %g\n", lyw, syw); printf("power = %g, scale = %g\n", power, scale); scale2 = scale * pow(XYZ.Y, power); XYZ.X *= scale2; XYZ.Y *= scale2; XYZ.Z *= scale2; xy = XYZ_to_xy(XYZ); RGB = XYZ_to_RGB(XYZ); printf("%4.0f %7.1f %10.5g %10.5g %10.5g %10.2f %10.2f %10.2f %10.5g %10.5g\n", 1.0e+6/T, T, XYZ.X, XYZ.Y, XYZ.Z, RGB.R, RGB.G, RGB.B, xy.x, xy.y); for (mired = 1567.0; mired >= 1.0; mired -= 0.1) { T = 1.0e+6/mired; XYZ = planckian(T); /* Compress the dynamic range */ scale2 = scale * pow(XYZ.Y, power); XYZ.X *= scale2; XYZ.Y *= scale2; XYZ.Z *= scale2; RGB = XYZ_to_RGB(XYZ); m = rgbmax(RGB); capped = round(m) > 255.0; if (capped) { /* Higher temp than white point, need to cap */ scale2 = 255.0/m; XYZ.X *= scale2; XYZ.Y *= scale2; XYZ.Z *= scale2; RGB.R *= scale2; RGB.G *= scale2; RGB.B *= scale2; } xy = XYZ_to_xy(XYZ); printf("%6.1f%c %7.1f %10.5g %10.5g %10.5g %10.2f %10.2f %10.2f %10.5g %10.5g\n", mired, capped ? '*' : ' ', T, XYZ.X, XYZ.Y, XYZ.Z, RGB.R, RGB.G, RGB.B, xy.x, xy.y); if (signbit(RGB.R)) RGB.R = 0.0; if (signbit(RGB.G)) RGB.G = 0.0; if (signbit(RGB.B)) RGB.B = 0.0; fprintf(f, "%.1f,%.2f,%.6f,%.6f,%.6f\n", mired, T, RGB.R, RGB.G, RGB.B); if (RGB.R < 0.5 && RGB.G < 0.5) break; } } static void spectral_table(FILE *f) { const struct RGB blue_RGB = {0.0, 0.0, 255.0}; struct cie_XYZ blue; struct cie_XYZ XYZ; struct cie_xy xy, redxy; struct RGB RGB; double nm; /* Normalize to all blue intensity */ blue = RGB_to_XYZ(blue_RGB); RGB = XYZ_to_RGB(blue); xy = XYZ_to_xy(blue); printf("Blue: RGB = %.1f %.1f %.1f Y = %.3f xy = %.3f %.3f\n", RGB.R, RGB.G, RGB.B, blue.Y, xy.x, xy.y); RGB.R = 255.0; RGB.G = 255.0; RGB.B = 255.0; XYZ = RGB_to_XYZ(RGB); xy = XYZ_to_xy(XYZ); RGB = XYZ_to_RGB(XYZ); printf("Peak: RGB = %.1f %.1f %.1f Y = %.6f xy = %.3f %.3f\n", RGB.R, RGB.G, RGB.B, XYZ.Y, xy.x, xy.y); RGB.R = 255.0; RGB.G = 0.0; RGB.B = 0.0; XYZ = RGB_to_XYZ(RGB); redxy = XYZ_to_xy(XYZ); RGB = XYZ_to_RGB(XYZ); printf("Redk: RGB = %.1f %.1f %.1f Y = %.6f xy = %.3f %.3f\n", RGB.R, RGB.G, RGB.B, XYZ.Y, redxy.x, redxy.y); for (nm = 466.0; nm <= 800.05; nm += 0.1) { xy = spectral(nm); XYZ = xyY_to_XYZ(xy, blue.Y); RGB = XYZ_to_RGB(XYZ); printf("%5.1f RGB = %7.3f %7.3f %7.3f Y = %.6f xy = %.3f %.3f\n", nm, RGB.R, RGB.G, RGB.B, XYZ.Y, xy.x, xy.y); if (signbit(RGB.R)) RGB.R = 0.0; if (signbit(RGB.G)) RGB.G = 0.0; if (signbit(RGB.B)) RGB.B = 0.0; fprintf(f,"%.1f,%.6f,%.6f,%.6f\n", nm, RGB.R, RGB.G, RGB.B); if (xy.x > redxy.x) break; } } /* * Generate CSV tables */ int main(void) { FILE *f; RGBXYZ_init(); f = fopen("thermal.csv", "w"); thermal_table(f); fclose(f); f = fopen("spectral.csv","w"); spectral_table(f); fclose(f); return 0; }