Subversion Repositories Scribus

Rev

Blame | Last modification | View Log | RSS feed

//
//  Little cms - profiler construction set
//  Copyright (C) 1998-2001 Marti Maria
//
// THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
// EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
// WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
//
// IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
// INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
// OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
// WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
// LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
// OF THIS SOFTWARE.
//
// This file is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// As a special exception to the GNU General Public License, if you
// distribute this file as part of a program that contains a
// configuration script generated by Autoconf, you may include it under
// the same distribution terms that you use for the rest of that program.
//
// Version 1.08a


#include "lcmsprf.h"


// ----------------------------------------------------------------- Patch collections

BOOL        cdecl cmsxPCollLoadFromSheet(LPMEASUREMENT m, LCMSHANDLE hSheet);

BOOL        cdecl cmsxPCollBuildMeasurement(LPMEASUREMENT m,
                                            const char *ReferenceSheet,
                                            const char *MeasurementSheet,
                                            DWORD dwNeededSamplesType);

LPPATCH     cdecl cmsxPCollGetPatch(LPMEASUREMENT m, int n);

LPPATCH     cdecl cmsxPCollGetPatchByName(LPMEASUREMENT m, const char* Name, int* lpPos);
LPPATCH     cdecl cmsxPCollGetPatchByPos(LPMEASUREMENT m, int row, int col);
LPPATCH     cdecl cmsxPCollAddPatchRGB(LPMEASUREMENT m, const char *Name,
                                      double r, double g, double b,
                                      LPcmsCIEXYZ XYZ, LPcmsCIELab Lab);

// Sets of patches

SETOFPATCHES cdecl cmsxPCollBuildSet(LPMEASUREMENT m, BOOL lDefault);
int          cdecl cmsxPCollCountSet(LPMEASUREMENT m, SETOFPATCHES Set);
BOOL         cdecl cmsxPCollValidatePatches(LPMEASUREMENT m, DWORD dwFlags);


// Collect "need" patches of the specific kind, return the number of collected (that
// could be less if set of patches is exhausted)


void         cdecl cmsxPCollPatchesGS(LPMEASUREMENT m, SETOFPATCHES Result);

int          cdecl cmsxPCollPatchesNearRGB(LPMEASUREMENT m, SETOFPATCHES Valids,
                                            double r, double g, double b, int need, SETOFPATCHES Result);

int          cdecl cmsxPCollPatchesNearNeutral(LPMEASUREMENT m, SETOFPATCHES Valids,
                                            int need, SETOFPATCHES Result);

int          cdecl cmsxPCollPatchesNearPrimary(LPMEASUREMENT m, SETOFPATCHES Valids,
                                           int nChannel, int need, SETOFPATCHES Result);

int          cdecl cmsxPCollPatchesInLabCube(LPMEASUREMENT m, SETOFPATCHES Valids,
                                           double Lmin, double LMax, double a, double b, SETOFPATCHES Result);

int          cdecl cmsxPCollPatchesInGamutLUT(LPMEASUREMENT m, SETOFPATCHES Valids,
                                                                                          LPLUT Gamut, SETOFPATCHES Result);

LPPATCH          cdecl cmsxPCollFindWhite(LPMEASUREMENT m, SETOFPATCHES Valids, double* Distance);
LPPATCH          cdecl cmsxPCollFindBlack(LPMEASUREMENT m, SETOFPATCHES Valids, double* Distance);
LPPATCH      cdecl cmsxPCollFindPrimary(LPMEASUREMENT m, SETOFPATCHES Valids, int Channel, double* Distance);


// ------------------------------------------------------------- Implementation

#define IS(x)   EqualsTo(c, x)

// A wrapper on stricmp()

static
BOOL EqualsTo(const char* a, const char *b)
{
        return (stricmp(a, b) == 0);
}


// Does return a bitwise mask holding the measurements contained in a Sheet

static
DWORD MaskOfDataSet(LCMSHANDLE hSheet)
{
        char** Names;
        int i, n;
        DWORD dwMask = 0;

        n = cmsxIT8EnumDataFormat(hSheet, &Names);

        for (i=0; i < n; i++) {

                char *c = Names[i];

                if (IS("RGB_R") || IS("RGB_G") || IS("RGB_B"))
                                dwMask |= PATCH_HAS_RGB;
                else
                if (IS("XYZ_X") || IS("XYZ_Y") || IS("XYZ_Z"))
                                dwMask |= PATCH_HAS_XYZ;
                else
                if (IS("LAB_L") || IS("LAB_A") ||IS("LAB_B"))
                                dwMask |= PATCH_HAS_Lab;
                else
                if (IS("STDEV_DE"))
                    dwMask |= PATCH_HAS_STD_DE;
        }

        return dwMask;
}


// Addition of a patch programatically

LPPATCH cmsxPCollAddPatchRGB(LPMEASUREMENT m, const char *Name,
                        double r, double g, double b,
                        LPcmsCIEXYZ XYZ, LPcmsCIELab Lab)
{
        LPPATCH p;

        p = m->Patches + m->nPatches++;

        strcpy(p -> Name, Name);

        p -> Colorant.RGB[0] = r;
        p -> Colorant.RGB[1] = g;
        p -> Colorant.RGB[2] = b;
        p -> dwFlags = PATCH_HAS_RGB;

        if (XYZ) {

                p -> XYZ = *XYZ;
                p -> dwFlags |= PATCH_HAS_XYZ;
        }

        if (Lab) {
                p -> Lab = *Lab;
                p -> dwFlags |= PATCH_HAS_Lab;
        }

       
        return p;
}

// Some vendors does store colorant data in a non-standard way,
// i.e, from 0.0..1.0 or from 0.0..100.0 This routine tries to
// detect such situations

static
void NormalizeColorant(LPMEASUREMENT m)
{
        int i, j;
        double MaxColorant=0;
        double Normalize;

       
        for (i=0; i < m -> nPatches; i++) {


           LPPATCH p = m -> Patches + i;
                       
                   for (j=0; j < MAXCHANNELS; j++) {
                                                if (p ->Colorant.Hexa[j] > MaxColorant)
                                                                MaxColorant = p ->Colorant.Hexa[j];
                   }
        }

        // Ok, some heuristics

        if (MaxColorant < 2)
                Normalize = 255.0;  // goes 0..1
        else
        if (MaxColorant < 102)
                Normalize = 2.55;  // goes 0..100
        else
        if (MaxColorant > 300)
                Normalize = (255.0 / 65535.0);  // Goes 0..65535.0
        else
                return;         // Is ok


        // Rearrange patches
        for (i=0; i < m -> nPatches; i++) {


           LPPATCH p = m -> Patches + i;
                   for (j=0; j < MAXCHANNELS; j++)
                                p ->Colorant.Hexa[j] *= Normalize;
        }

}


// Load a collection from a Sheet

BOOL cmsxPCollLoadFromSheet(LPMEASUREMENT m, LCMSHANDLE hSheet)
{
    int i;  
    DWORD dwMask;


    if (m -> nPatches == 0) {

            m -> nPatches = (int) cmsxIT8GetPropertyDbl(hSheet, "NUMBER_OF_SETS");                             
            m -> Patches  = calloc(m -> nPatches, sizeof(PATCH));

            if (m -> Patches == NULL) {
                cmsxIT8Free(hSheet);
                return FALSE;
            }

            for (i=0; i < m -> nPatches; i++) {

                    LPPATCH p = m -> Patches + i;
                                        p -> dwFlags = 0;
                                        cmsxIT8GetPatchName(hSheet, i, p ->Name);                              
                                 
            }

    }


    // Build mask according to data format

    dwMask = MaskOfDataSet(hSheet);


    // Read items. Set flags accordly.
    for (i = 0; i < m->nPatches; i++) {

        LPPATCH Patch = m -> Patches + i;
                             
        // Fill in data according to mask

        if (dwMask & PATCH_HAS_Lab) {

        if (cmsxIT8GetDataSetDbl(hSheet, Patch->Name, "LAB_L", &Patch -> Lab.L) &&
            cmsxIT8GetDataSetDbl(hSheet, Patch->Name, "LAB_A", &Patch -> Lab.a) &&
            cmsxIT8GetDataSetDbl(hSheet, Patch->Name, "LAB_B", &Patch -> Lab.b))

                                   Patch -> dwFlags |= PATCH_HAS_Lab;
        }

        if (dwMask & PATCH_HAS_XYZ) {

        if (cmsxIT8GetDataSetDbl(hSheet, Patch->Name, "XYZ_X", &Patch -> XYZ.X) &&
            cmsxIT8GetDataSetDbl(hSheet, Patch->Name, "XYZ_Y", &Patch -> XYZ.Y) &&
            cmsxIT8GetDataSetDbl(hSheet, Patch->Name, "XYZ_Z", &Patch -> XYZ.Z))

                                  Patch -> dwFlags |= PATCH_HAS_XYZ;

        }

        if (dwMask & PATCH_HAS_RGB) {

        if (cmsxIT8GetDataSetDbl(hSheet, Patch->Name, "RGB_R", &Patch -> Colorant.RGB[0]) &&
            cmsxIT8GetDataSetDbl(hSheet, Patch->Name, "RGB_G", &Patch -> Colorant.RGB[1]) &&
            cmsxIT8GetDataSetDbl(hSheet, Patch->Name, "RGB_B", &Patch -> Colorant.RGB[2]))

                                Patch -> dwFlags |= PATCH_HAS_RGB;
        }

        if (dwMask & PATCH_HAS_STD_DE) {
           
            if (cmsxIT8GetDataSetDbl(hSheet, Patch->Name, "STDEV_DE", &Patch -> dEStd))

                                Patch -> dwFlags |= PATCH_HAS_STD_DE;

        }
       
    }

        NormalizeColorant(m);
    return TRUE;
}


// Does save parameters to a empty sheet

BOOL cmsxPCollSaveToSheet(LPMEASUREMENT m, LCMSHANDLE it8)
{                      
        int nNumberOfSets   = cmsxPCollCountSet(m, m->Allowed);
        int nNumberOfFields = 0;
        DWORD dwMask = 0;
        int i;

        // Find mask of fields
        for (i=0; i < m ->nPatches; i++) {
                if (m ->Allowed[i]) {

                                LPPATCH p = m ->Patches + i;
                                dwMask |= p ->dwFlags;
                }
        }

        nNumberOfFields = 1; // SampleID

        if (dwMask & PATCH_HAS_RGB)
                        nNumberOfFields += 3;

        if (dwMask & PATCH_HAS_XYZ)
                        nNumberOfFields += 3;

        if (dwMask & PATCH_HAS_Lab)
                        nNumberOfFields += 3;
       

    cmsxIT8SetPropertyDbl(it8, "NUMBER_OF_SETS", nNumberOfSets);
    cmsxIT8SetPropertyDbl(it8, "NUMBER_OF_FIELDS", nNumberOfFields);

        nNumberOfFields = 0;
    cmsxIT8SetDataFormat(it8, nNumberOfFields++, "SAMPLE_ID");

        if (dwMask & PATCH_HAS_RGB) {

                cmsxIT8SetDataFormat(it8, nNumberOfFields++, "RGB_R");
                cmsxIT8SetDataFormat(it8, nNumberOfFields++, "RGB_G");
                cmsxIT8SetDataFormat(it8, nNumberOfFields++, "RGB_B");
        }
   
        if (dwMask & PATCH_HAS_XYZ) {
               
                cmsxIT8SetDataFormat(it8, nNumberOfFields++, "XYZ_X");
                cmsxIT8SetDataFormat(it8, nNumberOfFields++, "XYZ_Y");
                cmsxIT8SetDataFormat(it8, nNumberOfFields++, "XYZ_Z");
               
        }


        if (dwMask & PATCH_HAS_XYZ) {
               
                cmsxIT8SetDataFormat(it8, nNumberOfFields++, "LAB_L");
                cmsxIT8SetDataFormat(it8, nNumberOfFields++, "LAB_A");
                cmsxIT8SetDataFormat(it8, nNumberOfFields++, "LAB_B");
               
        }

        for (i=0; i < m ->nPatches; i++) {
                if (m ->Allowed[i]) {

                        LPPATCH Patch = m ->Patches + i;

                        cmsxIT8SetDataSet(it8, Patch->Name, "SAMPLE_ID", Patch->Name);
                       
                        if (dwMask & PATCH_HAS_RGB) {
                                cmsxIT8SetDataSetDbl(it8, Patch->Name, "RGB_R", Patch ->Colorant.RGB[0]);
                                cmsxIT8SetDataSetDbl(it8, Patch->Name, "RGB_G", Patch ->Colorant.RGB[1]);
                                cmsxIT8SetDataSetDbl(it8, Patch->Name, "RGB_B", Patch ->Colorant.RGB[2]);
                        }

                        if (dwMask & PATCH_HAS_XYZ) {
                                cmsxIT8SetDataSetDbl(it8, Patch->Name, "XYZ_X", Patch ->XYZ.X);
                                cmsxIT8SetDataSetDbl(it8, Patch->Name, "XYZ_Y", Patch ->XYZ.Y);
                                cmsxIT8SetDataSetDbl(it8, Patch->Name, "XYZ_Z", Patch ->XYZ.Z);                        
                        }      

                        if (dwMask & PATCH_HAS_Lab) {                          
                                cmsxIT8SetDataSetDbl(it8, Patch->Name, "LAB_L", Patch ->Lab.L);
                                cmsxIT8SetDataSetDbl(it8, Patch->Name, "LAB_A", Patch ->Lab.a);
                                cmsxIT8SetDataSetDbl(it8, Patch->Name, "LAB_B", Patch ->Lab.b);
                               
                        }      
                }
        }

        return TRUE;
}

static
void FixLabOnly(LPMEASUREMENT m)
{
        int i;

        for (i=0; i < m ->nPatches; i++) {
       
                        LPPATCH p = m ->Patches + i;
                        if ((p ->dwFlags & PATCH_HAS_Lab) &&
                                !(p ->dwFlags & PATCH_HAS_XYZ))
                        {
                                cmsLab2XYZ(cmsD50_XYZ(), &p->XYZ, &p ->Lab);

                                p ->XYZ.X *= 100.;
                                p ->XYZ.Y *= 100.;
                                p ->XYZ.Z *= 100.;

                                p ->dwFlags |= PATCH_HAS_XYZ;
                        }
       
        }

}


// Higher level function. Does merge reference and measurement sheet into
// a MEASUREMENT struct. Data to keep is described in dwNeededSamplesType
// mask as follows:
//  
// PATCH_HAS_Lab         0x00000001
// PATCH_HAS_XYZ         0x00000002
// PATCH_HAS_RGB         0x00000004
// PATCH_HAS_CMY         0x00000008
// PATCH_HAS_CMYK        0x00000010
// PATCH_HAS_HEXACRM     0x00000020
// PATCH_HAS_STD_Lab     0x00010000
// PATCH_HAS_STD_XYZ     0x00020000
// PATCH_HAS_STD_RGB     0x00040000
// PATCH_HAS_STD_CMY     0x00080000
// PATCH_HAS_STD_CMYK    0x00100000
// PATCH_HAS_STD_HEXACRM 0x00100000
// PATCH_HAS_MEAN_DE     0x01000000
// PATCH_HAS_STD_DE      0x02000000
// PATCH_HAS_CHISQ       0x04000000
//
// See lprof.h for further info


BOOL cmsxPCollBuildMeasurement(LPMEASUREMENT m,
                               const char *ReferenceSheet,
                               const char *MeasurementSheet,
                               DWORD dwNeededSamplesType)
{
    LCMSHANDLE hSheet;
        BOOL rc = TRUE;

    ZeroMemory(m, sizeof(MEASUREMENT));


    if (ReferenceSheet != NULL && *ReferenceSheet) {

                hSheet = cmsxIT8LoadFromFile(ReferenceSheet);      
                if (hSheet == NULL) return FALSE;              
               
                                rc = cmsxPCollLoadFromSheet(m,  hSheet);
                cmsxIT8Free(hSheet);
    }

    if (!rc) return FALSE;

    if (MeasurementSheet != NULL && *MeasurementSheet) {

                hSheet = cmsxIT8LoadFromFile(MeasurementSheet);
                if (hSheet == NULL) return FALSE;

                rc = cmsxPCollLoadFromSheet(m,  hSheet);
                cmsxIT8Free(hSheet);
    }

    if (!rc) return FALSE;


        // Fix up -- If only Lab is present, then compute
        // XYZ based on D50

        FixLabOnly(m);

    cmsxPCollValidatePatches(m, dwNeededSamplesType);
    return TRUE;
}



void cmsxPCollFreeMeasurements(LPMEASUREMENT m)
{
    if (m->Patches)
        free(m->Patches);

    m->Patches  = NULL;
    m->nPatches = 0;
   
    if (m -> Allowed)
        free(m -> Allowed);

}

// Retrieval functions

LPPATCH cmsxPCollGetPatchByName(LPMEASUREMENT m, const char* name, int* lpPos)
{
    int i;
    for (i=0; i < m->nPatches; i++)
    {
            if (m -> Allowed)
                if (!m -> Allowed[i])
                        continue;

                if (EqualsTo(m->Patches[i].Name, name)) {
                        if (lpPos) *lpPos = i;
                        return m->Patches + i;
                }
    }

    return NULL;
}




// -------------------------------------------------------------------- Sets


SETOFPATCHES cmsxPCollBuildSet(LPMEASUREMENT m, BOOL lDefault)
{
    SETOFPATCHES Full = (SETOFPATCHES) malloc(m -> nPatches * sizeof(BOOL));
    int i;

    for (i=0; i < m -> nPatches; i++)
        Full[i] = lDefault;

    return Full;
}

int cmsxPCollCountSet(LPMEASUREMENT m, SETOFPATCHES Set)
{
    int i, Count = 0;

    for (i = 0; i < m -> nPatches; i++) {

        if (Set[i])
            Count++;
        }

    return Count;
}


// Validate patches

BOOL cmsxPCollValidatePatches(LPMEASUREMENT m, DWORD dwFlags)
{
        int i, n;

                if (m->Allowed)
                                free(m->Allowed);

        m -> Allowed = cmsxPCollBuildSet(m, TRUE);
       
        // Check for flags
        for (i=n=0; i < m -> nPatches; i++) {

                LPPATCH p = m -> Patches + i;              
                m -> Allowed[i] = ((p -> dwFlags & dwFlags) == dwFlags);
               
        }

        return TRUE;
}


// Several filters


// This filter does validate patches placed on 'radius' distance of a
// device-color space. Currently only RGB is supported

static
void PatchesByRGB(LPMEASUREMENT m, SETOFPATCHES Valids,
                                  double R, double G, double B, double radius, SETOFPATCHES Result)
{
    int  i;
    double ra, rmax = sqrt(radius / 255.);
    double dR, dG, dB;
    LPPATCH p;

    for (i=0; i < m->nPatches; i++) {

       
       if (Valids[i]) {

        p = m->Patches + i;

        dR = fabs(R - p -> Colorant.RGB[0]) / 255.;
        dG = fabs(G - p -> Colorant.RGB[1]) / 255.;
        dB = fabs(B - p -> Colorant.RGB[2]) / 255.;

        ra = sqrt(dR*dR + dG*dG + dB*dB);

        if (ra <= rmax)
            Result[i] = TRUE;      
        else
            Result[i] = FALSE;

       }
    }

}


// This filter does validate patches placed at dEmax radius
// in the device-independent side.

static
void PatchesByLab(LPMEASUREMENT m, SETOFPATCHES Valids,
                  double L, double a, double b, double dEmax, SETOFPATCHES Result)
{
    int  i;
    double dE, dEMaxSQR = sqrt(dEmax);
    double dL, da, db;
    LPPATCH p;


    for (i=0; i < m->nPatches; i++) {

       
       if (Valids[i]) {

        p = m->Patches + i;

        dL = fabs(L - p -> Lab.L);
        da = fabs(a - p -> Lab.a);
        db = fabs(b - p -> Lab.b);

        dE = sqrt(dL*dL + da*da + db*db);
       
        if (dE <= dEMaxSQR)
            Result[i] = TRUE;
        else
            Result[i] = FALSE;
       }
    }
}


// Restrict Lab in a cube of variable sides. Quick and dirty out-of-gamut
// stripper used in estimations.

static
void  PatchesInLabCube(LPMEASUREMENT m, SETOFPATCHES Valids,
                            double Lmin, double Lmax, double da, double db, SETOFPATCHES Result)
{
        int i;
 
        for (i=0; i < m -> nPatches; i++) {

   
                if (Valids[i]) {

                        LPPATCH p = m -> Patches + i;

                        if ((p->Lab.L >= Lmin && p->Lab.L <= Lmax) &&
                            (fabs(p -> Lab.a) < da) &&
                            (fabs(p -> Lab.b) < db))

                             Result[i] = TRUE;
                         else  
                             Result[i] = FALSE;
                }
        }

}

// Restrict to low colorfullness

static
void PatchesOfLowC(LPMEASUREMENT m, SETOFPATCHES Valids,
                            double Cmax, SETOFPATCHES Result)
{
        int i;
                cmsCIELCh LCh;
 
        for (i=0; i < m -> nPatches; i++) {

   
                if (Valids[i]) {

                        LPPATCH p = m -> Patches + i;

                                                cmsLab2LCh(&LCh, &p->Lab);


                        if (LCh.C < Cmax)
                             Result[i] = TRUE;
                         else  
                             Result[i] = FALSE;
                }
        }

}



// Primary can be -1 for specifying device gray. Does return patches
// on device-space Colorants. dEMax is the maximum allowed ratio

static
void PatchesPrimary(LPMEASUREMENT m, SETOFPATCHES Valids,
                           int nColorant, double dEMax, SETOFPATCHES Result)
{
        int i, j;
        double n, dE;

        for (i=0; i < m -> nPatches; i++) {

   
                if (Valids[i]) {

                        LPPATCH p = m -> Patches + i;


                        if (nColorant < 0) // device-grey?
                        {
                                                            // cross.
                                                       
                                double drg = fabs(p -> Colorant.RGB[0] - p -> Colorant.RGB[1]) / 255.;
                                double drb = fabs(p -> Colorant.RGB[0] - p -> Colorant.RGB[2]) / 255.;
                                double dbg = fabs(p -> Colorant.RGB[1] - p -> Colorant.RGB[2]) / 255.;

                                dE = (drg*drg + drb*drb + dbg*dbg);


                        }
                        else {
                                dE = 0.;
                                for (j=0; j < 3; j++) {

                                        if (j != nColorant) {

                                                n = p -> Colorant.RGB[j] / 255.;
                                                dE += (n * n);


                                        }
                                }
                        }



                        if (sqrt(dE) < dEMax)
                             Result[i] = TRUE;
                         else  
                             Result[i] = FALSE;
                }
        }

}


// The high level extractors -----------------------------------------------------

int cmsxPCollPatchesNearRGB(LPMEASUREMENT m, SETOFPATCHES Valids,
                                                        double r, double g, double b,
                                                        int need, SETOFPATCHES Result)
{
    double radius;
    int nCollected;

    // Collect points inside of a sphere or radius 'radius' by RGB

    radius = 1;
    do {
        PatchesByRGB(m, Valids, r, g, b, radius, Result);

        nCollected = cmsxPCollCountSet(m, Result);
        if (nCollected <= need) {

            radius += 1.0;
        }

    } while (nCollected <= need && radius < 256.);

    return nCollected; // Can be less than needed!
}


int cmsxPCollPatchesNearNeutral(LPMEASUREMENT m, SETOFPATCHES Valids,
                                                                int need, SETOFPATCHES Result)
{
        int nGrays;
        double Cmax;

        Cmax = 1.;
        do {


                                PatchesOfLowC(m, Valids, Cmax, Result);                            
               
                nGrays = cmsxPCollCountSet(m, Result);
                if (nGrays <= need) {

                        Cmax += .2;
                }

        } while (nGrays <= need && Cmax < 10.);

        return nGrays;
}


int cmsxPCollPatchesInLabCube(LPMEASUREMENT m, SETOFPATCHES Valids,
                              double Lmin, double Lmax, double a, double b,
                                                          SETOFPATCHES Result)


{        
        PatchesInLabCube(m, Valids, Lmin, Lmax, a, b, Result);
        return cmsxPCollCountSet(m, Result);                
}




int cmsxPCollPatchesNearPrimary(LPMEASUREMENT m,
                          SETOFPATCHES Valids,
                          int nChannel,
                          int need,
                          SETOFPATCHES Result)
{
    double radius;
    int nCollected;

    // Collect points inside of a sphere or radius 'radius' by RGB

    radius = 0.05;
    do {
        PatchesPrimary(m, Valids, nChannel, radius, Result);

        nCollected = cmsxPCollCountSet(m, Result);
        if (nCollected <= need) {

            radius += 0.01;
        }

    } while (nCollected <= need && radius < 256.);

    return nCollected;

}


static
void AddOneGray(LPMEASUREMENT m, int n, SETOFPATCHES Grays)
{
    LPPATCH p;
    char Buffer[cmsxIT8_GRAYCOLS];
    int pos;

    if (n == 0) strcpy(Buffer, "DMIN");
    else
        if (n == cmsxIT8_GRAYCOLS - 1) strcpy(Buffer, "DMAX");
        else
            sprintf(Buffer, "GS%d", n);

    p = cmsxPCollGetPatchByName(m, Buffer, &pos);

    if (p)
        Grays[pos] = TRUE;
}



void cmsxPCollPatchesGS(LPMEASUREMENT m, SETOFPATCHES Result)
{

    int i;

    for (i=0; i < cmsxIT8_GRAYCOLS; i++)
        AddOneGray(m, i, Result);
}



// Refresh RGB of all patches after prelinearization

void cmsxPCollLinearizePatches(LPMEASUREMENT m, SETOFPATCHES Valids, LPGAMMATABLE Gamma[3])
{
    int i;

    for (i=0; i < m -> nPatches; i++) {

       if (Valids[i]) {

        LPPATCH p = m -> Patches + i;

        cmsxApplyLinearizationTable(p -> Colorant.RGB, Gamma, p -> Colorant.RGB);
        }
    }

}


int cmsxPCollPatchesInGamutLUT(LPMEASUREMENT m, SETOFPATCHES Valids,
                                                                                LPLUT Gamut, SETOFPATCHES Result)
{
        int i;
        int nCollected = 0;

    for (i=0; i < m -> nPatches; i++) {

       if (Valids[i]) {

        LPPATCH p = m -> Patches + i;
                WORD EncodedLab[3];
                WORD dE;

                cmsFloat2LabEncoded(EncodedLab, &p->Lab);
                cmsEvalLUT(Gamut, EncodedLab, &dE);
                Result[i] = (dE < 2) ? TRUE : FALSE;                           
                if (Result[i]) nCollected++;
        }
    }

        return nCollected;
}

LPPATCH cmsxPCollFindWhite(LPMEASUREMENT m, SETOFPATCHES Valids, double* TheDistance)
{
        int i;
        LPPATCH Candidate = NULL;
        double Distance, CandidateDistance = 255;
        double dR, dG, dB;

        Candidate = cmsxPCollGetPatchByName(m, "DMIN", NULL);
        if (Candidate) {

                if (TheDistance) *TheDistance = 0.0;
                return Candidate;
        }

    for (i=0; i < m -> nPatches; i++) {

       if (Valids[i]) {

        LPPATCH p = m -> Patches + i;

                dR = fabs(255.0 - p -> Colorant.RGB[0]) / 255.0;
        dG = fabs(255.0 - p -> Colorant.RGB[1]) / 255.0;
        dB = fabs(255.0 - p -> Colorant.RGB[2]) / 255.0;

        Distance = sqrt(dR*dR + dG*dG + dB*dB);

                if (Distance < CandidateDistance) {
                        Candidate = p;
                        CandidateDistance = Distance;
                }
           }                   
    }

        if (TheDistance)
                *TheDistance = floor(CandidateDistance * 255.0 + .5);

        return Candidate;
}

LPPATCH cmsxPCollFindBlack(LPMEASUREMENT m, SETOFPATCHES Valids, double* TheDistance)
{
        int i;
        LPPATCH Candidate = NULL;
        double Distance, CandidateDistance = 255;
        double dR, dG, dB;


        Candidate = cmsxPCollGetPatchByName(m, "DMAX", NULL);
        if (Candidate) {

                if (TheDistance) *TheDistance = 0.0;
                return Candidate;
        }

    for (i=0; i < m -> nPatches; i++) {

       if (Valids[i]) {

        LPPATCH p = m -> Patches + i;

                dR = (p -> Colorant.RGB[0]) / 255.0;
        dG = (p -> Colorant.RGB[1]) / 255.0;
        dB = (p -> Colorant.RGB[2]) / 255.0;

        Distance = sqrt(dR*dR + dG*dG + dB*dB);

                if (Distance < CandidateDistance) {
                        Candidate = p;
                        CandidateDistance = Distance;
                }
           }                   
    }

        if (TheDistance)
                *TheDistance = floor(CandidateDistance * 255.0 + .5);

        return Candidate;
}


LPPATCH cmsxPCollFindPrimary(LPMEASUREMENT m, SETOFPATCHES Valids, int Channel, double* TheDistance)
{
        int i;
        LPPATCH Candidate = NULL;
        double Distance, CandidateDistance = 255;
        double dR, dG, dB;
        const struct {
                                 double r, g, b;

                                } RGBPrimaries[3] = {          
                                        { 255.0, 0, 0},
                                        { 0, 255.0, 0},
                                        { 0, 0, 255  }};


    for (i=0; i < m -> nPatches; i++) {

       if (Valids[i]) {

        LPPATCH p = m -> Patches + i;

                dR = fabs(RGBPrimaries[Channel].r - p -> Colorant.RGB[0]) / 255.0;
        dG = fabs(RGBPrimaries[Channel].g - p -> Colorant.RGB[1]) / 255.0;
        dB = fabs(RGBPrimaries[Channel].b - p -> Colorant.RGB[2]) / 255.0;

        Distance = sqrt(dR*dR + dG*dG + dB*dB);

                if (Distance < CandidateDistance) {
                        Candidate = p;
                        CandidateDistance = Distance;
                }
           }                   
    }

        if (TheDistance)
                *TheDistance = floor(CandidateDistance * 255.0 + .5);

        return Candidate;
}