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-2016 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
71typedef struct {
72
73    cmsFloat64Number r;
74    cmsFloat64Number alpha;
75    cmsFloat64Number theta;
76
77} cmsSpherical;
78
79typedef  enum {
80        GP_EMPTY,
81        GP_SPECIFIED,
82        GP_MODELED
83
84    } GDBPointType;
85
86
87typedef struct {
88
89    GDBPointType Type;
90    cmsSpherical p;         // Keep also alpha & theta of maximum
91
92} cmsGDBPoint;
93
94
95typedef 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
105typedef 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
115typedef 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
129static
130cmsFloat64Number _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
147static
148void 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
170static
171void 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
196static
197void 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
210static
211void 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
222static
223void 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
245static
246cmsBool 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
331cmsHANDLE  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
342void 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
351static
352cmsGDBPoint* 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
387cmsBool 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
419cmsBool 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
456static
457const 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
468static
469int 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
502static
503cmsBool 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.
579cmsBool 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
621cmsBool 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
766