| 12 |  |  | 
| 13 |  | #include <stdio.h> | 
| 14 |  | #include <string.h> | 
| 15 | + | #include <math.h> | 
| 16 |  | #include "color.h" | 
| 17 |  |  | 
| 18 |  | #define CEPS    1e-4                    /* color epsilon */ | 
| 271 |  | COLOR  ciecolor; | 
| 272 |  |  | 
| 273 |  | spec_cie(ciecolor, s, e); | 
| 274 | < | cie_rgb(col, ciecolor); | 
| 274 | > | colortrans(col, xyz2rgbmat, ciecolor); | 
| 275 |  | } | 
| 276 |  |  | 
| 277 |  |  | 
| 278 |  | static double | 
| 279 |  | spec_dot(                       /* spectrum dot-product with cumulative observer */ | 
| 280 | < | SCOLOR scol, | 
| 280 | > | const SCOLOR scol, | 
| 281 |  | int  ncs, | 
| 282 |  | const float wlpt[4], | 
| 283 |  | const unsigned short cumul[], | 
| 312 |  | void | 
| 313 |  | scolor2cie(                     /* accurate conversion from spectrum to XYZ */ | 
| 314 |  | COLOR col, | 
| 315 | < | SCOLOR scol, | 
| 315 | > | const SCOLOR scol, | 
| 316 |  | int ncs, | 
| 317 |  | const float wlpt[4] | 
| 318 |  | ) | 
| 330 |  | void | 
| 331 |  | scolor2rgb(                     /* accurate conversion from spectrum to RGB */ | 
| 332 |  | COLOR col, | 
| 333 | < | SCOLOR scol, | 
| 333 | > | const SCOLOR scol, | 
| 334 |  | int ncs, | 
| 335 |  | const float wlpt[4] | 
| 336 |  | ) | 
| 346 |  | } | 
| 347 |  |  | 
| 348 |  |  | 
| 349 | + | void | 
| 350 | + | scolor_out(                     /* prepare (spectral) color for output */ | 
| 351 | + | COLORV *cout, | 
| 352 | + | RGBPRIMS pr, | 
| 353 | + | const SCOLOR cres | 
| 354 | + | ) | 
| 355 | + | { | 
| 356 | + | static COLORMAT xyz2myp; | 
| 357 | + | static RGBPRIMP lastp = NULL; | 
| 358 | + |  | 
| 359 | + | if (!pr) {                      /* output is spectral */ | 
| 360 | + | copyscolor(cout, cres); | 
| 361 | + | } else if (pr == stdprims) {    /* output is standard RGB */ | 
| 362 | + | scolor_rgb(cout, cres); | 
| 363 | + | } else if (pr == xyzprims) {    /* output is XYZ */ | 
| 364 | + | scolor_cie(cout, cres); | 
| 365 | + | scalecolor(cout, WHTEFFICACY); | 
| 366 | + | } else if (NCSAMP > 3) {        /* spectral -> custom RGB */ | 
| 367 | + | COLOR   xyz; | 
| 368 | + | if (lastp != pr) | 
| 369 | + | compxyz2rgbWBmat(xyz2myp, lastp=pr); | 
| 370 | + | scolor_cie(xyz, cres); | 
| 371 | + | colortrans(cout, xyz2myp, xyz); | 
| 372 | + | clipgamut(cout, xyz[CIEY], CGAMUT_LOWER, cblack, cwhite); | 
| 373 | + | } else {                        /* else copy unknown RGB */ | 
| 374 | + | copycolor(cout, cres); | 
| 375 | + | } | 
| 376 | + | } | 
| 377 | + |  | 
| 378 | + |  | 
| 379 |  | double | 
| 380 | < | scolor_photopic(                /* compute scotopic integral for spectral color */ | 
| 381 | < | SCOLOR  scol | 
| 380 | > | scolor2photopic(                /* compute scotopic integral for spectral color */ | 
| 381 | > | const SCOLOR  scol, | 
| 382 | > | int ncs, | 
| 383 | > | const float wlpt[4] | 
| 384 |  | ) | 
| 385 |  | { | 
| 386 | < | if (NCSAMP == 3) | 
| 386 | > | if (ncs == 3) | 
| 387 |  | return bright(scol); | 
| 388 |  |  | 
| 389 | < | return(spec_dot(scol, NCSAMP, WLPART, cie_y_cumul, CIE_Y_WLMIN, CIE_Y_WLMAX)); | 
| 389 | > | return(spec_dot(scol, ncs, wlpt, cie_y_cumul, CIE_Y_WLMIN, CIE_Y_WLMAX)); | 
| 390 |  | } | 
| 391 |  |  | 
| 392 |  |  | 
| 393 |  | double | 
| 394 | + | scolor2scotopic(                /* compute Y channel for spectral color */ | 
| 395 | + | const SCOLOR  scol, | 
| 396 | + | int ncs, | 
| 397 | + | const float wlpt[4] | 
| 398 | + | ) | 
| 399 | + | { | 
| 400 | + | return(spec_dot(scol, ncs, wlpt, scotopic_cumul, SCOTOPIC_WLMIN, SCOTOPIC_WLMAX)); | 
| 401 | + | } | 
| 402 | + |  | 
| 403 | + |  | 
| 404 | + | double | 
| 405 | + | scolor2melanopic(               /* compute melanopic integral for spectral color */ | 
| 406 | + | const SCOLOR  scol, | 
| 407 | + | int ncs, | 
| 408 | + | const float wlpt[4] | 
| 409 | + | ) | 
| 410 | + | { | 
| 411 | + | return(spec_dot(scol, ncs, wlpt, melanopic_cumul, MELANOPIC_WLMIN, MELANOPIC_WLMAX)); | 
| 412 | + | } | 
| 413 | + |  | 
| 414 | + |  | 
| 415 | + | double | 
| 416 | + | scolor_photopic(                /* compute scotopic integral for spectral color */ | 
| 417 | + | const SCOLOR  scol | 
| 418 | + | ) | 
| 419 | + | { | 
| 420 | + | return(scolor2photopic(scol, NCSAMP, WLPART)); | 
| 421 | + | } | 
| 422 | + |  | 
| 423 | + |  | 
| 424 | + | double | 
| 425 |  | scolor_scotopic(                /* compute Y channel for spectral color */ | 
| 426 | < | SCOLOR  scol | 
| 426 | > | const SCOLOR  scol | 
| 427 |  | ) | 
| 428 |  | { | 
| 429 | < | return(spec_dot(scol, NCSAMP, WLPART, scotopic_cumul, SCOTOPIC_WLMIN, SCOTOPIC_WLMAX)); | 
| 429 | > | return(scolor2scotopic(scol, NCSAMP, WLPART)); | 
| 430 |  | } | 
| 431 |  |  | 
| 432 |  |  | 
| 433 |  | double | 
| 434 |  | scolor_melanopic(               /* compute melanopic integral for spectral color */ | 
| 435 | < | SCOLOR  scol | 
| 435 | > | const SCOLOR  scol | 
| 436 |  | ) | 
| 437 |  | { | 
| 438 | < | return(spec_dot(scol, NCSAMP, WLPART, melanopic_cumul, MELANOPIC_WLMIN, MELANOPIC_WLMAX)); | 
| 438 | > | return(scolor2melanopic(scol, NCSAMP, WLPART)); | 
| 439 |  | } | 
| 440 |  |  | 
| 441 |  |  | 
| 442 |  | void | 
| 443 | + | convertscolorcol(               /* any uniform spectrum to working */ | 
| 444 | + | SCOLOR rcol, | 
| 445 | + | const COLORV src[], | 
| 446 | + | int snc, | 
| 447 | + | double swl0, | 
| 448 | + | double swl1 | 
| 449 | + | ) | 
| 450 | + | { | 
| 451 | + | if (NCSAMP > 3) {               /* spectrum -> spectrum */ | 
| 452 | + | convertscolor(rcol, NCSAMP, WLPART[0], WLPART[3], | 
| 453 | + | src, snc, swl0, swl1); | 
| 454 | + | } else if ((snc <= MAXCSAMP) & (swl0 > swl1)) { | 
| 455 | + | float   wlpt[4];        /* no intermediate conversion needed */ | 
| 456 | + | wlpt[0] = swl0; wlpt[3] = swl1; | 
| 457 | + | wlpt[1] = WLPART[1]; wlpt[2] = WLPART[2]; | 
| 458 | + | scolor2rgb(rcol, (COLORV *)src, snc, wlpt); | 
| 459 | + | } else { | 
| 460 | + | SCOLOR  dcol;           /* else convert spectrum first */ | 
| 461 | + | int     dnc = snc*(WLPART[0] - WLPART[3])/fabs(swl0 - swl1) + .99; | 
| 462 | + | if (dnc > MAXCSAMP) dnc = MAXCSAMP; | 
| 463 | + | convertscolor(dcol, dnc, WLPART[0], WLPART[3], src, snc, swl0, swl1); | 
| 464 | + | scolor2rgb(rcol, dcol, dnc, WLPART); | 
| 465 | + | } | 
| 466 | + | } | 
| 467 | + |  | 
| 468 | + |  | 
| 469 | + | void | 
| 470 |  | cie_rgb(                        /* convert CIE color to standard RGB */ | 
| 471 |  | COLOR   rgb, | 
| 472 | < | COLOR  xyz | 
| 472 | > | const COLOR  xyz | 
| 473 |  | ) | 
| 474 |  | { | 
| 475 |  | colortrans(rgb, xyz2rgbmat, xyz); | 
| 482 |  | COLOR  col, | 
| 483 |  | double  brt, | 
| 484 |  | int  gamut, | 
| 485 | < | COLOR  lower, | 
| 486 | < | COLOR  upper | 
| 485 | > | const COLOR  lower, | 
| 486 | > | const COLOR  upper | 
| 487 |  | ) | 
| 488 |  | { | 
| 489 |  | int  rflags = 0; | 
| 528 |  | void | 
| 529 |  | colortrans(                     /* convert c1 by mat and put into c2 */ | 
| 530 |  | COLOR  c2, | 
| 531 | < | COLORMAT  mat, | 
| 532 | < | COLOR  c1 | 
| 531 | > | const COLORMAT  mat, | 
| 532 | > | const COLOR  c1 | 
| 533 |  | ) | 
| 534 |  | { | 
| 535 |  | COLOR   cout; | 
| 545 |  | void | 
| 546 |  | multcolormat(                   /* multiply m1 by m2 and put into m3 */ | 
| 547 |  | COLORMAT  m3,                   /* m3 can be either m1 or m2 w/o harm */ | 
| 548 | < | COLORMAT  m2, | 
| 549 | < | COLORMAT  m1 | 
| 548 | > | const COLORMAT  m2, | 
| 549 | > | const COLORMAT  m1 | 
| 550 |  | ) | 
| 551 |  | { | 
| 552 |  | COLORMAT  mt; | 
| 738 |  | int | 
| 739 |  | compxyzWBmat(                   /* CIE von Kries transform from wht1 to wht2 */ | 
| 740 |  | COLORMAT  mat, | 
| 741 | < | float  wht1[2], | 
| 742 | < | float  wht2[2] | 
| 741 | > | const float  wht1[2], | 
| 742 | > | const float  wht2[2] | 
| 743 |  | ) | 
| 744 |  | { | 
| 745 |  | COLOR   cw1, cw2; |