1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3 * 4 * This code is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License version 2 only, as 6 * published by the Free Software Foundation. Oracle designates this 7 * particular file as subject to the "Classpath" exception as provided 8 * by Oracle in the LICENSE file that accompanied this code. 9 * 10 * This code is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 * version 2 for more details (a copy is included in the LICENSE file that 14 * accompanied this code). 15 * 16 * You should have received a copy of the GNU General Public License version 17 * 2 along with this work; if not, write to the Free Software Foundation, 18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19 * 20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21 * or visit www.oracle.com if you need additional information or have any 22 * questions. 23 */ 24 25 // This file is available under and governed by the GNU General Public 26 // License version 2 only, as published by the Free Software Foundation. 27 // However, the following notice accompanied the original version of this 28 // file: 29 // 30 //--------------------------------------------------------------------------------- 31 // 32 // Little Color Management System 33 // Copyright (c) 1998-2020 Marti Maria Saguer 34 // 35 // Permission is hereby granted, free of charge, to any person obtaining 36 // a copy of this software and associated documentation files (the "Software"), 37 // to deal in the Software without restriction, including without limitation 38 // the rights to use, copy, modify, merge, publish, distribute, sublicense, 39 // and/or sell copies of the Software, and to permit persons to whom the Software 40 // is furnished to do so, subject to the following conditions: 41 // 42 // The above copyright notice and this permission notice shall be included in 43 // all copies or substantial portions of the Software. 44 // 45 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 46 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 47 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 48 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 49 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 50 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 51 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 52 // 53 //--------------------------------------------------------------------------------- 54 // 55 56 #include "lcms2_internal.h" 57 58 59 // ------------------------------------------------------------------------ 60 61 // Gamut boundary description by using Jan Morovic's Segment maxima method 62 // Many thanks to Jan for allowing me to use his algorithm. 63 64 // r = C* 65 // alpha = Hab 66 // theta = L* 67 68 #define SECTORS 16 // number of divisions in alpha and theta 69 70 // Spherical coordinates 71 typedef struct { 72 73 cmsFloat64Number r; 74 cmsFloat64Number alpha; 75 cmsFloat64Number theta; 76 77 } cmsSpherical; 78 79 typedef enum { 80 GP_EMPTY, 81 GP_SPECIFIED, 82 GP_MODELED 83 84 } GDBPointType; 85 86 87 typedef struct { 88 89 GDBPointType Type; 90 cmsSpherical p; // Keep also alpha & theta of maximum 91 92 } cmsGDBPoint; 93 94 95 typedef struct { 96 97 cmsContext ContextID; 98 cmsGDBPoint Gamut[SECTORS][SECTORS]; 99 100 } cmsGDB; 101 102 103 // A line using the parametric form 104 // P = a + t*u 105 typedef struct { 106 107 cmsVEC3 a; 108 cmsVEC3 u; 109 110 } cmsLine; 111 112 113 // A plane using the parametric form 114 // Q = b + r*v + s*w 115 typedef struct { 116 117 cmsVEC3 b; 118 cmsVEC3 v; 119 cmsVEC3 w; 120 121 } cmsPlane; 122 123 124 125 // -------------------------------------------------------------------------------------------- 126 127 // ATAN2() which always returns degree positive numbers 128 129 static 130 cmsFloat64Number _cmsAtan2(cmsFloat64Number y, cmsFloat64Number x) 131 { 132 cmsFloat64Number a; 133 134 // Deal with undefined case 135 if (x == 0.0 && y == 0.0) return 0; 136 137 a = (atan2(y, x) * 180.0) / M_PI; 138 139 while (a < 0) { 140 a += 360; 141 } 142 143 return a; 144 } 145 146 // Convert to spherical coordinates 147 static 148 void ToSpherical(cmsSpherical* sp, const cmsVEC3* v) 149 { 150 151 cmsFloat64Number L, a, b; 152 153 L = v ->n[VX]; 154 a = v ->n[VY]; 155 b = v ->n[VZ]; 156 157 sp ->r = sqrt( L*L + a*a + b*b ); 158 159 if (sp ->r == 0) { 160 sp ->alpha = sp ->theta = 0; 161 return; 162 } 163 164 sp ->alpha = _cmsAtan2(a, b); 165 sp ->theta = _cmsAtan2(sqrt(a*a + b*b), L); 166 } 167 168 169 // Convert to cartesian from spherical 170 static 171 void ToCartesian(cmsVEC3* v, const cmsSpherical* sp) 172 { 173 cmsFloat64Number sin_alpha; 174 cmsFloat64Number cos_alpha; 175 cmsFloat64Number sin_theta; 176 cmsFloat64Number cos_theta; 177 cmsFloat64Number L, a, b; 178 179 sin_alpha = sin((M_PI * sp ->alpha) / 180.0); 180 cos_alpha = cos((M_PI * sp ->alpha) / 180.0); 181 sin_theta = sin((M_PI * sp ->theta) / 180.0); 182 cos_theta = cos((M_PI * sp ->theta) / 180.0); 183 184 a = sp ->r * sin_theta * sin_alpha; 185 b = sp ->r * sin_theta * cos_alpha; 186 L = sp ->r * cos_theta; 187 188 v ->n[VX] = L; 189 v ->n[VY] = a; 190 v ->n[VZ] = b; 191 } 192 193 194 // Quantize sector of a spherical coordinate. Saturate 360, 180 to last sector 195 // The limits are the centers of each sector, so 196 static 197 void QuantizeToSector(const cmsSpherical* sp, int* alpha, int* theta) 198 { 199 *alpha = (int) floor(((sp->alpha * (SECTORS)) / 360.0) ); 200 *theta = (int) floor(((sp->theta * (SECTORS)) / 180.0) ); 201 202 if (*alpha >= SECTORS) 203 *alpha = SECTORS-1; 204 if (*theta >= SECTORS) 205 *theta = SECTORS-1; 206 } 207 208 209 // Line determined by 2 points 210 static 211 void LineOf2Points(cmsLine* line, cmsVEC3* a, cmsVEC3* b) 212 { 213 214 _cmsVEC3init(&line ->a, a ->n[VX], a ->n[VY], a ->n[VZ]); 215 _cmsVEC3init(&line ->u, b ->n[VX] - a ->n[VX], 216 b ->n[VY] - a ->n[VY], 217 b ->n[VZ] - a ->n[VZ]); 218 } 219 220 221 // Evaluate parametric line 222 static 223 void GetPointOfLine(cmsVEC3* p, const cmsLine* line, cmsFloat64Number t) 224 { 225 p ->n[VX] = line ->a.n[VX] + t * line->u.n[VX]; 226 p ->n[VY] = line ->a.n[VY] + t * line->u.n[VY]; 227 p ->n[VZ] = line ->a.n[VZ] + t * line->u.n[VZ]; 228 } 229 230 231 232 /* 233 Closest point in sector line1 to sector line2 (both are defined as 0 <=t <= 1) 234 http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm 235 236 Copyright 2001, softSurfer (www.softsurfer.com) 237 This code may be freely used and modified for any purpose 238 providing that this copyright notice is included with it. 239 SoftSurfer makes no warranty for this code, and cannot be held 240 liable for any real or imagined damage resulting from its use. 241 Users of this code must verify correctness for their application. 242 243 */ 244 245 static 246 cmsBool ClosestLineToLine(cmsVEC3* r, const cmsLine* line1, const cmsLine* line2) 247 { 248 cmsFloat64Number a, b, c, d, e, D; 249 cmsFloat64Number sc, sN, sD; 250 //cmsFloat64Number tc; // left for future use 251 cmsFloat64Number tN, tD; 252 cmsVEC3 w0; 253 254 _cmsVEC3minus(&w0, &line1 ->a, &line2 ->a); 255 256 a = _cmsVEC3dot(&line1 ->u, &line1 ->u); 257 b = _cmsVEC3dot(&line1 ->u, &line2 ->u); 258 c = _cmsVEC3dot(&line2 ->u, &line2 ->u); 259 d = _cmsVEC3dot(&line1 ->u, &w0); 260 e = _cmsVEC3dot(&line2 ->u, &w0); 261 262 D = a*c - b * b; // Denominator 263 sD = tD = D; // default sD = D >= 0 264 265 if (D < MATRIX_DET_TOLERANCE) { // the lines are almost parallel 266 267 sN = 0.0; // force using point P0 on segment S1 268 sD = 1.0; // to prevent possible division by 0.0 later 269 tN = e; 270 tD = c; 271 } 272 else { // get the closest points on the infinite lines 273 274 sN = (b*e - c*d); 275 tN = (a*e - b*d); 276 277 if (sN < 0.0) { // sc < 0 => the s=0 edge is visible 278 279 sN = 0.0; 280 tN = e; 281 tD = c; 282 } 283 else if (sN > sD) { // sc > 1 => the s=1 edge is visible 284 sN = sD; 285 tN = e + b; 286 tD = c; 287 } 288 } 289 290 if (tN < 0.0) { // tc < 0 => the t=0 edge is visible 291 292 tN = 0.0; 293 // recompute sc for this edge 294 if (-d < 0.0) 295 sN = 0.0; 296 else if (-d > a) 297 sN = sD; 298 else { 299 sN = -d; 300 sD = a; 301 } 302 } 303 else if (tN > tD) { // tc > 1 => the t=1 edge is visible 304 305 tN = tD; 306 307 // recompute sc for this edge 308 if ((-d + b) < 0.0) 309 sN = 0; 310 else if ((-d + b) > a) 311 sN = sD; 312 else { 313 sN = (-d + b); 314 sD = a; 315 } 316 } 317 // finally do the division to get sc and tc 318 sc = (fabs(sN) < MATRIX_DET_TOLERANCE ? 0.0 : sN / sD); 319 //tc = (fabs(tN) < MATRIX_DET_TOLERANCE ? 0.0 : tN / tD); // left for future use. 320 321 GetPointOfLine(r, line1, sc); 322 return TRUE; 323 } 324 325 326 327 // ------------------------------------------------------------------ Wrapper 328 329 330 // Allocate & free structure 331 cmsHANDLE CMSEXPORT cmsGBDAlloc(cmsContext ContextID) 332 { 333 cmsGDB* gbd = (cmsGDB*) _cmsMallocZero(ContextID, sizeof(cmsGDB)); 334 if (gbd == NULL) return NULL; 335 336 gbd -> ContextID = ContextID; 337 338 return (cmsHANDLE) gbd; 339 } 340 341 342 void CMSEXPORT cmsGBDFree(cmsHANDLE hGBD) 343 { 344 cmsGDB* gbd = (cmsGDB*) hGBD; 345 if (hGBD != NULL) 346 _cmsFree(gbd->ContextID, (void*) gbd); 347 } 348 349 350 // Auxiliary to retrieve a pointer to the segmentr containing the Lab value 351 static 352 cmsGDBPoint* GetPoint(cmsGDB* gbd, const cmsCIELab* Lab, cmsSpherical* sp) 353 { 354 cmsVEC3 v; 355 int alpha, theta; 356 357 // Housekeeping 358 _cmsAssert(gbd != NULL); 359 _cmsAssert(Lab != NULL); 360 _cmsAssert(sp != NULL); 361 362 // Center L* by subtracting half of its domain, that's 50 363 _cmsVEC3init(&v, Lab ->L - 50.0, Lab ->a, Lab ->b); 364 365 // Convert to spherical coordinates 366 ToSpherical(sp, &v); 367 368 if (sp ->r < 0 || sp ->alpha < 0 || sp->theta < 0) { 369 cmsSignalError(gbd ->ContextID, cmsERROR_RANGE, "spherical value out of range"); 370 return NULL; 371 } 372 373 // On which sector it falls? 374 QuantizeToSector(sp, &alpha, &theta); 375 376 if (alpha < 0 || theta < 0 || alpha >= SECTORS || theta >= SECTORS) { 377 cmsSignalError(gbd ->ContextID, cmsERROR_RANGE, " quadrant out of range"); 378 return NULL; 379 } 380 381 // Get pointer to the sector 382 return &gbd ->Gamut[theta][alpha]; 383 } 384 385 // Add a point to gamut descriptor. Point to add is in Lab color space. 386 // GBD is centered on a=b=0 and L*=50 387 cmsBool CMSEXPORT cmsGDBAddPoint(cmsHANDLE hGBD, const cmsCIELab* Lab) 388 { 389 cmsGDB* gbd = (cmsGDB*) hGBD; 390 cmsGDBPoint* ptr; 391 cmsSpherical sp; 392 393 394 // Get pointer to the sector 395 ptr = GetPoint(gbd, Lab, &sp); 396 if (ptr == NULL) return FALSE; 397 398 // If no samples at this sector, add it 399 if (ptr ->Type == GP_EMPTY) { 400 401 ptr -> Type = GP_SPECIFIED; 402 ptr -> p = sp; 403 } 404 else { 405 406 407 // Substitute only if radius is greater 408 if (sp.r > ptr -> p.r) { 409 410 ptr -> Type = GP_SPECIFIED; 411 ptr -> p = sp; 412 } 413 } 414 415 return TRUE; 416 } 417 418 // Check if a given point falls inside gamut 419 cmsBool CMSEXPORT cmsGDBCheckPoint(cmsHANDLE hGBD, const cmsCIELab* Lab) 420 { 421 cmsGDB* gbd = (cmsGDB*) hGBD; 422 cmsGDBPoint* ptr; 423 cmsSpherical sp; 424 425 // Get pointer to the sector 426 ptr = GetPoint(gbd, Lab, &sp); 427 if (ptr == NULL) return FALSE; 428 429 // If no samples at this sector, return no data 430 if (ptr ->Type == GP_EMPTY) return FALSE; 431 432 // In gamut only if radius is greater 433 434 return (sp.r <= ptr -> p.r); 435 } 436 437 // ----------------------------------------------------------------------------------------------------------------------- 438 439 // Find near sectors. The list of sectors found is returned on Close[]. 440 // The function returns the number of sectors as well. 441 442 // 24 9 10 11 12 443 // 23 8 1 2 13 444 // 22 7 * 3 14 445 // 21 6 5 4 15 446 // 20 19 18 17 16 447 // 448 // Those are the relative movements 449 // {-2,-2}, {-1, -2}, {0, -2}, {+1, -2}, {+2, -2}, 450 // {-2,-1}, {-1, -1}, {0, -1}, {+1, -1}, {+2, -1}, 451 // {-2, 0}, {-1, 0}, {0, 0}, {+1, 0}, {+2, 0}, 452 // {-2,+1}, {-1, +1}, {0, +1}, {+1, +1}, {+2, +1}, 453 // {-2,+2}, {-1, +2}, {0, +2}, {+1, +2}, {+2, +2}}; 454 455 456 static 457 const struct _spiral { 458 459 int AdvX, AdvY; 460 461 } Spiral[] = { {0, -1}, {+1, -1}, {+1, 0}, {+1, +1}, {0, +1}, {-1, +1}, 462 {-1, 0}, {-1, -1}, {-1, -2}, {0, -2}, {+1, -2}, {+2, -2}, 463 {+2, -1}, {+2, 0}, {+2, +1}, {+2, +2}, {+1, +2}, {0, +2}, 464 {-1, +2}, {-2, +2}, {-2, +1}, {-2, 0}, {-2, -1}, {-2, -2} }; 465 466 #define NSTEPS (sizeof(Spiral) / sizeof(struct _spiral)) 467 468 static 469 int FindNearSectors(cmsGDB* gbd, int alpha, int theta, cmsGDBPoint* Close[]) 470 { 471 int nSectors = 0; 472 int a, t; 473 cmsUInt32Number i; 474 cmsGDBPoint* pt; 475 476 for (i=0; i < NSTEPS; i++) { 477 478 a = alpha + Spiral[i].AdvX; 479 t = theta + Spiral[i].AdvY; 480 481 // Cycle at the end 482 a %= SECTORS; 483 t %= SECTORS; 484 485 // Cycle at the begin 486 if (a < 0) a = SECTORS + a; 487 if (t < 0) t = SECTORS + t; 488 489 pt = &gbd ->Gamut[t][a]; 490 491 if (pt -> Type != GP_EMPTY) { 492 493 Close[nSectors++] = pt; 494 } 495 } 496 497 return nSectors; 498 } 499 500 501 // Interpolate a missing sector. Method identifies whatever this is top, bottom or mid 502 static 503 cmsBool InterpolateMissingSector(cmsGDB* gbd, int alpha, int theta) 504 { 505 cmsSpherical sp; 506 cmsVEC3 Lab; 507 cmsVEC3 Centre; 508 cmsLine ray; 509 int nCloseSectors; 510 cmsGDBPoint* Close[NSTEPS + 1]; 511 cmsSpherical closel, templ; 512 cmsLine edge; 513 int k, m; 514 515 // Is that point already specified? 516 if (gbd ->Gamut[theta][alpha].Type != GP_EMPTY) return TRUE; 517 518 // Fill close points 519 nCloseSectors = FindNearSectors(gbd, alpha, theta, Close); 520 521 522 // Find a central point on the sector 523 sp.alpha = (cmsFloat64Number) ((alpha + 0.5) * 360.0) / (SECTORS); 524 sp.theta = (cmsFloat64Number) ((theta + 0.5) * 180.0) / (SECTORS); 525 sp.r = 50.0; 526 527 // Convert to Cartesian 528 ToCartesian(&Lab, &sp); 529 530 // Create a ray line from centre to this point 531 _cmsVEC3init(&Centre, 50.0, 0, 0); 532 LineOf2Points(&ray, &Lab, &Centre); 533 534 // For all close sectors 535 closel.r = 0.0; 536 closel.alpha = 0; 537 closel.theta = 0; 538 539 for (k=0; k < nCloseSectors; k++) { 540 541 for(m = k+1; m < nCloseSectors; m++) { 542 543 cmsVEC3 temp, a1, a2; 544 545 // A line from sector to sector 546 ToCartesian(&a1, &Close[k]->p); 547 ToCartesian(&a2, &Close[m]->p); 548 549 LineOf2Points(&edge, &a1, &a2); 550 551 // Find a line 552 ClosestLineToLine(&temp, &ray, &edge); 553 554 // Convert to spherical 555 ToSpherical(&templ, &temp); 556 557 558 if ( templ.r > closel.r && 559 templ.theta >= (theta*180.0/SECTORS) && 560 templ.theta <= ((theta+1)*180.0/SECTORS) && 561 templ.alpha >= (alpha*360.0/SECTORS) && 562 templ.alpha <= ((alpha+1)*360.0/SECTORS)) { 563 564 closel = templ; 565 } 566 } 567 } 568 569 gbd ->Gamut[theta][alpha].p = closel; 570 gbd ->Gamut[theta][alpha].Type = GP_MODELED; 571 572 return TRUE; 573 574 } 575 576 577 // Interpolate missing parts. The algorithm fist computes slices at 578 // theta=0 and theta=Max. 579 cmsBool CMSEXPORT cmsGDBCompute(cmsHANDLE hGBD, cmsUInt32Number dwFlags) 580 { 581 int alpha, theta; 582 cmsGDB* gbd = (cmsGDB*) hGBD; 583 584 _cmsAssert(hGBD != NULL); 585 586 // Interpolate black 587 for (alpha = 0; alpha < SECTORS; alpha++) { 588 589 if (!InterpolateMissingSector(gbd, alpha, 0)) return FALSE; 590 } 591 592 // Interpolate white 593 for (alpha = 0; alpha < SECTORS; alpha++) { 594 595 if (!InterpolateMissingSector(gbd, alpha, SECTORS-1)) return FALSE; 596 } 597 598 599 // Interpolate Mid 600 for (theta = 1; theta < SECTORS; theta++) { 601 for (alpha = 0; alpha < SECTORS; alpha++) { 602 603 if (!InterpolateMissingSector(gbd, alpha, theta)) return FALSE; 604 } 605 } 606 607 // Done 608 return TRUE; 609 610 cmsUNUSED_PARAMETER(dwFlags); 611 } 612 613 614 615 616 // -------------------------------------------------------------------------------------------------------- 617 618 // Great for debug, but not suitable for real use 619 620 #if 0 621 cmsBool cmsGBDdumpVRML(cmsHANDLE hGBD, const char* fname) 622 { 623 FILE* fp; 624 int i, j; 625 cmsGDB* gbd = (cmsGDB*) hGBD; 626 cmsGDBPoint* pt; 627 628 fp = fopen (fname, "wt"); 629 if (fp == NULL) 630 return FALSE; 631 632 fprintf (fp, "#VRML V2.0 utf8\n"); 633 634 // set the viewing orientation and distance 635 fprintf (fp, "DEF CamTest Group {\n"); 636 fprintf (fp, "\tchildren [\n"); 637 fprintf (fp, "\t\tDEF Cameras Group {\n"); 638 fprintf (fp, "\t\t\tchildren [\n"); 639 fprintf (fp, "\t\t\t\tDEF DefaultView Viewpoint {\n"); 640 fprintf (fp, "\t\t\t\t\tposition 0 0 340\n"); 641 fprintf (fp, "\t\t\t\t\torientation 0 0 1 0\n"); 642 fprintf (fp, "\t\t\t\t\tdescription \"default view\"\n"); 643 fprintf (fp, "\t\t\t\t}\n"); 644 fprintf (fp, "\t\t\t]\n"); 645 fprintf (fp, "\t\t},\n"); 646 fprintf (fp, "\t]\n"); 647 fprintf (fp, "}\n"); 648 649 // Output the background stuff 650 fprintf (fp, "Background {\n"); 651 fprintf (fp, "\tskyColor [\n"); 652 fprintf (fp, "\t\t.5 .5 .5\n"); 653 fprintf (fp, "\t]\n"); 654 fprintf (fp, "}\n"); 655 656 // Output the shape stuff 657 fprintf (fp, "Transform {\n"); 658 fprintf (fp, "\tscale .3 .3 .3\n"); 659 fprintf (fp, "\tchildren [\n"); 660 661 // Draw the axes as a shape: 662 fprintf (fp, "\t\tShape {\n"); 663 fprintf (fp, "\t\t\tappearance Appearance {\n"); 664 fprintf (fp, "\t\t\t\tmaterial Material {\n"); 665 fprintf (fp, "\t\t\t\t\tdiffuseColor 0 0.8 0\n"); 666 fprintf (fp, "\t\t\t\t\temissiveColor 1.0 1.0 1.0\n"); 667 fprintf (fp, "\t\t\t\t\tshininess 0.8\n"); 668 fprintf (fp, "\t\t\t\t}\n"); 669 fprintf (fp, "\t\t\t}\n"); 670 fprintf (fp, "\t\t\tgeometry IndexedLineSet {\n"); 671 fprintf (fp, "\t\t\t\tcoord Coordinate {\n"); 672 fprintf (fp, "\t\t\t\t\tpoint [\n"); 673 fprintf (fp, "\t\t\t\t\t0.0 0.0 0.0,\n"); 674 fprintf (fp, "\t\t\t\t\t%f 0.0 0.0,\n", 255.0); 675 fprintf (fp, "\t\t\t\t\t0.0 %f 0.0,\n", 255.0); 676 fprintf (fp, "\t\t\t\t\t0.0 0.0 %f]\n", 255.0); 677 fprintf (fp, "\t\t\t\t}\n"); 678 fprintf (fp, "\t\t\t\tcoordIndex [\n"); 679 fprintf (fp, "\t\t\t\t\t0, 1, -1\n"); 680 fprintf (fp, "\t\t\t\t\t0, 2, -1\n"); 681 fprintf (fp, "\t\t\t\t\t0, 3, -1]\n"); 682 fprintf (fp, "\t\t\t}\n"); 683 fprintf (fp, "\t\t}\n"); 684 685 686 fprintf (fp, "\t\tShape {\n"); 687 fprintf (fp, "\t\t\tappearance Appearance {\n"); 688 fprintf (fp, "\t\t\t\tmaterial Material {\n"); 689 fprintf (fp, "\t\t\t\t\tdiffuseColor 0 0.8 0\n"); 690 fprintf (fp, "\t\t\t\t\temissiveColor 1 1 1\n"); 691 fprintf (fp, "\t\t\t\t\tshininess 0.8\n"); 692 fprintf (fp, "\t\t\t\t}\n"); 693 fprintf (fp, "\t\t\t}\n"); 694 fprintf (fp, "\t\t\tgeometry PointSet {\n"); 695 696 // fill in the points here 697 fprintf (fp, "\t\t\t\tcoord Coordinate {\n"); 698 fprintf (fp, "\t\t\t\t\tpoint [\n"); 699 700 // We need to transverse all gamut hull. 701 for (i=0; i < SECTORS; i++) 702 for (j=0; j < SECTORS; j++) { 703 704 cmsVEC3 v; 705 706 pt = &gbd ->Gamut[i][j]; 707 ToCartesian(&v, &pt ->p); 708 709 fprintf (fp, "\t\t\t\t\t%g %g %g", v.n[0]+50, v.n[1], v.n[2]); 710 711 if ((j == SECTORS - 1) && (i == SECTORS - 1)) 712 fprintf (fp, "]\n"); 713 else 714 fprintf (fp, ",\n"); 715 716 } 717 718 fprintf (fp, "\t\t\t\t}\n"); 719 720 721 722 // fill in the face colors 723 fprintf (fp, "\t\t\t\tcolor Color {\n"); 724 fprintf (fp, "\t\t\t\t\tcolor [\n"); 725 726 for (i=0; i < SECTORS; i++) 727 for (j=0; j < SECTORS; j++) { 728 729 cmsVEC3 v; 730 731 pt = &gbd ->Gamut[i][j]; 732 733 734 ToCartesian(&v, &pt ->p); 735 736 737 if (pt ->Type == GP_EMPTY) 738 fprintf (fp, "\t\t\t\t\t%g %g %g", 0.0, 0.0, 0.0); 739 else 740 if (pt ->Type == GP_MODELED) 741 fprintf (fp, "\t\t\t\t\t%g %g %g", 1.0, .5, .5); 742 else { 743 fprintf (fp, "\t\t\t\t\t%g %g %g", 1.0, 1.0, 1.0); 744 745 } 746 747 if ((j == SECTORS - 1) && (i == SECTORS - 1)) 748 fprintf (fp, "]\n"); 749 else 750 fprintf (fp, ",\n"); 751 } 752 fprintf (fp, "\t\t\t}\n"); 753 754 755 fprintf (fp, "\t\t\t}\n"); 756 fprintf (fp, "\t\t}\n"); 757 fprintf (fp, "\t]\n"); 758 fprintf (fp, "}\n"); 759 760 fclose (fp); 761 762 return TRUE; 763 } 764 #endif 765