summaryrefslogtreecommitdiff
path: root/spectro/disptechs.c
diff options
context:
space:
mode:
Diffstat (limited to 'spectro/disptechs.c')
-rw-r--r--spectro/disptechs.c726
1 files changed, 726 insertions, 0 deletions
diff --git a/spectro/disptechs.c b/spectro/disptechs.c
new file mode 100644
index 0000000..05b9045
--- /dev/null
+++ b/spectro/disptechs.c
@@ -0,0 +1,726 @@
+
+ /* Standardized display types */
+
+ /* Standardized display types for use with libinst */
+
+/*
+ * Argyll Color Correction System
+ *
+ * Author: Graeme W. Gill
+ * Date: 14/5/2014
+ *
+ * Copyright 2014 Graeme W. Gill
+ * All rights reserved.
+ *
+ * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
+ * see the License2.txt file for licencing details.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#ifndef SALONEINSTLIB
+#include "copyright.h"
+#include "aconfig.h"
+#include "icc.h"
+#else
+#include "sa_config.h"
+#endif /* !SALONEINSTLIB */
+#include "numsup.h"
+#include "conv.h"
+#include "disptechs.h"
+
+/* Other selection characters used:
+
+ "n" Non-refresh (Generic)
+ "r" Refresh (Generic)
+ "F" Factory base calibration
+ "R" Raw sensor values
+ "g" Generic
+
+ oemarch:
+ "C" CMF
+ "U" Custom
+ kleink10:
+ "P" DLP projector using ambient
+ "E" SMPTE C
+ "P" Klein DLP Lux
+ "d" Klein LED Bk LCD
+ "O" Sony EL OLED
+ "z" Eizo CG LCD
+ */
+
+static disptech_info disptech_info_array[] = {
+ {
+ disptech_none, /* Not applicable entry. Must be first */
+ "None", /* because disptech_get_list() assumes so */
+ "None",
+ 0,
+ 0.001,
+ 0.001,
+ NULL
+ },
+
+ {
+ disptech_crt,
+ "CRT",
+ "CRT",
+ 1,
+ DISPTECH_CRT_RISE,
+ DISPTECH_CRT_FALL,
+ "c"
+ },
+ {
+ disptech_plasma,
+ "Plasma",
+ "Plasma",
+ 1,
+ DISPTECH_CRT_RISE,
+ DISPTECH_CRT_FALL,
+ "m"
+ },
+
+ {
+ disptech_lcd,
+ "LCD",
+ "LCD",
+ 0,
+ DISPTECH_LCD_RISE,
+ DISPTECH_LCD_FALL,
+ "l"
+ },
+ {
+ disptech_lcd_ccfl,
+ "LCD CCFL",
+ "LCD CCFL",
+ 0,
+ DISPTECH_LCD_RISE,
+ DISPTECH_LCD_FALL,
+ "l"
+ },
+ {
+ disptech_lcd_ccfl_ips,
+ "LCD CCFL IPS",
+ "LCD CCFL IPS",
+ 0,
+ DISPTECH_LCD_RISE,
+ DISPTECH_LCD_FALL,
+ "l"
+ },
+ { disptech_lcd_ccfl_vpa,
+ "LCD CCFL VPA",
+ "LCD CCFL VPA",
+ 0,
+ DISPTECH_LCD_RISE,
+ DISPTECH_LCD_FALL,
+ "l"
+ },
+ { disptech_lcd_ccfl_tft,
+ "LCD CCFL TFT",
+ "LCD CCFL TFT",
+ 0,
+ DISPTECH_LCD_RISE,
+ DISPTECH_LCD_FALL,
+ "l"
+
+ },
+ { disptech_lcd_ccfl_wg,
+ "LCD CCFL Wide Gamut",
+ "LCD CCFL Wide Gamut",
+ 0,
+ DISPTECH_LCD_RISE,
+ DISPTECH_LCD_FALL,
+ "L"
+ },
+ { disptech_lcd_ccfl_wg_ips,
+ "LCD CCFL Wide Gamut IPS",
+ "LCD CCFL Wide Gamut IPS",
+ 0,
+ DISPTECH_LCD_RISE,
+ DISPTECH_LCD_FALL,
+ "L"
+ },
+ { disptech_lcd_ccfl_wg_vpa,
+ "LCD CCFL Wide Gamut VPA",
+ "LCD CCFL Wide Gamut VPA",
+ 0,
+ DISPTECH_LCD_RISE,
+ DISPTECH_LCD_FALL,
+ "L"
+ },
+ { disptech_lcd_ccfl_wg_tft,
+ "LCD CCFL Wide Gamut TFT",
+ "LCD CCFL Wide Gamut TFT",
+ 0,
+ DISPTECH_LCD_RISE,
+ DISPTECH_LCD_FALL,
+ "L"
+ },
+ { disptech_lcd_wled,
+ "LCD White LED",
+ "LCD White LED",
+ 0,
+ DISPTECH_LCD_RISE,
+ DISPTECH_LCD_FALL,
+ "e"
+ },
+ { disptech_lcd_wled_ips,
+ "LCD White LED IPS",
+ "LCD White LED IPS",
+ 0,
+ DISPTECH_LCD_RISE,
+ DISPTECH_LCD_FALL,
+ "e"
+ },
+ { disptech_lcd_wled_vpa,
+ "LCD White LED VPA",
+ "LCD White LED VPA",
+ 0,
+ DISPTECH_LCD_RISE,
+ DISPTECH_LCD_FALL,
+ "e"
+ },
+ { disptech_lcd_wled_tft,
+ "LCD White LED TFT",
+ "LCD White LED TFT",
+ 0,
+ DISPTECH_LCD_RISE,
+ DISPTECH_LCD_FALL,
+ "e"
+ },
+ { disptech_lcd_rgbled,
+ "LCD RGB LED",
+ "LCD RGB LED",
+ 0,
+ DISPTECH_LCD_RISE,
+ DISPTECH_LCD_FALL,
+ "b"
+ },
+ { disptech_lcd_rgbled_ips,
+ "LCD RGB LED IPS",
+ "LCD RGB LED IPS",
+ 0,
+ DISPTECH_LCD_RISE,
+ DISPTECH_LCD_FALL,
+ "b"
+ },
+ { disptech_lcd_rgbled_vpa,
+ "LCD RGB LED VPA",
+ "LCD RGB LED VPA",
+ 0,
+ DISPTECH_LCD_RISE,
+ DISPTECH_LCD_FALL,
+ "b"
+ },
+ { disptech_lcd_rgbled_tft,
+ "LCD RGB LED TFT",
+ "LCD RGB LED TFT",
+ 0,
+ DISPTECH_LCD_RISE,
+ DISPTECH_LCD_FALL,
+ "b"
+
+ },
+ { disptech_lcd_rgledp,
+ "LCD RG Phosphor",
+ "LCD RG Phosphor",
+ 0,
+ DISPTECH_LCD_RISE,
+ DISPTECH_LCD_FALL,
+ "h"
+ },
+ { disptech_lcd_rgledp_ips,
+ "LCD RG Phosphor IPS",
+ "LCD RG Phosphor IPS",
+ 0,
+ DISPTECH_LCD_RISE,
+ DISPTECH_LCD_FALL,
+ "h"
+ },
+ { disptech_lcd_rgledp_vpa,
+ "LCD RG Phosphor VPA",
+ "LCD RG Phosphor VPA",
+ 0,
+ DISPTECH_LCD_RISE,
+ DISPTECH_LCD_FALL,
+ "h"
+ },
+ { disptech_lcd_rgledp_tft,
+ "LCD RG Phosphor TFT",
+ "LCD RG Phosphor TFT",
+ 0,
+ DISPTECH_LCD_RISE,
+ DISPTECH_LCD_FALL,
+ "h"
+ },
+
+ { disptech_oled,
+ "LED OLED",
+ "LED OLED",
+ 0,
+ DISPTECH_LED_RISE,
+ DISPTECH_LED_FALL,
+ "o"
+ },
+ { disptech_amoled,
+ "LED AMOLED",
+ "LED AMOLED",
+ 0,
+ DISPTECH_LED_RISE,
+ DISPTECH_LED_FALL,
+ "a"
+ },
+
+ { disptech_dlp,
+ "Projector",
+ "DLP Projector",
+ 1,
+ DISPTECH_DLP_RISE,
+ DISPTECH_DLP_FALL,
+ "p"
+ },
+ { disptech_dlp_rgb,
+ "Projector RGB Filter Wheel",
+ "DLP Projector RGB Filter Wheel",
+ 1,
+ DISPTECH_DLP_RISE,
+ DISPTECH_DLP_FALL,
+ "p"
+ },
+ { disptech_dlp_rgbw,
+ "Projector RGBW Filter Wheel",
+ "DPL Projector RGBW Filter Wheel",
+ 1,
+ DISPTECH_DLP_RISE,
+ DISPTECH_DLP_FALL,
+ "p"
+ },
+ { disptech_dlp_rgbcmy,
+ "Projector RGBCMY Filter Wheel",
+ "DLP Projector RGBCMY Filter Wheel",
+ 1,
+ DISPTECH_DLP_RISE,
+ DISPTECH_DLP_FALL,
+ "p"
+ },
+
+ {
+ disptech_unknown,
+ "Unknown",
+ "Unknown",
+ 1,
+ DISPTECH_WORST_RISE,
+ DISPTECH_WORST_FALL,
+ "u"
+ },
+
+ {
+ disptech_end /* End marker */
+ }
+};
+
+
+static unknown_ix = -1;
+
+static find_unknown() {
+ int i;
+
+ for (i = 0; disptech_info_array[i].dtech != disptech_end; i++) {
+ if (disptech_info_array[i].dtech == disptech_unknown) {
+ unknown_ix = i;
+ break;
+ }
+ }
+}
+
+/* Given the enum id, return the matching disptech_info entry */
+/* Return the disptech_unknown entry if not matched */
+disptech_info *disptech_get_id(disptech id) {
+ int i;
+
+ for (i = 0; disptech_info_array[i].dtech != disptech_end; i++) {
+ if (disptech_info_array[i].dtech == id)
+ return &disptech_info_array[i];
+ }
+
+ if (unknown_ix < 0)
+ find_unknown();
+ return &disptech_info_array[unknown_ix];
+}
+
+/* Given the string id, return the matching disptech_info entry */
+/* Return the disptech_unknown entry if not matched */
+disptech_info *disptech_get_strid(char *strid) {
+ int i;
+
+ for (i = 0; disptech_info_array[i].dtech != disptech_end; i++) {
+ if (strcmp(disptech_info_array[i].strid, strid) == 0)
+ return &disptech_info_array[i];
+ }
+
+ if (unknown_ix < 0)
+ find_unknown();
+ return &disptech_info_array[unknown_ix];
+}
+
+/* For each selector we need to:
+
+ check each selector char
+ if already used,
+ remove it.
+ if no selector remain,
+ allocate a free one.
+ mark all used selectors
+
+ We treat the first selector as more important
+ than any aliases that come after it, so we need
+ to do two passes to resolve what gets used.
+*/
+
+/* Set the selection characters */
+/* return NZ if we have run out */
+/* If flag & 3 == 1, deal with all selectors & remove any already used */
+/* If flag & 3 == 2, deal with just primary selectors & remove any already used */
+/* If flag & 3 == 3, deal with just secondary selectors & remove any already used */
+/* If flag & 4, allocate selectors to those that don't have any */
+int disptechs_set_sel(int flag, char *sel, char *usels, int *k, char *asels) {
+ char *d, *s, i;
+
+ /* First remove any used chars from selector */
+ if (flag & 3) {
+ for (i = 0, d = s = sel; *s != '\000'; s++, i++) {
+ if (((flag & 3) == 3 && i == 0) /* Ignore and keep primary selector */
+ || ((flag & 3) == 2 && i == 1)) { /* Ignore and keep secondary selectors */
+ *d++ = *s;
+ continue;
+ }
+ if (usels[*s] == 0) { /* If selector is not currently used */
+ *d++ = *s;
+ usels[*s] = 1;
+ }
+ }
+ *d = '\000';
+ }
+
+ /* Add a selector if we need one */
+ if ((flag & 4) && sel[0] == '\000') {
+
+ /* Locate the next unused selector */
+ for (;;) {
+ if (asels[*k] == '\000') /* Run out of selectors */
+ return 1;
+ if (usels[*k] == 0)
+ break;
+ (*k)++;
+ }
+ sel[0] = asels[*k];
+ sel[1] = '\000';
+ usels[sel[0]] = 1;
+ (*k)++;
+ }
+
+ return 0;
+}
+
+/* Return the display tech list with unique lsel lectors */
+disptech_info *disptech_get_list() {
+ disptech_info *list = &disptech_info_array[1]; /* skip disptech_none entry */
+
+ int i, k;
+ char usels[256]; /* Used selectors */
+ static char *asels = "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+ for (i = 0; i < 256; i++)
+ usels[i] = 0;
+ k = 0; /* Next selector index */
+
+ /* Add entries from the static list and their primary selectors */
+ for (i = 0; list[i].dtech != disptech_end; i++) {
+ strcpy(list[i].lsel, list[i].sel);
+
+ if (disptechs_set_sel(2, list[i].lsel, usels, &k, asels)) {
+ a1loge(g_log, 1, "disptech_get_list run out of selectors\n");
+ break;
+ }
+ }
+
+ /* Create needed selectors */
+ for (i = 0; list[i].dtech != disptech_end; i++)
+ disptechs_set_sel(4, list[i].lsel, usels, &k, asels);
+
+ /* Verify or delete any secondary selectors from the list */
+ for (i = 0; list[i].dtech != disptech_end; i++)
+ disptechs_set_sel(3, list[i].lsel, usels, &k, asels);
+
+ return list;
+}
+
+/* Locate the display list item that matches the given selector. */
+/* Return NULL if not found */
+disptech_info *disptech_select(disptech_info *list, char c) {
+ int i;
+
+ for (i = 0; list[i].dtech != disptech_end; i++) {
+ if (c == list[i].lsel[0])
+ return &list[i];
+ }
+
+ return NULL;
+}
+
+
+/* ------------------------------------------- */
+
+/*
+ Display settling time model. This is primarily tailored
+ to phosphor type response (ie. CRT or Plasma), but LCD
+ should be somewhat similar
+
+ Outline:
+
+ Use sRGB as the device model.
+
+ For the target RGB, compute the partial derivative of
+ delta E with respect to R, G & B, and then multiply
+ that by desired dE accuracy to get the target R, G & B
+ delta from the target, and then put that into
+ the exponential rise/fall model to compute settling time.
+ Choose the worst of the 3.
+
+ Should really change the code to compute partial derivative
+ directly, to speed code up.
+
+ We assume the phosphor stimulus is the proportional to the
+ light output required (ie. that the CRT/encoding non-linearity
+ is in the electron gun, not the electron->phoshor->light mechanism. )
+*/
+
+/* Convert gamma encoded rgb to linear light rgb */
+static void rgb2rgbl(double *rgbl, double *rgb) {
+ int i;
+ for (i = 0; i < 3; i++) {
+ if (rgb[i] < 0.04045)
+ rgbl[i] = rgb[i]/12.92;
+ else
+ rgbl[i] = pow((rgb[i] + 0.055)/1.055, 2.4);
+ }
+}
+
+/* Convert linear light rgb to L*a*b* */
+static void rgbl2lab(double *lab, double *rgbl) {
+ int i;
+ double xyz[3];
+ static double mat[3][3] = {
+ { 0.412391, 0.212639, 0.019331 }, /* Red */
+ { 0.357584, 0.715169, 0.119195 }, /* Green */
+ { 0.180481, 0.072192, 0.950532 } /* Blue */
+ };
+
+ icmMulBy3x3(xyz, mat, rgbl);
+ icmXYZ2Lab(&icmD65, lab, xyz);
+}
+
+#ifdef NEVER
+/* Convert linear light rgb to L*a*b* with partial derivatives */
+static void ddrgbl2lab(double *lab, double dout[3][3], double *rgbl) {
+ int i, j, k;
+ double xyz[3];
+ static double mat[9] = {
+ 0.412391, 0.212639, 0.019331, /* Red */
+ 0.357584, 0.715169, 0.119195, /* Green */
+ 0.180481, 0.072192, 0.950532 /* Blue */
+ };
+ double dxyzlab[3][3]; /* Part. Deriv of [xyz] with respect to [rgb] */
+ double dlabxyz[3][3]; /* Part. Deriv of [lab] from [xyz] */
+
+ icxdpdiiMulBy3x3Parm(xyz, dxyzlab, mat, rgbl);
+ icxdXYZ2Lab(&icmD65, lab, dlabxyz, xyz);
+
+ /* Compute the partial derivative of Lab from rgb */
+ for (k = 0; k < 3; k++) {
+ for (j = 0; j < 3; j++) {
+ dout[k][j] = 0.0;
+ for (i = 0; i < 3; i++) {
+ dout[k][j] += dlabxyz[k][i] * dxyzlab[i][j];
+ }
+ }
+ }
+}
+#endif /* NEVER */
+
+/* Convert linear light rgb to L*a*b* delta E partial derivatives */
+static void drgbl2lab(/* double *lab, */double deout[3], double *rgbl) {
+ int i, j, k;
+ static double mat[3][3] = {
+ { 0.412391, 0.212639, 0.019331 }, /* Red */
+ { 0.357584, 0.715169, 0.119195 }, /* Green */
+ { 0.180481, 0.072192, 0.950532 } /* Blue */
+ };
+ double xyz[3];
+ double wp[3], tin[3], dtin[3];
+ double dlabxyz[3][3]; /* Part. Deriv of [lab] from [xyz] */
+ double dout[3][3]; /* Part. Deriv of [lab] from [rgb] */
+
+ /* rgb to XYZ */
+ for (i = 0; i < 3; i++) {
+ xyz[i] = 0.0;
+ for (j = 0; j < 3; j++) {
+ xyz[i] += mat[i][j] * rgbl[j];
+ }
+ }
+
+ /* XYZ to perceptual Lab with partial derivatives. */
+ wp[0] = icmD65.X, wp[1] = icmD65.Y, wp[2] = icmD65.Z;
+
+ for (i = 0; i < 3; i++) {
+ tin[i] = xyz[i]/wp[i];
+ dtin[i] = 1.0/wp[i];
+
+ if (tin[i] > 0.008856451586) {
+ dtin[i] *= pow(tin[i], -2.0/3.0) / 3.0;
+ tin[i] = pow(tin[i],1.0/3.0);
+ } else {
+ dtin[i] *= 7.787036979;
+ tin[i] = 7.787036979 * tin[i] + 16.0/116.0;
+ }
+ }
+
+/* lab[0] = 116.0 * tin[1] - 16.0; */
+ dlabxyz[0][0] = 0.0;
+ dlabxyz[0][1] = 116.0 * dtin[1];
+ dlabxyz[0][2] = 0.0;
+
+/* lab[1] = 500.0 * (tin[0] - tin[1]); */
+ dlabxyz[1][0] = 500.0 * dtin[0];
+ dlabxyz[1][1] = 500.0 * -dtin[1];
+ dlabxyz[1][2] = 0.0;
+
+/* lab[2] = 200.0 * (tin[1] - tin[2]); */
+ dlabxyz[2][0] = 0.0 * mat[0][1];
+ dlabxyz[2][1] = 200.0 * dtin[1];
+ dlabxyz[2][2] = 200.0 * -dtin[2];
+
+ /* Compute the partial derivative of delta E from rgb */
+ for (j = 0; j < 3; j++) {
+ deout[j] = 0.0;
+ for (k = 0; k < 3; k++) {
+ dout[k][j] = 0.0;
+ for (i = 0; i < 3; i++) {
+ dout[k][j] += dlabxyz[k][i] * mat[i][j];
+ }
+ deout[j] += dout[k][j] * dout[k][j];
+ }
+ deout[j] = sqrt(deout[j]);
+ }
+}
+
+double disp_settle_time(double *orgb, double *nrgb, double rise, double fall, double dE) {
+ int i, j;
+ double orgbl[3], nrgbl[3]; /* Linear light RGB */
+ double drgb[3]; /* Partial derivative of RGB wrt to dE at new rgb */
+ double argbl[3]; /* Acceptable RGB */
+ double stime[3]; /* Settling time */
+ double kr, kf;
+ double xtime = 0.0;
+
+ /* Convert rgb's to linear light rgb */
+ rgb2rgbl(orgbl, orgb);
+ rgb2rgbl(nrgbl, nrgb);
+
+//printf("orgb = %f %f %f\n", orgb[0], orgb[1], orgb[2]);
+//printf("nrgb = %f %f %f\n", nrgb[0], nrgb[1], nrgb[2]);
+//printf("orgbl = %f %f %f\n", orgbl[0], orgbl[1], orgbl[2]);
+//printf("nrgbl = %f %f %f\n", nrgbl[0], nrgbl[1], nrgbl[2]);
+//printf("dE = %f\n", dE);
+
+ /* Compute partial derivative */
+ drgbl2lab(drgb, nrgbl);
+//printf("drgb = %f %f %f\n", drgb[0], drgb[1], drgb[2]);
+
+#ifdef NEVER
+ /* Calculate partial derivative explicitely to check */
+ {
+ double rlab[3], lab[3];
+ double xdrgb[3]; /* Partial derivative of RGB wrt to dE at new rgb */
+
+ /* Reference Lab */
+ rgbl2lab(rlab, nrgbl);
+//printf("rlab = %f %f %f\n", rlab[0], rlab[1], rlab[2]);
+
+ for (j = 0; j < 3; j++) {
+ double del;
+
+ if (nrgbl[j] > 0.5)
+ del = -1e-6;
+ else
+ del = 1e-6;
+
+ nrgbl[j] += del;
+ rgbl2lab(lab, nrgbl);
+ nrgbl[j] -= del;
+//printf("check pde of in %d = lab %f, %f, %f\n",j, (lab[0] - rlab[0])/del, (lab[1] - rlab[1])/del, (lab[2] - rlab[2])/del);
+ xdrgb[j] = icmLabDE(rlab, lab)/fabs(del);
+ }
+printf("chk drgb = %f %f %f\n", xdrgb[0], xdrgb[1], xdrgb[2]);
+ }
+#endif /* NEVER */
+
+ /* Compute rgb value that would give targ delta E */
+ for (j = 0; j < 3; j++) {
+ double del;
+
+ del = dE/drgb[j];
+
+ if (orgbl[j] < nrgbl[j]) {
+ argbl[j] = nrgbl[j] - del;
+ if (argbl[j] < orgbl[j])
+ argbl[j] = orgbl[j];
+ } else {
+ argbl[j] = nrgbl[j] + del;
+ if (argbl[j] > orgbl[j])
+ argbl[j] = orgbl[j];
+ }
+ }
+
+//{ double rlab[3], lab[3];
+//rgbl2lab(lab, argbl);
+//rgbl2lab(rlab, nrgbl);
+//printf("argbl = %f %f %f\n", argbl[0], argbl[1], argbl[2]);
+//printf("dE for argbl = %f\n",icmLabDE(rlab, lab));
+//}
+
+ /* Compute the modelled time from orgbl to argbl */
+ kr = rise/log(1.0 - 0.9); /* Exponent constant for 90% change*/
+ kf = fall/log(1.0 - 0.9); /* Exponent constant for 90% change*/
+
+ for (j = 0; j < 3; j++) {
+ double el, dl, n, t;
+
+ dl = (argbl[j] - orgbl[j])/(nrgbl[j] - orgbl[j]);
+
+ if (fabs(dl) < 1e-6) {
+ stime[j] = 0.0;
+ continue;
+ }
+
+ if (nrgbl[j] > orgbl[j])
+ stime[j] = kr * log(1.0 - dl);
+ else
+ stime[j] = kf * log(1.0 - dl);
+
+ if (stime[j] > xtime && stime[j] < 5.0)
+ xtime = stime[j];
+ }
+//printf("stime = %f %f %f\n", stime[0], stime[1], stime[2]);
+//printf("returning = %f\n",xtime);
+
+ return xtime;
+}
+
+
+
+
+