1
2 /* Standardized display types */
3
4 /* Standardized display types for use with libinst */
5
6 /*
7 * Argyll Color Correction System
8 *
9 * Author: Graeme W. Gill
10 * Date: 14/5/2014
11 *
12 * Copyright 2014 Graeme W. Gill
13 * All rights reserved.
14 *
15 * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
16 * see the License2.txt file for licencing details.
17 *
18 */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <ctype.h>
23 #include <string.h>
24 #include <time.h>
25 #include "numsup.h"
26 #ifndef SALONEINSTLIB
27 #include "copyright.h"
28 #include "aconfig.h"
29 #include "icc.h"
30 #else
31 #include "sa_config.h"
32 #include "sa_conv.h"
33 #endif /* !SALONEINSTLIB */
34 #include "conv.h"
35 #include "disptechs.h"
36
37 /* Other selection characters used,
38 that shouldn't be used in the disptech_info_array[] entries :
39
40 "n" Non-refresh (Generic)
41 "r" Refresh (Generic)
42 "F" Factory base calibration
43 "R" Raw sensor values
44 "g" Generic
45
46 oemarch:
47 "C" CMF
48 "U" Custom
49 kleink10:
50 "P" DLP projector using ambient
51 "E" SMPTE C
52 "P" Klein DLP Lux
53 "d" Klein LED Bk LCD
54 "O" Sony EL OLED
55 "z" Eizo CG LCD
56 */
57
58 /* We deliberately duplicate the selection characters, */
59 /* because it's not usual to offer the whole list, just */
60 /* a sub-set which may not clash. */
61 /* disptechs_set_sel() should be used to present */
62 /* unique selectors. */
63 static disptech_info disptech_info_array[] = {
64 {
65 disptech_none, /* Not applicable entry. Must be first */
66 "None", /* because disptech_get_list() assumes so */
67 "None",
68 0,
69 0.001,
70 0.001,
71 NULL
72 },
73
74 {
75 disptech_crt,
76 "CRT",
77 "CRT",
78 1,
79 DISPTECH_CRT_RISE,
80 DISPTECH_CRT_FALL,
81 "c"
82 },
83 {
84 disptech_plasma,
85 "Plasma",
86 "Plasma",
87 1,
88 DISPTECH_CRT_RISE,
89 DISPTECH_CRT_FALL,
90 "m"
91 },
92
93 {
94 disptech_lcd,
95 "LCD",
96 "LCD",
97 0,
98 DISPTECH_LCD_RISE,
99 DISPTECH_LCD_FALL,
100 "l"
101 },
102 {
103 disptech_lcd_ccfl,
104 "LCD CCFL",
105 "LCD CCFL",
106 0,
107 DISPTECH_LCD_RISE,
108 DISPTECH_LCD_FALL,
109 "l"
110 },
111 {
112 disptech_lcd_ccfl_ips,
113 "LCD CCFL IPS",
114 "LCD CCFL IPS",
115 0,
116 DISPTECH_LCD_RISE,
117 DISPTECH_LCD_FALL,
118 "l"
119 },
120 { disptech_lcd_ccfl_vpa,
121 "LCD CCFL VPA",
122 "LCD CCFL VPA",
123 0,
124 DISPTECH_LCD_RISE,
125 DISPTECH_LCD_FALL,
126 "l"
127 },
128 { disptech_lcd_ccfl_tft,
129 "LCD CCFL TFT",
130 "LCD CCFL TFT",
131 0,
132 DISPTECH_LCD_RISE,
133 DISPTECH_LCD_FALL,
134 "l"
135
136 },
137 { disptech_lcd_ccfl_wg,
138 "LCD CCFL Wide Gamut",
139 "LCD CCFL Wide Gamut",
140 0,
141 DISPTECH_LCD_RISE,
142 DISPTECH_LCD_FALL,
143 "L"
144 },
145 { disptech_lcd_ccfl_wg_ips,
146 "LCD CCFL Wide Gamut IPS",
147 "LCD CCFL Wide Gamut IPS",
148 0,
149 DISPTECH_LCD_RISE,
150 DISPTECH_LCD_FALL,
151 "L"
152 },
153 { disptech_lcd_ccfl_wg_vpa,
154 "LCD CCFL Wide Gamut VPA",
155 "LCD CCFL Wide Gamut VPA",
156 0,
157 DISPTECH_LCD_RISE,
158 DISPTECH_LCD_FALL,
159 "L"
160 },
161 { disptech_lcd_ccfl_wg_tft,
162 "LCD CCFL Wide Gamut TFT",
163 "LCD CCFL Wide Gamut TFT",
164 0,
165 DISPTECH_LCD_RISE,
166 DISPTECH_LCD_FALL,
167 "L"
168 },
169 { disptech_lcd_wled,
170 "LCD White LED",
171 "LCD White LED",
172 0,
173 DISPTECH_LCD_RISE,
174 DISPTECH_LCD_FALL,
175 "e"
176 },
177 { disptech_lcd_wled_ips,
178 "LCD White LED IPS",
179 "LCD White LED IPS",
180 0,
181 DISPTECH_LCD_RISE,
182 DISPTECH_LCD_FALL,
183 "e"
184 },
185 { disptech_lcd_wled_vpa,
186 "LCD White LED VPA",
187 "LCD White LED VPA",
188 0,
189 DISPTECH_LCD_RISE,
190 DISPTECH_LCD_FALL,
191 "e"
192 },
193 { disptech_lcd_wled_tft,
194 "LCD White LED TFT",
195 "LCD White LED TFT",
196 0,
197 DISPTECH_LCD_RISE,
198 DISPTECH_LCD_FALL,
199 "e"
200 },
201 { disptech_lcd_rgbled,
202 "LCD RGB LED",
203 "LCD RGB LED",
204 0,
205 DISPTECH_LCD_RISE,
206 DISPTECH_LCD_FALL,
207 "b"
208 },
209 { disptech_lcd_rgbled_ips,
210 "LCD RGB LED IPS",
211 "LCD RGB LED IPS",
212 0,
213 DISPTECH_LCD_RISE,
214 DISPTECH_LCD_FALL,
215 "b"
216 },
217 { disptech_lcd_rgbled_vpa,
218 "LCD RGB LED VPA",
219 "LCD RGB LED VPA",
220 0,
221 DISPTECH_LCD_RISE,
222 DISPTECH_LCD_FALL,
223 "b"
224 },
225 { disptech_lcd_rgbled_tft,
226 "LCD RGB LED TFT",
227 "LCD RGB LED TFT",
228 0,
229 DISPTECH_LCD_RISE,
230 DISPTECH_LCD_FALL,
231 "b"
232
233 },
234 { disptech_lcd_rgledp,
235 "LCD RG Phosphor",
236 "LCD RG Phosphor",
237 0,
238 DISPTECH_LCD_RISE,
239 DISPTECH_LCD_FALL,
240 "h"
241 },
242 { disptech_lcd_rgledp_ips,
243 "LCD RG Phosphor IPS",
244 "LCD RG Phosphor IPS",
245 0,
246 DISPTECH_LCD_RISE,
247 DISPTECH_LCD_FALL,
248 "h"
249 },
250 { disptech_lcd_rgledp_vpa,
251 "LCD RG Phosphor VPA",
252 "LCD RG Phosphor VPA",
253 0,
254 DISPTECH_LCD_RISE,
255 DISPTECH_LCD_FALL,
256 "h"
257 },
258 { disptech_lcd_rgledp_tft,
259 "LCD RG Phosphor TFT",
260 "LCD RG Phosphor TFT",
261 0,
262 DISPTECH_LCD_RISE,
263 DISPTECH_LCD_FALL,
264 "h"
265 },
266
267 { disptech_oled,
268 "LED OLED",
269 "LED OLED",
270 0,
271 DISPTECH_LED_RISE,
272 DISPTECH_LED_FALL,
273 "o"
274 },
275 { disptech_amoled,
276 "LED AMOLED",
277 "LED AMOLED",
278 0,
279 DISPTECH_LED_RISE,
280 DISPTECH_LED_FALL,
281 "a"
282 },
283
284 { disptech_dlp,
285 "Projector",
286 "DLP Projector",
287 1,
288 DISPTECH_DLP_RISE,
289 DISPTECH_DLP_FALL,
290 "p"
291 },
292 { disptech_dlp_rgb,
293 "Projector RGB Filter Wheel",
294 "DLP Projector RGB Filter Wheel",
295 1,
296 DISPTECH_DLP_RISE,
297 DISPTECH_DLP_FALL,
298 "p"
299 },
300 { disptech_dlp_rgbw,
301 "Projector RGBW Filter Wheel",
302 "DPL Projector RGBW Filter Wheel",
303 1,
304 DISPTECH_DLP_RISE,
305 DISPTECH_DLP_FALL,
306 "p"
307 },
308 { disptech_dlp_rgbcmy,
309 "Projector RGBCMY Filter Wheel",
310 "DLP Projector RGBCMY Filter Wheel",
311 1,
312 DISPTECH_DLP_RISE,
313 DISPTECH_DLP_FALL,
314 "p"
315 },
316
317 {
318 disptech_unknown,
319 "Unknown",
320 "Unknown",
321 1,
322 DISPTECH_WORST_RISE,
323 DISPTECH_WORST_FALL,
324 "u"
325 },
326
327 {
328 disptech_end /* End marker */
329 }
330 };
331
332
333 static int unknown_ix = -1;
334
find_unknown()335 static void find_unknown() {
336 int i;
337
338 for (i = 0; disptech_info_array[i].dtech != disptech_end; i++) {
339 if (disptech_info_array[i].dtech == disptech_unknown) {
340 unknown_ix = i;
341 break;
342 }
343 }
344 }
345
346 /* Given the enum id, return the matching disptech_info entry */
347 /* Return the disptech_unknown entry if not matched */
disptech_get_id(disptech id)348 disptech_info *disptech_get_id(disptech id) {
349 int i;
350
351 for (i = 0; disptech_info_array[i].dtech != disptech_end; i++) {
352 if (disptech_info_array[i].dtech == id)
353 return &disptech_info_array[i];
354 }
355
356 if (unknown_ix < 0)
357 find_unknown();
358 return &disptech_info_array[unknown_ix];
359 }
360
361 /* Given the string id, return the matching disptech_info entry */
362 /* Return the disptech_unknown entry if not matched */
disptech_get_strid(char * strid)363 disptech_info *disptech_get_strid(char *strid) {
364 int i;
365
366 for (i = 0; disptech_info_array[i].dtech != disptech_end; i++) {
367 if (strcmp(disptech_info_array[i].strid, strid) == 0)
368 return &disptech_info_array[i];
369 }
370
371 if (unknown_ix < 0)
372 find_unknown();
373 return &disptech_info_array[unknown_ix];
374 }
375
376 /* For each selector we need to:
377
378 check each selector char
379 if already used,
380 remove it.
381 if no selector remain,
382 allocate a free one from the fallback list.
383 mark all used selectors
384
385 We treat the first selector as more important
386 than any aliases that come after it, and the
387 aliases as more important than the fallback list,
388 so we need to do three passes through all the selections.
389 */
390
391 /* Append the selection characters. */
392 /* If a selector is set, its index will be set in usels[]. */
393 /* Remove any used selectors from isel[]. */
394 /* If flag == 0, set from just first suggested selector. */
395 /* If flag == 1, set from just suggested selectors. */
396 /* If flag == 2, set from suggested and fallback selectors */
397 /* If flag == 3, append from suggested selectors */
disptechs_set_sel(int flag,int ix,char * osel,char * isel,char * usels,int * k,char * asels)398 void disptechs_set_sel(
399 int flag, /* See above */
400 int ix, /* Index of entry being set */
401 char *osel, /* Append unique selectors to this string. */
402 char *isel, /* Pointer to string list of suggested selectors, */
403 char *usels, /* char[256] initially -1, to track used selector entry index */
404 int *k, /* Index of next available selector in asels */
405 char *asels /* String list of fallback selectors to choose from, in order. */
406 ) {
407 char *iisel = isel, i;
408
409 //a1logd(g_log, 1,"disptechs_set_sel: flag %d, ix %d, osel '%s', isel '%s', k %d\n",flag, ix,osel,isel,*k);
410
411 if (flag != 3) {
412 /* See if we already have a selecor character */
413 if (osel[0] != '\000') {
414 //a1logd(g_log, 1," already set OK\n");
415 return;
416 }
417 } else {
418 if (isel[0] == '\000') {
419 //a1logd(g_log, 1," nothing to set from\n");
420 return; /* Nothing to set from */
421 }
422
423 /* Get ready to append */
424 osel += strlen(osel);
425 }
426
427 /* Set or add from the first unsed suggested selectors */
428 for (i = 0; *isel != '\000'; isel++, i++) {
429 if (flag == 0 && i > 0) {
430 //a1logd(g_log, 1," run out of primaries\n");
431 break; /* Looked at primary */
432 }
433 if (usels[*isel] == ((char)-1)) { /* If this selector is not currently used */
434 //a1logd(g_log, 1," added to '%c' from %d\n", *isel, i);
435 osel[0] = *isel; /* Use it */
436 osel[1] = '\000';
437 usels[osel[0]] = ix;
438
439 /* Remove all used/discarded from isel, in case we are called again. */
440 for (isel++; ;isel++, iisel++) {
441 *iisel = *isel;
442 if (*isel == '\000')
443 break;
444 }
445 return;
446 }
447 //a1logd(g_log, 1," sel '%c' at %d is used by ix %d\n", *isel, i, usels[*isel]);
448 }
449
450 /* If we get here, we haven't managed to add anything from the remaining */
451 /* selectors, so mark the candidate list as empty: */
452 iisel[0] = '\000';
453
454 if (flag != 2) {
455 //a1logd(g_log, 1," returning without add\n");
456 return;
457 }
458
459 /* Get the next unused char in fallback list */
460 for (;asels[*k] != '\000'; (*k)++) {
461 if (usels[asels[*k]] == ((char)-1)) /* Unused */
462 break;
463 }
464 if (asels[*k] != '\000') {
465 //a1logd(g_log, 1," set int to fallback '%c' at %d\n", asels[*k], *k);
466 osel[0] = asels[*k];
467 osel[1] = '\000';
468 usels[osel[0]] = ix;
469 (*k)++;
470 return;
471 }
472
473 //a1logd(g_log, 1," returning after fallback without add\n");
474 /* If we got here, we failed to add a selector */
475 return;
476 }
477
478 /* Return the display tech list with unique lsel lectors */
disptech_get_list()479 disptech_info *disptech_get_list() {
480 disptech_info *list = &disptech_info_array[1]; /* skip disptech_none entry */
481
482 int i, k;
483 char usels[256]; /* Used selectors */
484 static char *asels = "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
485
486 for (i = 0; i < 256; i++)
487 usels[i] = ((char)-1);
488 k = 0; /* Next selector index */
489
490 /* Add entries from the static list and their primary selectors */
491 for (i = 0; list[i].dtech != disptech_end; i++) {
492 //a1logd(g_log,1,"tech[%d] '%s' sels = '%s'\n",i,list[i].desc,list[i].sel);
493 strcpy(list[i].isel, list[i].sel);
494 list[i].lsel[0] = '\000';
495 disptechs_set_sel(0, i, list[i].lsel, list[i].isel, usels, &k, asels);
496 }
497
498 /* Set selectors from secondary */
499 for (i = 0; list[i].dtech != disptech_end; i++)
500 disptechs_set_sel(1, i, list[i].lsel, list[i].isel, usels, &k, asels);
501
502 /* Set remainder from fallback */
503 for (i = 0; list[i].dtech != disptech_end; i++)
504 disptechs_set_sel(2, i, list[i].lsel, list[i].isel, usels, &k, asels);
505
506 return list;
507 }
508
509 /* Locate the display list item that matches the given selector. */
510 /* Return NULL if not found */
disptech_select(disptech_info * list,char c)511 disptech_info *disptech_select(disptech_info *list, char c) {
512 int i;
513
514 for (i = 0; list[i].dtech != disptech_end; i++) {
515 if (c == list[i].lsel[0])
516 return &list[i];
517 }
518
519 return NULL;
520 }
521
522
523 /* ------------------------------------------- */
524
525 /*
526 Display settling time model. This is primarily tailored
527 to phosphor type response (ie. CRT or Plasma), but LCD
528 should be somewhat similar
529
530 Outline:
531
532 Use sRGB as the device model.
533
534 For the target RGB, compute the partial derivative of
535 delta E with respect to R, G & B, and then multiply
536 that by desired dE accuracy to get the target R, G & B
537 delta from the target, and then put that into
538 the exponential rise/fall model to compute settling time.
539 Choose the worst of the 3.
540
541 Should really change the code to compute partial derivative
542 directly, to speed code up.
543
544 We assume the phosphor stimulus is the proportional to the
545 light output required (ie. that the CRT/encoding non-linearity
546 is in the electron gun, not the electron->phoshor->light mechanism. )
547 */
548
549 /* Convert gamma encoded rgb to linear light rgb */
rgb2rgbl(double * rgbl,double * rgb)550 static void rgb2rgbl(double *rgbl, double *rgb) {
551 int i;
552 for (i = 0; i < 3; i++) {
553 if (rgb[i] < 0.04045)
554 rgbl[i] = rgb[i]/12.92;
555 else
556 rgbl[i] = pow((rgb[i] + 0.055)/1.055, 2.4);
557 }
558 }
559
560 /* Convert linear light rgb to L*a*b* */
rgbl2lab(double * lab,double * rgbl)561 static void rgbl2lab(double *lab, double *rgbl) {
562 int i;
563 double xyz[3];
564 static double mat[3][3] = {
565 { 0.412391, 0.212639, 0.019331 }, /* Red */
566 { 0.357584, 0.715169, 0.119195 }, /* Green */
567 { 0.180481, 0.072192, 0.950532 } /* Blue */
568 };
569
570 icmMulBy3x3(xyz, mat, rgbl);
571 icmXYZ2Lab(&icmD65, lab, xyz);
572 }
573
574 #ifdef NEVER
575 /* Convert linear light rgb to L*a*b* with partial derivatives */
ddrgbl2lab(double * lab,double dout[3][3],double * rgbl)576 static void ddrgbl2lab(double *lab, double dout[3][3], double *rgbl) {
577 int i, j, k;
578 double xyz[3];
579 static double mat[9] = {
580 0.412391, 0.212639, 0.019331, /* Red */
581 0.357584, 0.715169, 0.119195, /* Green */
582 0.180481, 0.072192, 0.950532 /* Blue */
583 };
584 double dxyzlab[3][3]; /* Part. Deriv of [xyz] with respect to [rgb] */
585 double dlabxyz[3][3]; /* Part. Deriv of [lab] from [xyz] */
586
587 icxdpdiiMulBy3x3Parm(xyz, dxyzlab, mat, rgbl);
588 icxdXYZ2Lab(&icmD65, lab, dlabxyz, xyz);
589
590 /* Compute the partial derivative of Lab from rgb */
591 for (k = 0; k < 3; k++) {
592 for (j = 0; j < 3; j++) {
593 dout[k][j] = 0.0;
594 for (i = 0; i < 3; i++) {
595 dout[k][j] += dlabxyz[k][i] * dxyzlab[i][j];
596 }
597 }
598 }
599 }
600 #endif /* NEVER */
601
602 /* Convert linear light rgb to L*a*b* delta E partial derivatives */
drgbl2lab(double deout[3],double * rgbl)603 static void drgbl2lab(/* double *lab, */double deout[3], double *rgbl) {
604 int i, j, k;
605 static double mat[3][3] = {
606 { 0.412391, 0.212639, 0.019331 }, /* Red */
607 { 0.357584, 0.715169, 0.119195 }, /* Green */
608 { 0.180481, 0.072192, 0.950532 } /* Blue */
609 };
610 double xyz[3];
611 double wp[3], tin[3], dtin[3];
612 double dlabxyz[3][3]; /* Part. Deriv of [lab] from [xyz] */
613 double dout[3][3]; /* Part. Deriv of [lab] from [rgb] */
614
615 /* rgb to XYZ */
616 for (i = 0; i < 3; i++) {
617 xyz[i] = 0.0;
618 for (j = 0; j < 3; j++) {
619 xyz[i] += mat[i][j] * rgbl[j];
620 }
621 }
622
623 /* XYZ to perceptual Lab with partial derivatives. */
624 wp[0] = icmD65.X, wp[1] = icmD65.Y, wp[2] = icmD65.Z;
625
626 for (i = 0; i < 3; i++) {
627 tin[i] = xyz[i]/wp[i];
628 dtin[i] = 1.0/wp[i];
629
630 if (tin[i] > 0.008856451586) {
631 dtin[i] *= pow(tin[i], -2.0/3.0) / 3.0;
632 tin[i] = pow(tin[i],1.0/3.0);
633 } else {
634 dtin[i] *= 7.787036979;
635 tin[i] = 7.787036979 * tin[i] + 16.0/116.0;
636 }
637 }
638
639 /* lab[0] = 116.0 * tin[1] - 16.0; */
640 dlabxyz[0][0] = 0.0;
641 dlabxyz[0][1] = 116.0 * dtin[1];
642 dlabxyz[0][2] = 0.0;
643
644 /* lab[1] = 500.0 * (tin[0] - tin[1]); */
645 dlabxyz[1][0] = 500.0 * dtin[0];
646 dlabxyz[1][1] = 500.0 * -dtin[1];
647 dlabxyz[1][2] = 0.0;
648
649 /* lab[2] = 200.0 * (tin[1] - tin[2]); */
650 dlabxyz[2][0] = 0.0 * mat[0][1];
651 dlabxyz[2][1] = 200.0 * dtin[1];
652 dlabxyz[2][2] = 200.0 * -dtin[2];
653
654 /* Compute the partial derivative of delta E from rgb */
655 for (j = 0; j < 3; j++) {
656 deout[j] = 0.0;
657 for (k = 0; k < 3; k++) {
658 dout[k][j] = 0.0;
659 for (i = 0; i < 3; i++) {
660 dout[k][j] += dlabxyz[k][i] * mat[i][j];
661 }
662 deout[j] += dout[k][j] * dout[k][j];
663 }
664 deout[j] = sqrt(deout[j]);
665 }
666 }
667
disp_settle_time(double * orgb,double * nrgb,double rise,double fall,double dE)668 double disp_settle_time(double *orgb, double *nrgb, double rise, double fall, double dE) {
669 int i, j;
670 double orgbl[3], nrgbl[3]; /* Linear light RGB */
671 double drgb[3]; /* Partial derivative of RGB wrt to dE at new rgb */
672 double argbl[3]; /* Acceptable RGB */
673 double stime[3]; /* Settling time */
674 double kr, kf;
675 double xtime = 0.0;
676
677 /* Convert rgb's to linear light rgb */
678 rgb2rgbl(orgbl, orgb);
679 rgb2rgbl(nrgbl, nrgb);
680
681 //printf("orgb = %f %f %f\n", orgb[0], orgb[1], orgb[2]);
682 //printf("nrgb = %f %f %f\n", nrgb[0], nrgb[1], nrgb[2]);
683 //printf("orgbl = %f %f %f\n", orgbl[0], orgbl[1], orgbl[2]);
684 //printf("nrgbl = %f %f %f\n", nrgbl[0], nrgbl[1], nrgbl[2]);
685 //printf("dE = %f\n", dE);
686
687 /* Compute partial derivative */
688 drgbl2lab(drgb, nrgbl);
689 //printf("drgb = %f %f %f\n", drgb[0], drgb[1], drgb[2]);
690
691 #ifdef NEVER
692 /* Calculate partial derivative explicitely to check */
693 {
694 double rlab[3], lab[3];
695 double xdrgb[3]; /* Partial derivative of RGB wrt to dE at new rgb */
696
697 /* Reference Lab */
698 rgbl2lab(rlab, nrgbl);
699 //printf("rlab = %f %f %f\n", rlab[0], rlab[1], rlab[2]);
700
701 for (j = 0; j < 3; j++) {
702 double del;
703
704 if (nrgbl[j] > 0.5)
705 del = -1e-6;
706 else
707 del = 1e-6;
708
709 nrgbl[j] += del;
710 rgbl2lab(lab, nrgbl);
711 nrgbl[j] -= del;
712 //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);
713 xdrgb[j] = icmLabDE(rlab, lab)/fabs(del);
714 }
715 printf("chk drgb = %f %f %f\n", xdrgb[0], xdrgb[1], xdrgb[2]);
716 }
717 #endif /* NEVER */
718
719 /* Compute rgb value that would give targ delta E */
720 for (j = 0; j < 3; j++) {
721 double del;
722
723 del = dE/drgb[j];
724
725 if (orgbl[j] < nrgbl[j]) {
726 argbl[j] = nrgbl[j] - del;
727 if (argbl[j] < orgbl[j])
728 argbl[j] = orgbl[j];
729 } else {
730 argbl[j] = nrgbl[j] + del;
731 if (argbl[j] > orgbl[j])
732 argbl[j] = orgbl[j];
733 }
734 }
735
736 //{ double rlab[3], lab[3];
737 //rgbl2lab(lab, argbl);
738 //rgbl2lab(rlab, nrgbl);
739 //printf("argbl = %f %f %f\n", argbl[0], argbl[1], argbl[2]);
740 //printf("dE for argbl = %f\n",icmLabDE(rlab, lab));
741 //}
742
743 /* Compute the modelled time from orgbl to argbl */
744 kr = rise/log(1.0 - 0.9); /* Exponent constant for 90% change*/
745 kf = fall/log(1.0 - 0.9); /* Exponent constant for 90% change*/
746
747 for (j = 0; j < 3; j++) {
748 double el, dl, n, t;
749
750 dl = (argbl[j] - orgbl[j])/(nrgbl[j] - orgbl[j]);
751
752 if (fabs(dl) < 1e-6) {
753 stime[j] = 0.0;
754 continue;
755 }
756
757 if (nrgbl[j] > orgbl[j])
758 stime[j] = kr * log(1.0 - dl);
759 else
760 stime[j] = kf * log(1.0 - dl);
761
762 if (stime[j] > xtime && stime[j] < 5.0)
763 xtime = stime[j];
764 }
765 //printf("stime = %f %f %f\n", stime[0], stime[1], stime[2]);
766 //printf("returning = %f\n",xtime);
767
768 return xtime;
769 }
770
771
772
773
774
775